Repository: argonlefou/DemulShooter Branch: master Commit: 81109c14d226 Files: 622 Total size: 4.6 MB Directory structure: gitextract_lulljexd/ ├── .editorconfig ├── .gitignore ├── DemulShooter/ │ ├── DemulShooter.csproj │ ├── DemulShooterWindow.cs │ ├── Ds.settings │ ├── Game_WndSpray.cs │ ├── Games/ │ │ ├── Game.cs │ │ ├── Game_ArcadePcWaterSprite2.cs │ │ ├── Game_ArcadepcFireHero.cs │ │ ├── Game_ArcadepcGhostBusters.cs │ │ ├── Game_ArcadepcHsfr.cs │ │ ├── Game_ArcadepcMechaTornado.cs │ │ ├── Game_ArcadepcPvzLastStand.cs │ │ ├── Game_ArcadepcRobinHood.cs │ │ ├── Game_ArcadepcTopGun.cs │ │ ├── Game_ArcadepcWaterWar2.cs │ │ ├── Game_ArcadepcWws.cs │ │ ├── Game_CxbxGsquad.cs │ │ ├── Game_CxbxHod3.cs │ │ ├── Game_CxbxVcop3.cs │ │ ├── Game_CxbxVcop3_Old.cs │ │ ├── Game_Demul.cs │ │ ├── Game_DemulAtomiswave.cs │ │ ├── Game_DemulHikaru.cs │ │ ├── Game_DemulJvs.cs │ │ ├── Game_DemulManicpnc.cs │ │ ├── Game_DemulNaomi.cs │ │ ├── Game_Dolphin4.cs │ │ ├── Game_Dolphin5.cs │ │ ├── Game_Es4PointBlankX.cs │ │ ├── Game_GameWaxAkuma.cs │ │ ├── Game_GvrAliens.cs │ │ ├── Game_GvrAliensHasp.cs │ │ ├── Game_GvrFarCry.cs │ │ ├── Game_GvrFearLand.cs │ │ ├── Game_KonamiCastlevania.cs │ │ ├── Game_KonamiCoopers9.cs │ │ ├── Game_KonamiGashaaaan2.cs │ │ ├── Game_KonamiLethalEnforcer3.cs │ │ ├── Game_KonamiWartran.cs │ │ ├── Game_Lindbergh2spicy.cs │ │ ├── Game_LindberghGsquadEvo.cs │ │ ├── Game_LindberghHotd4.cs │ │ ├── Game_LindberghHotd4Ex.cs │ │ ├── Game_LindberghHotd4Sp.cs │ │ ├── Game_LindberghHotdEx.cs │ │ ├── Game_LindberghLgj.cs │ │ ├── Game_LindberghLgjsp.cs │ │ ├── Game_LindberghRambo.cs │ │ ├── Game_Model2Bel.cs │ │ ├── Game_Model2Gunblade.cs │ │ ├── Game_Model2Hotd.cs │ │ ├── Game_Model2Rchase2.cs │ │ ├── Game_Model2Vcop.cs │ │ ├── Game_Model2Vcop2.cs │ │ ├── Game_PpmPoliceTrainer2.cs │ │ ├── Game_Re2Transformers2.cs │ │ ├── Game_RtAliensArmageddon.cs │ │ ├── Game_RtJurassicPark.cs │ │ ├── Game_RtTargetTerror.cs │ │ ├── Game_RtTerminatorSalvation.cs │ │ ├── Game_RtWalkingDead.cs │ │ ├── Game_RwGunman.cs │ │ ├── Game_RwLGI.cs │ │ ├── Game_RwLGI3D.cs │ │ ├── Game_RwOpGhost.cs │ │ ├── Game_RwSDR.cs │ │ ├── Game_RwSGG.cs │ │ ├── Game_RwTargetBravo.cs │ │ ├── Game_RwTransformers.cs │ │ ├── Game_TtxBlockKingBallShooter.cs │ │ ├── Game_TtxEadp.cs │ │ ├── Game_TtxGaiaAttack4.cs │ │ ├── Game_TtxGundam.cs │ │ ├── Game_TtxGundam_V2.cs │ │ ├── Game_TtxGungun2.cs │ │ ├── Game_TtxHauntedMuseum.cs │ │ ├── Game_TtxHauntedMuseum2.cs │ │ ├── Game_TtxSha.cs │ │ ├── Game_WndAdCop95.cs │ │ ├── Game_WndAdCopOverseas.cs │ │ ├── Game_WndAlienSafari.cs │ │ ├── Game_WndArtIsDead.cs │ │ ├── Game_WndBE.cs │ │ ├── Game_WndBlueEstate.cs │ │ ├── Game_WndBonbon95.cs │ │ ├── Game_WndBugBusters.cs │ │ ├── Game_WndColtWildWestShootout.cs │ │ ├── Game_WndFriction.cs │ │ ├── Game_WndHeavyFire3Pc.cs │ │ ├── Game_WndHeavyFire4Pc.cs │ │ ├── Game_WndHod2pc.cs │ │ ├── Game_WndHod3pc.cs │ │ ├── Game_WndHotdoPc.cs │ │ ├── Game_WndMadBullets.cs │ │ ├── Game_WndProjectGreenBeat.cs │ │ ├── Game_WndReload.cs │ │ └── Game__Unity.cs │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── Resources/ │ │ ├── x360kb_hfirea1p.ini │ │ └── x360kb_hfirea2p.ini │ └── app.config ├── DemulShooter.sln ├── DemulShooterX64/ │ ├── DemulShooterWindowX64.cs │ ├── DemulShooterX64.csproj │ ├── Games/ │ │ ├── Game.cs │ │ ├── Game_AllsHodSd.cs │ │ ├── Game_ArcadePcMechaDino.cs │ │ ├── Game_ArcadePcMissionImpossible.cs │ │ ├── Game_ArcadepcBullseye.cs │ │ ├── Game_ArcadepcDinoInvasion.cs │ │ ├── Game_ArcadepcDrakon.cs │ │ ├── Game_ArcadepcDrakon_NoPlugin.cs │ │ ├── Game_ArcadepcElevatorActionInvasion.cs │ │ ├── Game_ArcadepcMIB.cs │ │ ├── Game_ArcadepcMarsSortie.cs │ │ ├── Game_ArcadepcNightHunterArcade.cs │ │ ├── Game_ArcadepcOnePoint.cs │ │ ├── Game_ArcadepcRaccoonRampage.cs │ │ ├── Game_ArcadepcRha.cs │ │ ├── Game_ArcadepcSkullOfShadow.cs │ │ ├── Game_ArcadepcTopGun2.cs │ │ ├── Game_ArcadepcTra.cs │ │ ├── Game_ArcadepcWisdomZombies.cs │ │ ├── Game_Es3Lla.cs │ │ ├── Game_Es3Tc5.cs │ │ ├── Game_Flycast.cs │ │ ├── Game_FlycastAtomiswave.cs │ │ ├── Game_FlycastNaomi.cs │ │ ├── Game_FlycastNinjaslt.cs │ │ ├── Game_NuLuigiMansion.cs │ │ ├── Game_NuLuigiMansion_v2.cs │ │ ├── Game_RtNerfArcade.cs │ │ ├── Game_S357DarkEscape.cs │ │ ├── Game_S357DeadStormPirates.cs │ │ ├── Game_S357RazingStorm.cs │ │ ├── Game_S357SailorZombie.cs │ │ ├── Game_WndBhapc.cs │ │ ├── Game_WndBigBuckHunterUltimate.cs │ │ ├── Game_WndBlueEstate.cs │ │ ├── Game_WndDcop.cs │ │ ├── Game_WndHotdremakeArcade.cs │ │ ├── Game_WndOpWolfReturn.cs │ │ └── Game__Unity.cs │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ └── app.config ├── DemulShooter_GUI/ │ ├── DemulShooter_GUI.csproj │ ├── GUI_AnalogCalibration.Designer.cs │ ├── GUI_AnalogCalibration.cs │ ├── GUI_AnalogCalibration.resx │ ├── GUI_Button.Designer.cs │ ├── GUI_Button.cs │ ├── GUI_Button.resx │ ├── GUI_Player.Designer.cs │ ├── GUI_Player.cs │ ├── GUI_Player.resx │ ├── GUI_RawInputHID.Designer.cs │ ├── GUI_RawInputHID.cs │ ├── GUI_RawInputHID.resx │ ├── GUI_RawInputMouse.Designer.cs │ ├── GUI_RawInputMouse.cs │ ├── GUI_RawInputMouse.resx │ ├── Game_Bhapc - Copy.cs │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── Resources/ │ │ ├── Crosshair.cur │ │ ├── WiimoteNew_v4.ini │ │ ├── WiimoteNew_v5.ini │ │ ├── v5-kusa-Nk1ztL1zd9hqYxZvsSzanK-00001G │ │ ├── v5-kusa-RfXcExAKkmrdG4qseqx2b8-00001G │ │ ├── v5-kusa-YS0mZH4vvSfUP58FQH907h-00001G │ │ ├── v5-kusa-aQwdyxfu7HwmKZwJpsrgjQ-00001G │ │ ├── v5-kusa-i7aj8qVvJVSC4AVE3TbxET-00001G │ │ ├── v5-kusa-qkvp7tpoTGacuznXZ31HmQ-00001G │ │ └── v5-kusa-rvYuWdKw9d618McpKm75ir-00001G │ ├── Wnd_DemulShooterGui.Designer.cs │ ├── Wnd_DemulShooterGui.cs │ ├── Wnd_DemulShooterGui.resx │ └── app.config ├── DsCore/ │ ├── Config/ │ │ ├── Configurator.cs │ │ └── PlayerSettings.cs │ ├── DsCore.csproj │ ├── INIFile.cs │ ├── IPC/ │ │ ├── DsTcpData.cs │ │ ├── DsTcp_Client.cs │ │ ├── DsTcp_TcpPacket.cs │ │ ├── MMFH_HotdRemakeArcade.cs │ │ ├── MMF_DataStruct.cs │ │ ├── MappedMemoryFileHelper.cs │ │ └── MappedMemoryFile_Helper_old.cs │ ├── Logger.cs │ ├── MameOutput/ │ │ ├── AsyncGameOutput.cs │ │ ├── BlinkGameOutput.cs │ │ ├── GameOutput.cs │ │ ├── MMT.cs │ │ ├── Network/ │ │ │ ├── Net_OutputClient.cs │ │ │ └── Net_OutputHelper.cs │ │ ├── OutputId.cs │ │ ├── SyncBlinkingGameOutput.cs │ │ └── WindowMessage/ │ │ ├── Wm_OutputClient.cs │ │ ├── Wm_OutputDataStruct.cs │ │ └── Wm_OutputHelper.cs │ ├── Memory/ │ │ ├── Codecave.cs │ │ ├── InjectionStruct.cs │ │ └── NopStruct.cs │ ├── MemoryX64/ │ │ ├── Codecave.cs │ │ ├── InjectionStruct.cs │ │ └── NopStruct.cs │ ├── RawInput/ │ │ ├── Hid/ │ │ │ ├── HidPButtonCaps.cs │ │ │ ├── HidPCaps.cs │ │ │ ├── HidPReportType.cs │ │ │ ├── HidPValueCaps.cs │ │ │ ├── HidUsage.cs │ │ │ ├── HidUsageAndPage.cs │ │ │ ├── HidUsagePage.cs │ │ │ └── NtStatus.cs │ │ ├── RawInputController.cs │ │ ├── RawInputHelper.cs │ │ └── User32/ │ │ ├── RawHid.cs │ │ ├── RawInputDataKeyboard.cs │ │ ├── RawInputDataMouse.cs │ │ ├── RawInputDevice.cs │ │ ├── RawInputDeviceFlag.cs │ │ ├── RawInputDeviceInfo.cs │ │ ├── RawInputDeviceList.cs │ │ ├── RawInputDeviceType.cs │ │ ├── RawInputHeader.cs │ │ └── RawInputUiCommand.cs │ ├── Win32/ │ │ ├── CopyDataStruct.cs │ │ ├── FileMapAccessType.cs │ │ ├── HardwareScanCode.cs │ │ ├── Input.cs │ │ ├── KbdLlHookStruct.cs │ │ ├── MemoryAllocType.cs │ │ ├── MemoryFreeType.cs │ │ ├── MemoryPageProtect.cs │ │ ├── MsLlHookStruct.cs │ │ ├── PageProtection.cs │ │ ├── Point.cs │ │ ├── QueryUserNotificationState.cs │ │ ├── Rect.cs │ │ ├── SystemMetricsIndex.cs │ │ ├── VirtualKeyCode.cs │ │ ├── VirtualKeyMapType.cs │ │ ├── Win32Api.cs │ │ ├── Win32Define.cs │ │ └── WndClassEx.cs │ ├── XInput/ │ │ ├── XInputBatteryInformation.cs │ │ ├── XInputCapabilities.cs │ │ ├── XInputDeviceSubtype.cs │ │ ├── XInputGamepad.cs │ │ ├── XInputHandler.cs │ │ ├── XInputState.cs │ │ ├── XInputVibration.cs │ │ └── XinputButtonFlags.cs │ └── XOutput/ │ └── XOutput.cs ├── DsDiag/ │ ├── DsDiag.csproj │ ├── Ds_Diag.Designer.cs │ ├── Ds_Diag.cs │ ├── Ds_Diag.resx │ ├── Ds_Diag_Button.Designer.cs │ ├── Ds_Diag_Button.cs │ ├── Ds_Diag_Button.resx │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ └── app.config ├── README.md └── UnityPlugins/ ├── UnityPlugin_BepInEx_DCOP/ │ ├── DCOP_BepInEx_DemulShooter_Plugin.ini │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── Patch/ │ │ ├── Uduino/ │ │ │ ├── mUduinoConnection_DesktopSerial.cs │ │ │ └── mUduinoManager.cs │ │ ├── _Unity.cs │ │ ├── mGunScript.cs │ │ ├── mHighscoreManager.cs │ │ ├── mLifeTracker.cs │ │ ├── mPauseGame.cs │ │ ├── mSetMousecursor.cs │ │ ├── mU_hitboxHit.cs │ │ ├── mU_onContinueCountDown.cs │ │ ├── mU_onPlayerShoot.cs │ │ └── mU_policeLight.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_DCOP.csproj │ └── UnityPlugin_BepInEx_DCOP.sln ├── UnityPlugin_BepInEx_DRK/ │ ├── DemulShooter_Plugin.cs │ ├── Drakon_BepInEx_DemulShooter_Plugin.ini │ ├── INIFile.cs │ ├── Patch/ │ │ ├── SBK/ │ │ │ ├── Matrix/ │ │ │ │ └── mMatrixManager.cs │ │ │ ├── PneumaticSeatsystem/ │ │ │ │ └── mPneumaticSeatManager.cs │ │ │ ├── RS232/ │ │ │ │ └── mRS232DLL.cs │ │ │ ├── Skyride_Turret/ │ │ │ │ └── mTurret.cs │ │ │ ├── TurretSystem/ │ │ │ │ └── mTurretManager.cs │ │ │ └── mApplicationManager.cs │ │ ├── _Unity.cs │ │ ├── mChooseLevelWindow.cs │ │ ├── mContinueWindow.cs │ │ ├── mHudWindow.cs │ │ ├── mInactivePlayerState.cs │ │ ├── mInputsManager.cs │ │ ├── mLaser2D.cs │ │ ├── mPlayerWindow.cs │ │ ├── mTarget.cs │ │ ├── mTitleWindow.cs │ │ └── mUnityEngine.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_Drakon.csproj │ └── UnityPlugin_BepInEx_Drakon.sln ├── UnityPlugin_BepInEx_MARSS/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── MarsSortie_BepInEx_DemulShooter_Plugin.ini │ ├── Patch/ │ │ ├── MRCQ/ │ │ │ ├── mGameDatabase.cs │ │ │ ├── mHardware.cs │ │ │ ├── mLocalization.cs │ │ │ ├── mSignalKey.cs │ │ │ ├── mSuperDogManager.cs │ │ │ └── mSystemError.cs │ │ ├── Now/ │ │ │ ├── mBulletCounter.cs │ │ │ └── mPlayerWeapon.cs │ │ └── mArcadeShowCursor.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_MarsSortie.csproj │ └── UnityPlugin_BepInEx_MarsSortie.sln ├── UnityPlugin_BepInEx_MIA/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── MissionImpossible_BepInEx_DemulShooter_Plugin.ini │ ├── Patch/ │ │ ├── UGGameShell/ │ │ │ ├── mGame.cs │ │ │ ├── mGameSetting.cs │ │ │ └── mShellData.cs │ │ ├── _Unity.cs │ │ ├── mBootSequence.cs │ │ ├── mConfigManager.cs │ │ ├── mGameLogManager.cs │ │ ├── mGameObjPoolManager.cs │ │ ├── mHistoryDataManager.cs │ │ ├── mInputManager.cs │ │ ├── mModecontroller.cs │ │ ├── mPlayer.cs │ │ ├── mPlayerGunBase.cs │ │ ├── mShellManager.cs │ │ ├── mUGNetConnector.cs │ │ ├── mUIManager.cs │ │ └── mUIShootPoint.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_MissionImpossible.csproj │ └── UnityPlugin_BepInEx_MissionImpossible.sln ├── UnityPlugin_BepInEx_MIB/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── MIB_BepInEx_DemulShooter_Plugin.ini │ ├── Patch/ │ │ ├── MIB/ │ │ │ ├── mMIBClient.cs │ │ │ ├── mMIBCore.cs │ │ │ └── mMIBGameSettings.cs │ │ ├── _Unity.cs │ │ ├── mCreditsUI.cs │ │ ├── mGlobal.cs │ │ ├── mGun.cs │ │ ├── mGunRig.cs │ │ ├── mInitialisation.cs │ │ ├── mLaserEmitter.cs │ │ ├── mLights.cs │ │ ├── mPatchTemplate.cs │ │ ├── mRS232.cs │ │ ├── mUI.cs │ │ ├── mUIPlayerCrosshair.cs │ │ └── mUnityEngine.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_MIB.csproj │ ├── UnityPlugin_BepInEx_MIB.sln │ └── mAmwaysOnTop.cs ├── UnityPlugin_BepInEx_NHA2/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── NightHunter2_BepInEx_DemulShooter_Plugin.ini │ ├── Patch/ │ │ ├── _Unity.cs │ │ ├── mGame Device_data_gun_core.cs │ │ ├── mconfig_gun.cs │ │ ├── mdog_check_new.cs │ │ ├── mgame_device_sub_gun.cs │ │ ├── mgame_mark_3d_obj.cs │ │ ├── mgame_player_is_hit.cs │ │ ├── mgun_body.cs │ │ ├── mgun_body_tx_wuti.cs │ │ ├── minput_manage.cs │ │ ├── minput_obj_change_bullet.cs │ │ ├── mnew_game_gui_connect_player_slider.cs │ │ ├── mshow_info_manage_for_objs.cs │ │ ├── mtest_screen.cs │ │ ├── mzhichi_hanshu_pos.cs │ │ └── mzzp_houtai_manage_check_control.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_NHA2.csproj │ └── UnityPlugin_BepInEx_NHA2.sln ├── UnityPlugin_BepInEx_OWR/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── OperationWolf_BepInEx_DemulShooter_Plugin.ini │ ├── Patch/ │ │ ├── _Unity.cs │ │ ├── mCursorMouseMouve.cs │ │ ├── mGameManager.cs │ │ ├── mInterfaceModule.cs │ │ ├── mPlayer.cs │ │ ├── mPlayerInOutModule.cs │ │ ├── mScreenDuckHunt_InOutSystem.cs │ │ ├── mScreenMouseLook_InOutSystem.cs │ │ ├── mScreenView.cs │ │ └── mWeapon.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_OperationWolf.csproj │ └── UnityPlugin_BepInEx_OperationWolf.sln ├── UnityPlugin_BepInEx_PBX/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── PointBlankX_BepInEx_DemulShooter_Plugin.ini │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_PointBlankX.csproj │ ├── UnityPlugin_BepInEx_PointBlankX.sln │ └── patch/ │ ├── _Unity.cs │ ├── mBNUsioController.cs │ ├── mBootSceneController.cs │ ├── mConfig.cs │ ├── mErrorMessageHandler.cs │ ├── mGlobalData.cs │ ├── mInputWrapper.cs │ ├── mPlayer.cs │ ├── mTitleController.cs │ └── mUIPlayerPanel.cs ├── UnityPlugin_BepInEx_PVZ/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── Patch/ │ │ ├── _Unity.cs │ │ ├── mInput.cs │ │ ├── mPvzCore.cs │ │ └── mPvzCrosshair.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── PvZ_BepInEx_DemulShooter_Plugin.ini │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_PVZ.csproj │ └── UnityPlugin_BepInEx_PVZ.sln ├── UnityPlugin_BepInEx_RHA/ │ ├── Demulshooter_Plugin.cs │ ├── INIFile.cs │ ├── Patch/ │ │ ├── NAEMGFFONID.cs │ │ ├── SBK/ │ │ │ ├── Matrix/ │ │ │ │ └── mMatrixManager.cs │ │ │ ├── Sixensecore/ │ │ │ │ └── mDevice.cs │ │ │ └── mApplicationManager.cs │ │ ├── _Unity.cs │ │ ├── mArcadeGamePlayManager.cs │ │ ├── mArcadeManager.cs │ │ ├── mCrosshairWindow.cs │ │ ├── mGunDetection.cs │ │ ├── mKaboomAdrioFxPlusFourFeeders.cs │ │ ├── mKaboomManager.cs │ │ ├── mLEDManager.cs │ │ ├── mLanguageLocalizer.cs │ │ ├── mLevelSelectorWindow.cs │ │ ├── mNewInputManager.cs │ │ ├── mPlayerManager.cs │ │ ├── mPlayerPrefs.cs │ │ ├── mSBKInputManager.cs │ │ ├── mWorldManager.cs │ │ └── mWorldSelectorWindow.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── RabbidsHollywood_BepInEx_DemulShooter_Plugin.ini │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_RabbidsHollywood.csproj │ └── UnityPlugin_BepInEx_RabbidsHollywood.sln ├── UnityPlugin_BepInEx_RTNA/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── NerfArcade_BepInEx_DemulShooter_Plugin.ini │ ├── Patch/ │ │ ├── _Unity.cs │ │ ├── mGame.cs │ │ ├── mIOManager.cs │ │ ├── mPlayerInfo.cs │ │ ├── mPlayerReticle.cs │ │ ├── mSys_Linux.cs │ │ └── mUnityNatives.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_NerfArcade.csproj │ └── UnityPlugin_BepInEx_NerfArcade.sln ├── UnityPlugin_BepInEx_TRA/ │ ├── DemulShooter_Plugin.cs │ ├── INIFile.cs │ ├── Patch/ │ │ ├── SBK/ │ │ │ └── Encryption/ │ │ │ └── mDataTransform.cs │ │ ├── TRA/ │ │ │ └── Timeline/ │ │ │ └── Skip/ │ │ │ └── mSkipMixerBehaviour.cs │ │ ├── _Unity.cs │ │ ├── mArcadeManager.cs │ │ ├── mCheatsManager.cs │ │ ├── mCrosshairWindow.cs │ │ ├── mInputManager.cs │ │ ├── mKaboomAdrioFxPlusFourFeeders.cs │ │ ├── mKaboomManager.cs │ │ ├── mLEDManager.cs │ │ ├── mLanguageLocalizer.cs │ │ ├── mOperatorMenu.cs │ │ ├── mPlayer.cs │ │ ├── mPlayerManager.cs │ │ └── mSBKInputManager.cs │ ├── PluginController.cs │ ├── PluginControllerButton.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TcpData.cs │ ├── TcpInputData.cs │ ├── TcpOutputData.cs │ ├── TcpPacket.cs │ ├── TombRaider_BepInEx_DemulShooter_Plugin.ini │ ├── UnityLibs/ │ │ └── List_of_dll_to_copy_here.txt │ ├── UnityPlugin_BepInEx_TombRaider.csproj │ └── UnityPlugin_BepInEx_TombRaider.sln └── UnityPlugin_BepInEx_WWS/ ├── Demulshooter_Plugin.cs ├── INIFile.cs ├── Patch/ │ ├── MVSDK/ │ │ └── mBaseGun.cs │ ├── _Unity.cs │ ├── mBaseCamera.cs │ ├── mBaseCom.cs │ ├── mGameBeginUIController.cs │ ├── mGameChooseUI2.cs │ ├── mGameStart.cs │ ├── mGameUIController.cs │ ├── mInputController.cs │ ├── mLSerialPort.cs │ ├── mMVSettings.cs │ ├── mPlayerData.cs │ ├── mPlayerUIController.cs │ ├── mSettingController.cs │ ├── mShootController.cs │ └── mShowGamePointController.cs ├── PluginController.cs ├── PluginControllerButton.cs ├── Properties/ │ └── AssemblyInfo.cs ├── TcpData.cs ├── TcpInputData.cs ├── TcpOutputData.cs ├── TcpPacket.cs ├── UnityLibs/ │ └── List_of_dll_to_copy_here.txt ├── UnityPlugin_BepInEx_WWS.csproj ├── UnityPlugin_BepInEx_WWS.sln └── WildWestShootout_BepInEx_DemulShooter_Plugin.ini ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # Supprimer la ligne ci-dessous si vous voulez hériter les paramètres .editorconfig des répertoires supérieurs root = true # Fichiers C# [*.cs] #### Options EditorConfig principales #### # Indentation et espacement indent_size = 4 indent_style = space tab_width = 4 # Préférences de nouvelle ligne end_of_line = crlf insert_final_newline = false #### Conventions de codage .NET #### # Organiser les instructions Using dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = true file_header_template = unset # Préférences de this. et Me. dotnet_style_qualification_for_event = false dotnet_style_qualification_for_field = false dotnet_style_qualification_for_method = false dotnet_style_qualification_for_property = false # Préférences des mots clés de langage par rapport aux types BCL dotnet_style_predefined_type_for_locals_parameters_members = true dotnet_style_predefined_type_for_member_access = true # Préférences de parenthèses dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity dotnet_style_parentheses_in_other_binary_operators = always_for_clarity dotnet_style_parentheses_in_other_operators = never_if_unnecessary dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity # Préférences de modificateur dotnet_style_require_accessibility_modifiers = for_non_interface_members # Préférences de niveau expression dotnet_style_coalesce_expression = true dotnet_style_collection_initializer = false dotnet_style_explicit_tuple_names = true dotnet_style_namespace_match_folder = true dotnet_style_null_propagation = true dotnet_style_object_initializer = true dotnet_style_operator_placement_when_wrapping = beginning_of_line dotnet_style_prefer_auto_properties = true dotnet_style_prefer_collection_expression = when_types_loosely_match dotnet_style_prefer_compound_assignment = true dotnet_style_prefer_conditional_expression_over_assignment = true dotnet_style_prefer_conditional_expression_over_return = true dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed dotnet_style_prefer_inferred_anonymous_type_member_names = true dotnet_style_prefer_inferred_tuple_names = true dotnet_style_prefer_is_null_check_over_reference_equality_method = true dotnet_style_prefer_simplified_boolean_expressions = true dotnet_style_prefer_simplified_interpolation = true # Préférences de champ dotnet_style_readonly_field = false:none # Préférences de paramètre dotnet_code_quality_unused_parameters = all # Préférences de suppression dotnet_remove_unnecessary_suppression_exclusions = none # Préférences de nouvelle ligne dotnet_style_allow_multiple_blank_lines_experimental = true dotnet_style_allow_statement_immediately_after_block_experimental = true #### Conventions de codage C# #### # Préférences de var csharp_style_var_elsewhere = false csharp_style_var_for_built_in_types = false csharp_style_var_when_type_is_apparent = false # Membres expression-bodied csharp_style_expression_bodied_accessors = true csharp_style_expression_bodied_constructors = false csharp_style_expression_bodied_indexers = true csharp_style_expression_bodied_lambdas = true csharp_style_expression_bodied_local_functions = false csharp_style_expression_bodied_methods = false csharp_style_expression_bodied_operators = false csharp_style_expression_bodied_properties = true # Préférences correspondants au modèle csharp_style_pattern_matching_over_as_with_null_check = true csharp_style_pattern_matching_over_is_with_cast_check = true csharp_style_prefer_extended_property_pattern = true csharp_style_prefer_not_pattern = true csharp_style_prefer_pattern_matching = true csharp_style_prefer_switch_expression = true # Préférences de vérification de valeur Null csharp_style_conditional_delegate_call = true # Préférences de modificateur csharp_prefer_static_local_function = true csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async csharp_style_prefer_readonly_struct = true csharp_style_prefer_readonly_struct_member = true # Préférences de bloc de code csharp_prefer_braces = true csharp_prefer_simple_using_statement = true csharp_style_namespace_declarations = block_scoped csharp_style_prefer_method_group_conversion = true csharp_style_prefer_primary_constructors = true csharp_style_prefer_top_level_statements = true # Préférences de niveau expression csharp_prefer_simple_default_expression = true csharp_style_deconstructed_variable_declaration = true csharp_style_implicit_object_creation_when_type_is_apparent = true csharp_style_inlined_variable_declaration = true csharp_style_prefer_index_operator = true csharp_style_prefer_local_over_anonymous_function = true csharp_style_prefer_null_check_over_type_check = true csharp_style_prefer_range_operator = true csharp_style_prefer_tuple_swap = true csharp_style_prefer_utf8_string_literals = true csharp_style_throw_expression = true csharp_style_unused_value_assignment_preference = discard_variable csharp_style_unused_value_expression_statement_preference = discard_variable # Préférences pour la directive 'using' csharp_using_directive_placement = outside_namespace # Préférences de nouvelle ligne csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true csharp_style_allow_embedded_statements_on_same_line_experimental = true #### Règles de formatage C# #### # Préférences de nouvelle ligne csharp_new_line_before_catch = true csharp_new_line_before_else = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true # Préférences de mise en retrait csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_case_contents_when_block = true csharp_indent_labels = one_less_than_current csharp_indent_switch_labels = true # Préférences d'espace csharp_space_after_cast = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after csharp_space_around_declaration_statements = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_open_square_brackets = false csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false csharp_space_between_method_call_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Préférences d'enveloppement csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Styles de nommage #### # Règles de nommage dotnet_naming_rule.interface_should_be_commence_par_i.severity = suggestion dotnet_naming_rule.interface_should_be_commence_par_i.symbols = interface dotnet_naming_rule.interface_should_be_commence_par_i.style = commence_par_i dotnet_naming_rule.types_should_be_casse_pascal.severity = suggestion dotnet_naming_rule.types_should_be_casse_pascal.symbols = types dotnet_naming_rule.types_should_be_casse_pascal.style = casse_pascal dotnet_naming_rule.membres_autres_que_des_champs_should_be_casse_pascal.severity = suggestion dotnet_naming_rule.membres_autres_que_des_champs_should_be_casse_pascal.symbols = membres_autres_que_des_champs dotnet_naming_rule.membres_autres_que_des_champs_should_be_casse_pascal.style = casse_pascal # Spécifications de symboles dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.membres_autres_que_des_champs.applicable_kinds = property, event, method dotnet_naming_symbols.membres_autres_que_des_champs.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.membres_autres_que_des_champs.required_modifiers = # Styles de nommage dotnet_naming_style.casse_pascal.required_prefix = dotnet_naming_style.casse_pascal.required_suffix = dotnet_naming_style.casse_pascal.word_separator = dotnet_naming_style.casse_pascal.capitalization = pascal_case dotnet_naming_style.commence_par_i.required_prefix = I dotnet_naming_style.commence_par_i.required_suffix = dotnet_naming_style.commence_par_i.word_separator = dotnet_naming_style.commence_par_i.capitalization = pascal_case ================================================ FILE: .gitignore ================================================ # Created by https://www.gitignore.io/api/visualstudio ### VisualStudio ### ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates #Licenced DLL for Unity plugin Assembly-CSharp.dll UnityEngine.dll UnityEngine.Networking.dll UnityEngine.UI.dll UnityEngine*.dll Virtuallyz*.dll Unity*.dll # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ ### VisualStudio Patch ### # By default, sensitive information, such as encrypted password # should be stored in the .pubxml.user file. *.pubxml.user # End of https://www.gitignore.io/api/visualstudio ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser ================================================ FILE: DemulShooter/DemulShooter.csproj ================================================  Debug x86 8.0.30703 2.0 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599} WinExe Properties DemulShooter DemulShooter v4.8 512 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 false x86 pdbonly true bin\Release\ TRACE prompt 4 false DemulShooter_Icon.ico True True Resources.resx True True Settings.settings {BD88CC4D-2944-43F8-85C3-A274A45487AF} DsCore ResXFileCodeGenerator Resources.Designer.cs SettingsSingleFileGenerator Settings.Designer.cs False .NET Framework 3.5 SP1 Client Profile false False .NET Framework 3.5 SP1 true False Windows Installer 3.1 true ================================================ FILE: DemulShooter/DemulShooterWindow.cs ================================================ using System; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Security.Principal; using System.Threading; using System.Windows.Forms; using DemulShooter.Games; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); public class DemulShooterWindow : ApplicationContext { //A Message-Loop only window will be created to be able to receive WM_INPUT messages private IntPtr _RawMessageWnd_hWnd = IntPtr.Zero; private WndProc delegWndProc; private const string DEMULSHOOTER_CONF_FILENAME = "config.ini"; private string _UserDefinedIniFile = string.Empty; //Timer for Hooking TimeOut private System.Timers.Timer _TimerHookTimeout; //Tray Icon private ContextMenu _TrayIconMenu; private System.Windows.Forms.NotifyIcon _TrayIcon; //Available RawInput devices (filters by thir Type) private RawInputController[] _AvailableControllers; //Low-Level Hooks private Win32API.HookProc _MouseHookProc; private IntPtr _MouseHookID = IntPtr.Zero; private Win32API.HookProc _KeyboardHookProc; private IntPtr _KeyboardHookID = IntPtr.Zero; //Output (MameHooker) private Wm_OutputHelper _Wm_OutputHelper; //Output (Network) private Net_OutputHelper _Net_OutputHelper; private Thread _OutputUpdateLoop; //Game options private Game _Game; private string _Rom = String.Empty; private string _Target = String.Empty; private UInt32 _Ddinumber = 3; private string _DemulVersion = String.Empty; private bool _HardFfl = false; private double _ForceScalingX = 1.0; private bool _NoInput = false; //InterProcessCommunication (Memory Mapped Files) private const String DEMULSHOOTER_INPUTS_MMF_NAME = "DemulShooter_MMF_Inputs"; private const String DEMULSHOOTER_OUTPUTS_MMF_NAME = "DemulShooter_MMF_Outputs"; private const String DEMULSHOOTER_INPUTS_MUTEX_NAME = "DemulShooter_Inputs_Mutex"; private const String DEMULSHOOTER_OUTPUTS_MUTEX_NAME = "DemulShooter_Outputs_Mutex"; private bool _EnableInputsIpc = false; private bool _EnableOutputsIpc = false; private DsCore.IPC.MemoryMappedFileHelper_Old _MMF_Inputs; private DsCore.IPC.MemoryMappedFileHelper_Old _MMF_Outputs; public DemulShooterWindow(string[] Args, bool isVerbose, bool enableTrace) { //Stop program if Demulshooter already running Process[] pDemulShooter = Process.GetProcessesByName("DemulShooter"); if (pDemulShooter.Length > 1) { MessageBox.Show("Another instance of DemulShooter is already running.\nPlease terminate it before launching a new one", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(0); } //Creating TrayIcon and TrayIcon "Exit" menu Application.ApplicationExit += new EventHandler(OnApplicationExit); InitializeComponent(); //Creating the timeout Timer _TimerHookTimeout = new System.Timers.Timer(); _TimerHookTimeout.Enabled = false; _TimerHookTimeout.Elapsed += tHookTimeOut_Elapsed; Logger.IsEnabled = isVerbose; Logger.IsTraceEnabled = enableTrace; Logger.InitLogFileName(); Logger.WriteLog(""); Logger.WriteLog("---------------- Program Start -- DemulShooter v" + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString() + " ----------------"); // Parsing commandline arguments for (int i = 0; i < Args.Length; i++) { Logger.WriteLog("Cmdline arg " + i + " : " + Args[i]); if (Args[i].ToLower().StartsWith("-ddinumber")) { try { //-1 to transform to a 0-based index for later calculation _Ddinumber = UInt32.Parse((Args[i].Split('='))[1].Trim()) - 1; } catch { Logger.WriteLog("-ddinumber parameter not good, it will keep default value"); } } else if (Args[i].ToLower().StartsWith("-forcescalingx")) { String sX = (Args[i].Split('='))[1].Trim(); try { if (sX.Contains("/") && sX.Split('/').Length > 1) { double d1 = Double.Parse(sX.Split('/')[0]); double d2 = Double.Parse(sX.Split('/')[1]); _ForceScalingX = d1 / d2; } else _ForceScalingX = Double.Parse(sX, CultureInfo.InvariantCulture); Logger.WriteLog("-ForceScalingX parameter set to " + _ForceScalingX.ToString()); } catch { Logger.WriteLog("Can't set -ForceScalingX option : " + sX + " is not a valid value"); } } else if (Args[i].ToLower().Equals("-hardffl")) { _HardFfl = true; } else if (Args[i].ToLower().Equals("-ipcinputs")) { _EnableInputsIpc = true; } else if (Args[i].ToLower().Equals("-ipcoutputs")) { _EnableOutputsIpc = true; } else if (Args[i].ToLower().Equals("-noinput")) { _NoInput = true; } else if (Args[i].ToLower().StartsWith("-profile")) { _UserDefinedIniFile = (Args[i].Split('='))[1].Trim(); } else if (Args[i].ToLower().StartsWith("-rom")) { _Rom = (Args[i].Split('='))[1].Trim(); } else if (Args[i].ToLower().StartsWith("-target")) { _Target = (Args[i].Split('='))[1].Trim(); if (_Target.StartsWith("demul")) { _DemulVersion = _Target.Substring(5, 3); } } } if (_TrayIcon != null) _TrayIcon.Text += "[" + _Target + "] [" + _Rom + "]"; Logger.WriteLog("Running as Administrator : " + IsRunningAsAdmin().ToString()); //Finding plugged devices _AvailableControllers = RawInputHelper.GetRawInputDevices(new RawInputDeviceType[] { RawInputDeviceType.RIM_TYPEHID, RawInputDeviceType.RIM_TYPEMOUSE }); Logger.WriteLog("Found " + _AvailableControllers.Length + " available RawInput devices :"); foreach (RawInputController c in _AvailableControllers) { try { Logger.WriteLog(" + [" + c.DeviceType.ToString() + "] " + c.DeviceName); } catch (Exception ex) { Logger.WriteLog("ERROR : " + ex.Message.ToString()); } } //Reading config file to get parameters if (_UserDefinedIniFile != string.Empty) Configurator.GetInstance().ReadDsConfig(_UserDefinedIniFile); else Configurator.GetInstance().ReadDsConfig(AppDomain.CurrentDomain.BaseDirectory + @"\" + DEMULSHOOTER_CONF_FILENAME); foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { Logger.WriteLog("P" + Player.ID + " mode = " + Player.Mode); if (Player.Mode == PlayerSettings.PLAYER_MODE_RAWINPUT) { bool bControllerfound = false; Logger.WriteLog("P" + Player.ID + " device = " + Player.DeviceName); foreach (RawInputController Controller in _AvailableControllers) { //Usually , the device name never change and it's easy to find back a controller if (Controller.DeviceName == Player.DeviceName) { Player.RIController = Controller; Player.RIController.Selected_AxisX = Player.HidAxisX; Player.RIController.Selected_AxisY = Player.HidAxisY; Player.RIController.Selected_OnScreenTriggerButton = Player.HidButton_OnScreenTrigger; Player.RIController.Selected_ActionButton = Player.HidButton_Action; Player.RIController.Selected_OffScreenTriggerButton = Player.HidButton_OffScreenTrigger; Logger.WriteLog("P" + Player.ID + " device plugged and found, Handle = 0x" + Controller.DeviceHandle); Logger.WriteLog("P" + Player.ID + " device : " + Controller.ManufacturerName + " / " + Controller.ProductName); bControllerfound = true; break; } } //Unfortunatelly, on a few cases (SONY DS4 for example), a part of the device name changes so we will check again //with what seems to be a fixed part of the name if (!bControllerfound) { /*foreach (RawInputController Controller in _AvailableControllers) { //Usually , the device name never change and it's easy to find back a controller if (Controller.DeviceName == Player.DeviceName) { Player.RIController = Controller; Player.RIController.Set_AxisX(Player.HidAxisX); Player.RIController.Set_AxisY(Player.HidAxisY); Player.RIController.Set_Button_OnScreenTrigger_Index(Player.HidButton_OnScreenTrigger_Index); Player.RIController.Set_Button_Action_Index(Player.HidButton_Action_Index); Player.RIController.Set_Button_OffScreenTrigger_Index(Player.HidButton_OffScreenTrigger_Index); Logger.WriteLog("P" + Player.ID + " device plugged and found, Handle = 0x" + Controller.DeviceHandle); Logger.WriteLog("P" + Player.ID + " device : " + Controller.ProductName + " / " + Controller.DeviceName); bControllerfound = true; break; } }*/ } } else Logger.WriteLog("P" + Player.ID + " Gamepad ID = " + Player.GamepadID); } //Info on Monitor (max resolution) //// Disabled for now -- may cause trouble on some computer( ???) /* try { var scope = new System.Management.ManagementScope(); var q = new System.Management.ObjectQuery("SELECT * FROM CIM_VideoControllerResolution"); String Maxres = String.Empty; using (var searcher = new System.Management.ManagementObjectSearcher(scope, q)) { var results = searcher.Get(); foreach (var item in results) { Maxres = item["Caption"].ToString(); } } Logger.WriteLog("Monitor maximum resolution = " + Maxres); } catch (Exception Ex) { Logger.WriteLog("Error detecting monitor maximum resolution : " + Ex.Message.ToString()); } */ //Setting up IPC for inputs/outputs if (_EnableInputsIpc) { _MMF_Inputs = new DsCore.IPC.MemoryMappedFileHelper_Old(DEMULSHOOTER_INPUTS_MUTEX_NAME); _MMF_Inputs.MMFInit(DEMULSHOOTER_INPUTS_MMF_NAME, 2048); } if (_EnableOutputsIpc) { _MMF_Outputs = new DsCore.IPC.MemoryMappedFileHelper_Old(DEMULSHOOTER_OUTPUTS_MUTEX_NAME); _MMF_Outputs.MMFInit(DEMULSHOOTER_OUTPUTS_MMF_NAME, 2048); } CreateRawMessageWindow(); //Register to RawInput thanks to the previously created window Handle RawInputDevice[] rid = new RawInputDevice[3]; rid[0].UsagePage = HidUsagePage.GENERIC; rid[0].Usage = HidUsage.Joystick; rid[0].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[0].hwndTarget = _RawMessageWnd_hWnd; rid[1].UsagePage = HidUsagePage.GENERIC; rid[1].Usage = HidUsage.Mouse; rid[1].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[1].hwndTarget = _RawMessageWnd_hWnd; rid[2].UsagePage = HidUsagePage.GENERIC; rid[2].Usage = HidUsage.Gamepad; rid[2].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[2].hwndTarget = _RawMessageWnd_hWnd; if (!Win32API.RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) { MessageBox.Show("Failed to register raw input device(s).", "DemulShooter Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Application.Exit(); } //Starting Mame-style output daemon if (Configurator.GetInstance().OutputEnabled) { Logger.WriteLog("Starting Output daemon..."); if (Configurator.GetInstance().Wm_OutputEnabled) { Logger.WriteLog("Creating Window Message Output Helper..."); _Wm_OutputHelper = new Wm_OutputHelper(_RawMessageWnd_hWnd); _Wm_OutputHelper.Start(); } if (Configurator.GetInstance().Net_OutputEnabled) { Logger.WriteLog("Creating Network Output Helper..."); _Net_OutputHelper = new Net_OutputHelper(_Rom); _Net_OutputHelper.Start(); } _OutputUpdateLoop = new Thread(new ThreadStart(ReadAndSendOutput_Thread)); _OutputUpdateLoop.Start(); } //Starting the fun... if (_Target.Length > 0 && (_Rom.Length > 0 || _Target.StartsWith("dolphin"))) { //Install Low-Level mouse hook ApplyMouseHook(); //Install Low Level keyboard hook ApplyKeyboardHook(); /* //Running Xinput daemon if needed bool EnableXInputProc = false; foreach (ControllerDevice Device in _ControllerDevices) { if (Device.GamepadID != -1) { EnableXInputProc = true; break; } } if (EnableXInputProc) Bgw_XInput.RunWorkerAsync(); */ //Coastal Games if (_Target.Equals("arcadepc")) { switch (_Rom.ToLower()) { case "gbusters": { _Game = new Game_ArcadepcGhostBusters(_Rom.ToLower()); } break; case "pvz": { _Game = new Game_ArcadepcPvzLastStand(_Rom.ToLower()); }; break; case "rhood": { _Game = new Game_ArcadepcRobinHood(_Rom.ToLower()); } ; break; case "wws": { _Game = new Game_ArcadepcWws(_Rom.ToLower()); }; break; default : break; } } //Chihiro games else if (_Target.Equals("chihiro")) { switch (_Rom.ToLower()) { case "vcop3_old": { _Game = new Game_CxbxVcop3_Old(_Rom.ToLower()); }; break; case "gsquad": { _Game = new Game_CxbxGsquad(_Rom.ToLower()); }; break; case "hod3": { _Game = new Game_CxbxHod3(_Rom.ToLower()); }; break; case "vcop3": { _Game = new Game_CxbxVcop3(_Rom.ToLower()); }; break; default : break; } } //Demul games else if (_Target.StartsWith("demul")) { if (_Rom.ToLower().Equals("confmiss") || _Rom.ToLower().Equals("deathcox") || _Rom.ToLower().StartsWith("hotd2") || _Rom.ToLower().Equals("lupinsho") || _Rom.ToLower().Equals("mok")) { _Game = new Game_DemulNaomi(_Rom.ToLower(), _DemulVersion); } else if (_Rom.ToLower().StartsWith("ninjaslt")) { _Game = new Game_DemulJvs(_Rom.ToLower(), _DemulVersion); } else if (_Rom.ToLower().Equals("braveff")) { _Game = new Game_DemulHikaru(_Rom.ToLower(), _DemulVersion); } else if (_Rom.ToLower().Equals("manicpnc") || _Rom.ToLower().Equals("pokasuka")) { _Game = new Game_DemulManicpnc(_Rom.ToLower()); } else { _Game = new Game_DemulAtomiswave(_Rom.ToLower(), _DemulVersion); } } //Dolphin else if (_Target.Equals("dolphin5")) { _Game = new Game_Dolphin5(_Rom.ToLower(), _Ddinumber); } //Es4 else if (_Target.Equals("es4")) { switch (_Rom.ToLower()) { case "pblankx": { _Game = new Game_Es4PointBlankX(_Rom.ToLower()); }; break; default: break; } } //Gamewax game else if (_Target.Equals("gamewax")) { switch (_Rom.ToLower()) { case "akuma": { _Game = new Game_GameWaxAkuma(_Rom.ToLower()); } break; default : break; } } //GlobalVR game else if (_Target.Equals("globalvr")) { switch (_Rom.ToLower()) { case "aliens": { _Game = new Game_GvrAliens(_Rom.ToLower()); } break; case "farcry": { _Game = new Game_GvrFarCry(_Rom.ToLower()); } break; case "fearland": { _Game = new Game_GvrFearLand(_Rom.ToLower(), _HardFfl); } break; default: break; } } //KONAMI Arcade else if (_Target.Equals("konami")) { switch (_Rom.ToLower()) { case "coop9": { _Game = new Game_KonamiCoopers9(_Rom.ToLower()); } break; case "gashn2": { _Game = new Game_KonamiGashaaaan2(_Rom.ToLower()); } break; case "hcv": { _Game = new Game_KonamiCastlevania(_Rom.ToLower()); } break; case "le3": { _Game = new Game_KonamiLethalEnforcers3(_Rom.ToLower()); } break; case "wartran": { _Game = new Game_KonamiWartran(_Rom.ToLower()); } break; default: break; } } //Lindbergh else if (_Target.Equals("lindbergh")) { switch (_Rom.ToLower()) { case "2spicy": { _Game = new Game_Lindbergh2spicy(_Rom.ToLower()); } break; case "gsevo": { _Game = new Game_LindberghGsquadEvo(_Rom.ToLower()); } break; case "hotd4": { _Game = new Game_LindberghHotd4(_Rom.ToLower()); } break; case "hotd4sp": { _Game = new Game_LindberghHotd4Sp(_Rom.ToLower()); } break; case "hotdex": { _Game = new Game_LindberghHotdEx(_Rom.ToLower()); } break; case "lgj": { _Game = new Game_LindberghLgj(_Rom.ToLower()); } break; case "lgjsp": { _Game = new Game_LindberghLgjsp(_Rom.ToLower()); } break; case "rambo": { _Game = new Game_LindberghRambo(_Rom.ToLower()); } break; default: break; } } //All model2 roms share same method else if (_Target.Equals("model2")) { switch (_Rom.ToLower()) { case "bel": { _Game = new Game_Model2Bel(_Rom.ToLower()); } break; case "gunblade": { _Game = new Game_Model2Gunblade(_Rom.ToLower()); } break; case "hotd": { _Game = new Game_Model2Hotd(_Rom.ToLower()); } break; case "rchase2": { _Game = new Game_Model2Rchase2(_Rom.ToLower()); } break; case "vcop": { _Game = new Game_Model2Vcop(_Rom.ToLower()); } break; case "vcop2": { _Game = new Game_Model2Vcop2(_Rom.ToLower()); } break; default: break; } } //All model2 roms share same method else if (_Target.Equals("ppmarket")) { switch (_Rom.ToLower()) { case "policetr2": { _Game = new Game_PpmPoliceTrainer2(_Rom.ToLower()); } break; default: break; } } //Raw Thrill Games else if (_Target.Equals("rawthrill")) { switch (_Rom.ToLower()) { case "aa": { _Game = new Game_RtAliensArmageddon(_Rom.ToLower()); } break; case "jp": { _Game = new Game_RtJurassicPark(_Rom.ToLower()); } break; case "ts": { _Game = new Game_RtTerminatorSalvation(_Rom.ToLower()); } break; case "ttg": { _Game = new Game_RtTargetTerror(_Rom.ToLower()); }; break; case "wd": { _Game = new Game_RtWalkingDead(_Rom.ToLower()); } break; default : break; } } else if (_Target.Equals("ringedge2")) { switch (_Rom.ToLower()) { case "tsr": { _Game = new Game_Re2Transformers2(_Rom.ToLower()); }; break; } } //Ring system else if (_Target.Equals("ringwide")) { switch (_Rom.ToLower()) { case "sgg": { _Game = new Game_RwSGG(_Rom.ToLower()); } break; case "lgi": { _Game = new Game_RwLGI(_Rom.ToLower()); } break; case "lgi3d": { _Game = new Game_RwLGI3D(_Rom.ToLower()); } break; case "mng": { _Game = new Game_RwGunman(_Rom.ToLower()); }; break; case "og": { _Game = new Game_RwOpGhost(_Rom.ToLower()); } break; case "sdr": { _Game = new Game_RwSDR(_Rom.ToLower()); }; break; case "tb": { _Game = new Game_RwTargetBravo(_Rom.ToLower()); } ; break; case "tha": { _Game = new Game_RwTransformers(_Rom.ToLower()); }; break; default: break; } } //TTX game else if (_Target.Equals("ttx")) { switch (_Rom.ToLower()) { case "bkbs": { _Game = new Game_TtxBlockKingBallShooter(_Rom.ToLower()); } break; case "eadp": { _Game = new Game_TtxEadp(_Rom.ToLower()); } break; case "gattack4": { _Game = new Game_TtxGaiaAttack4(_Rom.ToLower()); } break; case "gsoz": { _Game = new Game_TtxGundam_V2(_Rom.ToLower()); } break; case "gsoz2p": { _Game = new Game_TtxGundam_V2(_Rom.ToLower()); } break; case "hmuseum": { _Game = new Game_TtxHauntedMuseum(_Rom.ToLower()); } break; case "hmuseum2": { _Game = new Game_TtxHauntedMuseum2(_Rom.ToLower(), _HardFfl); }break; case "mgungun2": { _Game = new Game_TtxGungun2(_Rom.ToLower()); } break; case "sha": { _Game = new Game_TtxSha(_Rom.ToLower()); } break; default: break; } } //Windows Games else if (_Target.Equals("windows")) { switch (_Rom.ToLower()) { case "ads": { _Game = new Game_WndAlienSafari(_Rom.ToLower()); } break; case "artdead": { _Game = new Game_WndArtIsDead(_Rom.ToLower()); } break; case "coltwws": { _Game = new Game_WndColtWildWestShootout(_Rom.ToLower()); } break; case "bugbust": { _Game = new Game_WndBugBusters(_Rom.ToLower()); } break; case "friction": { _Game = new Game_WndFriction(_Rom.ToLower()); } break; case "hfa": { _Game = new Game_WndHeavyFire3Pc(_Rom.ToLower()); }; break; case "hfss": { _Game = new Game_WndHeavyFire4Pc(_Rom.ToLower()); }; break; case "hod2pc": { _Game = new Game_WndHod2pc(_Rom.ToLower()); }; break; case "hod3pc": { _Game = new Game_WndHod3pc(_Rom.ToLower()); }; break; case "hodo": { _Game = new Game_WndHotdoPc(_Rom.ToLower()); }; break; case "madbul": { _Game = new Game_WndMadBullets(_Rom.ToLower()); }; break; case "pgbeat": { _Game = new Game_WndProjectGreenBeat(_Rom.ToLower()); }; break; case "reload": { _Game = new Game_WndReload(_Rom.ToLower()); }; break; default: break; } } //W.I.P Games else if (_Target.Equals("wip")) { switch (_Rom.ToLower()) { case "adcop": { _Game = new Game_WndAdCop95(_Rom.ToLower()); }; break; case "adcopsea": { _Game = new Game_WndAdCopOverseas(_Rom.ToLower()); }; break; case "bonbon": { _Game = new Game_WndBonbon95(_Rom.ToLower()); }; break; case "spray": { _Game = new Game_WndSpray(_Rom.ToLower()); }; break; case "fha": { _Game = new Game_ArcadepcFireHero(_Rom.ToLower()); } break; case "hsfr": { _Game = new Game_ArcadepcHsfr(_Rom.ToLower()); }break; case "mecht": { _Game = new Game_ArcadepcMechaTornado(_Rom.ToLower()); }break; case "topgun": { _Game = new Game_ArcadepcTopGun(_Rom.ToLower()); } break; case "hwspr2": { _Game = new Game_ArcadepcWaterSprite2(_Rom.ToLower()); }; break; case "hwwar2": { _Game = new Game_ArcadepcWaterWar2(_Rom.ToLower()); }; break; case "be": { _Game = new Game_WndBlueEstate(_Rom.ToLower()); };break; default: break; } } if (_Game != null) _Game.OnGameHooked += new Game.GameHookedHandler(OnGameHooked); //starting the TimeOut Timer if (Configurator.GetInstance().HookTimeout != 0) { _TimerHookTimeout.Interval = (Configurator.GetInstance().HookTimeout * 1000); _TimerHookTimeout.Start(); } } } /// /// Check if user has Elevated rights (Admin) /// /// private bool IsRunningAsAdmin() { bool isElevated; try { using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) { WindowsPrincipal principal = new WindowsPrincipal(identity); isElevated = principal.IsInRole(WindowsBuiltInRole.Administrator); } return isElevated; } catch (Exception Ex) { Logger.WriteLog("Error checking Admin rights for current user : " + Ex.Message.ToString()); return false; } } /// /// Create a messageLoop-only Window (invsible) to treat WM_* messages /// /// public bool CreateRawMessageWindow() { delegWndProc = myWndProc; WNDCLASSEX wind_class = new WNDCLASSEX(); wind_class.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX)); wind_class.style = 0; wind_class.hbrBackground = IntPtr.Zero; wind_class.cbClsExtra = 0; wind_class.cbWndExtra = 0; wind_class.hInstance = Marshal.GetHINSTANCE(this.GetType().Module); // alternative: Process.GetCurrentProcess().Handle; wind_class.hIcon = IntPtr.Zero; wind_class.hCursor = IntPtr.Zero; wind_class.lpszMenuName = null; wind_class.lpszClassName = "RI_MsgLoop"; wind_class.lpfnWndProc = Marshal.GetFunctionPointerForDelegate(delegWndProc); wind_class.hIconSm = IntPtr.Zero; ushort regResult = Win32API.RegisterClassEx(ref wind_class); if (regResult == 0) { uint error = Win32API.GetLastError(); return false; } string wndClass = wind_class.lpszClassName; //This version worked and resulted in a non-zero hWnd //_hWnd = CreateWindowEx(0, regResult, "Hello Win32", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 300, 400, IntPtr.Zero, IntPtr.Zero, wind_class.hInstance, IntPtr.Zero); IntPtr HWND_MESSAGE = new IntPtr(-3); _RawMessageWnd_hWnd = Win32API.CreateWindowEx(0, regResult, "DemulShooter_RawInputWnd", 0, 0, 0, 0, 0, HWND_MESSAGE, IntPtr.Zero, wind_class.hInstance, IntPtr.Zero); if (_RawMessageWnd_hWnd == ((IntPtr)0)) { uint error = Win32API.GetLastError(); return false; } return true; //The explicit message pump is not necessary, messages are obviously dispatched by the framework. //However, if the while loop is implemented, the functions are called... Windows mysteries... //MSG msg; //while (GetMessage(out msg, IntPtr.Zero, 0, 0) != 0) //{ // TranslateMessage(ref msg); // DispatchMessage(ref msg); //} } /// /// Window Message-Loop /// private IntPtr myWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { if (Configurator.GetInstance().OutputEnabled && Configurator.GetInstance().Wm_OutputEnabled && _Wm_OutputHelper != null) { if (msg == _Wm_OutputHelper.MameOutput_RegisterClient) { _Wm_OutputHelper.RegisterClient(wParam, (UInt32)lParam); } else if (msg == _Wm_OutputHelper.MameOutput_UnregisterClient) { _Wm_OutputHelper.UnregisterClient(wParam, (UInt32)lParam); } else if (msg == _Wm_OutputHelper.MameOutput_GetIdString) { uint Id = (uint)lParam; Logger.WriteLog("MameHooker GetIdString message received for ID=" + Id.ToString()); if (Id == 0) { /*if (_Game.ProcessHooked) _Wm_OutputHelper.SendIdString(wParam, _Rom, 0); else _Wm_OutputHelper.SendIdString(wParam, "___empty", 0);*/ _Wm_OutputHelper.SendIdString(wParam, _Rom, 0); _Wm_OutputHelper.RomNameSent = true; } else { if (_Game != null && _Game.Outputs.Count > 0) { String s = _Game.GetOutputDescriptionFromId(Id); _Wm_OutputHelper.SendIdString(wParam, s, Id); } } } } switch (msg) { case Win32Define.WM_INPUT: { ProcessRawInputMessage(lParam); } break; case Win32Define.WM_QUIT: { Logger.WriteLog("myWndProc() => WM_QUIT message received !"); } break; default: break; } return Win32API.DefWindowProc(hWnd, msg, wParam, lParam); } /// /// Processing of the RawInput message : /// - Detection of the device creating the event /// - Retrieve data /// - Scale Raw axis values to Screen -> ClientWindow -> Game values /// - Send axis values and buttons to the Game /// /// private void ProcessRawInputMessage(IntPtr RawInputHandle) { foreach (RawInputController Controller in _AvailableControllers) { if (Controller.isSourceOfRawInputMessage(RawInputHandle)) { foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { if (Player.DeviceName == Controller.DeviceName) { if (_Game != null && _Game.ProcessHooked) { Controller.ProcessRawInputData(RawInputHandle); Logger.WriteLog("RawData event for Player #" + Player.ID.ToString() + ":"); Logger.WriteLog("Device rawinput data (Hex) = [ " + Player.RIController.Computed_X.ToString("X8") + ", " + Player.RIController.Computed_Y.ToString("X8") + " ]"); //Overrriding RAWINPUT data (relative movement) for single mouse by a call to GetCursorPos WIN32 API //That way we can get mouse position as if it's Absolute position if (Controller.DeviceType == RawInputDeviceType.RIM_TYPEMOUSE && Controller.IsRelativeCoordinates) { Logger.WriteLog("Relative positionning detected, switching to cursor position :"); POINT p = new POINT(); if (Win32API.GetCursorPos(out p)) { Logger.WriteLog("WM_MOUSEMOVE Cursor position = [ " + p.X + "," + p.Y + " ]"); Player.RIController.Computed_X = p.X; Player.RIController.Computed_Y = p.Y; Logger.WriteLog("OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); } else { Logger.WriteLog("WM_MOUSEMOVE : GetCursorPos() returned error"); return; } } if (_EnableInputsIpc) _MMF_Inputs.UpdateRawPlayerData(Player.ID, (UInt32)Player.RIController.Computed_X, (UInt32)Player.RIController.Computed_Y); _Game.GetScreenResolution(); Logger.WriteLog("PrimaryScreen Size (Px) = [ " + _Game.ScreenWidth + "x" + _Game.ScreenHeight + " ]"); if (!Controller.IsRelativeCoordinates) { //If manual calibration override for analog guns if (Player.RIController.DeviceType == RawInputDeviceType.RIM_TYPEHID && Player.AnalogAxisRangeOverride) { Logger.WriteLog("Overriding player axis range values : X => [ " + Player.AnalogManual_Xmin.ToString() + ", " + Player.AnalogManual_Xmax.ToString() + " ], Y => [ " + Player.AnalogManual_Ymin.ToString() + ", " + Player.AnalogManual_Ymax.ToString() + " ]"); Player.RIController.Computed_X = _Game.ScreenScale(Player.RIController.Computed_X, Player.AnalogManual_Xmin, Player.AnalogManual_Xmax, 0, _Game.ScreenWidth); Player.RIController.Computed_Y = _Game.ScreenScale(Player.RIController.Computed_Y, Player.AnalogManual_Ymin, Player.AnalogManual_Ymax, 0, _Game.ScreenHeight); } else { Player.RIController.Computed_X = _Game.ScreenScale(Player.RIController.Computed_X, Player.RIController.Axis_X_Min, Player.RIController.Axis_X_Max, 0, _Game.ScreenWidth); Player.RIController.Computed_Y = _Game.ScreenScale(Player.RIController.Computed_Y, Player.RIController.Axis_Y_Min, Player.RIController.Axis_Y_Max, 0, _Game.ScreenHeight); } //Optionnal invert axis if (Player.InvertAxis_X) Player.RIController.Computed_X = _Game.ScreenWidth - Player.RIController.Computed_X; if (Player.InvertAxis_Y) Player.RIController.Computed_Y = _Game.ScreenHeight - Player.RIController.Computed_Y; Logger.WriteLog("OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); if (Configurator.GetInstance().Act_Labs_Offset_Enable) { Player.RIController.Computed_X += Player.Act_Labs_Offset_X; Player.RIController.Computed_Y += Player.Act_Labs_Offset_Y; Logger.WriteLog("ActLabs adaptated OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); } } //Change X asxis scaling based on user requirements if (_ForceScalingX != 1.0) { Logger.WriteLog("Forcing X Scaling = " + _ForceScalingX.ToString()); double HalfScreenSize = (double)_Game.ScreenWidth / 2.0; double NewX = (((double)Player.RIController.Computed_X - HalfScreenSize) * _ForceScalingX) + HalfScreenSize; Player.RIController.Computed_X = Convert.ToInt32(NewX); Logger.WriteLog("Forced scaled OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); } _Game.IsFullscreen = _Game.GetFullscreenStatus(); if (!_Game.IsFullscreen) { Logger.WriteLog("ClientWindow Style = Windowed"); //Retrieve info for debug/replay purpose _Game.GetClientwindowInfo(); if (!_Game.ClientScale(Player)) { Logger.WriteLog("Error converting screen location to client location"); return; } Logger.WriteLog("ClientWindow Location (px) = [ " + _Game.clientWindowLocation.X.ToString() + ", " + _Game.clientWindowLocation.Y.ToString() + " ]"); Logger.WriteLog("ClientWindow Size (px) = [ " + (_Game.WindowRect.Right - _Game.WindowRect.Left).ToString() + "x" + (_Game.WindowRect.Bottom - _Game.WindowRect.Top).ToString() + " ]"); Logger.WriteLog("OnClient Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); if (!_Game.GetClientRect()) { Logger.WriteLog("Error getting client Rect"); return; } } else { Logger.WriteLog("ClientWindow Style = FullScreen"); //No need to translate coordinates from screen -> client and risk error. //As fuulscreen, we will consider window size = screen size Rect r = new Rect(); r.Top = 0; r.Left = 0; r.Bottom = _Game.ScreenHeight; r.Right = _Game.ScreenWidth; _Game.ClientRect = r; } if (!_Game.GameScale(Player)) { Logger.WriteLog("Error converting client location to game location"); return; } Logger.WriteLog("Game Position (Hex) = [ " + Player.RIController.Computed_X.ToString("X4") + ", " + Player.RIController.Computed_Y.ToString("X4") + " ]"); Logger.WriteLog("Game Position (Dec) = [ " + Player.RIController.Computed_X.ToString() + ", " + Player.RIController.Computed_Y.ToString() + " ]"); if (Controller.Computed_Buttons != 0) Logger.WriteLog("Controller Buttons Events : " + Player.RIController.Computed_Buttons.ToString()); Logger.WriteLog("-"); if (!_NoInput) _Game.SendInput(Player); if (_EnableInputsIpc) { _MMF_Inputs.UpdateComputedPlayerData(Player.ID, Player.RIController.Computed_X, Player.RIController.Computed_Y, Player.RIController.Hid_Buttons); if (_MMF_Inputs.WriteData() != 0) Logger.WriteLog("Succesfully copied P" + Player.ID.ToString() + " data to MMF " + _MMF_Inputs.MemoryFileName); } } } } } } } /// /// Output handling thread : /// This infinite loop will check the targeted game values and send them to registerd Output clients /// private void ReadAndSendOutput_Thread() { while (true) { if (_Game != null && _Game.ProcessHooked) { _Game.UpdateOutputValues(); if (Configurator.GetInstance().Wm_OutputEnabled && _Wm_OutputHelper != null && _Wm_OutputHelper.RomNameSent) _Wm_OutputHelper.SendValues(_Game.Outputs); if (Configurator.GetInstance().Net_OutputEnabled && _Net_OutputHelper!= null) _Net_OutputHelper.BroadcastValues(_Game.Outputs); } DsCore.Win32.Win32API.MM_BeginPeriod(1); Thread.Sleep(Configurator.GetInstance().OutputPollingDelay); DsCore.Win32.Win32API.MM_EndPeriod(1); } } /// /// GUI Init : mostly TrayIcon + Menu /// private void InitializeComponent() { //Tray Icon //Looking for explorer.exe process, if not skip TrayIcon to make the program work Process[] pExplorer = Process.GetProcessesByName("explorer"); if (pExplorer.Length > 0) { _TrayIcon = new NotifyIcon(); _TrayIcon.Text = "DemulShooter"; _TrayIcon.Icon = DemulShooter.Properties.Resources.DemulShooter_UnHooked_Icon; _TrayIconMenu = new ContextMenu(); _TrayIconMenu.MenuItems.Add("Exit", OnTrayExitSelected); _TrayIcon.ContextMenu = _TrayIconMenu; _TrayIcon.Visible = true; } } /// /// Exit from TrayIcon menu entry /// private void OnTrayExitSelected(object sender, EventArgs e) { Application.Exit(); } private void OnGameHooked(object sender, EventArgs e) { if (_TrayIcon != null) { _TrayIcon.Icon = DemulShooter.Properties.Resources.DemulShooter_Hooked_Icon; _TrayIcon.Text += "[Hooked]"; } //Stopping the Timeout timer _TimerHookTimeout.Stop(); if (_Wm_OutputHelper != null) { _Game.SetMamePauseState(false); _Wm_OutputHelper.SendValues(_Game.Outputs); } //Sending info to the Net_OutputHelper if existing if (_Net_OutputHelper != null) { _Net_OutputHelper.SetGameHookedState(true); _Net_OutputHelper.BroadcatStartMessage(); } } private void OnApplicationExit(object sender, EventArgs e) { Logger.WriteLog("Cleaning things before exiting application..."); CleanAppBeforeExit(); } /// /// Application Exit cleanup /// private void CleanAppBeforeExit() { if (_OutputUpdateLoop != null) _OutputUpdateLoop.Abort(); if (_Wm_OutputHelper != null) { //Simply sending MameStop may cause MameHooker to not release dll properly //MAME goes from MameStop to MameStart with PAUSE enabled and "__empty" rom //Which is not possible here due to the WndProc quitting before getting MameHooker request ?? // Solution would be to Send a new MameStart with no values before MAmeStopping again _Game.SetMamePauseState(true); _Wm_OutputHelper.SendValues(_Game.Outputs); _Wm_OutputHelper.Stop(); _Wm_OutputHelper.Start(); _Wm_OutputHelper.Stop(); } if (_Net_OutputHelper != null) { _Net_OutputHelper.Stop(); } //Cleanup so that the icon will be removed when the application is closed if (_TrayIcon != null) { _TrayIcon.Visible = false; _TrayIcon.Dispose(); } } /// /// Low-level mouse hook. /// Some game will need this to block mouse inputs so that we can inject our own values. /// protected void ApplyMouseHook() { _MouseHookProc = new Win32API.HookProc(MouseHookCallback); using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) _MouseHookID = Win32API.SetWindowsHookEx(Win32Define.WH_MOUSE_LL, _MouseHookProc, Win32API.GetModuleHandle(curModule.ModuleName), 0); if (_MouseHookID == IntPtr.Zero) { Logger.WriteLog("MouseHook Error : " + Marshal.GetLastWin32Error()); } else { Logger.WriteLog("LowLevelMouseHook installed !"); } } private IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { /*if (_UseSingleMouse) { if (nCode >= 0 && (UInt32)wParam == Win32Define.WM_MOUSEMOVE) { MSLLHOOKSTRUCT s = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); int x = s.pt.X; int y = s.pt.Y; } }*/ if (_Game != null && _Game.ProcessHooked) return _Game.MouseHookCallback(_MouseHookID, nCode, wParam, lParam); else return Win32API.CallNextHookEx(_MouseHookID, nCode, wParam, lParam); } protected void RemoveMouseHook() { Win32API.UnhookWindowsHookEx(_MouseHookID); } /// /// Low-level Keyboard hook. /// protected void ApplyKeyboardHook() { _KeyboardHookProc = new Win32API.HookProc(KeyboardHookCallback); using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) _KeyboardHookID = Win32API.SetWindowsHookEx(Win32Define.WH_KEYBOARD_LL, _KeyboardHookProc, Win32API.GetModuleHandle(curModule.ModuleName), 0); if (_KeyboardHookID == IntPtr.Zero) { Logger.WriteLog("KeyboardHook Error : " + Marshal.GetLastWin32Error()); } else { Logger.WriteLog("LowLevel-KeyboardHook installed !"); } } protected virtual IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (_Game != null && _Game.ProcessHooked && !_NoInput) { try { Logger.WriteLog("KeyboardHook Event : wParam = 0x " + wParam.ToString("X8") + ", lParam = 0x" + lParam.ToString("X8")); //First step : use the Hook to determine if a virtual Middle/Right button has been pushed if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { Logger.WriteLog("KeyboardHook Event : WM_KEYDOWN event detected"); KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); Logger.WriteLog("KBDLLHOOKSTRUCT : " + s.ToString()); foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { if (Player.isVirtualMouseButtonsEnabled && Player.RIController != null) { if (s.scanCode == Player.DIK_VirtualMouseButton_Left) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Left detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OnScreenTriggerDown; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Middle) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Middle detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.ActionDown; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Right) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Right detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OffScreenTriggerDown; _Game.SendInput(Player); } } } Logger.WriteLog("-"); } if ((UInt32)wParam == Win32Define.WM_KEYUP) { Logger.WriteLog("KeyboardHook Event : WM_KEYUP event detected"); KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); Logger.WriteLog("KBDLLHOOKSTRUCT : " + s.ToString()); foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { if (Player.isVirtualMouseButtonsEnabled && Player.RIController != null) { if (s.scanCode == Player.DIK_VirtualMouseButton_Left) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Left detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OnScreenTriggerUp; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Middle) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Middle detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.ActionUp; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Right) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Right detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OffScreenTriggerUp; _Game.SendInput(Player); } } } Logger.WriteLog("-"); } //Second step : forward the event to the Game return _Game.KeyboardHookCallback(_MouseHookID, nCode, wParam, lParam); } catch (Exception Ex) { Logger.WriteLog("Error handling KeyboardHookCallback : " + Ex.Message.ToString()); return _Game.KeyboardHookCallback(_MouseHookID, nCode, wParam, lParam); } } else return Win32API.CallNextHookEx(_KeyboardHookID, nCode, wParam, lParam); } protected void RemoveKeyboardHook() { Win32API.UnhookWindowsHookEx(_KeyboardHookID); } //quit application if TimeOut enabled for game hooking private void tHookTimeOut_Elapsed(Object Sender, EventArgs e) { Logger.WriteLog("Hook timeout expired, exiting application."); CleanAppBeforeExit(); Environment.Exit(0); } } } ================================================ FILE: DemulShooter/Ds.settings ================================================  coucou 50 ================================================ FILE: DemulShooter/Game_WndSpray.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndSpray : Game { //Memory values private InjectionStruct _CoinKeyCode_InjectionStruct = new InjectionStruct(0x0000A45A, 5); private NopStruct _Nop_P1IoControls = new NopStruct(0x0000AAA4, 5); private NopStruct _Nop_P2IoControls = new NopStruct(0x0000AA95, 5); private NopStruct _Nop_MouseAxis = new NopStruct(0x0000A401, 6); private UInt32 _Patch_MouseButtons_Offset = 0x0000A3B9; private UInt32 _P1_X_Offset = 0x0005AC8C; private UInt32 _P1_Y_Offset = 0x0005AC90; private UInt32 _P1_Trigger_Offset = 0x0005AC94; private UInt32 _P2_X_Offset = 0x0005AC70; private UInt32 _P2_Y_Offset = 0x0005AC74; private UInt32 _P2_Trigger_Offset = 0x0005AC78; private UInt32 _P1_Life_Offset = 0x00055094; private UInt32 _P2_Life_Offset = 0x00055098; private UInt32 _P1_Ammo_Offset = 0x000520A0; //private UInt32 _P2_Ammo_Offset = 0x000520A0; /// /// Constructor /// public Game_WndSpray(String RomName) : base(RomName, "SprayGun") { _KnownMd5Prints.Add("Spray v1.0.0.1 - Original exe", "3f73bd67f4247900c12eea7932ea1517"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-640] //Y => [0-480] double dMaxX = 640.0; double dMaxY = 480.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them. /// protected override void Apply_InputsMemoryHack() { SetHack_CoinKey(); //Disabling the calls to the functions overriding controls with IO board //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1IoControls); //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2IoControls); //Removing Mouse events to have full controls on P1 //Shunt WM buttons message tests WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Patch_MouseButtons_Offset, new byte[]{ 0xEB, 0x0D }); //Same thing for Axis SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_MouseAxis); /*SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_LbuttonUp); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_RbuttonDown); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_RbuttonUp); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_MouseMove); //If a gamepad is detected, looks like it's looping and overriding values //This removes the whole loop, maybe just noping +10F5E / +10F70 is enough and less risky ? SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_GamePadLoop); //Some other kind of devices reset buttons states too SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_HidLoopTrigger_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_HidLoopTrigger_2); //By default, this PC-version force the coins counter to 9 when displaying the start screen. Removing it.... SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_InitCoinsCounter); if (_HideCrosshair) SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_ShowCrosshairInGame);*/ Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Coin KEY is set to ENTER and Handled in the message loop. /// The original code is switching from the parameter value, and not simply comparing it, so we need to change the value before the switch. /// private void SetHack_CoinKey() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp dword ptr [ebp+10],35 CaveMemory.Write_StrBytes("83 7D 10 35"); //jne Next CaveMemory.Write_StrBytes("75 04"); //mov [ebp+10],0000000D CaveMemory.Write_StrBytes("C6 45 10 0D"); //movsx edx,word ptr [ebp+14] CaveMemory.Write_StrBytes("0F BF 55 14"); //push edx CaveMemory.Write_StrBytes("52"); //Inject it CaveMemory.InjectToOffset(_CoinKeyCode_InjectionStruct, "Coin KEY"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x00); } //To reload, set back Gaz tank capacity to 1.0f (full) if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset, new byte[] { 0x00, 0x00, 0x80, 0x3F }); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { // } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset, new byte[] { 0x00, 0x00, 0x80, 0x3F }); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { // } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x00); } //To reload, set back Gaz tank capacity to 1.0f (full) /*if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset, new byte[] { 0x00, 0x00, 0x80, 0x3F }); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { // } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset, new byte[] { 0x00, 0x00, 0x80, 0x3F }); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { // }*/ } } #endregion } } ================================================ FILE: DemulShooter/Games/Game.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Timers; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.Win32; namespace DemulShooter { public class Game { public delegate void GameHookedHandler(object sender, EventArgs e); public event GameHookedHandler OnGameHooked; //Games options protected bool _NoAutoFire = false; protected bool _HideCrosshair = false; protected bool _HideGuns = false; protected bool _DisableInputHack = false; protected bool _DisableWindow = false; protected string _CustomTargetProcessName = string.Empty; protected bool _VerboseEnable; protected bool _WidescreenHack = false ; #region Process variables protected System.Timers.Timer _tProcess; protected String _RomName = string.Empty; protected Process _TargetProcess; protected String _Target_Process_Name = String.Empty; protected IntPtr _TargetProcess_MemoryBaseAddress = IntPtr.Zero; protected IntPtr _ProcessHandle = IntPtr.Zero; protected IntPtr _GameWindowHandle = IntPtr.Zero; //MD5 check of target binaries, may help to know if it's the wrong version or not compatible protected Dictionary _KnownMd5Prints; protected String _TargetProcess_Md5Hash = string.Empty; //Output values handling protected List _Outputs; public List Outputs { get { return _Outputs; } } protected bool _ProcessHooked; public bool ProcessHooked { get { return _ProcessHooked; } } #endregion #region Custom Databanks protected UInt32 _InputsDatabank_Address = 0; protected UInt32 _OutputsDatabank_Address = 0; #endregion #region Custom Outputs protected int _P1_Life = 0; protected int _P2_Life = 0; protected int _P3_Life = 0; protected int _P4_Life = 0; protected int _P1_Ammo = 0; protected int _P2_Ammo = 0; protected int _P3_Ammo = 0; protected int _P4_Ammo = 0; protected int _P1_LastLife = 0; protected int _P2_LastLife = 0; protected int _P3_LastLife = 0; protected int _P4_LastLife = 0; protected int _P1_LastAmmo = 0; protected int _P2_LastAmmo = 0; protected int _P3_LastAmmo = 0; protected int _P4_LastAmmo = 0; #endregion /// /// Common constructor for every single games /// /// DemumShooter [-rom] Parameter /// Executable name to hook in process list /// Create a debug.txt file if TRUE public Game(String RomName, String TargetProcessName) { GetGameOptions(); _KnownMd5Prints = new Dictionary(); GetScreenResolution(); Logger.WriteLog("Windows screen scaling : " + GetScreenScaling()); _WindowRect = new Rect(); _clientWindowLocation = new POINT(0, 0); _RomName = RomName; _ProcessHooked = false; _Target_Process_Name = _CustomTargetProcessName == string.Empty ? TargetProcessName : _CustomTargetProcessName; Logger.WriteLog("Target process name : " + _Target_Process_Name + ".exe"); CreateOutputList(); _tProcess = new System.Timers.Timer(); _tProcess.Interval = 500; _tProcess.Elapsed += new ElapsedEventHandler(tProcess_Elapsed); _tProcess.Enabled = true; } ~Game() { if (_XOutputManager != null) { if (_Player1_X360_Gamepad_Port != 0) UninstallX360Gamepad(1); if (_Player2_X360_Gamepad_Port != 0) UninstallX360Gamepad(2); } } protected virtual void tProcess_Elapsed(Object sender, EventArgs e) {} /// /// Raise custom event to main window (to change TrayIcon status) /// protected void RaiseGameHookedEvent() { // Make sure someone is listening to event if (OnGameHooked == null) return; OnGameHooked(this, new EventArgs()); } private void GetGameOptions() { string[] sArgs = Environment.GetCommandLineArgs(); foreach (string sOption in sArgs) { if (sOption.ToLower().Equals("-noautofire")) { _NoAutoFire = true; } else if (sOption.ToLower().Equals("-nocrosshair")) { _HideCrosshair = true; } else if (sOption.ToLower().Equals("-nogun")) { _HideGuns = true; } else if (sOption.ToLower().Equals("-noinput")) { _DisableInputHack = true; } else if (sOption.ToLower().Equals("-noresize")) { _DisableWindow = true; } else if (sOption.ToLower().StartsWith("-pname=")) { _CustomTargetProcessName = (sOption.ToLower().Split('='))[1].Trim(); if (_CustomTargetProcessName.EndsWith(".exe")) _CustomTargetProcessName = _CustomTargetProcessName.Substring(0, _CustomTargetProcessName.Length - 4); } else if (sOption.ToLower().Equals("-v")) { _VerboseEnable = true; } else if (sOption.ToLower().Equals("-widescreen")) { _WidescreenHack = true; } } } #region MD5 Verification /// /// Compute the MD5 hash of the target executable and compare it to the known list of MD5 Hashes /// This can be usefull if people are using some unknown dump with different memory, /// or a wrong version of emulator /// This is absolutely not blocking, just for debuging with output log /// protected void CheckExeMd5() { CheckMd5(_TargetProcess.MainModule.FileName); } protected void CheckMd5(String TargetFileName) { GetMd5HashAsString(TargetFileName); Logger.WriteLog("MD5 hash of " + TargetFileName + " = " + _TargetProcess_Md5Hash); String FoundMd5 = String.Empty; foreach (KeyValuePair pair in _KnownMd5Prints) { if (pair.Value.ToLower() == _TargetProcess_Md5Hash) { FoundMd5 = pair.Key; break; } } if (FoundMd5 == String.Empty) { Logger.WriteLog(@"/!\ MD5 Hash unknown, DemulShooter may not work correctly with this target /!\"); } else { Logger.WriteLog("MD5 Hash is corresponding to a known target = " + FoundMd5); } } /// /// Compute the MD5 hash from the target file. /// /// Full filepath of the targeted executable. private void GetMd5HashAsString(String FileName) { if (File.Exists(FileName)) { using (var md5 = MD5.Create()) { using (var stream = File.OpenRead(FileName)) { var hash = md5.ComputeHash(stream); _TargetProcess_Md5Hash = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } } } } #endregion #region Screen protected int _screenWidth; public int ScreenWidth { get { return _screenWidth; } } protected int _screenHeight; public int ScreenHeight { get { return _screenHeight; } } protected int _screenCursorPosX; public int screenCursorPosX { get { return _screenCursorPosX; } set { _screenCursorPosX = value; } } protected int _screenCursorPosY; public int screenCursorPosY { get { return _screenCursorPosY; } set { _screenCursorPosY = value; } } protected Rect _WindowRect; public Rect WindowRect { get { return _WindowRect; } set { _WindowRect = value; } } protected POINT _clientWindowLocation; public POINT clientWindowLocation { get { return _clientWindowLocation; } set { _clientWindowLocation = value; } } protected Rect _ClientRect; public Rect ClientRect { get { return _ClientRect; } set { _ClientRect = value; } } protected bool _IsFullscreen; public bool IsFullscreen { get { return _IsFullscreen; } set { _IsFullscreen = value; } } /// /// HKEY_CURRENT_USER\Control Panel\Desktop\ ///Key: LogPixels ///Values: ///96 – Smaller 100% ///120 – Medium 125% ///144 - Larger 150% ///192 – Extra Large 200% ///240 – Custom 250% ///288 – Custom 300% ///384 – Custom 400% ///480 – Custom 500%*/ /// private string GetScreenScaling() { IntPtr desktopDc = Win32API.GetDC(IntPtr.Zero); // Get native resolution //#DEFINE LOGPIXELSX 88 //#DEFINE LOGPIXELSY 90 int horizontalDPI = Win32API.GetDeviceCaps(desktopDc, 88); int verticalDPI = Win32API.GetDeviceCaps(desktopDc, 90); return (horizontalDPI * 100 / 96).ToString() + "% (HorizontalDPI=" + horizontalDPI + ", VerticalDPI=" + verticalDPI + ")"; /*switch (horizontalDPI.ToString()) { case "96": return "100%"; case "120": return "125%"; case "144": return "150%"; case "192": return "200%"; case "240": return "250%"; case "288": return "300%"; case "384": return "400%"; case "480": return "500%"; default: return @"Unknown value : " + horizontalDPI.ToString(); }*/ } public virtual bool GetFullscreenStatus() { QUERY_USER_NOTIFICATION_STATE state; int r = Win32API.SHQueryUserNotificationState(out state); if (r == 0) { Logger.WriteLog("NotificationState: " + state.ToString()); switch (state) { // A screen saver is displayed, the machine is locked, or a nonactive Fast User Switching session is in progress. case QUERY_USER_NOTIFICATION_STATE.QUNS_NOT_PRESENT: return false; // A full-screen application is running or Presentation Settings are applied. Presentation Settings allow a user to put their machine into a state fit for an uninterrupted presentation, such as a set of PowerPoint slides, with a single click. case QUERY_USER_NOTIFICATION_STATE.QUNS_BUSY: return true; // A full-screen (exclusive mode) Direct3D application is running. case QUERY_USER_NOTIFICATION_STATE.QUNS_RUNNING_D3D_FULL_SCREEN: return true; // The user has activated Windows presentation settings to block notifications and pop-up messages. case QUERY_USER_NOTIFICATION_STATE.QUNS_PRESENTATION_MODE: return false; // None of the other states are found, notifications can be freely sent. case QUERY_USER_NOTIFICATION_STATE.QUNS_ACCEPTS_NOTIFICATIONS: return false; // We are in OOBE quiet period case QUERY_USER_NOTIFICATION_STATE.QUNS_QUIET_TIME: return false; // A Windows Store app is running. case QUERY_USER_NOTIFICATION_STATE.QUNS_APP: return false; default: return false; } } else { Logger.WriteLog("Error running SHQueryUserNotificationState: " + r.ToString()); return false; } } public virtual void GetScreenResolution() { _screenWidth = Win32API.GetSystemMetrics(SystemMetricsIndex.SM_CXSCREEN); _screenHeight = Win32API.GetSystemMetrics(SystemMetricsIndex.SM_CYSCREEN); /*_screenWidth = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; _screenHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;*/ } public void GetScreenresolution2() { IntPtr hDesktop = Win32API.GetDesktopWindow(); Rect DesktopRect = new Rect(); Win32API.GetWindowRect(hDesktop, ref DesktopRect); _screenWidth = DesktopRect.Right; _screenHeight = DesktopRect.Bottom; } /// /// Contains value inside min-max range /// protected int Clamp(int val, int minVal, int maxVal) { if (val > maxVal) return maxVal; else if (val < minVal) return minVal; else return val; } /// /// Transforming 0x0000-0xFFFF absolute rawdata to absolute x,y position on Desktop resolution /// public int ScreenScale(int val, int fromMinVal, int fromMaxVal, int toMinVal, int toMaxVal) { return ScreenScale(val, fromMinVal, fromMinVal, fromMaxVal, toMinVal, toMinVal, toMaxVal); } protected int ScreenScale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal) { double fromRange; double frac; if (fromMaxVal > fromMinVal) { val = Clamp(val, fromMinVal, fromMaxVal); if (val > fromOffVal) { fromRange = (double)(fromMaxVal - fromOffVal); frac = (double)(val - fromOffVal) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMinVal); frac = (double)(val - fromOffVal) / fromRange; } else return toOffVal; } else if (fromMinVal > fromMaxVal) { val = Clamp(val, fromMaxVal, fromMinVal); if (val > fromOffVal) { fromRange = (double)(fromMinVal - fromOffVal); frac = (double)(fromOffVal - val) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMaxVal); frac = (double)(fromOffVal - val) / fromRange; } else return toOffVal; } else return toOffVal; double toRange; if (toMaxVal > toMinVal) { if (frac >= 0) toRange = (double)(toMaxVal - toOffVal); else toRange = (double)(toOffVal - toMinVal); return toOffVal + (int)(toRange * frac); } else { if (frac >= 0) toRange = (double)(toOffVal - toMaxVal); else toRange = (double)(toMinVal - toOffVal); return toOffVal - (int)(toRange * frac); } } /// /// Convert screen location of pointer to Client area location /// public virtual bool ClientScale(PlayerSettings PlayerData) { //Retrieve info for debug/replay purpose GetClientwindowInfo(); //Convert Screen location to Client location if (_TargetProcess != null) { POINT p = new POINT(PlayerData.RIController.Computed_X, PlayerData.RIController.Computed_Y); if (Win32API.ScreenToClient(_GameWindowHandle, ref p)) { PlayerData.RIController.Computed_X = (p.X); PlayerData.RIController.Computed_Y = (p.Y); return true; } else return false; } else return false; } public void GetClientwindowInfo() { if (_TargetProcess != null) { if (Win32API.GetWindowRect(_GameWindowHandle, ref _WindowRect)) { _clientWindowLocation.X = _WindowRect.Left; _clientWindowLocation.Y = _WindowRect.Top; } else { _clientWindowLocation.X = 0; _clientWindowLocation.Y = 0; } } } /// /// Get the Client rect to translate axis coordinate to game coordinates /// Only used in windowed Mode /// public virtual bool GetClientRect() { //Window size _ClientRect = new Rect(); if (Win32API.GetClientRect(_GameWindowHandle, ref _ClientRect)) return true; else return false; } /// /// Convert client area pointer location to Game speciffic data for memory injection /// public virtual bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)TotalResX) PlayerData.RIController.Computed_X= (int)TotalResX; if (PlayerData.RIController.Computed_Y > (int)TotalResX) PlayerData.RIController.Computed_X = (int)TotalResY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Game Configuration /// /// Read memory values in .cfg file, whose name depends on the targeted system /// Used for DEMUL targets /// protected virtual void ReadGameData() { } /// /// Read memory values in .cfg file, whose name depends on the MD5 hash of the targeted exe. /// Mostly used for PC games /// /// protected virtual void ReadGameDataFromMd5Hash(String GameData_Folder) { String ConfigFile = AppDomain.CurrentDomain.BaseDirectory + GameData_Folder + @"\" + _TargetProcess_Md5Hash + ".cfg"; if (File.Exists(ConfigFile)) { Logger.WriteLog("Reading game memory setting from " + ConfigFile); using (StreamReader sr = new StreamReader(ConfigFile)) { String line; String FieldName = String.Empty; line = sr.ReadLine(); while (line != null) { if (!line.StartsWith(";")) { String[] buffer = line.Split('='); if (buffer.Length > 1) { try { FieldName = "_" + buffer[0].Trim(); if (buffer[0].Contains("Nop")) { NopStruct n = new NopStruct(buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, n); Logger.WriteLog(FieldName + " successfully set to following value : 0x" + n.MemoryOffset.ToString("X8") + "|" + n.Length.ToString()); } else if (buffer[0].Contains("InjectionStruct")) { InjectionStruct n = new InjectionStruct(buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, n); Logger.WriteLog(FieldName + " successfully set to following value : 0x" + n.InjectionOffset.ToString("X8") + "|" + n.Length.ToString()); } else if (buffer[0].Contains("DIK")) { HardwareScanCode sc = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, sc); Logger.WriteLog(FieldName + " successfully set to following value :" + sc.ToString()); } else if (buffer[0].Contains("VK")) { VirtualKeyCode vk = (VirtualKeyCode)Enum.Parse(typeof(VirtualKeyCode), buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, vk); Logger.WriteLog(FieldName + " successfully set to following value :" + vk.ToString()); } else { UInt32 v = UInt32.Parse(buffer[1].Substring(3).Trim(), NumberStyles.HexNumber); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, v); Logger.WriteLog(FieldName + " successfully set to following value : 0x" + v.ToString("X8")); } } catch (Exception ex) { Logger.WriteLog("Error reading game data for " + FieldName + " : " + ex.Message.ToString()); } } } line = sr.ReadLine(); } sr.Close(); } } else { Logger.WriteLog("File not found : " + ConfigFile); } } #endregion #region Memory Hack x86 protected virtual void Apply_MemoryHacks() { if (!_DisableInputHack) Apply_InputsMemoryHack(); else Logger.WriteLog("Input Hack disabled"); Apply_OutputsMemoryHack(); if (_HideCrosshair) { Logger.WriteLog("Applying No-Crosshair hack..."); Apply_NoCrosshairMemoryHack(); } } protected virtual void Apply_InputsMemoryHack() {} protected virtual void Create_InputsDataBank() { try { Codecave CaveMemoryInputs = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemoryInputs.Open(); CaveMemoryInputs.Alloc(0x800); _InputsDatabank_Address = CaveMemoryInputs.CaveAddress; Logger.WriteLog("Custom input data will be stored at : 0x" + CaveMemoryInputs.CaveAddress.ToString("X8")); } catch (Exception Ex) { Logger.WriteLog("Impossible to create Inputs Databank : " + Ex.Message); } } protected virtual void Apply_OutputsMemoryHack() {} protected virtual void Create_OutputsDataBank() { try{ Codecave CaveMemoryOutputs = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemoryOutputs.Open(); CaveMemoryOutputs.Alloc(0x800); _OutputsDatabank_Address = CaveMemoryOutputs.CaveAddress; Logger.WriteLog("Custom output data will be stored at : 0x" + CaveMemoryOutputs.CaveAddress.ToString("X8")); } catch (Exception Ex) { Logger.WriteLog("Impossible to create Outputs Databank : " + Ex.Message); } } protected virtual void Apply_NoCrosshairMemoryHack() {} public virtual void SendInput(PlayerSettings PlayerData) {} protected Byte ReadByte(UInt32 Address) { byte[] Buffer = { 0 }; UInt32 bytesRead = 0; if (!Win32API.ReadProcessMemory(_ProcessHandle, Address, Buffer, 1, ref bytesRead)) { Logger.WriteLog("Cannot read memory at address 0x" + Address.ToString("X8")); } return Buffer[0]; } protected Byte[] ReadBytes(UInt32 Address, UInt32 BytesCount) { byte[] Buffer = new byte[BytesCount]; UInt32 bytesRead = 0; if (!Win32API.ReadProcessMemory(_ProcessHandle, Address, Buffer, (UInt32)Buffer.Length, ref bytesRead)) { Logger.WriteLog("Cannot read memory at address 0x" + Address.ToString("X8")); } return Buffer; } protected UInt32 ReadPtr(UInt32 PtrAddress) { byte[] Buffer = ReadBytes(PtrAddress, 4); return BitConverter.ToUInt32(Buffer, 0); } protected UInt32 ReadPtrChain(UInt32 BaseAddress, UInt32[] Offsets) { byte[] Buffer = ReadBytes(BaseAddress, 4); UInt32 Ptr = BitConverter.ToUInt32(Buffer, 0); if (Ptr == 0) { return 0; } else { for (int i = 0; i < Offsets.Length; i++) { Buffer = ReadBytes(Ptr + Offsets[i], 4); Ptr = BitConverter.ToUInt32(Buffer, 0); if (Ptr == 0) return 0; } } return Ptr; } protected bool WriteByte(UInt32 Address, byte Value) { UInt32 bytesWritten = 0; Byte[] Buffer = { Value }; if (Win32API.WriteProcessMemory(_ProcessHandle, Address, Buffer, 1, ref bytesWritten)) { if (bytesWritten == 1) return true; else return false; } else return false; } protected bool WriteBytes(UInt32 Address, byte[] Buffer) { UInt32 bytesWritten = 0; if (Win32API.WriteProcessMemory(_ProcessHandle, Address, Buffer, (UInt32)Buffer.Length, ref bytesWritten)) { if (bytesWritten == Buffer.Length) return true; else return false; } else return false; } protected void SetNops(UInt32 BaseAddress, NopStruct Nop) { for (UInt32 i = 0; i < Nop.Length; i++) { UInt32 Address = (UInt32)BaseAddress + Nop.MemoryOffset + i; if (!WriteByte(Address, 0x90)) { Logger.WriteLog("Impossible to NOP address 0x" + Address.ToString("X8")); break; } } } protected void Apply_OR_ByteMask(UInt32 MemoryAddress, byte Mask) { byte b = ReadByte(MemoryAddress); b |= Mask; WriteByte(MemoryAddress, b); } protected void Apply_AND_ByteMask(UInt32 MemoryAddress, byte Mask) { byte b = ReadByte(MemoryAddress); b &= Mask; WriteByte(MemoryAddress, b); } #endregion #region MAME-Like Outputs protected virtual void CreateOutputList() { _Outputs = new List(); } public virtual void UpdateOutputValues() { } public void SetMamePauseState(bool PauseState) { if (PauseState) SetOutputValue(OutputId.MamePause, 1); else SetOutputValue(OutputId.MamePause, 0); } /// /// Return a GameOutput object corresponding to a desired GameOutputId /// /// Desired GameOutputId /// Desired GameOutput object protected GameOutput GetOutputById(OutputId Id) { foreach (GameOutput CurrentOutput in _Outputs) { if (CurrentOutput.Id == (uint)Id) return CurrentOutput; } return null; } /// /// Update a value for the desired GameOutput /// /// GameOutput Id to update /// Value to update the GameOutput object protected void SetOutputValue(OutputId Id, int Value) { foreach (GameOutput CurrentOutput in _Outputs) { if (CurrentOutput.Id == (uint)Id) { CurrentOutput.OutputValue = Value; break; } } } /// /// Return the Text description for a desired Id /// /// Desired GameOutput Id /// GameOutput string description public String GetOutputDescriptionFromId(uint Id) { foreach (GameOutput o in _Outputs) { if (o.Id == Id) return o.Name; } return String.Empty; } #endregion #region XOutput protected XOutput _XOutputManager = null; protected int _Player1_X360_Gamepad_Port = 0; protected int _Player2_X360_Gamepad_Port = 0; /// /// Create and plug a virtual XInput device /// /// XInput ID of the device to create and plug (From 1 To 4) protected virtual void InstallX360Gamepad(int Player) { if (_XOutputManager != null) { if (_XOutputManager.isVBusExists()) { for (int i = 1; i < 5; i++) { if (_XOutputManager.PlugIn(i)) { if (Player == 1) { Logger.WriteLog("Plugged P1 virtual Gamepad to port " + i.ToString()); _Player1_X360_Gamepad_Port = i; } else if (Player == 2) { Logger.WriteLog("Plugged P2 virtual Gamepad to port " + i.ToString()); _Player2_X360_Gamepad_Port = i; } break; } else Logger.WriteLog("Failed to plug virtual GamePad to port " + i.ToString() + ". (Port already used ?)"); } } else { Logger.WriteLog("ScpBus driver not found or not installed"); } } else { Logger.WriteLog("XOutputManager Creation Failed !"); } } protected bool UninstallX360Gamepad(int Player) { if (_XOutputManager != null) { if (Player == 1 && _Player1_X360_Gamepad_Port != 0) { if (_XOutputManager.Unplug(_Player1_X360_Gamepad_Port, true)) { Logger.WriteLog("Succesfully unplug P1 virtual Gamepad on port " + _Player1_X360_Gamepad_Port.ToString()); return true; } else { Logger.WriteLog("Failed to unplug P1 virtual Gamepad on port " + _Player1_X360_Gamepad_Port.ToString()); return false; } } else if (Player == 2 && _Player2_X360_Gamepad_Port != 0) { if (_XOutputManager.Unplug(_Player2_X360_Gamepad_Port, true)) { Logger.WriteLog("Succesfully unplug P2 virtual Gamepad on port " + _Player2_X360_Gamepad_Port.ToString()); return true; } else { Logger.WriteLog("Failed to unplug P2 virtual Gamepad on port " + _Player2_X360_Gamepad_Port.ToString()); return false; } } } return true; } #endregion #region Keyboard SendKeys /// /// Simulate a keyboard key action (up or down) /// /// Hardware ScanCode of the key to simulate /// State of the key. (0=Down, 1 = Up) private void SendKey(HardwareScanCode Keycode, KeybdInputFlags KeybdInputFlags) { INPUT[] InputData = new INPUT[1]; InputData[0].type = InputType.INPUT_KEYBOARD; InputData[0].ki.wScan = Keycode; InputData[0].ki.dwFlags = KeybdInputFlags; InputData[0].ki.time = 0; InputData[0].ki.dwExtraInfo = IntPtr.Zero; if ( Win32API.SendInput(1, InputData, Marshal.SizeOf(typeof(INPUT))) == 0) { Logger.WriteLog("SendInput API failed : wScan=" + Keycode.ToString() + ", dwFlags=" + KeybdInputFlags.ToString()); Logger.WriteLog("GetLastError returned : " + Marshal.GetLastWin32Error().ToString()); } } /// /// Send KeyUp and KeyDown separated by a desired delay /// /// DirectInput Keycode (hardware scan code) /// Delay in milliseconds protected void SendKeyStroke(HardwareScanCode Keycode, int DelayPressed) { SendKeyDown(Keycode); System.Threading.Thread.Sleep(DelayPressed); SendKeyUp(Keycode); } protected void SendKeyDown(HardwareScanCode Keycode) { SendKey(Keycode, KeybdInputFlags.KEYEVENTF_SCANCODE); } protected void SendKeyUp(HardwareScanCode Keycode) { SendKey(Keycode, KeybdInputFlags.KEYEVENTF_KEYUP | KeybdInputFlags.KEYEVENTF_SCANCODE); } /// /// VirtualKeyCode inputs to send /// /// protected void Send_VK_KeyDown(VirtualKeyCode Keycode) { Win32API.keybd_event(Keycode, 0, KeybdInputFlags.KEYEVENTF_EXTENDEDKEY | 0, 0); } protected void Send_VK_KeyUp(VirtualKeyCode Keycode) { Win32API.keybd_event(Keycode, 0, KeybdInputFlags.KEYEVENTF_EXTENDEDKEY | KeybdInputFlags.KEYEVENTF_KEYUP, 0); } /// /// Convert a HardwareScanCode to a corresponding VirtualKeyCode /// /// Hardware Scancode to convert /// public VirtualKeyCode MapScanCodeToVirtualKeyCode(HardwareScanCode ScanCode) { UInt32 Vk = Win32API.MapVirtualKey((UInt32)ScanCode, VirtualKeyMapType.MAPVK_VSC_TO_VK); return (VirtualKeyCode)Vk; } /// /// Convert a VirtualScanCode to a corresponding HardwareScanCode /// /// Hardware Scancode to convert /// public HardwareScanCode MapScanCodeToVirtualKeyCode(VirtualKeyCode ScanCode) { UInt32 Vk = Win32API.MapVirtualKey((UInt32)ScanCode, VirtualKeyMapType.MAPVK_VK_TO_VSC); return (HardwareScanCode)Vk; } #endregion #region LowLevel Hooks /// /// Low-Level mouse hook callback to process data. /// This procedure will be override by each Game according to it's needs /// public virtual IntPtr MouseHookCallback(IntPtr MouseHookID, int nCode, IntPtr wParam, IntPtr lParam) { return Win32API.CallNextHookEx(MouseHookID, nCode, wParam, lParam); } /// /// Low-Level keyboard hook callback to process data. /// This procedure will be override by each Game according to it's needs /// public virtual IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion /// /// Select a specific window from the target process where the title contains a wanted string /// /// /// protected bool FindGameWindow_Contains(string TargetWindowTitle) { foreach (IntPtr handle in EnumerateProcessWindowHandles(_TargetProcess.Id)) { int length = Win32API.GetWindowTextLength(handle); if (length >= 0) { StringBuilder builder = new StringBuilder(length); Win32API.GetWindowText(handle, builder, length + 1); string WindowTitle = builder.ToString(); Logger.WriteLog("Found a window : Handle = 0x" + handle.ToString("X8") + ", Title = " + WindowTitle); if (WindowTitle.StartsWith("FPS:") || WindowTitle.ToLower().Contains(TargetWindowTitle.ToLower())) { _GameWindowHandle = handle; Logger.WriteLog("=> Selecting 0x" + handle.ToString("X8") + " as game Window Handle"); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle = " + _TargetProcess.MainWindowTitle); return true; } } } return false; } /// /// Select a specific window from the target process where the title is excatly the specific string /// /// /// protected bool FindGameWindow_Equals(string TargetWindowTitle) { foreach (IntPtr handle in EnumerateProcessWindowHandles(_TargetProcess.Id)) { int length = Win32API.GetWindowTextLength(handle); if (length >= 0) { StringBuilder builder = new StringBuilder(length); Win32API.GetWindowText(handle, builder, length + 1); string WindowTitle = builder.ToString(); Logger.WriteLog("Found a window : Handle = 0x" + handle.ToString("X8") + ", Title = " + WindowTitle); if (WindowTitle.StartsWith("FPS:") || WindowTitle.Equals(TargetWindowTitle)) { _GameWindowHandle = handle; Logger.WriteLog("=> Selecting 0x" + handle.ToString("X8") + " as game Window Handle"); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); return true; } } } return false; } /// /// Get the list of Windows for a given process /// protected static IEnumerable EnumerateProcessWindowHandles(int processId) { List handles = new List(); foreach (ProcessThread thread in Process.GetProcessById(processId).Threads) { Win32API.EnumThreadWindows(thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero); } return handles; } /// /// Get the Window Title /// /// protected string Get_GameWindowTitle() { string sTitle = string.Empty; int length = Win32API.GetWindowTextLength(_GameWindowHandle); if (length >= 0) { StringBuilder builder = new StringBuilder(length); Win32API.GetWindowText(_GameWindowHandle, builder, length + 1); sTitle = builder.ToString(); } return sTitle; } /// /// Check a series of bytes agains some awaited values /// protected bool CheckBytes(UInt32 AddressToCheck, byte[] BytesToFind) { Logger.WriteLog("Checking Bytes at 0x" + AddressToCheck.ToString("X8") + "..."); byte[] ReadBuffer = ReadBytes(AddressToCheck, (uint)BytesToFind.Length); for (int i = 0; i < BytesToFind.Length; i++) { Logger.WriteLog("Read: 0x" + ReadBuffer[i].ToString("X2") + ", Awaited: 0x" + BytesToFind[i].ToString("X2")); if (ReadBuffer[i] != BytesToFind[i]) return false; } return true; } } } ================================================ FILE: DemulShooter/Games/Game_ArcadePcWaterSprite2.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; namespace DemulShooter.Games { public class Game_ArcadepcWaterSprite2 : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] WaterFire = null; public byte WaterPump = 0; public byte GameLed = 0; public byte[] StartLed = null; public byte[] TicketFeeder = null; public byte[] BigGun = null; public byte[] Damaged = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcWaterSprite2(String RomName) : base(RomName, "SpriteT2_EN", "SpriteT2_EN") { _KnownMd5Prints.Add("Happy Water Sprite 2 - v1.2", "f5cda675424c894c781e82cf9c4d61b0"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_LmpStart, 100)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_LmpStart, 100)); _Outputs.Add(new GameOutput(OutputId.P1_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P2_WaterFire)); _Outputs.Add(new GameOutput(OutputId.WaterPump)); _Outputs.Add(new GameOutput(OutputId.LmpUpperCtrlPanel)); _Outputs.Add(new GameOutput(OutputId.P1_BigGun)); _Outputs.Add(new GameOutput(OutputId.P2_BigGun)); _Outputs.Add(new GameOutput(OutputId.P1_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P2_TicketFeeder)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, GetBlinkingLightState(((OutputData)_OutputData).StartLed[0])); SetOutputValue(OutputId.P2_LmpStart, GetBlinkingLightState(((OutputData)_OutputData).StartLed[1])); SetOutputValue(OutputId.P1_WaterFire, ((OutputData)_OutputData).WaterFire[0]); SetOutputValue(OutputId.P2_WaterFire, ((OutputData)_OutputData).WaterFire[1]); SetOutputValue(OutputId.WaterPump, ((OutputData)_OutputData).WaterPump); SetOutputValue(OutputId.LmpUpperCtrlPanel, ((OutputData)_OutputData).GameLed); SetOutputValue(OutputId.P1_BigGun, ((OutputData)_OutputData).BigGun[0]); SetOutputValue(OutputId.P2_BigGun, ((OutputData)_OutputData).BigGun[1]); SetOutputValue(OutputId.P1_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[0]); SetOutputValue(OutputId.P2_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[1]); if (((OutputData)_OutputData).Damaged[0] == 1) SetOutputValue(OutputId.P1_Damaged, 1); if (((OutputData)_OutputData).Damaged[1] == 1) SetOutputValue(OutputId.P2_Damaged, 1); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); } } //Game sets values as this : //0 = OFF //1 = CONSTANT ON //2 = BLINK //We need -1 instead of 2 for custom blinking output state private int GetBlinkingLightState(byte ReadValue) { if (ReadValue == 2) return -1; else return ReadValue; } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcFireHero.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Management.Instrumentation; using System.Reflection; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; namespace DemulShooter.Games { public class Game_ArcadepcFireHero : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] StartLed = null; public byte Leftflash = 0; public byte Rightflash = 0; public byte ConsoleLight = 0; public byte FireLight = 0; public byte Fog = 0; public byte WaterPump = 0; public byte[] GunShake = null; public byte[] SmallGun = null; public byte[] BigGun = null; public byte[] WaterRotate = null; public byte[] Ticket = null; public float[] Life = null; public byte[] Damaged = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcFireHero(String RomName) : base(RomName, "FireHero", "FireFighter2") { _KnownMd5Prints.Add("Fire Hero v1.4.6", "a1b853ec0d1a597afc41ac7a448166f3"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } /// /// Overriding the Procedure as the game Window Title can be changed in the game ini files /// So looking in those file to know what we are looking for /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one String PlayerSetting_FilePath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", _Target_Process_Name + @"_Data\StreamingAssets\Setting\PlayerSetting.ini"); Logger.WriteLog(PlayerSetting_FilePath); if (File.Exists(PlayerSetting_FilePath)) { try { INIFile Player_IniFile = new INIFile(PlayerSetting_FilePath); _MainWindowTitle = Player_IniFile.IniReadValue("Window", "Title"); Logger.WriteLog("Found " + PlayerSetting_FilePath + " value to change Window Title to : " + _MainWindowTitle); } catch (Exception Ex) { Logger.WriteLog("Error reading config file : " + PlayerSetting_FilePath); Logger.WriteLog(Ex.Message.ToString()); } } else { Logger.WriteLog(PlayerSetting_FilePath + " not found"); } if (FindGameWindow_Equals(_MainWindowTitle)) { String AssemblyDllPath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", _Target_Process_Name + @"_Data\Managed\Assembly-CSharp.dll"); CheckMd5(AssemblyDllPath); //Start TcpClient to dial with Unity Game _Tcpclient = new DsTcp_Client("127.0.0.1", DsTcp_Client.DS_TCP_CLIENT_PORT); _Tcpclient.PacketReceived += DsTcp_Client_PacketReceived; _Tcpclient.TcpConnected += DsTcp_client_TcpConnected; _Tcpclient.Connect(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); System.Windows.Forms.Application.Exit(); } } } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpLeft)); _Outputs.Add(new GameOutput(OutputId.LmpRight)); _Outputs.Add(new GameOutput(OutputId.Lmp_RedLight)); _Outputs.Add(new GameOutput(OutputId.LmpPanel)); _Outputs.Add(new GameOutput(OutputId.WaterPump)); _Outputs.Add(new GameOutput(OutputId.P1_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P2_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P1_BigGun)); _Outputs.Add(new GameOutput(OutputId.P2_BigGun)); _Outputs.Add(new GameOutput(OutputId.P1_WaterRotate)); _Outputs.Add(new GameOutput(OutputId.P2_WaterRotate)); _Outputs.Add(new GameOutput(OutputId.SmokeSwitch)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P1_Damaged)); _Outputs.Add(new GameOutput(OutputId.P2_Damaged)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).StartLed[0]); SetOutputValue(OutputId.P2_LmpStart, ((OutputData)_OutputData).StartLed[1]); SetOutputValue(OutputId.LmpLeft, ((OutputData)_OutputData).Leftflash); SetOutputValue(OutputId.LmpRight, ((OutputData)_OutputData).Rightflash); SetOutputValue(OutputId.Lmp_RedLight, ((OutputData)_OutputData).FireLight); SetOutputValue(OutputId.LmpPanel, ((OutputData)_OutputData).ConsoleLight); SetOutputValue(OutputId.WaterPump, ((OutputData)_OutputData).WaterPump); SetOutputValue(OutputId.P1_WaterFire, ((OutputData)_OutputData).SmallGun[0]); SetOutputValue(OutputId.P2_WaterFire, ((OutputData)_OutputData).SmallGun[1]); SetOutputValue(OutputId.P1_BigGun, ((OutputData)_OutputData).BigGun[0]); SetOutputValue(OutputId.P2_BigGun, ((OutputData)_OutputData).BigGun[1]); SetOutputValue(OutputId.P1_WaterRotate, ((OutputData)_OutputData).WaterRotate[0]); SetOutputValue(OutputId.P2_WaterRotate, ((OutputData)_OutputData).WaterRotate[1]); SetOutputValue(OutputId.SmokeSwitch, ((OutputData)_OutputData).Fog); SetOutputValue(OutputId.P1_GunMotor, ((OutputData)_OutputData).GunShake[0]); SetOutputValue(OutputId.P2_GunMotor, ((OutputData)_OutputData).GunShake[1]); SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P1_Credits, ((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, ((OutputData)_OutputData).Credits[1]); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcGhostBusters.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_ArcadepcGhostBusters : Game { private const String GAMEDATA_FOLDER = @"MemoryData\ice\gbusters"; //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction_v115 = 0x08197EF5; private UInt32 _RomLoaded_check_Instruction_v117 = 0x08198B4C; private UInt32 _CrosshairHack_Address = 0x0806673D; private UInt32 _Lamp_LeftGunTip_Address = 0x08F16668; private UInt32 _Lamp_LeftGunBack_Address = 0x08F16680; private UInt32 _Lamp_RightGunTip_Address = 0x08F16670; private UInt32 _Lamp_RightGunBack_Address = 0x08F1668C; private UInt32 _AgitatorMotorState_Address = 0x08F16698; private UInt32 _AgitatorMotorDirection_Address = 0x08F16694; private UInt32 _P1_BallShooterStatus_Address = 0x08F16650; private UInt32 _P2_BallShooterStatus_Address = 0x08F16654; private InjectionStruct _BallShooter_InjectionStruct = new InjectionStruct(0x08190191, 9); private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_ArcadepcGhostBusters(String RomName) : base(RomName, "BudgieLoader") { //Only for documentation, version check is done by reading code position in memory, as we don't have access to the ELF path _KnownMd5Prints.Add("Ghostbusters - v1.15", "8de0a59ff3c10420959038d6769b646c"); _KnownMd5Prints.Add("Ghostbusters - v1.17", "ce10cf8f57b0fffd7fc5437622033b3a"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } // /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is v1.15 or v1.17 binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_check_Instruction_v117, 5); if (buffer[0] == 0x89 && buffer[1] == 0x04 && buffer[2] == 0x95 && buffer[3] == 0xC4 && buffer[4] == 0x65) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Ghostbusters - v1.17 binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Ghostbusters - v1.17"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { buffer = ReadBytes(_RomLoaded_check_Instruction_v115, 5); if (buffer[0] == 0x89 && buffer[1] == 0x04 && buffer[2] == 0x95 && buffer[3] == 0xC4 && buffer[4] == 0x65) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Ghostbusters - v1.15 binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Ghostbusters - v1.15"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game not Loaded, waiting..."); } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 4; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercept call to change Ball Shooting event value /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax, [ebp+08] CaveMemory.Write_StrBytes("8B 45 08"); //mov edx,[ebp+0C] CaveMemory.Write_StrBytes("8B 55 0C"); //cmp eax,00000004 CaveMemory.Write_StrBytes("3D 04 00 00 00"); //je P1 CaveMemory.Write_StrBytes("74 09"); //cmp edx,00000005 CaveMemory.Write_StrBytes("3D 05 00 00 00"); //je P2 CaveMemory.Write_StrBytes("74 0D"); //jmp Exit CaveMemory.Write_StrBytes("EB 14"); //P1: //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_RecoilStatus_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress)); //mov [eax], edx CaveMemory.Write_StrBytes("09 10"); //pop eax CaveMemory.Write_StrBytes("58"); //jmp Exit CaveMemory.Write_StrBytes("EB 09"); //P2: //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P2_RecoilStatus_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_RecoilStatus_CaveAddress)); //mov [eax], edx CaveMemory.Write_StrBytes("09 10"); //pop eax CaveMemory.Write_StrBytes("58"); //Exit: //add eax, 24 CaveMemory.Write_StrBytes("83 C0 24"); CaveMemory.InjectToAddress(_BallShooter_InjectionStruct, "Recoil"); } protected override void Apply_NoCrosshairMemoryHack() { if (_HideCrosshair) { //Replacing crosshair X value in ECX by -300.0f WriteBytes(_CrosshairHack_Address, new byte[] { 0xB9, 0x00, 0x00, 0x96, 0xC3, 0x90, 0x90 }); } else { //Putting back original code for crosshairs //mov ecx,[ecx*8+08AD232C] WriteBytes(_CrosshairHack_Address, new byte[] { 0x8B, 0x0C, 0xCD, 0x2C, 0x23, 0xAD, 0x08 }); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpGunTip)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGunBack)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGunTip)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGunBack)); _Outputs.Add(new GameOutput(OutputId.BallAgitator_State)); _Outputs.Add(new GameOutput(OutputId.BallAgitator_Direction)); _Outputs.Add(new GameOutput(OutputId.P1_BallShooter)); _Outputs.Add(new GameOutput(OutputId.P2_BallShooter)); //Custom Outputs _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Lamp Values SetOutputValue(OutputId.P1_LmpGunTip, GetLampValueAsInt(_Lamp_LeftGunTip_Address)); SetOutputValue(OutputId.P1_LmpGunBack, GetLampValueAsInt(_Lamp_LeftGunBack_Address)); SetOutputValue(OutputId.P2_LmpGunTip, GetLampValueAsInt(_Lamp_RightGunTip_Address)); SetOutputValue(OutputId.P2_LmpGunBack, GetLampValueAsInt(_Lamp_RightGunBack_Address)); //Ball Agitator motor : one outputs gets the state (spinning or not) and the other one get the direction (0 = Right, 1 = Left) SetOutputValue(OutputId.BallAgitator_State, GetMotorValueAsInt(_AgitatorMotorState_Address)); SetOutputValue(OutputId.BallAgitator_Direction, GetMotorValueAsInt(_AgitatorMotorDirection_Address)); //BallShooter status SetOutputValue(OutputId.P1_BallShooter, ReadByte(_P1_BallShooterStatus_Address)); SetOutputValue(OutputId.P2_BallShooter, ReadByte(_P2_BallShooterStatus_Address)); if (ReadByte(_P1_RecoilStatus_CaveAddress) > 0) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilStatus_CaveAddress, 0); } if (ReadByte(_P2_RecoilStatus_CaveAddress) > 0) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilStatus_CaveAddress, 0); } } //Lamp values are between 0 and 0x7FF private int GetLampValueAsInt(UInt32 LmpAddress) { Int32 iLmpValue = BitConverter.ToInt32(ReadBytes(LmpAddress, 4), 0); return (int)((float)iLmpValue / 2047.0f * 100.0f); } //Motor values are between 0 and 0xFFF private int GetMotorValueAsInt(UInt32 LmpAddress) { Int32 iMtrValue = BitConverter.ToInt32(ReadBytes(LmpAddress, 4), 0); return (int)((float)iMtrValue / 4095.0f * 100.0f); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcHsfr.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_ArcadepcHsfr : Game { /*** MEMORY ADDRESSES **/ private InjectionStruct _UAfControlManager_MoveGunController_InjectionStruct = new InjectionStruct(0x0042E9DB, 5); //Outputs private InjectionStruct _UafGameManager_Update_InjectionStruct = new InjectionStruct(0x0043A14A, 6); private InjectionStruct _UafControlManager_OpenLight_InjectionStruct = new InjectionStruct(0x00428844, 6); private InjectionStruct _UafControlManager_CloseLight_InjectionStruct = new InjectionStruct(0x00428A34, 6); private InjectionStruct _UafControlManager_StartSeatShake_InjectionStruct = new InjectionStruct(0x00430CB8, 6); private InjectionStruct _UafControlManager_CloseSeatShake_InjectionStruct = new InjectionStruct(0x00430E48, 6); private InjectionStruct _UafControlManager_StartShakeApparatusOut_InjectionStruct = new InjectionStruct(0x00430A48, 6); private InjectionStruct _UafControlManager_CloseShake_InjectionStruct = new InjectionStruct(0x00430978, 6); private InjectionStruct _UafControlManager_WaterMistControl_InjectionStruct = new InjectionStruct(0x0042FA03, 6); private InjectionStruct _UafControlManager_Update_InjectionStruct = new InjectionStruct(0x00428DB2, 5); private InjectionStruct _SgPlayer_PlayEffectHitted_InjectionStruct = new InjectionStruct(0x003BD333, 6); //Custom Input Address private UInt32 _AxisX_Array_CaveAddress; private UInt32 _AxisY_Array_CaveAddress; private UInt32 _Light_Array_CaveAddress; private UInt32 _Motor_Array_CaveAddress; private UInt32 _DamageArray_CaveAddress; private UInt32 _UafGameManager_Instance_CaveAddress; private UInt32 _UafControlManager_Instance_CaveAddress; private UInt32 _WaterMistState_CaveAddress; private IntPtr _GameAssemblyDll_BaseAddress = IntPtr.Zero; /// /// Constructor /// public Game_ArcadepcHsfr(String RomName) : base(RomName, "FireHero") { _KnownMd5Prints.Add("Hot Shots Fire Rescue - v3.3.20221123r.60.1PUMP", "eec51a30066a2a19fa7d3ad494518eab"); _tProcess.Start(); Logger.WriteLog("Waiting for Coastal " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("gameassembly.dll")) { _GameAssemblyDll_BaseAddress = m.BaseAddress; if (_GameAssemblyDll_BaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals("FireHero")) { String AssemblyDllPath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", "GameAssembly.dll"); CheckMd5(AssemblyDllPath); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// Game is using 2 differents axis : /// 1 For the shooting target and collision (which is the window size boundaries) /// 1 To display crosshair on screen (which seems to be 1920 for X and Y changes according to the window ratio /// Origin is Bottom-Left (inverted from Windows Lightgun data origins, Top-Left) /// The regular PlayerData.RIController.Computed_X and PlayerData.RIController.Computed_Y will store shooting values /// Other private fields will store Crosshair display values /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dRatio = TotalResX / TotalResY; Logger.WriteLog("Game Window ratio = " + dRatio); //Crosshair X => [-960, 960] //Crosshair Y => [-540, 540] double dMinX = -960.0; double dMaxX = 960.0; double dMinY = -540.0; double dMaxY = 540.0; double dRangeX = dMaxX - dMinX; double dRangeY = dMaxY - dMinY; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / TotalResX) - dRangeX / 2); PlayerData.RIController.Computed_Y = Convert.ToInt16((Math.Round(dRangeY * PlayerData.RIController.Computed_Y / TotalResY) - dRangeY / 2) * -1); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _AxisX_Array_CaveAddress = _InputsDatabank_Address; _AxisY_Array_CaveAddress = _InputsDatabank_Address + 0x10; SetHack_Axis(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Inside UAF_ControllerManager.MoveGunController(), if CONTROL_TYPE is keyboard, the game waits for a KEY to be pressed to update Values /// Removing that part and inserting our own values, forcing a TRUE return (UAF_ControllerManager.flag = 1) /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov byte ptr [esi+000001D4],01 CaveMemory.Write_StrBytes("C6 86 D4 01 00 00 01"); //mov eax,[ebp+1C] CaveMemory.Write_StrBytes("8B 45 1C"); //mov esi,_AxisX_Array_CaveAddress CaveMemory.Write_StrBytes("BE"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_AxisX_Array_CaveAddress)); //mov esi,[esi+ebx*4] CaveMemory.Write_StrBytes("8B 34 9E"); //mov [eax],esi CaveMemory.Write_StrBytes("89 30"); //mov esi,_AxisY_Array_CaveAddress CaveMemory.Write_StrBytes("BE"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_AxisY_Array_CaveAddress)); //mov esi,[esi+ebx*4] CaveMemory.Write_StrBytes("8B 34 9E"); //mov [eax+04],esi CaveMemory.Write_StrBytes("89 70 04"); //mov eax,00000001 CaveMemory.Write_StrBytes("B8 01 00 00 00"); //pop edi CaveMemory.Write_StrBytes("5F"); //pop esi CaveMemory.Write_StrBytes("5E"); //pop ebx CaveMemory.Write_StrBytes("5B"); //mov esp,ebp CaveMemory.Write_StrBytes("8B E5"); //pop ebp CaveMemory.Write_StrBytes("5D"); //ret CaveMemory.Write_StrBytes("C3"); //Inject it CaveMemory.InjectToOffset(_UAfControlManager_MoveGunController_InjectionStruct, "UAF_ControlManager.MovegunController()"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _Light_Array_CaveAddress = _OutputsDatabank_Address; _Motor_Array_CaveAddress = _OutputsDatabank_Address + 0x10; _DamageArray_CaveAddress = _OutputsDatabank_Address + 0x20; _UafGameManager_Instance_CaveAddress = _OutputsDatabank_Address + 0x30; _WaterMistState_CaveAddress = _OutputsDatabank_Address + 0x40; _UafControlManager_Instance_CaveAddress = _OutputsDatabank_Address + 0x44; SetHack_GetGameManagerInstance(); SetHack_ControlManagerInstance(); SetHack_LightsOn(); SetHack_LightsOff(); SetHack_SeatOn(); SetHack_SeatOff(); SetHack_GunShakeOn(); SetHack_GunShakeOff(); SetHack_Damage(); SetHack_MistState(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Hooking to UAF_GameManager.Update() will allow us to get the Instance pointer of the Singleton /// Used later to get coins, player status, etc... /// private void SetHack_GetGameManagerInstance() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov edi,[ebp+08] CaveMemory.Write_StrBytes("8B 7D 08"); //add esp,04 CaveMemory.Write_StrBytes("83 C4 04"); //mov [_UafGameManager_Instance_CaveAddress],edi CaveMemory.Write_StrBytes("89 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_UafGameManager_Instance_CaveAddress)); //Inject it CaveMemory.InjectToOffset(_UafGameManager_Update_InjectionStruct, "UAF_GameManager.Update()"); } /// /// Intercepting calls to function, where we can get Player ID, Lamp Id and Lamp State (1 = ON, 2 = Blink) /// private void SetHack_LightsOn() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[eax+08] CaveMemory.Write_StrBytes("8B 40 08"); //push eax CaveMemory.Write_StrBytes("50"); //mov esi,[ebp+0C] CaveMemory.Write_StrBytes("8B 75 0C"); //shl esi,02 CaveMemory.Write_StrBytes("C1 E6 02"); //add esi,[ebp+10] CaveMemory.Write_StrBytes("03 75 10"); //add esi,_Light_Array_CaveAddress CaveMemory.Write_StrBytes("81 C6"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Light_Array_CaveAddress)); //mov eax,[ebp+14] CaveMemory.Write_StrBytes("8B 45 14"); //mov [esi],al CaveMemory.Write_StrBytes("88 06"); //pop eax CaveMemory.Write_StrBytes("58"); //mov esi,[ebp+10] CaveMemory.Write_StrBytes("8B 75 10"); //Inject it CaveMemory.InjectToOffset(_UafControlManager_OpenLight_InjectionStruct, "UAF_ControlManager.OpenLight()"); } private void SetHack_LightsOff() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[eax+08] CaveMemory.Write_StrBytes("8B 40 08"); //push eax CaveMemory.Write_StrBytes("50"); //mov edi,[ebp+0C] CaveMemory.Write_StrBytes("8B 7D 0C"); //shl edi,02 CaveMemory.Write_StrBytes("C1 E7 02"); //add edi,[ebp+10] CaveMemory.Write_StrBytes("03 7D 10"); //add edi,_Light_Array_CaveAddress CaveMemory.Write_StrBytes("81 C7"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Light_Array_CaveAddress)); //xor eax, eax CaveMemory.Write_StrBytes("31 C0"); //mov [edi],al CaveMemory.Write_StrBytes("88 07"); //pop eax CaveMemory.Write_StrBytes("58"); //mov edi,[ebp+10] CaveMemory.Write_StrBytes("8B 7D 10"); //Inject it CaveMemory.InjectToOffset(_UafControlManager_CloseLight_InjectionStruct, "UAF_ControlManager.CloseLight()"); } /// /// Intercepting calls to function, where we can get Player ID /// private void SetHack_SeatOn() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[ebp+0C] CaveMemory.Write_StrBytes("8B 45 0C"); //add eax,_Motor_Array_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Motor_Array_CaveAddress)); //mov byte ptr[eax],1 CaveMemory.Write_StrBytes("C6 00 01"); //mov eax,[edi+0000020C] CaveMemory.Write_StrBytes("8B 87 0C 02 00 00"); //Inject it CaveMemory.InjectToOffset(_UafControlManager_StartSeatShake_InjectionStruct, "UAF_ControlManager.StartSeatShake()"); } private void SetHack_SeatOff() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[ebp+0C] CaveMemory.Write_StrBytes("8B 45 0C"); //add eax,_Motor_Array_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Motor_Array_CaveAddress)); //mov byte ptr[eax],0 CaveMemory.Write_StrBytes("C6 00 00"); //mov eax,[edi+0000020C] CaveMemory.Write_StrBytes("8B 87 0C 02 00 00"); //Inject it CaveMemory.InjectToOffset(_UafControlManager_CloseSeatShake_InjectionStruct, "UAF_ControlManager.CloseSeatShake()"); } /// /// Intercepting calls to function, where we can get Player ID /// private void SetHack_GunShakeOn() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[ebp+0C] CaveMemory.Write_StrBytes("8B 45 0C"); //add eax,_Motor_Array_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Motor_Array_CaveAddress + 4)); //mov byte ptr[eax],1 CaveMemory.Write_StrBytes("C6 00 01"); //mov eax,[edi+00000208] CaveMemory.Write_StrBytes("8B 87 08 02 00 00"); //Inject it CaveMemory.InjectToOffset(_UafControlManager_StartShakeApparatusOut_InjectionStruct, "UAF_ControlManager.StartShakeApparatusOut()"); } private void SetHack_GunShakeOff() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[ebp+0C] CaveMemory.Write_StrBytes("8B 45 0C"); //add eax,_Motor_Array_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Motor_Array_CaveAddress + 4)); //mov byte ptr[eax],0 CaveMemory.Write_StrBytes("C6 00 00"); //mov eax,[edi+00000208] CaveMemory.Write_StrBytes("8B 87 08 02 00 00"); //Inject it CaveMemory.InjectToOffset(_UafControlManager_CloseShake_InjectionStruct, "UAF_ControlManager.CloseShake()"); } /// /// Intercepting calls to function, where we can get Player ID /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //and esp, -10 CaveMemory.Write_StrBytes("83 E4 F0"); //sub esp,28 CaveMemory.Write_StrBytes("83 EC 28"); //mov ecx,[ebp+10] CaveMemory.Write_StrBytes("8B 4D 10"); //cmp ecx, -1 CaveMemory.Write_StrBytes("83 F9 FF"); //jne Next CaveMemory.Write_StrBytes("75 0D"); //mov ecx,_DamageArray_CaveAddress CaveMemory.Write_StrBytes("B9"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_DamageArray_CaveAddress)); //mov [ecx],01010101 CaveMemory.Write_StrBytes("C7 01 01 01 01 01"); //jmp Exit CaveMemory.Write_StrBytes("EB 09"); //add ecx,_DamageArray_CaveAddress CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_DamageArray_CaveAddress)); //mov byte ptr[ecx],1 CaveMemory.Write_StrBytes("C6 01 01"); //Inject it CaveMemory.InjectToOffset(_SgPlayer_PlayEffectHitted_InjectionStruct, "SG_Player.PlayEffectHitted()"); } /// /// Intercepting call to change the state /// private void SetHack_MistState() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //and esp, -10 CaveMemory.Write_StrBytes("83 E4 F0"); //sub esp,38 CaveMemory.Write_StrBytes("83 EC 38"); //mov ecx,[ebp+0C] CaveMemory.Write_StrBytes("8B 4D 0C"); //mov [_WaterMistState_CaveAddress], ecx CaveMemory.Write_StrBytes("89 0D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_WaterMistState_CaveAddress)); //Inject it CaveMemory.InjectToOffset(_UafControlManager_WaterMistControl_InjectionStruct, "UAF_ControlManager.WaterMistState()"); } /// /// Hooking to UAF_ControlManager.Update() will allow us to get the Instance pointer of the Singleton /// Used later to get water status for guns private void SetHack_ControlManagerInstance() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov esi,[ebp+08] CaveMemory.Write_StrBytes("8B 75 08"); //xor edx,edx //mov [_UafControlManager_Instance_CaveAddress],esi CaveMemory.Write_StrBytes("89 35"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_UafControlManager_Instance_CaveAddress)); //Inject it CaveMemory.InjectToOffset(_UafControlManager_Update_InjectionStruct, "UAF_ControlManager.Update()"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((float)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y); WriteBytes(_AxisX_Array_CaveAddress + (UInt32)((PlayerData.ID - 1) * 4), bufferX); WriteBytes(_AxisY_Array_CaveAddress + (UInt32)((PlayerData.ID - 1) * 4), bufferY); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P2_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P3_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P4_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P1_BigGun)); _Outputs.Add(new GameOutput(OutputId.P2_BigGun)); _Outputs.Add(new GameOutput(OutputId.P3_BigGun)); _Outputs.Add(new GameOutput(OutputId.P4_BigGun)); _Outputs.Add(new GameOutput(OutputId.SmokeSwitch)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_LmpStart, 100)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_LmpStart, 100)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P3_LmpStart, 100)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P4_LmpStart, 100)); _Outputs.Add(new BlinkGameOutput(OutputId.P1_LmpGun, 100)); _Outputs.Add(new BlinkGameOutput(OutputId.P2_LmpGun, 100)); _Outputs.Add(new BlinkGameOutput(OutputId.P3_LmpGun, 100)); _Outputs.Add(new BlinkGameOutput(OutputId.P4_LmpGun, 100)); _Outputs.Add(new GameOutput(OutputId.P1_ChairShake)); _Outputs.Add(new GameOutput(OutputId.P2_ChairShake)); _Outputs.Add(new GameOutput(OutputId.P3_ChairShake)); _Outputs.Add(new GameOutput(OutputId.P4_ChairShake)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P3_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P4_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { int[] Coins = new int[4]; byte[] IsGaming = new byte[4]; UInt32 pGameManager = ReadPtr(_UafGameManager_Instance_CaveAddress); if (pGameManager != 0) { UInt32 pCoins = ReadPtr(pGameManager + 0x40); if (pCoins != 0) { int ArrayLength = (int)ReadPtr(pCoins + 0x0C); for (int i = 0; i < ArrayLength; i++) { Coins[i] = (int)ReadPtr((UInt32)(pCoins + 0x10 + 4 * i)); } } UInt32 pIsGaming = ReadPtr(pGameManager + 0x5C); if (pIsGaming != 0) { int ArrayLength = (int)ReadPtr(pIsGaming + 0x0C); for (int i = 0; i < ArrayLength; i++) { IsGaming[i] = ReadByte((UInt32)(pIsGaming + 0x10 + i)); } } } int[] SmallWater = new int[4]; int[] BigWater = new int[4]; UInt32 pControlManager = ReadPtr(_UafControlManager_Instance_CaveAddress); if (pControlManager != 0) { UInt32 pWaterPump = ReadPtr(pControlManager + 0x1E8); if (pWaterPump != 0) { int ArrayLength = (int)ReadPtrChain(pWaterPump + 0x08, new UInt32[] { 0x00 }); int ItemLength = (int)ReadPtrChain(pWaterPump + 0x08, new UInt32[] { 0x08 }); for (int i = 0; i < ArrayLength; i++) { BigWater[i] = (int)ReadByte((UInt32)(pWaterPump + 0x10 + ItemLength * i)); SmallWater[i] = (int)ReadByte((UInt32)(pWaterPump + 0x10 + ItemLength * i + 1)); } } } SetOutputValue(OutputId.P1_WaterFire, SmallWater[0]); SetOutputValue(OutputId.P2_WaterFire, SmallWater[1]); SetOutputValue(OutputId.P3_WaterFire, SmallWater[2]); SetOutputValue(OutputId.P4_WaterFire, SmallWater[3]); SetOutputValue(OutputId.P1_BigGun, BigWater[0]); SetOutputValue(OutputId.P2_BigGun, BigWater[1]); SetOutputValue(OutputId.P3_BigGun, BigWater[2]); SetOutputValue(OutputId.P4_BigGun, BigWater[3]); SetOutputValue(OutputId.SmokeSwitch, ReadByte(_WaterMistState_CaveAddress)); SetOutputValue(OutputId.P1_LmpStart, GetBlinkingLightState(ReadByte(_Light_Array_CaveAddress + 1))); SetOutputValue(OutputId.P1_LmpGun, ReadByte(_Light_Array_CaveAddress + 2)); SetOutputValue(OutputId.P2_LmpStart, GetBlinkingLightState(ReadByte(_Light_Array_CaveAddress + 5))); SetOutputValue(OutputId.P2_LmpGun, ReadByte(_Light_Array_CaveAddress + 6)); SetOutputValue(OutputId.P3_LmpStart, GetBlinkingLightState(ReadByte(_Light_Array_CaveAddress + 9))); SetOutputValue(OutputId.P3_LmpGun, ReadByte(_Light_Array_CaveAddress + 10)); SetOutputValue(OutputId.P4_LmpStart, GetBlinkingLightState(ReadByte(_Light_Array_CaveAddress + 13))); SetOutputValue(OutputId.P4_LmpGun, ReadByte(_Light_Array_CaveAddress + 14)); SetOutputValue(OutputId.P1_ChairShake, ReadByte(_Motor_Array_CaveAddress)); SetOutputValue(OutputId.P2_ChairShake, ReadByte(_Motor_Array_CaveAddress + 1)); SetOutputValue(OutputId.P3_ChairShake, ReadByte(_Motor_Array_CaveAddress + 2)); SetOutputValue(OutputId.P4_ChairShake, ReadByte(_Motor_Array_CaveAddress + 3)); SetOutputValue(OutputId.P1_GunMotor, ReadByte(_Motor_Array_CaveAddress + 4)); SetOutputValue(OutputId.P2_GunMotor, ReadByte(_Motor_Array_CaveAddress + 5)); SetOutputValue(OutputId.P3_GunMotor, ReadByte(_Motor_Array_CaveAddress + 6)); SetOutputValue(OutputId.P4_GunMotor, ReadByte(_Motor_Array_CaveAddress + 7)); //When the game "Hits" some player, ID can be -1 (= All Players ?) //In that case the codeCave will set the Damage flags to all players //And will will filter here with the player Status to only enable the Damage output for playing players if (ReadByte(_DamageArray_CaveAddress) == 1 && IsGaming[0] == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_DamageArray_CaveAddress, 0x00); } if (ReadByte(_DamageArray_CaveAddress + 1) == 1 && IsGaming[1] == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_DamageArray_CaveAddress + 1, 0x00); } if (ReadByte(_DamageArray_CaveAddress + 2) == 1 && IsGaming[2] == 1) { SetOutputValue(OutputId.P3_Damaged, 1); WriteByte(_DamageArray_CaveAddress + 2, 0x00); } if (ReadByte(_DamageArray_CaveAddress + 3) == 1 && IsGaming[3] == 1) { SetOutputValue(OutputId.P4_Damaged, 1); WriteByte(_DamageArray_CaveAddress + 3, 0x00); } SetOutputValue(OutputId.P1_Credits, Coins[0]); SetOutputValue(OutputId.P2_Credits, Coins[1]); SetOutputValue(OutputId.P3_Credits, Coins[2]); SetOutputValue(OutputId.P4_Credits, Coins[3]); } //Game sets values as this : //0 = OFF //1 = CONSTANT ON //2 = CONSTANT OFF //We need -1 instead of 2 for custom blinking output state private int GetBlinkingLightState(byte ReadValue) { if (ReadValue == 2) return -1; else return ReadValue; } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcMechaTornado.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooter.Games { public class Game_ArcadepcMechaTornado : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte Atomizer = 0; public byte[] IsPlaying = null; public byte[] Shake = null; public byte[] RotatingMotor = null; public byte[] WaterPower = null; public byte[] StartLight = null; public byte[] PlayerLight = null; public byte[] LightBelt = null; public byte[] Damaged = null; public byte[] Recoil = null; public float[] Life = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcMechaTornado(String RomName) : base(RomName, "mecha", "jixiesheqiu") { _KnownMd5Prints.Add("Mecha Tornado Arcade - v1.5", "32458270101d83dd6e0f08d0c617bf7e"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P3_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P4_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P3_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P4_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P3_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P4_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Shaker)); _Outputs.Add(new GameOutput(OutputId.P2_Shaker)); _Outputs.Add(new GameOutput(OutputId.P3_Shaker)); _Outputs.Add(new GameOutput(OutputId.P4_Shaker)); _Outputs.Add(new GameOutput(OutputId.P1_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P2_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P3_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P4_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P1_BigGun)); _Outputs.Add(new GameOutput(OutputId.P2_BigGun)); _Outputs.Add(new GameOutput(OutputId.P3_BigGun)); _Outputs.Add(new GameOutput(OutputId.P4_BigGun)); //_Outputs.Add(new GameOutput(OutputId.P1_TicketFeeder)); //_Outputs.Add(new GameOutput(OutputId.P2_TicketFeeder)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).StartLight[0]); SetOutputValue(OutputId.P2_LmpStart, ((OutputData)_OutputData).StartLight[1]); SetOutputValue(OutputId.P3_LmpStart, ((OutputData)_OutputData).StartLight[2]); SetOutputValue(OutputId.P4_LmpStart, ((OutputData)_OutputData).StartLight[3]); SetOutputValue(OutputId.P1_LmpFront, ((OutputData)_OutputData).PlayerLight[0]); SetOutputValue(OutputId.P2_LmpFront, ((OutputData)_OutputData).PlayerLight[1]); SetOutputValue(OutputId.P3_LmpFront, ((OutputData)_OutputData).PlayerLight[2]); SetOutputValue(OutputId.P4_LmpFront, ((OutputData)_OutputData).PlayerLight[3]); SetOutputValue(OutputId.P1_GunMotor, ((OutputData)_OutputData).RotatingMotor[0]); SetOutputValue(OutputId.P2_GunMotor, ((OutputData)_OutputData).RotatingMotor[1]); SetOutputValue(OutputId.P3_GunMotor, ((OutputData)_OutputData).RotatingMotor[2]); SetOutputValue(OutputId.P4_GunMotor, ((OutputData)_OutputData).RotatingMotor[3]); SetOutputValue(OutputId.P1_Shaker, ((OutputData)_OutputData).Shake[0]); SetOutputValue(OutputId.P2_Shaker, ((OutputData)_OutputData).Shake[1]); SetOutputValue(OutputId.P3_Shaker, ((OutputData)_OutputData).Shake[2]); SetOutputValue(OutputId.P4_Shaker, ((OutputData)_OutputData).Shake[3]); SetOutputValue(OutputId.P1_WaterFire, ((OutputData)_OutputData).WaterPower[0]); SetOutputValue(OutputId.P2_WaterFire, ((OutputData)_OutputData).WaterPower[1]); SetOutputValue(OutputId.P3_WaterFire, ((OutputData)_OutputData).WaterPower[2]); SetOutputValue(OutputId.P4_WaterFire, ((OutputData)_OutputData).WaterPower[3]); //SetOutputValue(OutputId.WaterPump, _OutputData.WaterPump); if (((OutputData)_OutputData).IsPlaying[0] == 1) { SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P1_CtmRecoil, (int)((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P1_Damaged, (int)((OutputData)_OutputData).Damaged[0]); } else { SetOutputValue(OutputId.P1_Life, 0); } if (((OutputData)_OutputData).IsPlaying[1] == 1) { SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P2_CtmRecoil, (int)((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P2_Damaged, (int)((OutputData)_OutputData).Damaged[1]); } else { SetOutputValue(OutputId.P2_Life, 0); } if (((OutputData)_OutputData).IsPlaying[2] == 1) { SetOutputValue(OutputId.P3_Life, (int)((OutputData)_OutputData).Life[2]); SetOutputValue(OutputId.P3_CtmRecoil, (int)((OutputData)_OutputData).Recoil[2]); SetOutputValue(OutputId.P3_Damaged, (int)((OutputData)_OutputData).Damaged[2]); } else { SetOutputValue(OutputId.P3_Life, 0); } if (((OutputData)_OutputData).IsPlaying[3] == 1) { SetOutputValue(OutputId.P4_Life, (int)((OutputData)_OutputData).Life[3]); SetOutputValue(OutputId.P4_CtmRecoil, (int)((OutputData)_OutputData).Recoil[3]); SetOutputValue(OutputId.P4_Damaged, (int)((OutputData)_OutputData).Damaged[3]); } else { SetOutputValue(OutputId.P4_Life, 0); } SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, (int)((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, (int)((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcPvzLastStand.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter.Games { internal class Game_ArcadepcPvzLastStand : Game { private class InputData : DsTcpData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public InputData(int PlayerNumber) : base(PlayerNumber) { } } protected string _MainWindowTitle = string.Empty; private DsTcp_Client _Tcpclient; private InputData _InputData; private const int MAX_PLAYERS = 1; //Inputs private UInt32 _SystemButtons_Offset = 0x00119A41; private UInt32 _TriggerButton_Offset = 0x001120CA; //Outputs private UInt32 _Outputs_Offset = 0x00118AC0; private UInt32 _Credits_Offset = 0x0010AF80; private InjectionStruct _CustomDamage_InjectionStruct = new InjectionStruct(0x000091BD, 5); private UInt32 _CustomDamageOriginalDword_Offset = 0x00072080; private UInt32 _P1_DamageStatus_CaveAddress = 0; // private HardwareScanCode _DIK_Coin = HardwareScanCode.DIK_5; private HardwareScanCode _DIK_Trigger = HardwareScanCode.DIK_1; //This game needs to hook to 2 different processes : //shell.exe for outputs hack //Pvz.exe for Inputs and WindowSize private Process _PvzWindowProcess; /// /// Constructor /// public Game_ArcadepcPvzLastStand(String RomName) : base(RomName, "shell") { _KnownMd5Prints.Add("Shell.exe v1.0.0 - Original", "c875cf47549a70f2e94c4059b52be2eb"); _KnownMd5Prints.Add("Shell.exe v1.0.0 - Patch_v2 by Ducon", "48fdf6363dde5fac142864689d5757c8"); _KnownMd5Prints.Add("Shell.exe v1.0.0 - Patch_vArgon by Ducon", "878736c94d934bc105d15a2c19192889"); _InputData = new InputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); Process[] PvzProcesses = Process.GetProcessesByName("pvz"); if (processes.Length > 0 && PvzProcesses.Length > 0) { _TargetProcess = PvzProcesses[0]; _PvzWindowProcess = PvzProcesses[0]; // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals("PvzCore")) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); //Start TcpClient to dial with Unity Game _Tcpclient = new DsTcp_Client("127.0.0.1", DsTcp_Client.DS_TCP_CLIENT_PORT); _Tcpclient.TcpConnected += DsTcp_client_TcpConnected; _Tcpclient.Connect(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } ~Game_ArcadepcPvzLastStand() { if (_Tcpclient != null) _Tcpclient.Disconnect(); } /// /// Sending a TCP message on connection /// private void DsTcp_client_TcpConnected(Object Sender, EventArgs e) { if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; if (_DisableInputHack) _InputData.EnableInputsHack = 0; else _InputData.EnableInputsHack = 1; _Tcpclient.SendMessage(_InputData.ToByteArray()); } #region Screen public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)TotalResX) PlayerData.RIController.Computed_X = (int)TotalResX; if (PlayerData.RIController.Computed_Y > (int)TotalResY) PlayerData.RIController.Computed_Y = (int)TotalResY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x00; SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Code injection where the game is calling for rumble because of damage. /// That way we can known when a player is damaged and make our own output. /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov _P1_DamageStatus_CaveAddress, 1 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_DamageStatus_CaveAddress)); CaveMemory.Write_StrBytes("01"); //push Shell.exe+OFFSET CaveMemory.Write_StrBytes("68"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _CustomDamageOriginalDword_Offset)); //Inject it CaveMemory.InjectToOffset(_CustomDamage_InjectionStruct, "Dammage"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { _InputData.Axis_X[0] = (float)PlayerData.RIController.Computed_X; _InputData.Axis_Y[0] = (float)PlayerData.RIController.Computed_Y; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TriggerButton_Offset, 0x01); _InputData.Trigger[0] = 1; } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TriggerButton_Offset, 0x00); _InputData.Trigger[0] = 0; } } if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } /// /// Low-level Keyboard hook callback. /// Shell.exe window need to have focus to register system button or Fire button /// Using this hook we can modify the Shell memory directly when it's not focused public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _DIK_Coin) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _SystemButtons_Offset, 0x40); } if (s.scanCode == _DIK_Trigger) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TriggerButton_Offset, 0x01); } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == _DIK_Coin) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _SystemButtons_Offset, 0xBF); } if (s.scanCode == _DIK_Trigger) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TriggerButton_Offset, 0x00); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_R)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_G)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_B)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.TicketDrive)); _Outputs.Add(new GameOutput(OutputId.TicketMeter)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs byte OutputData = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset); SetOutputValue(OutputId.P1_LmpGun_R, OutputData & 0x01); SetOutputValue(OutputId.P1_LmpGun_G, OutputData >> 1 & 0x01); SetOutputValue(OutputId.P1_LmpGun_B, OutputData >> 2 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, OutputData >> 3 & 0x01); SetOutputValue(OutputId.P1_Lmp_R, OutputData >> 4 & 0x01); SetOutputValue(OutputId.P1_Lmp_G, OutputData >> 5 & 0x01); SetOutputValue(OutputId.TicketDrive, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 1) >> 1 & 0x01); //[Recoil] custom Output SetOutputValue(OutputId.P1_CtmRecoil, OutputData >> 3 & 0x01); //Custom Outputs: //[Damaged] custom Output if (ReadByte(_P1_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_DamageStatus_CaveAddress, 0x00); } //Credits SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcRobinHood.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { internal class Game_ArcadepcRobinHood : Game { private const String GAMEDATA_FOLDER = @"MemoryData\sega\rhood"; //MEMORY ADDRESSES private UInt32 _Credits_Offset = 0x009B32FC; private UInt32 _ShotArrowCount_Offset = 0x009C616C; private UInt32 _SceneIndex_Offset = 0x009C6138; private UInt32 _Outputs_Struct_Offset = 0x009E6DE8; private InjectionStruct _Outputs_InjectionStruct = new InjectionStruct(0x0000137B, 5); private enum OutputType : int { StartLamp = 0x94, TicketOut = 0x96, Bonus_LowByte = 0x97, Bonus_HighByte = 0x98 } //This one is created by the launcher and will be read private UInt32 _OutputsRecopy_CaveAddress = 0; /// /// Constructor /// public Game_ArcadepcRobinHood(String RomName) : base(RomName, "game") { //JConfig and Teknoparrot are sharing the same offsets, non need for memory files for now... _KnownMd5Prints.Add("Robin Hood v2.06-A", "6d5b7bcf5211271559ab63403d5d11f2"); _KnownMd5Prints.Add("Robin Hood v2.04-E", "1bee8a829b18f5e33b0ba22ba467dcc9"); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { foreach (Process p in Process.GetProcessesByName(_Target_Process_Name)) { _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _OutputsRecopy_CaveAddress = _OutputsDatabank_Address; SetHack_RecopyOutputs(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// Outputs are stored in a roll buffer: /// [+0] 4 Bytes for next index /// [+0x0C] Output Indexes /// [+0x8C] Corresponding values /// /// Index: /// 0x94: Start Light (0=OFF, 1=ON, 0x2F=FLASH) /// 0x96; Tickets to send /// 0x97: Bonus value, low byte /// 0x98: Bonus value, High byte private void SetHack_RecopyOutputs() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push ebx CaveMemory.Write_StrBytes("53"); //push ecx CaveMemory.Write_StrBytes("51"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //Loop: //cmp [game.exe+Outputs_Struct_Offset],eax CaveMemory.Write_StrBytes("39 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Struct_Offset)); //jng Exit CaveMemory.Write_StrBytes("0F 8E 18 00 00 00"); //movzx ebx,byte ptr [eax+game.exe+Outputs_ID_Table] CaveMemory.Write_StrBytes("0F B6 98"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Struct_Offset + 0x0C)); //add ebx,_Outputs_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_OutputsRecopy_CaveAddress)); //mov cl,[eax+game.exe+eax+game.exe+Outputs_Values_Table] CaveMemory.Write_StrBytes("8A 88"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Struct_Offset + 0x8C)); //mov [ebx],cl CaveMemory.Write_StrBytes("88 0B"); //inc eax CaveMemory.Write_StrBytes("40"); //jmp Loop CaveMemory.Write_StrBytes("EB DC"); //mov [game.exe+Outputs_Struct_Offset],00000000 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Struct_Offset)); CaveMemory.Write_StrBytes("00 00 00 00"); //pop ecx CaveMemory.Write_StrBytes("59"); //pop ebx CaveMemory.Write_StrBytes("5B"); //mov eax, 50 CaveMemory.Write_StrBytes("B8 50 00 00 00"); //Inject it CaveMemory.InjectToOffset(_Outputs_InjectionStruct, "MouseAxis"); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.BonusDisplay)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte StartLampState = ReadByte(_OutputsRecopy_CaveAddress + (uint)OutputType.StartLamp); switch (StartLampState) { case 0x00: SetOutputValue(OutputId.P1_CtmLmpStart, 0); break; case 0x01: SetOutputValue(OutputId.P1_CtmLmpStart, 1); break; case 0x2F: SetOutputValue(OutputId.P1_CtmLmpStart, -1); break; default: break; } //Game is filtering thanks to this value: 0 = Player increase arrow shot, 2/3/4 is attract mode increasing arrow shot byte SceneIndex = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _SceneIndex_Offset); _P1_Ammo = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ShotArrowCount_Offset, 4), 0); if (SceneIndex == 0) { if (_P1_Ammo > _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); } _P1_LastAmmo = _P1_Ammo; int Credits = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, 4), 0); int BonusValueDisplay = BitConverter.ToInt32(ReadBytes(_OutputsRecopy_CaveAddress + (uint)OutputType.Bonus_LowByte, 4), 0); SetOutputValue(OutputId.BonusDisplay, BonusValueDisplay); SetOutputValue(OutputId.Credits, Credits); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcTopGun.cs ================================================ using System; using System.Collections.Generic; using DemulShooter.Games; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooter { public class Game_ArcadepcTopGun : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte IsPlaying = 0; public byte Recoil = 0; public int Ammo = 0; public int Credits = 0; public byte[] GiftDoor = null; public byte LeftKeyLight = 0; public byte CenterKeyLight = 0; public byte RightKeyLight = 0; public byte ScreenSideLight = 0; public byte GiftLight = 0; public byte TicketFeeder = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 1; /// /// Constructor /// public Game_ArcadepcTopGun(String RomName) : base(RomName, "BiuBiuGun", "BiuBiuGun") { _KnownMd5Prints.Add("TopGun v2.0.0.3.1.18", "c091dfa6112dcd0b6270d6f636fce5af"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil); SetOutputValue(OutputId.P1_Ammo, (int)((OutputData)_OutputData).Ammo); SetOutputValue(OutputId.Credits, (int)((OutputData)_OutputData).Credits); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcWaterWar2.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; namespace DemulShooter.Games { public class Game_ArcadepcWaterWar2 : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] WaterFire = null; public byte WaterPump = 0; public byte[] StartLed = null; public byte[] TicketFeeder = null; public byte[] BigGun = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcWaterWar2(String RomName) : base(RomName, "Water2_EN", "Water2_EN") { _KnownMd5Prints.Add("Happy Water War 2 - v1.1", "f3fb78a0aa85562caba6ffbc6f295e13"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P2_WaterFire)); _Outputs.Add(new GameOutput(OutputId.WaterPump)); _Outputs.Add(new GameOutput(OutputId.P1_BigGun)); _Outputs.Add(new GameOutput(OutputId.P2_BigGun)); _Outputs.Add(new GameOutput(OutputId.P1_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P2_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).StartLed[0]); SetOutputValue(OutputId.P2_LmpStart, ((OutputData)_OutputData).StartLed[1]); SetOutputValue(OutputId.P1_WaterFire, ((OutputData)_OutputData).WaterFire[0]); SetOutputValue(OutputId.P2_WaterFire, ((OutputData)_OutputData).WaterFire[1]); SetOutputValue(OutputId.WaterPump, ((OutputData)_OutputData).WaterPump); SetOutputValue(OutputId.P1_BigGun, ((OutputData)_OutputData).BigGun[0]); SetOutputValue(OutputId.P2_BigGun, ((OutputData)_OutputData).BigGun[1]); SetOutputValue(OutputId.P1_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[0]); SetOutputValue(OutputId.P2_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[1]); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_ArcadepcWws.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooter.Games { public class Game_ArcadepcWws : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Reload = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public int[] Ammo = null; public int[] Life = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcWws(String RomName) : base(RomName, "CowBoy", "CowBoy") { _KnownMd5Prints.Add("Wild West Shoutout original dump - V3.3.2103T", "4f543c469818c1db8bc856be84f0131e"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) ((InputData)_InputData).Reload[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) ((InputData)_InputData).Reload[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); /*_Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil));*/ _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); if (((OutputData)_OutputData).IsPlaying[0] == 1) { SetOutputValue(OutputId.P1_Life, ((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P1_Ammo, ((OutputData)_OutputData).Ammo[0]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); } else { SetOutputValue(OutputId.P1_Life, 0); SetOutputValue(OutputId.P1_Ammo, 0); } if (((OutputData)_OutputData).IsPlaying[1] == 1) { SetOutputValue(OutputId.P2_Life, ((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P2_Ammo, ((OutputData)_OutputData).Ammo[1]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); } else { SetOutputValue(OutputId.P2_Life, 0); SetOutputValue(OutputId.P2_Ammo, 0); } SetOutputValue(OutputId.P1_Credits, ((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, ((OutputData)_OutputData).Credits[1]); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_CxbxGsquad.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { /// /// For this hack, Cxbx-Reloaded must be started with the following command line : /// cxbxr-ldr.exe /load [PATH_TO_ROM] /// This will result in one (and only one Process), and easier to target and get window handle, /// whereas running Cxbx GUI, then choosing a Rom will create 2 different processes (sources : Cxbx Wiki) /// Last tested on build CI-0b69563 (2022-11-20) /// class Game_CxbxGsquad : Game { /*** MEMORY ADDRESSES **/ private InjectionStruct _JvsButtons_InjectionStruct = new InjectionStruct(0x000752B2, 7); private InjectionStruct _JvsAxis_InjectionStruct = new InjectionStruct(0x000752D2, 6); private UInt32 _Buttons_CaveAddress = 0; private UInt32 _Axis_CaveAddress = 0; private UInt32 _P1_Calibration_Offset = 0x0004344B4; private UInt32 _P2_Calibration_Offset = 0x0004344CC; //Outputs private UInt32 _JvsOutputBuffer_Offset = 0x004F10F0; private UInt32 _P1_Status_Offset = 0x00455A28; private UInt32 _LifePtr_Offset = 0x00455B14; private UInt32 _AmmoPtr_Offset = 0x00441828; private UInt32 _Credits_Offset = 0x004A9EA0; private UInt32 _RomLoaded_CheckIntructionn_Offset = 0x000752BE; /// /// Constructor /// public Game_CxbxGsquad(String RomName) : base(RomName, "cxbxr-ldr") { _tProcess.Start(); _KnownMd5Prints.Add("Cxbxr-ldr.exe - Chihiro-DS 1.0", "d3675f7bb270072f33d9106497dd9bbc"); Logger.WriteLog("Waiting for Chihiro " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //This is HEX code for the instrution we're testing to see which process is the good one to hook byte[] bTest = new byte[] { 0x8D, 0x54, 0x24, 0x0F }; if (CheckBytes((UInt32)_TargetProcess_MemoryBaseAddress + _RomLoaded_CheckIntructionn_Offset, bTest)) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog("WindowHandle = " + _GameWindowHandle.ToString()); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [00 ; 0xFF] => 256 //Y => [00; 0xFF] => 256 double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _Buttons_CaveAddress = _InputsDatabank_Address; _Axis_CaveAddress = _InputsDatabank_Address + 0x10; SetHack_Buttons(); SetHack_Axis(); SetHack_Calibration(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Replacing the memory read from JVS message received by our own buffer /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[_Buttons_CaveAddress] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //mov [edi],eax CaveMemory.Write_StrBytes("89 07"); //mov cl,[_Buttons_CaveAddress + 4] CaveMemory.Write_StrBytes("8A 0D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress + 4)); //Inject it CaveMemory.InjectToOffset(_JvsButtons_InjectionStruct, "Buttons"); } /// /// Replacing the memory read from JVS message received by our own buffer /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ecx, _Axis_CaveAddress CaveMemory.Write_StrBytes("B9"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_CaveAddress)); //mov edx,[ecx] CaveMemory.Write_StrBytes("8B 11"); //Inject it CaveMemory.InjectToOffset(_JvsAxis_InjectionStruct, "Axis"); } /// /// /// JVS data is computed to [-320 / +320] and [-240 / +240] values thanks to calibration values /// Overwriting calibration data with values tested to have perfect accuracy /// private void SetHack_Calibration() { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Calibration_Offset, new byte[] { 0xFD, 0xFF, 0xE0, 0x00, 0xCF, 0xFE, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2C, 0x01, 0x02, 0x00, 0x00, 0x00, 0x23, 0xFF }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Calibration_Offset, new byte[] { 0xFD, 0xFF, 0xE0, 0x00, 0xCF, 0xFE, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2C, 0x01, 0x02, 0x00, 0x00, 0x00, 0x23, 0xFF }); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteByte(_Axis_CaveAddress + 1, (byte)PlayerData.RIController.Computed_X); WriteByte(_Axis_CaveAddress + 3, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x01); Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0xFD); Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 2, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 2, 0xBF); } else if (PlayerData.ID == 2) { WriteByte(_Axis_CaveAddress + 5, (byte)PlayerData.RIController.Computed_X); WriteByte(_Axis_CaveAddress + 7, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x01); Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0xFD); Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 4, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 4, 0xBF); } } public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_1) Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x80); else if (s.scanCode == HardwareScanCode.DIK_2) Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x80); else if (s.scanCode == HardwareScanCode.DIK_O) Apply_OR_ByteMask(_Buttons_CaveAddress + 2, 0x80); //P1 ACTION else if (s.scanCode == HardwareScanCode.DIK_P) Apply_OR_ByteMask(_Buttons_CaveAddress + 4, 0x80); //P2 ACTION else if (s.scanCode == HardwareScanCode.DIK_9) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x04); //SERVICE else if (s.scanCode == HardwareScanCode.DIK_0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x80); //TEST } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == HardwareScanCode.DIK_1) Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_2) Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_O) Apply_AND_ByteMask(_Buttons_CaveAddress + 2, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_P) Apply_AND_ByteMask(_Buttons_CaveAddress + 4, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_9) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFB); else if (s.scanCode == HardwareScanCode.DIK_0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0x7F); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); //Never activated ? _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); //_Outputs.Add(new GameOutput(OutputId.LmpCoin)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 7 & 1); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 2 & 1); SetOutputValue(OutputId.LmpBillboard, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 4 & 1); SetOutputValue(OutputId.P1_LmpHolder, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 3 & 1); SetOutputValue(OutputId.P2_LmpHolder, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 5 & 1); SetOutputValue(OutputId.P1_GunRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 6 & 1); SetOutputValue(OutputId.P2_GunRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 1 & 1); //Customs Outputs //Custom recoil will be triggered by the original recoil pulse SetOutputValue(OutputId.P1_CtmRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 6 & 1); SetOutputValue(OutputId.P2_CtmRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 1 & 1); //Player Status : //[2] : In-Game int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset); int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset + 0x1AC); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (P1_Status == 2) { _P1_Life = (int)ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _LifePtr_Offset, new UInt32[] { 0xB8 }); _P1_Ammo = (int)ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _AmmoPtr_Offset, new UInt32[] { 0x38, 0x0C }); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 2) { _P2_Life = (int)ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _LifePtr_Offset + 0x1AC, new UInt32[] { 0xB8 }); _P2_Ammo = (int)ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _AmmoPtr_Offset, new UInt32[] { 0x38 + 0x650, 0x0C }); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_CxbxHod3.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { /// /// For this hack, Cxbx-Reloaded must be started with the following command line : /// cxbxr-ldr.exe /load [PATH_TO_ROM] /// This will result in one (and only one Process), and easier to target and get window handle, /// whereas running Cxbx GUI, then choosing a Rom will create 2 different processes (sources : Cxbx Wiki) /// Last tested on build CI-0b69563 (2022-11-20) /// class Game_CxbxHod3 : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Inputs_OFFSET = 0x00267108; private UInt32 _P1_BYTE1_INDEX = 0x0D; private UInt32 _P1_BYTE2_INDEX = 0x0E; private UInt32 _P2_BYTE1_INDEX = 0x0F; private UInt32 _P2_BYTE2_INDEX = 0x10; /*private UInt32 _P1_X_INDEX = 0x12; private UInt32 _P1_Y_INDEX = 0x14; private UInt32 _P2_X_INDEX = 0x16; private UInt32 _P2_Y_INDEX = 0x18; private NopStruct _Nop_P1_X = new NopStruct(0x00001B62, 4); private NopStruct _Nop_P1_Y = new NopStruct(0x00001B6B, 4); private NopStruct _Nop_P2_X = new NopStruct(0x00001B74, 4); private NopStruct _Nop_P2_Y = new NopStruct(0x00001B7D, 4);*/ private NopStruct _Nop_P1_BYTE1 = new NopStruct(0x00001B21, 3); private NopStruct _Nop_P1_BYTE2 = new NopStruct(0x00001B30, 3); private NopStruct _Nop_P2_BYTE1 = new NopStruct(0x00001B3F, 3); private NopStruct _Nop_P2_BYTE2 = new NopStruct(0x00001B4E, 3); private InjectionStruct _AxisX_InjectionStruct = new InjectionStruct(0x00069F95, 5); private InjectionStruct _AxisY_InjectionStruct = new InjectionStruct(0x00069FC5, 5); private UInt32 _RomLoaded_CheckIntructionn_Offset = 0x00001B62; //Outputs private UInt32 _JvsOutputBuffer_Offset = 0x004C78D8; //private UInt32 _GameScreen_Offset = 0x0026A410; private UInt32 _P1_Status_Offset = 0x00266708; private UInt32 _P2_Status_Offset = 0x00266934; private UInt32 _P1_Ammo_Offset = 0x00266760; private UInt32 _P2_Ammo_Offset = 0x0026698C; private UInt32 _P1_Life_Offset = 0x00266714; private UInt32 _P2_Life_Offset = 0x00266940; private UInt32 _Credits_Offset = 0x004BBA38; private UInt32 _P1_X_CaveAddress = 0; private UInt32 _P1_Y_CaveAddress = 0; private UInt32 _P2_X_CaveAddress = 0; private UInt32 _P2_Y_CaveAddress = 0; /// /// Constructor /// public Game_CxbxHod3(String RomName) : base(RomName, "cxbxr-ldr") { _tProcess.Start(); _KnownMd5Prints.Add("Cxbxr-ldr.exe - Chihiro-DS 1.0", "d3675f7bb270072f33d9106497dd9bbc"); Logger.WriteLog("Waiting for Chihiro " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //This is HEX code for the instrution we're testing to see which process is the good one to hook byte[] bTest = new byte[] { 0x66, 0x89, 0x4E, 0x12 }; if (CheckBytes((UInt32)_TargetProcess_MemoryBaseAddress + _RomLoaded_CheckIntructionn_Offset, bTest)) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog("WindowHandle = " + _GameWindowHandle.ToString()); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [-320 ; +320] => 640 //Y => [-240; +240] => 480 double dMinX = -320.0; double dMaxX = 320.0; double dMinY = -240.0; double dMaxY = 240.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / TotalResX) - dRangeX / 2); PlayerData.RIController.Computed_Y = Convert.ToInt16((Math.Round(dRangeY * PlayerData.RIController.Computed_Y / TotalResY) - dRangeY / 2) * -1); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P1_Y_CaveAddress = _InputsDatabank_Address + 4; _P2_X_CaveAddress = _InputsDatabank_Address + 8; _P2_Y_CaveAddress = _InputsDatabank_Address + 12; SetHack_Buttons(); SetHack_Axis_X(); SetHack_Axis_Y(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Buttons() { //Simply NOPing procedure parts where the game writes buttons values from JVS data //For each player, there are 2 bytes of button data : //BYTE1 : 0x80=START, 0x02=TRIGGER, 0x01=OUT_OF_SCREEN //BYTE2 : 0x80=PUMP, 0x40= ?? SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_BYTE1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_BYTE2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_BYTE1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_BYTE2); } private void SetHack_Axis_X() { //Further down the axis computing, the game translates 0x00-0xFF JVS axis data to -320/+320 value after calibration data //Injecting our values at that point Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,ebp CaveMemory.Write_StrBytes("8B C5"); //shl eax,03 CaveMemory.Write_StrBytes("C1 E0 03"); //add eax, _P1_X_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_CaveAddress)); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //Inject it CaveMemory.InjectToOffset(_AxisX_InjectionStruct, "Axis"); } private void SetHack_Axis_Y() { //Further down the axis computing, the game translates 0x00-0xFF JVS axis data to -240/+240 value after calibration data //Injecting our values at that point Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,ebp CaveMemory.Write_StrBytes("8B C5"); //shl eax,03 CaveMemory.Write_StrBytes("C1 E0 03"); //add eax, _P1_X_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Y_CaveAddress)); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //Inject it CaveMemory.InjectToOffset(_AxisY_InjectionStruct, "Axis"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes(PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes(PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P1_BYTE1_INDEX, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P1_BYTE1_INDEX, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P1_BYTE2_INDEX, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P1_BYTE2_INDEX, 0x7F); } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P2_BYTE1_INDEX, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P2_BYTE1_INDEX, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P2_BYTE2_INDEX, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P2_BYTE2_INDEX, 0x7F); } } public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_1) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P1_BYTE1_INDEX, 0x80); else if (s.scanCode == HardwareScanCode.DIK_2) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P2_BYTE1_INDEX, 0x80); } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == HardwareScanCode.DIK_1) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P1_BYTE1_INDEX, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_2) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _Inputs_OFFSET + _P2_BYTE1_INDEX, 0x7F); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 7 & 1); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 4 & 1); //Customs Outputs //Player Status : //[5] : In-Game //[4] : Continue Screen //[66] : Game Over //[9] : Attract Demo int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset); int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Status_Offset); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (P1_Status == 5) { _P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); _P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 5) { _P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset); _P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_CxbxVcop3.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { /// /// For this hack, Cxbx-Reloaded must be started with the following command line : /// cxbxr-ldr.exe /load [PATH_TO_ROM] /// This will result in one (and only one Process), and easier to target and get window handle, /// whereas running Cxbx GUI, then choosing a Rom will create 2 different processes (sources : Cxbx Wiki) /// Last tested on build CI-0b69563 (2022-11-20) /// class Game_CxbxVcop3 : Game { /*** MEMORY ADDRESSES **/ private InjectionStruct _JvsButtons_InjectionStruct = new InjectionStruct(0x0006CAD8, 5); private InjectionStruct _JvsAxis_InjectionStruct = new InjectionStruct(0x0006D531, 6); private UInt32 _Buttons_CaveAddress = 0; private UInt32 _Axis_CaveAddress = 0; private UInt32 _P1_Calibration_Offset = 0x00031E52C; private UInt32 _P2_Calibration_Offset = 0x00031E544; //Outputs private UInt32 _JvsOutputBuffer_Offset = 0x004F28D0; private UInt32 _P1_Status_Offset = 0x0036D468; private UInt32 _P1_Life_Offset = 0x0036D474; private UInt32 _P1_Ammo_Offset = 0x00357942; private UInt32 _Credits_Offset = 0x004D9018; private UInt32 _RomLoaded_CheckIntructionn_Offset = 0x0006A3D6; /// /// Constructor /// public Game_CxbxVcop3(String RomName) : base(RomName, "cxbxr-ldr") { _tProcess.Start(); _KnownMd5Prints.Add("Cxbxr-ldr.exe - Chihiro-DS 1.0", "d3675f7bb270072f33d9106497dd9bbc"); Logger.WriteLog("Waiting for Chihiro " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //This is HEX code for the instrution we're testing to see which process is the good one to hook byte[] bTest = new byte[] { 0x8B, 0xE8 }; if (CheckBytes((UInt32)_TargetProcess_MemoryBaseAddress + _RomLoaded_CheckIntructionn_Offset, bTest)) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog("WindowHandle = " + _GameWindowHandle.ToString()); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [00 ; 0xFF] => 256 //Y => [00; 0xFF] => 256 double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _Buttons_CaveAddress = _InputsDatabank_Address; _Axis_CaveAddress = _InputsDatabank_Address + 0x10; SetHack_Buttons(); SetHack_Axis(); SetHack_Calibration(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Replacing the memory read from JVS message received by our own buffer /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push esi CaveMemory.Write_StrBytes("56"); //mov esi, _Buttons_CaveAddress CaveMemory.Write_StrBytes("BE"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //Inject it CaveMemory.InjectToOffset(_JvsButtons_InjectionStruct, "Buttons"); } /// /// Replacing the memory read from JVS message received by our own buffer /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov edx, _Axis_CaveAddress CaveMemory.Write_StrBytes("BA"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_CaveAddress)); //mov ecx,[edx] CaveMemory.Write_StrBytes("8B 0A"); //Inject it CaveMemory.InjectToOffset(_JvsAxis_InjectionStruct, "Axis"); } /// /// JVS data is computed to [-320 / +320] and [-240 / +240] values thanks to calibration values /// Overwriting calibration data with values tested to have perfect accuracy /// private void SetHack_Calibration() { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Calibration_Offset, new byte[] { 0xFD, 0xFF, 0xE0, 0x00, 0xCF, 0xFE, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2C, 0x01, 0x02, 0x00, 0x00, 0x00, 0x23, 0xFF }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Calibration_Offset, new byte[] { 0xFD, 0xFF, 0xE0, 0x00, 0xCF, 0xFE, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2C, 0x01, 0x02, 0x00, 0x00, 0x00, 0x23, 0xFF }); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteByte(_Axis_CaveAddress + 1, (byte)PlayerData.RIController.Computed_X); WriteByte(_Axis_CaveAddress + 3, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 5, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 5, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 5, 0x01); Apply_OR_ByteMask(_Buttons_CaveAddress + 5, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 5, 0xFD); Apply_AND_ByteMask(_Buttons_CaveAddress + 5, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xBF); } else if (PlayerData.ID == 2) { WriteByte(_Axis_CaveAddress + 5, (byte)PlayerData.RIController.Computed_X); WriteByte(_Axis_CaveAddress + 7, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x01); Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0xFD); Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xBF); } } public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_1) Apply_OR_ByteMask(_Buttons_CaveAddress + 5, 0x80); else if (s.scanCode == HardwareScanCode.DIK_2) Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x80); else if (s.scanCode == HardwareScanCode.DIK_O) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x80); //P1 ACTION else if (s.scanCode == HardwareScanCode.DIK_P) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x80); //P2 ACTION /*else if (s.scanCode == HardwareScanCode.DIK_9) Apply_OR_ByteMask(_Buttons_CaveAddress + 4, 0x04); //SERVICE else if (s.scanCode == HardwareScanCode.DIK_0) Apply_OR_ByteMask(_Buttons_CaveAddress + 4, 0x80); //TEST*/ } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == HardwareScanCode.DIK_1) Apply_AND_ByteMask(_Buttons_CaveAddress + 5, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_2) Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_O) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0x7F); else if (s.scanCode == HardwareScanCode.DIK_P) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0x7F); /*else if (s.scanCode == HardwareScanCode.DIK_9) Apply_AND_ByteMask(_Buttons_CaveAddress + 4, 0xFB); else if (s.scanCode == HardwareScanCode.DIK_0) Apply_AND_ByteMask(_Buttons_CaveAddress + 4, 0x7F);*/ } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFoot)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFoot)); //_Outputs.Add(new GameOutput(OutputId.P1_LmpHead)); //_Outputs.Add(new GameOutput(OutputId.P2_LmpHead)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 7 & 1); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 4 & 1); SetOutputValue(OutputId.P1_LmpFoot, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 6 & 1); SetOutputValue(OutputId.P2_LmpFoot, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 3 & 1); //Customs Outputs //Player Status : //[0] : Inactive //[2] : In-Game //[8] : Continue Screen //[16] : Game Over //[128] : Attract Demo int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset); int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset + 0xAC); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (P1_Status == 2) { _P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); _P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 2) { _P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset + 0xAC); _P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset + 0xD8); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_CxbxVcop3_Old.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { /// /// For this hack, Cxbx-Reloaded must be started with the following command line : /// cxbxr-ldr.exe /load [PATH_TO_ROM] /// This will result in one (and only one Process), and easier to target and get window handle, /// whereas running Cxbx GUI, then choosing a Rom will create 2 different processes (sources : Cxbx Wiki) /// Last tested on build CI-0b69563 (2022-11-20) /// public class Game_CxbxVcop3_Old : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x3578F4; private UInt32 _P1_Y_Offset = 0x3578F8; private UInt32 _P2_X_Offset = 0x3579CC; private UInt32 _P2_Y_OFfset = 0x3579D0; private UInt32 _Buttons_Injection_Offset = 0x6C9B0; private UInt32 _Buttons_Injection_Return_Offset = 0x6C9B8; private NopStruct _Nop_X_1 = new NopStruct(0x0006A3D8, 3); private NopStruct _Nop_X_2 = new NopStruct(0x0006A403, 3); private NopStruct _Nop_Y_1 = new NopStruct(0x0006A41E, 3); private NopStruct _Nop_Y_2 = new NopStruct(0x0006A3F2, 3); private UInt32 _RomLoaded_CheckIntructionn_Offset = 0x0006A3D8; private UInt32 _P1_Buttons_CaveAddress = 0; private UInt32 _P2_Buttons_CaveAddress = 0; //Outputs private UInt32 _JvsOutputBuffer_Offset = 0x004F28D0; /// /// Constructor /// public Game_CxbxVcop3_Old(String RomName) : base(RomName, "cxbxr-ldr") { _tProcess.Start(); Logger.WriteLog("Waiting for Chihiro " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //This is HEX code for the instrution we're testing to see which process is the good one to hook byte[] bTest = new byte[] { 0x8B, 0xE8 }; if (CheckBytes((UInt32)_TargetProcess_MemoryBaseAddress + _RomLoaded_CheckIntructionn_Offset - 2, bTest)) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog("WindowHandle = " + _GameWindowHandle.ToString()); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [-320 ; +320] => 640 //Y => [-240; +240] => 480 double dMinX = -320.0; double dMaxX = 320.0; double dMinY = -240.0; double dMaxY = 240.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / TotalResX) - dRangeX / 2); PlayerData.RIController.Computed_Y = Convert.ToInt16((Math.Round(dRangeY * PlayerData.RIController.Computed_Y / TotalResY) - dRangeY / 2) * -1); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Buttons_CaveAddress = _InputsDatabank_Address; _P2_Buttons_CaveAddress = _InputsDatabank_Address + 0x20; SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Buttons() { //For P1 : //modify [edx+0x21] so that it corresponds to our values //EDX + 0x21 ==> 0x10 (START) //EDX + 0x22 ==> //EDX + 0x23 ==> 0xFF (TRIGGER) //EDX + 0x24 ==> 0xFF (RELOAD) //EDX + 0x25 ==> 0xFF (CHANGE WEAPON) //EDX + 0x26 ==> 0xFF (PEDAL) //Not doing anything //EDX + 0x29 ==> 0xFF (Bullet Time) //[ESP + 0x18] (after our push) contains controller ID (0->4) //We won't change any other input than Trigger, reload and Change Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov edx, [esp+04] CaveMemory.Write_StrBytes("8B 54 24 04"); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //push ecx CaveMemory.Write_StrBytes("51"); //cmp [esp+0x18], 0 CaveMemory.Write_StrBytes("83 7C 24 18 00"); //je Player1 CaveMemory.Write_StrBytes("0F 84 10 00 00 00"); //cmp [esp + 0x18], 1 CaveMemory.Write_StrBytes("83 7C 24 18 01"); //je Player2 CaveMemory.Write_StrBytes("0F 84 1B 00 00 00"); //jmp exit CaveMemory.Write_StrBytes("E9 30 00 00 00"); //Player1 : //mov eax, [_P1_Buttons_CaveAddress] byte[] b = BitConverter.GetBytes(_P1_Buttons_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //mov ebx, [_P1_Buttons_CaveAddress + 4] b = BitConverter.GetBytes(_P1_Buttons_CaveAddress + 4); CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(b); //mov ecx, [_P1_Buttons_CaveAddress + 8] b = BitConverter.GetBytes(_P1_Buttons_CaveAddress + 8); CaveMemory.Write_StrBytes("8B 0D"); CaveMemory.Write_Bytes(b); //jmp originalcode CaveMemory.Write_StrBytes("E9 11 00 00 00"); //Player2: //mov eax, [_P2_Buttons_CaveAddress] b = BitConverter.GetBytes(_P2_Buttons_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //mov ebx, [_P2_Buttons_CaveAddress] b = BitConverter.GetBytes(_P2_Buttons_CaveAddress + 4); CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(b); //mov ecx, [_P2_Buttons_CaveAddress + 8] b = BitConverter.GetBytes(_P2_Buttons_CaveAddress + 8); CaveMemory.Write_StrBytes("8B 0D"); CaveMemory.Write_Bytes(b); //originalcode: //mov [edx+0x23], al CaveMemory.Write_StrBytes("88 42 23"); //mov [edx+0x24], bl CaveMemory.Write_StrBytes("88 5A 24"); //mov [edx+0x29], cl CaveMemory.Write_StrBytes("88 4A 29"); //exit: //pop ecx CaveMemory.Write_StrBytes("59"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //mov cx, [edx+0x21] CaveMemory.Write_StrBytes("66 8B 4A 21"); //return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Noping Axis procedures SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y_2); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes(PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes(PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_P1_Buttons_CaveAddress + 8, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_P1_Buttons_CaveAddress + 8, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_P1_Buttons_CaveAddress + 4, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_P1_Buttons_CaveAddress + 4, 0x00); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_OFfset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 8, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 8, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 4, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 4, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFoot)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFoot)); //_Outputs.Add(new GameOutput(OutputId.P1_LmpHead)); //_Outputs.Add(new GameOutput(OutputId.P2_LmpHead)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 7 & 1); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 4 & 1); SetOutputValue(OutputId.P1_LmpFoot, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 6 & 1); SetOutputValue(OutputId.P2_LmpFoot, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsOutputBuffer_Offset + 16) >> 3 & 1); //Customs Outputs //Player Status : //[0] : Inactive //[2] : In-Game //[8] : Continue Screen //[16] : Game Over //[128] : Attract Demo int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0036D468); int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0036D514); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (P1_Status == 2) { _P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0036D474); _P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00357942); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 2) { _P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0036D520); _P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00357A1A); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Demul.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.Win32; namespace DemulShooter { class Game_Demul : Game { private const String GAMEDATA_FOLDER = @"MemoryData\demul"; private const String SYSTEM_NAOMIJVS = "naomiJvs"; private const String SYSTEM_NAOMI = "naomi"; /*** MEMORY ADDRESSES **/ protected UInt32 _Paddemul_Injection_Offset = 0x0002757A; protected UInt32 _Paddemul_Injection_Return_Offset = 0x00027582; protected UInt32 _Paddemul_PtrButtons_Offset = 0x00037E30; protected UInt32 _Paddemul_P1_Buttons_Offset = 0x00037E32; protected UInt32 _Paddemul_P1_X_Offset = 0x00037E34; protected UInt32 _Paddemul_P1_Y_Offset = 0x00037E36; protected UInt32 _Paddemul_P2_Buttons_Offset = 0x00037EB2; protected UInt32 _Paddemul_P2_X_Offset = 0x00037EB4; protected UInt32 _Paddemul_P2_Y_Offset = 0x00037EB6; private List _ListWidescreenHacks; /*** Process variables **/ protected IntPtr _PadDemul_ModuleBaseAddress = IntPtr.Zero; protected String _SystemName; protected String _DemulVersion; //Custom Outputs protected UInt32 _GameRAM_Address = 0x2C000000; //Demul loads the game RAM at 0x2C000000 address /// /// Constructor /// public Game_Demul(String RomName, String SystemName, String DemulVersion) : base(RomName, "demul") { _SystemName = SystemName; _DemulVersion = DemulVersion; _ListWidescreenHacks = new List(); _KnownMd5Prints.Add("Demul 0.7a_180428", "ce0a6fd5552903311a8935b6f60e26ad"); _KnownMd5Prints.Add("Demul 0.582", "ab4e7654e7b3a4743e9753221cc48fcd"); _KnownMd5Prints.Add("Demul 0.57", "3071ba77ff46d2137f46bfcd8dda5b4f"); _tProcess.Start(); Logger.WriteLog("Waiting for " + _SystemName + "game to hook....."); if (_WidescreenHack) { ReadWidescreenData(); } } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("paddemul.dll")) { _PadDemul_ModuleBaseAddress = m.BaseAddress; if (_PadDemul_ModuleBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog("Demul.exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8") + ", padDemul.dll = 0x" + _PadDemul_ModuleBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameData(); if (!_DisableInputHack) { if (_DemulVersion.Equals("057") || _DemulVersion.Equals("058")) SetHack_057(); else if (_DemulVersion.Equals("07a")) SetHack_07(); } else Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } else { if (_WidescreenHack && _ListWidescreenHacks.Count > 0) { foreach (WidescreenData d in _ListWidescreenHacks) { int RatioValue = BitConverter.ToInt32(ReadBytes(d.Address, 4), 0); if (RatioValue == d.DefaultValue) { WriteBytes(d.Address, BitConverter.GetBytes(d.WidescreenValue)); Logger.WriteLog("Widescreen Hack : wrote 0x" + d.WidescreenValue.ToString("X8") + " to address 0x" + d.Address.ToString("X8")); } } } } } } #region Game Configuration /// /// Read memory values in .cfg file /// protected override void ReadGameData() { String ConfigFile = AppDomain.CurrentDomain.BaseDirectory + GAMEDATA_FOLDER + @"\" + _DemulVersion + @"\" + _SystemName + ".cfg"; if (File.Exists(ConfigFile)) { Logger.WriteLog("Reading game memory setting from " + ConfigFile); using (StreamReader sr = new StreamReader(ConfigFile)) { String line; String FieldName = String.Empty; line = sr.ReadLine(); while (line != null) { String[] buffer = line.Split('='); if (buffer.Length > 1) { try { FieldName = "_" + buffer[0].Trim(); if (buffer[0].Contains("Nop")) { NopStruct n = new NopStruct(buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, n); } else { UInt32 v = UInt32.Parse(buffer[1].Substring(3).Trim(), NumberStyles.HexNumber); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, v); } } catch (Exception ex) { Logger.WriteLog("Error reading game data : " + ex.Message.ToString()); } } line = sr.ReadLine(); } sr.Close(); } } else { Logger.WriteLog("File not found : " + AppDomain.CurrentDomain.BaseDirectory + @"\" + GAMEDATA_FOLDER + @"\" + _DemulVersion + @"\" + _SystemName + ".cfg"); } } /// /// Read Widescreen memory values in .cfg file /// private void ReadWidescreenData() { if (File.Exists(AppDomain.CurrentDomain.BaseDirectory + @"\" + GAMEDATA_FOLDER + @"\Widescreen.cfg")) { _ListWidescreenHacks.Clear(); using (StreamReader sr = new StreamReader(AppDomain.CurrentDomain.BaseDirectory + @"\" + GAMEDATA_FOLDER + @"\Widescreen.cfg")) { String line; line = sr.ReadLine(); while (line != null) { string[] buffer = line.Split('='); if (buffer.Length > 1) { try { if (buffer[0].ToLower().Trim() == _RomName) { String[] tampon = buffer[1].Split('|'); WidescreenData d = new WidescreenData(); d.Address = UInt32.Parse(tampon[0].Trim().Substring(2), NumberStyles.HexNumber); d.DefaultValue = Int32.Parse(tampon[1].Trim().Substring(2), NumberStyles.HexNumber); d.WidescreenValue = Int32.Parse(tampon[2].Trim().Substring(2), NumberStyles.HexNumber); _ListWidescreenHacks.Add(d); Logger.WriteLog("Widescreen hack memory address = 0x" + d.Address.ToString("X8") + " , value = 0x" + d.WidescreenValue.ToString("X8")); } } catch (Exception ex) { Logger.WriteLog("Error reading Widescreen data : " + ex.Message.ToString()); } } line = sr.ReadLine(); } sr.Close(); } } else { Logger.WriteLog("File not found : " + AppDomain.CurrentDomain.BaseDirectory + @"\" + GAMEDATA_FOLDER + @"\" + _DemulVersion + @"\" + _SystemName + ".cfg"); } } #endregion #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); /* //pX and pY in width/heigt % of window double Xpercent = Mouse.pTarget.X * 100.0 / TotalResX; double Ypercent = Mouse.pTarget.Y * 100.0 / TotalResY; //0% = 0x00 and 100% = 0xFF for padDemul.dll Mouse.pTarget.X = Convert.ToInt16(Math.Round(Xpercent * 255.0 / 100.0)); Mouse.pTarget.Y = Convert.ToInt16(Math.Round(Ypercent * 255.0 / 100.0)); */ //Naomi + awave => 0000 - 00FF double dMaxX = 255.0; double dMaxY = 255.0; //JVS => 0000 - 0FFF and inverted Y if (_SystemName.Equals(SYSTEM_NAOMIJVS)) { dMaxX = 4095.0; dMaxY = 4095.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); } else { PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); } if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Code injection to block axis/input default reading from emulator /// protected virtual void SetHack_057() { } protected virtual void SetHack_07() { } #endregion #region Inputs /// /// Mouse callback for low level hook /// This is used to block LeftClick events on the window, because double clicking on the upper-left corner /// makes demul switch from Fullscreen to Windowed mode /// public override IntPtr MouseHookCallback(IntPtr MouseHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && (UInt32)wParam == Win32Define.WM_LBUTTONDOWN) { //Just blocking left clicks if (_DisableWindow) return new IntPtr(1); } return Win32API.CallNextHookEx(MouseHookID, nCode, wParam, lParam); } #endregion } class WidescreenData { public UInt32 Address; public Int32 DefaultValue; public Int32 WidescreenValue; public WidescreenData() { Address = 0; DefaultValue = 0; WidescreenValue = 0; } } } ================================================ FILE: DemulShooter/Games/Game_DemulAtomiswave.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_DemulAtomiswave : Game { /*** Process variables **/ private IntPtr _PadDemul_ModuleBaseAddress = IntPtr.Zero; private IntPtr _GpuModuleBaseAddress = IntPtr.Zero; private UInt32 _GpuDisplayType_Offset = 0; /*** MEMORY ADDRESSES **/ private UInt32 _Paddemul_Injection_Offset = 0x0002757A; private UInt32 _Paddemul_Injection_Return_Offset = 0x00027582; private UInt32 _Paddemul_PtrButtons_Offset = 0x00037E30; //private UInt32 _Paddemul_P1_Buttons_Offset = 0x00037E32; private UInt32 _Paddemul_Aw_P1_Start_Button_Offset = 0x00037E30; private UInt32 _Paddemul_Aw_P1_Fire_Button_Offset = 0x00037E32; private UInt32 _Paddemul_P1_X_Offset = 0x00037E34; //private UInt32 _Paddemul_P1_Y_Offset = 0x00037E36; //private UInt32 _Paddemul_P2_Buttons_Offset = 0x00037EB2; private UInt32 _Paddemul_Aw_P2_Start_Button_Offset = 0x00037EB0; private UInt32 _Paddemul_Aw_P2_Fire_Button_Offset = 0x00037EB2; private UInt32 _Paddemul_P2_X_Offset = 0x00037EB4; //private UInt32 _Paddemul_P2_Y_Offset = 0x00037EB6; private List _ListWidescreenHacks; public Game_DemulAtomiswave(String RomName, String DemulVersion) : base(RomName, "demul") { _ListWidescreenHacks = new List(); _tProcess.Start(); Logger.WriteLog("Waiting for Demul Atomiswave game " + _RomName + " to hook....."); if (_WidescreenHack) { //ReadWidescreenData(); } } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("paddemul.dll")) { _PadDemul_ModuleBaseAddress = m.BaseAddress; break; } } foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("gpudx11.dll")) { _GpuModuleBaseAddress = m.BaseAddress; Logger.WriteLog("gpuDX11.dll base address = 0x" + _GpuModuleBaseAddress.ToString("X8")); _GpuDisplayType_Offset = 0x0007F9DC; break; } else if (m.ModuleName.ToLower().Equals("gpudx11old.dll")) { _GpuModuleBaseAddress = m.BaseAddress; Logger.WriteLog("gpuDX11old.dll base address = 0x" + _GpuModuleBaseAddress.ToString("X8")); _GpuDisplayType_Offset = 0x0005F920; break; } else if (m.ModuleName.ToLower().StartsWith("gpudx") && m.ModuleName.ToLower().EndsWith(".dll")) { _GpuModuleBaseAddress = m.BaseAddress; Logger.WriteLog("Only found " + m.ModuleName.ToLower() + " loaded. Incompatible module, reverting to old method"); } } if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero && _GpuModuleBaseAddress != IntPtr.Zero && _PadDemul_ModuleBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); if (!_DisableInputHack) Apply_InputsMemoryHack(); else Logger.WriteLog("Input Hack disabled"); RaiseGameHookedEvent(); _ProcessHooked = true; } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// For These games, there are no calibration available so we face a ratio issue problem /// Exemple : 16/9 monitor + 4/3 option in demul in fullscreen : aim is not good because of black borders /// /// To fix it, we try to read the setting (4/3, 16/9 or stretch) and resolution in demul's memory (in gpuDX11.dll) /// this way, we can do some math to know the exact position /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { //if no gpudx11.dll used or not found, default behavior with black border issue if (_GpuModuleBaseAddress == IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 640; double dMaxY = 480; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } else { try { //Display option in demul menu : 0=Stretch / 1 = 4:3 / 2 = 16:9 byte DisplayType = ReadByte((UInt32)_GpuModuleBaseAddress + _GpuDisplayType_Offset); ; Logger.WriteLog("Demul display type is : " + DisplayType.ToString()); double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //If stretch the whole window is used so, no change //If 4:3 we keep correct Y but have to change X because of black borders if (DisplayType == 1) { double RealX = TotalResY * 4.0 / 3.0; Logger.WriteLog("Game real resolution (Px) = [ " + RealX.ToString() + "x" + TotalResY.ToString() + " ]"); PlayerData.RIController.Computed_X -= ((int)TotalResX - (int)RealX) / 2; TotalResX = RealX; } //If 6:9 we keep the correct X but we have to change Y because of black borders if (DisplayType == 2) { double RealY = TotalResX * 9.0 / 16.0; Logger.WriteLog("Game real resolution (Px) = [ " + TotalResX.ToString() + "x" + RealY.ToString() + " ]"); PlayerData.RIController.Computed_Y -= ((int)TotalResY - (int)RealY) / 2; TotalResY = RealY; } double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp ecx, 2 CaveMemory.Write_StrBytes("83 F9 02"); //je @ CaveMemory.Write_StrBytes("0F 84 47 00 00 00"); //cmp ecx, 3 CaveMemory.Write_StrBytes("83 F9 03"); //je @ CaveMemory.Write_StrBytes("0F 84 3E 00 00 00"); //cmp ecx, 42 CaveMemory.Write_StrBytes("83 F9 42"); //je @ CaveMemory.Write_StrBytes("0F 84 35 00 00 00"); //cmp ecx, 43 CaveMemory.Write_StrBytes("83 F9 43"); //je @ CaveMemory.Write_StrBytes("0F 84 2C 00 00 00"); //cmp ecx, 0 CaveMemory.Write_StrBytes("83 F9 00"); //je @ CaveMemory.Write_StrBytes("0F 84 28 00 00 00"); //cmp ecx, 1 CaveMemory.Write_StrBytes("83 F9 01"); //je @ CaveMemory.Write_StrBytes("0F 84 1A 00 00 00"); //cmp ecx, 40 CaveMemory.Write_StrBytes("83 F9 40"); //je @ CaveMemory.Write_StrBytes("0F 84 16 00 00 00"); //cmp ecx, 41 CaveMemory.Write_StrBytes("83 F9 41"); //je @ CaveMemory.Write_StrBytes("0F 84 08 00 00 00"); //mov [ecx*2+padDemul.dll+OFFSET],ax CaveMemory.Write_StrBytes("66 89 04 4D"); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); //jmp @ CaveMemory.Write_jmp((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Return_Offset); //and eax, 08 CaveMemory.Write_StrBytes("83 E0 08"); //cmp eax, 0 CaveMemory.Write_StrBytes("83 F8 00"); //je @ CaveMemory.Write_StrBytes("0F 84 0A 00 00 00"); //or dword ptr [ecx*2+padDemul.dll+OFFSET],08 CaveMemory.Write_StrBytes("83 0C 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("08"); //jmp @ CaveMemory.Write_StrBytes("EB E5"); //and dword ptr [ecx*2+padDemul.dll+OFFSET],-09 { 247 } CaveMemory.Write_StrBytes("83 24 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("F7"); //jmp @ CaveMemory.Write_StrBytes("EB DB"); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Center Guns position at start IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Initialise pour prise en compte des guns direct byte[] init = { 0, 0x7f, 0, 0x7f }; WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Start_Button_Offset, 0xFF); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Fire_Button_Offset, 0xFF); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Start_Button_Offset, 0xFF); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Fire_Button_Offset, 0xFF); Logger.WriteLog("Atomiswave Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { //creating X-Y Hex value buffer to write memory byte[] buffer = { (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_Y >> 8), (byte)(PlayerData.RIController.Computed_Y & 0xFF) }; if (PlayerData.ID == 1) { WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Fire_Button_Offset, 0xFB); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Fire_Button_Offset, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { /* For Sprtshot, we force offscreen shoot for reload */ if (_RomName.Equals("sprtshot")) { buffer[0] = (byte)(0); buffer[1] = (byte)(0); buffer[2] = (byte)(0); buffer[3] = (byte)(0); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, buffer); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Fire_Button_Offset, 0xFB); System.Threading.Thread.Sleep(50); } else { //Read Value and set Bit 2 to 0 byte val = ReadByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Start_Button_Offset); val = (byte)(val & 0xFB); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Start_Button_Offset, val); } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { if (_RomName.Equals("sprtshot")) { WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Fire_Button_Offset, 0xFF); } else { //Read Value and set Bit 2 to 1 byte val = ReadByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Start_Button_Offset); val = (byte)(val | 0x04); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P1_Start_Button_Offset, val); } } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Fire_Button_Offset, 0xFB); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Fire_Button_Offset, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { /* For Sprtshot, we force offscreen shoot for reload */ if (_RomName.Equals("sprtshot")) { buffer[0] = (byte)(0); buffer[1] = (byte)(0); buffer[2] = (byte)(0); buffer[3] = (byte)(0); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, buffer); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Fire_Button_Offset, 0xFB); System.Threading.Thread.Sleep(50); } else { //Read Value and set Bit 2 to 0 byte val = ReadByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Start_Button_Offset); val = (byte)(val & 0xFB); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Start_Button_Offset, val); } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { if (_RomName.Equals("sprtshot")) { WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Fire_Button_Offset, 0xFF); } else { //Read Value and set Bit 2 to 1 byte val = ReadByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Start_Button_Offset); val = (byte)(val | 0x04); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Aw_P2_Start_Button_Offset, val); } } } } /// /// Mouse callback for low level hook /// This is used to block LeftClick events on the window, because double clicking on the upper-left corner /// makes demul switch from Fullscreen to Windowed mode /// public override IntPtr MouseHookCallback(IntPtr MouseHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && (UInt32)wParam == Win32Define.WM_LBUTTONDOWN) { //Just blocking left clicks if (_DisableWindow) return new IntPtr(1); } return Win32API.CallNextHookEx(MouseHookID, nCode, wParam, lParam); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_DemulHikaru.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_DemulHikaru : Game_Demul { public Game_DemulHikaru(String Rom, String DemulVersion) : base(Rom, "hikaru", DemulVersion) {} #region Memory Hack protected override void SetHack_07() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp ecx, 2 CaveMemory.Write_StrBytes("83 F9 02"); //je @ CaveMemory.Write_StrBytes("0F 84 47 00 00 00"); //cmp ecx, 3 CaveMemory.Write_StrBytes("83 F9 03"); //je @ CaveMemory.Write_StrBytes("0F 84 3E 00 00 00"); //cmp ecx, 4 CaveMemory.Write_StrBytes("83 F9 04"); //je @ CaveMemory.Write_StrBytes("0F 84 35 00 00 00"); //cmp ecx, 42 CaveMemory.Write_StrBytes("83 F9 42"); //je @ CaveMemory.Write_StrBytes("0F 84 2C 00 00 00"); //cmp ecx, 43 CaveMemory.Write_StrBytes("83 F9 43"); //je @ CaveMemory.Write_StrBytes("0F 84 23 00 00 00"); //cmp ecx, 44 CaveMemory.Write_StrBytes("83 F9 44"); //je @ CaveMemory.Write_StrBytes("0F 84 1A 00 00 00"); //cmp ecx, 1 CaveMemory.Write_StrBytes("83 F9 01"); //je @ CaveMemory.Write_StrBytes("0F 84 16 00 00 00"); //cmp ecx, 41 CaveMemory.Write_StrBytes("83 F9 41"); //je @ CaveMemory.Write_StrBytes("0F 84 0D 00 00 00"); //mov [ecx*2+padDemul.dll+OFFSET],ax CaveMemory.Write_StrBytes("66 89 04 4D"); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); //jmp @ CaveMemory.Write_jmp((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Return_Offset); //cmp ax,80 CaveMemory.Write_StrBytes("3D 80 00 00 00"); //jnl @ CaveMemory.Write_StrBytes("0F 8D 0D 00 00 00"); //and dword ptr [ecx*2+padDemul.dll+OFFSET],7F CaveMemory.Write_StrBytes("81 24 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("7F FF FF FF"); //jmp @ CaveMemory.Write_StrBytes("EB E3"); //or [ecx*2+padDemul.dll+OFFSET],00000080 CaveMemory.Write_StrBytes("81 0c 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("80 00 00 00"); //jmp @ CaveMemory.Write_StrBytes("EB D6"); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Center Guns position at start byte[] init = { 0, 0x7f, 0, 0x7f }; WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); Logger.WriteLog("Hikaru Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { //creating X-Y Hex value buffer to write memory byte[] buffer = { (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_Y >> 8), (byte)(PlayerData.RIController.Computed_Y & 0xFF) }; if (PlayerData.ID == 1) { WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset + 2, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset + 2, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset + 2, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset + 2, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.P2_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (_RomName.Equals("braveff")) Compute_Braveff_Outputs(); } private void Compute_Braveff_Outputs() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(0x007000C4) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(0x007000C4) >> 4 & 0x01); SetOutputValue(OutputId.P1_LmpPanel, ReadByte(0x007000C4) >> 5 & 0x01); SetOutputValue(OutputId.P2_LmpPanel, ReadByte(0x007000C4) >> 2 & 0x01); SetOutputValue(OutputId.P1_GunMotor, ReadByte(0x007000C4) >> 6 & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte(0x007000C4) >> 3 & 0x01); SetOutputValue(OutputId.Credits, ReadByte(0x20C00068)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_DemulJvs.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_DemulJvs : Game_Demul { private UInt32 _Outputs_Outputs_Offset = 0x0022C42D; private UInt32 _Outputs_Credits_Offset = 0x00480D0C; private UInt32 _Outputs_PlayerData_Offset = 0x003D9D30; private int _P1_LastDammage = 0; private int _P2_LastDammage = 0; public Game_DemulJvs(String Rom, String DemulVersion) : base(Rom, "naomiJvs", DemulVersion) { if (Rom.Equals("ninjaslt")) { _Outputs_Outputs_Offset = 0x0022C42D; _Outputs_Credits_Offset = 0x00480D0C; _Outputs_PlayerData_Offset = 0x003D9D30; //_Outputs_PlayerData_Offset = 0x003D9CB0; ? } else if (Rom.Equals("ninjaslta")) { _Outputs_Outputs_Offset = 0x0022C4AD; _Outputs_Credits_Offset = 0x00480D8C; _Outputs_PlayerData_Offset = 0x003D9D30; } else if (Rom.Equals("ninjasltj")) { _Outputs_Outputs_Offset = 0x0022C3F1; _Outputs_Credits_Offset = 0x00480CD4; _Outputs_PlayerData_Offset = 0x003D9C78; } else if (Rom.Equals("ninjasltu")) { _Outputs_Outputs_Offset = 0x0022C4AD; _Outputs_Credits_Offset = 0x00480D8C; _Outputs_PlayerData_Offset = 0x003D9D30; } } #region Memory Hack protected override void SetHack_057() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp ecx, 2 CaveMemory.Write_StrBytes("83 F9 02"); //je @ CaveMemory.Write_StrBytes("0F 84 4C 00 00 00"); //cmp ecx, 41 CaveMemory.Write_StrBytes("83 F9 41"); //je @ CaveMemory.Write_StrBytes("0F 84 43 00 00 00"); //cmp ecx, 03 CaveMemory.Write_StrBytes("83 F9 03"); //je @ CaveMemory.Write_StrBytes("0F 84 35 00 00 00"); //cmp ecx, 42 CaveMemory.Write_StrBytes("83 F9 42"); //je @ CaveMemory.Write_StrBytes("0F 84 2C 00 00 00"); //cmp edi, 04 CaveMemory.Write_StrBytes("83 FF 04"); //je @ CaveMemory.Write_StrBytes("0F 84 23 00 00 00"); //cmp edi, 05 CaveMemory.Write_StrBytes("83 FF 05"); //je @ CaveMemory.Write_StrBytes("0F 84 1A 00 00 00"); //cmp edi, 43 CaveMemory.Write_StrBytes("83 FF 43"); //je @ CaveMemory.Write_StrBytes("0F 84 11 00 00 00"); //cmp edi, 44 CaveMemory.Write_StrBytes("83 FF 44"); //je @ CaveMemory.Write_StrBytes("0F 84 08 00 00 00"); //mov [edi*2+padDemul.dll+OFFSET],ax CaveMemory.Write_StrBytes("66 89 0C 7D"); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); //jmp @ CaveMemory.Write_jmp((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Return_Offset); //push ecx CaveMemory.Write_StrBytes("51"); //and ecx,80 CaveMemory.Write_StrBytes("81 E1 80 00 00 00"); //cmp ecx,80 CaveMemory.Write_StrBytes("81 F9 80 00 00 00"); //je @ CaveMemory.Write_StrBytes("0F 84 24 00 00 00"); //and dword ptr [edi*2+padDemul.dll+OFFSET],7F CaveMemory.Write_StrBytes("81 24 7D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("7F FF FF FF"); //pop ecx CaveMemory.Write_StrBytes("59"); //push ecx CaveMemory.Write_StrBytes("51"); //and ecx,40 CaveMemory.Write_StrBytes("83 E1 40"); //cmp ecx,40 CaveMemory.Write_StrBytes("83 F9 40"); //je @ CaveMemory.Write_StrBytes("0F 84 19 00 00 00"); //and dword ptr [edi*2+padDemul.dll+OFFSET],BF CaveMemory.Write_StrBytes("83 24 7D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("BF"); //pop ecx CaveMemory.Write_StrBytes("59"); //jmp @ CaveMemory.Write_StrBytes("EB C4"); //or [edi*2+padDemul.dll+OFFSET],00000080 CaveMemory.Write_StrBytes("81 0C 7D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("80 00 00 00"); //pop ecx CaveMemory.Write_StrBytes("59"); //jmp @ CaveMemory.Write_StrBytes("EB DA"); //or [edi*2+padDemul.dll+OFFSET],40 CaveMemory.Write_StrBytes("83 0C 7D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("40"); //pop ecx CaveMemory.Write_StrBytes("59"); //jmp @ CaveMemory.Write_StrBytes("EB AB"); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset,Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Guns Init byte[] init = { 0xff, 0x07, 0xff, 0x07 }; WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); Logger.WriteLog("NaomiJVS Memory Hack complete !"); Logger.WriteLog("-"); } protected override void SetHack_07() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp ecx, 2 CaveMemory.Write_StrBytes("83 F9 02"); //je @ CaveMemory.Write_StrBytes("0F 84 4C 00 00 00"); //cmp ecx, 41 CaveMemory.Write_StrBytes("83 F9 41"); //je @ CaveMemory.Write_StrBytes("0F 84 43 00 00 00"); //cmp ecx, 03 CaveMemory.Write_StrBytes("83 F9 03"); //je @ CaveMemory.Write_StrBytes("0F 84 35 00 00 00"); //cmp ecx, 42 CaveMemory.Write_StrBytes("83 F9 42"); //je @ CaveMemory.Write_StrBytes("0F 84 2C 00 00 00"); //cmp ecx, 04 CaveMemory.Write_StrBytes("83 F9 04"); //je @ CaveMemory.Write_StrBytes("0F 84 23 00 00 00"); //cmp ecx, 05 CaveMemory.Write_StrBytes("83 F9 05"); //je @ CaveMemory.Write_StrBytes("0F 84 1A 00 00 00"); //cmp ecx, 43 CaveMemory.Write_StrBytes("83 F9 43"); //je @ CaveMemory.Write_StrBytes("0F 84 11 00 00 00"); //cmp ecx, 44 CaveMemory.Write_StrBytes("83 F9 44"); //je @ CaveMemory.Write_StrBytes("0F 84 08 00 00 00"); //mov [ecx*2+padDemul.dll+OFFSET],ax CaveMemory.Write_StrBytes("66 89 04 4D"); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); //jmp @ CaveMemory.Write_jmp((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Return_Offset); //push eax CaveMemory.Write_StrBytes("50"); //and eax,80 CaveMemory.Write_StrBytes("25 80 00 00 00"); //cmp eax,80 CaveMemory.Write_StrBytes("3D 80 00 00 00"); //je @ CaveMemory.Write_StrBytes("0F 84 24 00 00 00"); //and dword ptr [ecx*2+padDemul.dll+OFFSET],7F CaveMemory.Write_StrBytes("81 24 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("7F FF FF FF"); //pop eax CaveMemory.Write_StrBytes("58"); //push eax CaveMemory.Write_StrBytes("50"); //and eax,40 CaveMemory.Write_StrBytes("83 E0 40"); //cmp eax,40 CaveMemory.Write_StrBytes("83 F8 40"); //je @ CaveMemory.Write_StrBytes("0F 84 19 00 00 00"); //and dword ptr [ecx*2+padDemul.dll+OFFSET],BF CaveMemory.Write_StrBytes("83 24 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("BF"); //pop eax CaveMemory.Write_StrBytes("58"); //jmp @ CaveMemory.Write_StrBytes("EB C6"); //or [ecx*2+padDemul.dll+OFFSET],00000080 CaveMemory.Write_StrBytes("81 0C 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("80 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //jmp @ CaveMemory.Write_StrBytes("EB DA"); //or [ecx*2+padDemul.dll+OFFSET],40 CaveMemory.Write_StrBytes("83 0C 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("40"); //pop eax CaveMemory.Write_StrBytes("58"); //jmp @ CaveMemory.Write_StrBytes("EB AD"); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Guns Init byte[] init = { 0xFF, 0x07, 0xFF, 0x07 }; WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); Logger.WriteLog("NaomiJVS Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs public override void SendInput(PlayerSettings PlayerData) { //creating X-Y Hex value buffer to write memory byte[] buffer = { (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_Y & 0xFF), (byte)(PlayerData.RIController.Computed_Y >> 8) }; if (PlayerData.ID == 1) { WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, buffer); //Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { //Set On Screen flag WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset + 2, 0x1); //Trigger pressed Apply_OR_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x30); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0xCF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //Set Out Of Screen flag WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset + 2, 0); //Trigger pressed Apply_OR_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x30); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { //Set On Screen flag WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset + 2, 0x1); //Trigger released Apply_AND_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0xCF); } } else if (PlayerData.ID == 2) { //Write Axis WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, buffer); //Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { //Set On Screen flag WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset + 2, 0x1); //Trigger pressed Apply_OR_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x30); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0xCF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //Set Out Of Screen flag WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset + 2, 0); //Trigger pressed Apply_OR_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x30); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { //Set On Screen flag WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset + 2, 0x1); //Trigger released Apply_AND_ByteMask((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0xCF); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { UInt32 Outputs_Address = _GameRAM_Address + _Outputs_Outputs_Offset; _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Check if the game is in Gameplay mode if (ReadByte(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x1A) == 1) { //Didn't find any reliable "player state", but Life seem to stay at 0 when not playing, so we will use that //Note that at start, life may be > 0 if the player has never entered a game :( _P1_Life = (int)BitConverter.ToInt16(ReadBytes(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x54, 2), 0); _P2_Life = (int)BitConverter.ToInt16(ReadBytes(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x56, 2), 0); //For custom dammaged : //1) Solution 1 : Decrease life = small delay between the hit and the life beeing lost /*//[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1);*/ //2) solution 2 : Read a byte value wich is != 0 when hit (invicibility duration ?) but the "1" state duration is long and may trigger the output multiple times int P1_Dammage = ReadByte(_GameRAM_Address + _Outputs_PlayerData_Offset + 0xBC); int P2_Dammage = ReadByte(_GameRAM_Address + _Outputs_PlayerData_Offset + 0xBE); if (P1_Dammage != 0 & _P1_LastDammage == 0) SetOutputValue(OutputId.P1_Damaged, 1); if (P2_Dammage != 0 & _P2_LastDammage == 0) SetOutputValue(OutputId.P2_Damaged, 1); _P1_LastDammage = P1_Dammage; _P2_LastDammage = P2_Dammage; if (_P1_Life > 0) { _P1_Ammo = (int)BitConverter.ToInt16(ReadBytes(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x62, 2), 0); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; } if (_P2_Life > 0) { _P2_Ammo = (int)BitConverter.ToInt16(ReadBytes(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x64, 2), 0); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; } } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(Outputs_Address) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(Outputs_Address) >> 4 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, ReadByte(Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, ReadByte(Outputs_Address) >> 5 & 0x01); //Custom recoil will be activated just like original one //REMOVED !! The recoil is activated when shooting offscreen !! /*SetOutputValue(OutputId.P1_CtmRecoil, ReadByte((IntPtr)Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, ReadByte((IntPtr)Outputs_Address) >> 5 & 0x01);*/ //Credits SetOutputValue(OutputId.Credits, ReadByte(_GameRAM_Address + _Outputs_Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_DemulManicpnc.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_DemulManicpnc : Game { private IntPtr _GpuModuleBaseAddress = IntPtr.Zero; private UInt32 _GpuDisplayType_Offset = 0; /*** MEMORY ADDRESSES **/ private UInt32 _Injection_Offset = 0x001A1355; private UInt32 _Injection_ReturnOffset = 0x001A1361; private UInt32 _P1_Data_CaveAddress; private UInt32 _P2_Data_CaveAddress; private int[] _Btn_Status = new int[] { 0, 0}; private int[] _TriggerPushed = new int[] { 0, 0 }; private Timer[] _TimerHoldTrigger = new Timer[2]; public Game_DemulManicpnc(String RomName) : base(RomName, "demul") { _tProcess.Start(); _TimerHoldTrigger[0] = new Timer(); _TimerHoldTrigger[0].Interval = 20; _TimerHoldTrigger[0].Tick += new EventHandler(tHoldTriggerP1_Elapsed); _TimerHoldTrigger[0].Enabled = true; _TimerHoldTrigger[1] = new Timer(); _TimerHoldTrigger[1].Interval = 20; _TimerHoldTrigger[1].Tick += new EventHandler(tHoldTriggerP2_Elapsed); _TimerHoldTrigger[1].Enabled = true; Logger.WriteLog("Waiting for Demul Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("gpudx11.dll")) { _GpuModuleBaseAddress = m.BaseAddress; Logger.WriteLog("gpuDX11.dll base address = 0x" + _GpuModuleBaseAddress.ToString("X8")); _GpuDisplayType_Offset = 0x0007F9DC; break; } else if (m.ModuleName.ToLower().Equals("gpudx11old.dll")) { _GpuModuleBaseAddress = m.BaseAddress; Logger.WriteLog("gpuDX11old.dll base address = 0x" + _GpuModuleBaseAddress.ToString("X8")); _GpuDisplayType_Offset = 0x0005F920; break; } else if (m.ModuleName.ToLower().StartsWith("gpudx") && m.ModuleName.ToLower().EndsWith(".dll")) { _GpuModuleBaseAddress = m.BaseAddress; Logger.WriteLog("Only found " + m.ModuleName.ToLower() + " loaded. Incompatible module, reverting to old method"); } } if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero && _GpuModuleBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); if (!_DisableInputHack) Apply_InputsMemoryHack(); else Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// For These 2 games, there are no calibration available so we face a ratio issue problem /// Exemple : 16/9 monitor + 4/3 option in demul in fullscreen : aim is not good because of black borders /// /// To fix it, we try to read the setting (4/3, 16/9 or stretch) and resolution in demul's memory (in gpuDX11.dll) /// this way, we can do some math to know the exact position /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { //if no gpudx11.dll used or not found, default behavior with black border issue if (_GpuModuleBaseAddress == IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 640; double dMaxY = 480; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } else { try { //Display option in demul menu : 0=Stretch / 1=4:3 / 2 = 16:9 byte DisplayType = ReadByte((UInt32)_GpuModuleBaseAddress + _GpuDisplayType_Offset);; Logger.WriteLog("Demul display type is : " + DisplayType.ToString()); double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //If stretch the whole window is used so, no change //If 4:3 we keep correct Y but have to change X because of black borders if (DisplayType == 1) { double RealX = TotalResY * 4.0 / 3.0; Logger.WriteLog("Game real resolution (Px) = [ " + RealX.ToString() + "x" + TotalResY.ToString() + " ]"); PlayerData.RIController.Computed_X -= ((int)TotalResX - (int)RealX) / 2; TotalResX = RealX; } //If 6:9 we keep the correct X but we have to change Y because of black borders if (DisplayType == 2) { double RealY = TotalResX * 9.0 / 16.0; Logger.WriteLog("Game real resolution (Px) = [ " + TotalResX.ToString() + "x" + RealY.ToString() + " ]"); PlayerData.RIController.Computed_Y -= ((int)TotalResY - (int)RealY) / 2; TotalResY = RealY; } double dMaxX = 640; double dMaxY = 480; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Data_CaveAddress = _InputsDatabank_Address; _P2_Data_CaveAddress = _InputsDatabank_Address + 0x03; } /// /// Replace data by ours when writing to IO board /// private void SetHack_ButtonsAndAxis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx, []P1_data byte[] b = BitConverter.GetBytes(_P1_Data_CaveAddress); CaveMemory.Write_StrBytes("8B 0D"); CaveMemory.Write_Bytes(b); //call demul.exe+1A1400 CaveMemory.Write_call((UInt32)_TargetProcess.MainModule.BaseAddress + 0x001A1400); //mov ecx, [P2_data] b = BitConverter.GetBytes(_P2_Data_CaveAddress); CaveMemory.Write_StrBytes("8B 0D"); CaveMemory.Write_Bytes(b); //call demul.exe+1A1400 CaveMemory.Write_call((UInt32)_TargetProcess.MainModule.BaseAddress + 0x001A1400); //return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Injection_ReturnOffset); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); Logger.WriteLog("Pokasuka Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// For this one, P2 is totally disabled in Demul emulation /// Coordinates by player are 2x2bytes, encoded on 3 bytes. So we have to recreate the encoding to inject it on the IO board emulation /// The total output is : 6 bytes containing Buttons+X+Y for each players /// public override void SendInput(PlayerSettings PlayerData) { int Data = 0; int CurrentBtnStatus; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) _Btn_Status[PlayerData.ID - 1] |= 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { _Btn_Status[PlayerData.ID - 1] &= 0xE; _TriggerPushed[PlayerData.ID - 1] = 0; _TimerHoldTrigger[PlayerData.ID - 1].Stop(); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) _Btn_Status[PlayerData.ID - 1] |= 2; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) _Btn_Status[PlayerData.ID - 1] &= 0xD; Data = _Btn_Status[PlayerData.ID - 1]; Data &= 0x01; Data = ~Data + 1; Data &= 0x00000C00; CurrentBtnStatus = _Btn_Status[PlayerData.ID - 1]; if ((CurrentBtnStatus & 0x01) != 0) { if (_TriggerPushed[PlayerData.ID - 1] == 0) { _TimerHoldTrigger[PlayerData.ID - 1].Start(); } else { CurrentBtnStatus &= 0xFE; } } CurrentBtnStatus = CurrentBtnStatus << 0x0C; Data |= CurrentBtnStatus; Data |= PlayerData.RIController.Computed_Y; Data = Data << 0xA; Data |= PlayerData.RIController.Computed_X; byte[] buffer = BitConverter.GetBytes(Data); if (PlayerData.ID == 1) { WriteByte(_P1_Data_CaveAddress, buffer[0]); WriteByte(_P1_Data_CaveAddress + 1, buffer[1]); WriteByte(_P1_Data_CaveAddress + 2, buffer[2]); } else if (PlayerData.ID == 2) { WriteByte(_P2_Data_CaveAddress, buffer[0]); WriteByte(_P2_Data_CaveAddress + 1, buffer[1]); WriteByte(_P2_Data_CaveAddress + 2, buffer[2]); } } /// /// Mouse callback for low level hook /// This is used to block LeftClick events on the window, because double clicking on the upper-left corner /// makes demul switch from Fullscreen to Windowed mode /// public override IntPtr MouseHookCallback(IntPtr MouseHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && (UInt32)wParam == Win32Define.WM_LBUTTONDOWN) { //Just blocking left clicks if (_DisableWindow) return new IntPtr(1); } return Win32API.CallNextHookEx(MouseHookID, nCode, wParam, lParam); } /// /// To handle "drag and drop" on the game, we need to handle differently a first pull on trigger /// And a continous pull /// For this I'm using a timer to switch from the 1st state to the 2nd one because without it, the game is /// not registering the first trigger press and nothing works /// /// /// private void tHoldTriggerP1_Elapsed(Object Sender, EventArgs e) { _TriggerPushed[0] = 1; _TimerHoldTrigger[0].Stop(); } private void tHoldTriggerP2_Elapsed(Object Sender, EventArgs e) { _TriggerPushed[1] = 1; _TimerHoldTrigger[1].Stop(); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //From demul.exe+16E9F2 instruction bloc UInt32 OutputsAddress = ((UInt32)_TargetProcess_MemoryBaseAddress + 0x4F57F00) + ((UInt32)_TargetProcess_MemoryBaseAddress + 0x3219D28); OutputsAddress = ReadPtr(ReadPtr(OutputsAddress + 0x04)) + 0x01; Logger.WriteLog("Outputs Address = 0x" + OutputsAddress.ToString("X8")); //[FF FF] //P1 Red 0-F = Output Address //P1 Blue 0-F = Output Address //P2 Red 0-F = Output Address +1 //P2 Blue 0-F = Output Address +1 //Genuine Outputs SetOutputValue(OutputId.P1_Lmp_R, ReadByte(OutputsAddress) >> 4 & 0x0F); SetOutputValue(OutputId.P1_Lmp_B, ReadByte(OutputsAddress) & 0x0F); SetOutputValue(OutputId.P2_Lmp_R, ReadByte(OutputsAddress + 1) >> 4 & 0x0F); SetOutputValue(OutputId.P2_Lmp_B, ReadByte(OutputsAddress + 1) & 0x0F); SetOutputValue(OutputId.Credits, ReadByte(0x20200010)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_DemulNaomi.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_DemulNaomi : Game_Demul { public Game_DemulNaomi(String RomName, String DemulVersion) : base(RomName, "naomi", DemulVersion) {} #region Memory Hack protected override void SetHack_057() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp ecx, 2 CaveMemory.Write_StrBytes("83 FF 02"); //je @ CaveMemory.Write_StrBytes("0F 84 35 00 00 00"); //cmp ecx, 3 CaveMemory.Write_StrBytes("83 FF 03"); //je @ CaveMemory.Write_StrBytes("0F 84 2C 00 00 00"); //cmp ecx, 42 CaveMemory.Write_StrBytes("83 FF 42"); //je @ CaveMemory.Write_StrBytes("0F 84 23 00 00 00"); //cmp ecx, 43 CaveMemory.Write_StrBytes("83 FF 43"); //je @ CaveMemory.Write_StrBytes("0F 84 1A 00 00 00"); //cmp ecx, 1 CaveMemory.Write_StrBytes("83 FF 01"); //je @ CaveMemory.Write_StrBytes("0F 84 16 00 00 00"); //cmp ecx, 41 CaveMemory.Write_StrBytes("83 FF 41"); //je @ CaveMemory.Write_StrBytes("0F 84 0D 00 00 00"); //mov [edi*2+padDemul.dll+OFFSET],cx CaveMemory.Write_StrBytes("66 89 0C 7D"); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); //jmp @ CaveMemory.Write_jmp((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Return_Offset); //cmp ax,80 CaveMemory.Write_StrBytes("81 F9 80 00 00 00"); //jnl @ CaveMemory.Write_StrBytes("0F 8D 0D 00 00 00"); //and dword ptr [edi*2+padDemul.dll+OFFSET],7F CaveMemory.Write_StrBytes("81 24 7D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("7F FF FF FF"); //jmp @ CaveMemory.Write_StrBytes("EB E2"); //or [edi*2+padDemul.dll+OFFSET],00000080 CaveMemory.Write_StrBytes("81 0C 7D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("80 00 00 00"); //jmp @ CaveMemory.Write_StrBytes("EB D5"); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Center Guns position at start byte[] init = { 0, 0x7f, 0, 0x7f }; WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, init); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); Logger.WriteLog("Memory Hack complete !"); Logger.WriteLog("-"); } protected override void SetHack_07() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp ecx, 2 CaveMemory.Write_StrBytes("83 F9 02"); //je @ CaveMemory.Write_StrBytes("0F 84 35 00 00 00"); //cmp ecx, 3 CaveMemory.Write_StrBytes("83 F9 03"); //je @ CaveMemory.Write_StrBytes("0F 84 2C 00 00 00"); //cmp ecx, 42 CaveMemory.Write_StrBytes("83 F9 42"); //je @ CaveMemory.Write_StrBytes("0F 84 23 00 00 00"); //cmp ecx, 43 CaveMemory.Write_StrBytes("83 F9 43"); //je @ CaveMemory.Write_StrBytes("0F 84 1A 00 00 00"); //cmp ecx, 1 CaveMemory.Write_StrBytes("83 F9 01"); //je @ CaveMemory.Write_StrBytes("0F 84 16 00 00 00"); //cmp ecx, 41 CaveMemory.Write_StrBytes("83 F9 41"); //je @ CaveMemory.Write_StrBytes("0F 84 0D 00 00 00"); //mov [ecx*2+padDemul.dll+OFFSET],ax CaveMemory.Write_StrBytes("66 89 04 4D"); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); //jmp @ CaveMemory.Write_jmp((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Return_Offset); //cmp ax,80 CaveMemory.Write_StrBytes("3D 80 00 00 00"); //jnl @ CaveMemory.Write_StrBytes("0F 8D 0D 00 00 00"); //and dword ptr [ecx*2+padDemul.dll+OFFSET],7F CaveMemory.Write_StrBytes("81 24 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("7F FF FF FF"); //jmp @ CaveMemory.Write_StrBytes("EB E3"); //or [ecx*2+padDemul.dll+35E50],00000080 CaveMemory.Write_StrBytes("81 0c 4D"); Buffer.Clear(); Buffer.AddRange(BitConverter.GetBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_PtrButtons_Offset)); CaveMemory.Write_Bytes(Buffer.ToArray()); CaveMemory.Write_StrBytes("80 00 00 00"); //jmp @ CaveMemory.Write_StrBytes("EB D6"); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Center Guns position at start byte[] init = { 0, 0x7f, 0, 0x7f }; WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, init); //WriteByte(P1_INOUT_ADDRESS, 0x01); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, init); //WriteByte(P2_INOUT_ADDRESS, 0x01); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); Logger.WriteLog("Naomi Memory Hack complete !"); Logger.WriteLog("-"); //Codecave for WidescreenHack /*if (_Rom.Equals("lupinsho") && _WidescreenHack) { CaveMemory = new Memory(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp ecx, 00BD6154 CaveMemory.Write_StrBytes("81 F9 54 61 BD 00"); //je @ CaveMemory.Write_StrBytes("0F 84 06 00 00 00"); //mov [ecx+2C000000],edx CaveMemory.Write_StrBytes("89 91 00 00 00 2C"); //jmp @ CaveMemory.Write_jmp((int)_TargetProcess.MainModule.BaseAddress + 0x0019CCDC); jumpTo = CaveMemory.CaveAddress - ((int)_TargetProcess.MainModule.BaseAddress + 0x0019CCD6) - 5; Buffer.Clear(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32.WriteProcessMemory((int)ProcessHandle, (int)_TargetProcess.MainModule.BaseAddress + 0x0019CCD6, Buffer.ToArray(), Buffer.Count, ref bytesWritten); Logger.WriteLog("Added lupinsho special Widescreen codecave at 0x" + CaveMemory.CaveAddress.ToString("X8")); //WriteBytes(0x2CBD6154, BitConverter.GetBytes(0x3F800000)); }*/ } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { //creating X-Y Hex value buffer to write memory byte[] buffer = { (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_Y >> 8), (byte)(PlayerData.RIController.Computed_Y & 0xFF) }; if (PlayerData.ID == 1) { WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //lupinsho reloads only when cursor get to max value ( = out of window) so we force it for Gun who don't update in realtime if (_RomName.Equals("lupinsho")) { buffer[0] = (byte)(0); buffer[1] = (byte)(0xFF); buffer[2] = (byte)(0); buffer[3] = (byte)(0xFF); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_X_Offset, buffer); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x01); System.Threading.Thread.Sleep(50); } else WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P1_Buttons_Offset, 0x00); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //lupinsho reloads only when cursor get to max value ( = out of window) so we force it for Gun who don't update in realtime if (_RomName.Equals("lupinsho")) { buffer[0] = (byte)(0); buffer[1] = (byte)(0xFF); buffer[2] = (byte)(0); buffer[3] = (byte)(0xFF); WriteBytes((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_X_Offset, buffer); WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x01); } else WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte((UInt32)_PadDemul_ModuleBaseAddress + _Paddemul_P2_Buttons_Offset, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (_RomName.Equals("confmiss")) Compute_Confmiss_Outputs(); else if (_RomName.Equals("deathcox")) Compute_Deathcox_Outputs(); //Need better filter (Playing vs Not Playing to display ammo/life) else if (_RomName.Equals("hotd2")) Compute_Hotd2_Outputs(0x00096FA0); else if (_RomName.Equals("hotd2o")) Compute_Hotd2_Outputs(0x00096F58); else if (_RomName.Equals("hotd2p")) Compute_Hotd2_Outputs(0x00082D00); else if (_RomName.Equals("lupinsho")) Compute_Lupinsho_Outputs(); else if (_RomName.Equals("mok")) //Todo : Check for Status !! Compute_Mok_Outputs(); } private void Compute_Confmiss_Outputs() { //Player status : //[0] = Calibration/InGame //[1] = InGame //[2] = Continue //[4] = Game Over / Attract Mode / Menu UInt32 P1_Status_Address = _GameRAM_Address + 0x00154CB8; UInt32 P2_Status_Address = P1_Status_Address + 0x40; _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; UInt32 P1_Ammo_Address = _GameRAM_Address + 0x00155804; // = P1_Status_Address + 0xB4C ? UInt32 P2_Ammo_Address = _GameRAM_Address + 0x00155918; UInt32 Credits_Address = _GameRAM_Address + 0x001B551C; if (ReadByte(P1_Status_Address) == 0 || ReadByte(P1_Status_Address) == 1) { _P1_Life = ReadByte((P1_Status_Address + 0x14)); _P1_Ammo = ReadByte(P1_Ammo_Address); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte(P2_Status_Address) == 0 || ReadByte(P2_Status_Address) == 1) { _P2_Life = ReadByte((P2_Status_Address + 0x14)); _P2_Ammo = ReadByte(P2_Ammo_Address); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_GameRAM_Address + 0x000152AEA) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_GameRAM_Address + 0x000152AEA) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte(Credits_Address)); } private void Compute_Deathcox_Outputs() { //InGame Status : 0 = AttractMode/Demo/Menu, 1 = InGame UInt32 InGame_Address = _GameRAM_Address + 0x00096680; _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; UInt32 P1_Ammo_Address = _GameRAM_Address + 0x0018BFC9; UInt32 P2_Ammo_Address = P1_Ammo_Address + 0x3C; UInt32 Credits_Address = _GameRAM_Address + 0x00974A8; //P1 and P2 Enable : Display ammo and life when it's 0 ( not reliable but well...) UInt32 P1_Enable_Address = _GameRAM_Address + 0x00096634; UInt32 P2_Enable_Address = _GameRAM_Address + 0x00096638; if (ReadByte(P1_Enable_Address) == 0 && ReadByte(InGame_Address) == 1) { _P1_Life = (int)(BitConverter.ToSingle(ReadBytes(Credits_Address + 0x04, 4), 0) * 100); _P1_Ammo = ReadByte(P1_Ammo_Address); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte(P2_Enable_Address) == 0 && ReadByte(InGame_Address) == 1) { _P2_Life = (int)(BitConverter.ToSingle(ReadBytes(Credits_Address + 0x08, 4), 0) * 100); _P2_Ammo = ReadByte(P2_Ammo_Address); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_GameRAM_Address + 0x0001C8D16) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_GameRAM_Address + 0x0001C8D16) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte(Credits_Address)); } private void Compute_Hotd2_Outputs(UInt32 DataPtr) { //Player status : //[4] = Continue Screen //[5] = InGame //[6] = Game Over //[9] = Menu or Attract Mode UInt32 P1_Status_Address = _GameRAM_Address + (BitConverter.ToUInt32(ReadBytes(_GameRAM_Address + DataPtr, 4), 0) & 0x01FFFFFF) + 0x04; UInt32 P2_Status_Address = P1_Status_Address + 0x100; _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (ReadByte(P1_Status_Address) == 5) { _P1_Life = ReadByte(P1_Status_Address + 0x0C); _P1_Ammo = ReadByte(P1_Status_Address + 0x20); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte(P2_Status_Address) == 5) { _P2_Life = ReadByte(P2_Status_Address + 0x0C); _P2_Ammo = ReadByte(P2_Status_Address + 0x20); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(P1_Status_Address + 0xFB) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(P2_Status_Address + 0xFB) >> 6 & 0x01); SetOutputValue(OutputId.Credits, ReadByte(P1_Status_Address + 0x75C)); } private void Compute_Lupinsho_Outputs() { _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Game Status (float) : //0 : Title Screen //1 : Gameplay //2 : Demo play float GameStatus = BitConverter.ToSingle(ReadBytes(_GameRAM_Address + 0x00A4C758, 4), 0); if (GameStatus == 1.0f) { //Check if P1 and P2 are active to display their information float P1_Active = BitConverter.ToSingle(ReadBytes(_GameRAM_Address + 0x00A4C738, 4), 0); float P2_Active = BitConverter.ToSingle(ReadBytes(_GameRAM_Address + 0x00A4C73C, 4), 0); //if (true)//if (P1_Active == 1.0f) //{ _P1_Life = (int)(BitConverter.ToSingle(ReadBytes(_GameRAM_Address + 0x00A4C888, 4), 0)); _P1_Ammo = (int)(BitConverter.ToSingle(ReadBytes(_GameRAM_Address + 0x00A4C808, 4), 0)); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); //} //if(true)//if (P2_Active == 1.0f) //{ _P2_Life = (int)(BitConverter.ToSingle(ReadBytes(_GameRAM_Address + 0x00A4C88C, 4), 0)); _P2_Ammo = (int)(BitConverter.ToSingle(ReadBytes(_GameRAM_Address + 0x00A4C80C, 4), 0)); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); //} } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_GameRAM_Address + 0x00B77BF6) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_GameRAM_Address + 0x00B77BF6) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte(_GameRAM_Address + 0x00B69FD8)); } private void Compute_Mok_Outputs() { //Player status : UInt32 P1_Data_Address = _GameRAM_Address + (BitConverter.ToUInt32(ReadBytes(_GameRAM_Address + 0x00023464, 4), 0) & 0x01FFFFFF); UInt32 P2_Data_Address = P1_Data_Address + 0x64; _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Player Status : //1 : Title Screen //2,3,4 : Demo //5 : Attract Mode //6 : Game Over //7 : Playing (cut scene) //8 : Playing //9 : continue Screen Byte P1_Status = ReadByte(P1_Data_Address + 0x48); Byte P2_Status = ReadByte(P2_Data_Address + 0x48); if (P1_Status == 8 || P1_Status == 7) { _P1_Life = ReadByte(P1_Data_Address + 0x5C); _P1_Ammo = ReadByte(P1_Data_Address + 0x58); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 8 || P2_Status == 7) { _P2_Life = ReadByte(P2_Data_Address + 0x5C); _P2_Ammo = ReadByte(P2_Data_Address + 0x58); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_GameRAM_Address + 0x000EC396) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_GameRAM_Address + 0x000EC396) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte(P1_Data_Address + 0x7C)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Dolphin4.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_Dolphin4 : Game { /*** MEMORY ADDRESSES **/ private const UInt32 CONTROLS_PTR_OFFSET = 0x00F37C48; private const UInt32 KEYBMOUSE_X_OFFSET = 0x144; private const UInt32 KEYBMOUSE_Y_OFFSET = 0x148; private const UInt32 KEYBMOUSE_LBTN_OFFSET = 0x13C; private const UInt32 KEYBMOUSE_MBTN_OFFSET = 0x13E; private const UInt32 KEYBMOUSE_RBTN_OFFSET = 0x13D; private const UInt32 KEYBMOUSE_X_INJECTION_OFFSET = 0x004E64D9; private const UInt32 KEYBMOUSE_X_INJECTION_RETURN_OFFSET = 0x004E64DF; private const UInt32 KEYBMOUSE_Y_INJECTION_OFFSET = 0x004E64F5; private const UInt32 KEYBMOUSE_Y_INJECTION_RETURN_OFFSET = 0x004E64FA; private NopStruct _Nop_KeybMouse_Btn = new NopStruct(0x004E6602, 6); private const UInt32 ATRAK_X_OFFSET = 0x2c; private const UInt32 ATRAK_Y_OFFSET = 0x30; protected NopStruct _Nop_Atrak_Axis = new NopStruct(0x004E418A, 4); private const HardwareScanCode DIK_KEY_LCLICK = HardwareScanCode.DIK_S; private const HardwareScanCode DIK_KEY_MCLICK = HardwareScanCode.DIK_D; private const HardwareScanCode DIK_KEY_RCLICK = HardwareScanCode.DIK_F; /*** Process variables **/ protected UInt32 _DinputNumber = 0; protected UInt32 _DinputControls_BaseAddress = 0; protected UInt32 _BasePtr = 0; protected UInt32 _KeybMouse_BaseAddress = 0; protected UInt32 _ATRAK_BaseAddress = 0; /// /// Constructor /// public Game_Dolphin4(String RomName, UInt32 DinputNumber) : base(RomName, "Dolphin") { _DinputNumber = DinputNumber; _KnownMd5Prints.Add("",""); _tProcess.Start(); Logger.WriteLog("Waiting for Dolphin " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); System.Threading.Thread.Sleep(2000); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Direct input mouse : float from -1 to +1 if (PlayerData.ID == 1) { //Convert client coordonnate to [-1, 1-] coordonates double dX = PlayerData.RIController.Computed_X / TotalResX * 2.0 - 1.0; double dY = PlayerData.RIController.Computed_Y / TotalResY * 2.0 - 1.0; if (dX < -1) dX = -1; else if (dX > 1) dX = 1; if (dY < -1) dY = -1; else if (dY > 1) dY = 1; PlayerData.RIController.Computed_X =(int)(dX * 1000); PlayerData.RIController.Computed_Y = (int)(dY * 1000); } //Dinput ATRAK : //min = FFFFFF80 max 0000080 , change from FFFFFF to 000000 at zero //min = top left else if (PlayerData.ID == 2) { double dMax = 254.0; if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0xFF80; else if (PlayerData.RIController.Computed_X > (int)TotalResX) PlayerData.RIController.Computed_X = 0x0080; else PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMax * PlayerData.RIController.Computed_X / TotalResX) - 0x7F); if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0xFF80; else if (PlayerData.RIController.Computed_Y > (int)TotalResY) PlayerData.RIController.Computed_Y = 0x0080; else PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMax * PlayerData.RIController.Computed_Y / TotalResY) - 0x7F); } return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { //Calulation of base addresses for Dinput Keyboard/Mouse byte[] bTampon = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + CONTROLS_PTR_OFFSET, 8); _BasePtr = BitConverter.ToUInt32(bTampon, 0); Logger.WriteLog("ControlsPtr address = 0x" + _BasePtr.ToString("X8")); try { bTampon = ReadBytes(_BasePtr, 8); _KeybMouse_BaseAddress = BitConverter.ToUInt32(bTampon, 0); } catch { } Logger.WriteLog("DInput Keyboard/Mouse address = 0x" + _KeybMouse_BaseAddress.ToString("X8")); //ATRAK #2 -> 2e manette en Ptr+10 (1ere en Ptr + 4) si 2 aimtrak connectés !! Logger.WriteLog("DInput Player2 device number in the list : " + _DinputNumber + 1); try { bTampon = ReadBytes(_BasePtr + (0x4 * _DinputNumber), 8); _ATRAK_BaseAddress = BitConverter.ToUInt32(bTampon, 0); } catch { } Logger.WriteLog("DInput Device#2 address = 0x" + _ATRAK_BaseAddress.ToString("X8")); //Nops SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_KeybMouse_Btn); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Atrak_Axis); //CodeCave Axe X Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, @ Buffer.Add(0xB8); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress + 18)); CaveMemory.Write_Bytes(Buffer.ToArray()); //fstp dword ptr [eax] CaveMemory.Write_StrBytes("D9 18"); //pop eax CaveMemory.Write_StrBytes("58"); //fild dword ptr [esp+08] CaveMemory.Write_StrBytes("DB 44 24 08"); //jmp Exit CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + KEYBMOUSE_X_INJECTION_RETURN_OFFSET); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + KEYBMOUSE_X_INJECTION_OFFSET) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + KEYBMOUSE_X_INJECTION_OFFSET, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); Buffer = new List(); //push edx CaveMemory.Write_StrBytes("52"); //mov edx, @ Buffer.Add(0xBA); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress + 17)); CaveMemory.Write_Bytes(Buffer.ToArray()); //fstp dword ptr [edx] CaveMemory.Write_StrBytes("D9 1A"); //pop edx CaveMemory.Write_StrBytes("5A"); //add esp,1C { 28 } CaveMemory.Write_StrBytes("83 C4 1C"); //jmp Exit CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + KEYBMOUSE_Y_INJECTION_RETURN_OFFSET); //Code injection jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + KEYBMOUSE_Y_INJECTION_OFFSET) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + KEYBMOUSE_Y_INJECTION_OFFSET, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Center guns at start if (_KeybMouse_BaseAddress != 0) { WriteBytes(_KeybMouse_BaseAddress + KEYBMOUSE_X_OFFSET, new byte[] { 0, 0, 0, 0 }); WriteBytes(_KeybMouse_BaseAddress + KEYBMOUSE_X_OFFSET, new byte[] { 0, 0, 0, 0 }); WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_LBTN_OFFSET, 0x00); WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_RBTN_OFFSET, 0x00); WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_MBTN_OFFSET, 0x00); } if (_ATRAK_BaseAddress != 0) { WriteBytes(_ATRAK_BaseAddress + ATRAK_X_OFFSET, new byte[] { 0, 0, 0, 0 }); WriteBytes(_ATRAK_BaseAddress + ATRAK_Y_OFFSET, new byte[] { 0, 0, 0, 0 }); } Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1 && _KeybMouse_BaseAddress != 0) { float fX = (float)PlayerData.RIController.Computed_X / (float)1000; float fY = (float)PlayerData.RIController.Computed_Y / (float)1000; byte[] bufferX = BitConverter.GetBytes(fX); byte[] bufferY = BitConverter.GetBytes(fY); WriteBytes(_KeybMouse_BaseAddress + KEYBMOUSE_X_OFFSET, bufferX); WriteBytes(_KeybMouse_BaseAddress + KEYBMOUSE_Y_OFFSET, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_LBTN_OFFSET, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_LBTN_OFFSET, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_MBTN_OFFSET, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_MBTN_OFFSET, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_RBTN_OFFSET, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_KeybMouse_BaseAddress + KEYBMOUSE_RBTN_OFFSET, 0x00); } else if (PlayerData.ID == 2 && _ATRAK_BaseAddress != 00) { //Converting 0xFF80 to 0xFFFFFF80 and so on... byte[] bufferX = { (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_X >> 8) }; byte[] bufferY = { (byte)(PlayerData.RIController.Computed_Y & 0xFF), (byte)(PlayerData.RIController.Computed_Y >> 8), (byte)(PlayerData.RIController.Computed_Y >> 8), (byte)(PlayerData.RIController.Computed_Y >> 8) }; WriteBytes(_ATRAK_BaseAddress + ATRAK_X_OFFSET, bufferX); WriteBytes(_ATRAK_BaseAddress + ATRAK_Y_OFFSET, bufferY); //Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) SendKeyDown(DIK_KEY_LCLICK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) SendKeyUp(DIK_KEY_LCLICK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) SendKeyDown(DIK_KEY_MCLICK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) SendKeyUp(DIK_KEY_MCLICK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) SendKeyDown(DIK_KEY_RCLICK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) SendKeyUp(DIK_KEY_RCLICK); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Dolphin5.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_Dolphin5 : Game { /*** MEMORY ADDRESSES **/ private UInt32 Controls_Ptr_Offsett = 0x00E8BC80; private UInt32 KeybMouse_X_Offset = 0x168; private UInt32 KeybMouse_Y_Offset = 0x170; private UInt32 KeybMouse_LBtn_Offset = 0x15C; private UInt32 KeybMouse_MBtn_Offset = 0x15E; private UInt32 KeybMouse_RBtn_Offset = 0x15D; private NopStruct _Nop_KeybMouse_X_1 = new NopStruct(0x004E7B2B, 3); private NopStruct _Nop_KeybMouse_X_2 = new NopStruct(0x004E7AE6, 4); private NopStruct _Nop_KeybMouse_Y_1 = new NopStruct(0x004E7B2E, 3); private NopStruct _Nop_KeybMouse_Y_2 = new NopStruct(0x004E7B08, 4); private NopStruct _Nop_KeybMouse_Btn = new NopStruct(0x004E765D, 7); private const UInt32 ATRAK_X_Offset = 0x44; private const UInt32 ATRAK_Y_Offset = 0x48; private NopStruct _Nop_Atrak_Axis = new NopStruct(0x004E587C, 4); /*** Process variables **/ private UInt32 _DinputNumber = 0; private UInt32 _BasePtr = 0; private UInt32 _KeybMouse_BaseAddress = 0; private UInt32 _ATRAK_BaseAddress = 0; /// /// Constructor /// public Game_Dolphin5(String RomName, UInt32 DinputNumber) : base(RomName, "Dolphin") { _DinputNumber = DinputNumber; _KnownMd5Prints.Add("Dolphin_x86 v5.0", "9660ec7cddf093a1807cb25fe0946b8e"); _tProcess.Start(); Logger.WriteLog("Waiting for Dolphin " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; //_TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; _TargetProcess_MemoryBaseAddress = (IntPtr)0x400000; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); System.Threading.Thread.Sleep(2000); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Direct input mouse : double from -1 to +1 if (PlayerData.ID == 1) { //Convert client coordonnate to [-1, 1-] coordonates double dX = PlayerData.RIController.Computed_X / TotalResX * 2.0 - 1.0; double dY = PlayerData.RIController.Computed_Y / TotalResY * 2.0 - 1.0; if (dX < -1) dX = -1; else if (dX > 1) dX = 1; if (dY < -1) dY = -1; else if (dY > 1) dY = 1; PlayerData.RIController.Computed_X = (int)(dX * 1000); PlayerData.RIController.Computed_Y = (int)(dY * 1000); } //Dinput ATRAK : //min = FFFFFF80 max 0000080 , change from FFFFFF to 000000 at zero //min = top left else if (PlayerData.ID == 2) { double dMax = 254.0; if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0xFF80; else if (PlayerData.RIController.Computed_X > (int)TotalResX) PlayerData.RIController.Computed_X = 0x0080; else PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMax * PlayerData.RIController.Computed_X / TotalResX) - 0x7F); if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0xFF80; else if (PlayerData.RIController.Computed_Y > (int)TotalResY) PlayerData.RIController.Computed_Y = 0x0080; else PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMax * PlayerData.RIController.Computed_Y / TotalResY) - 0x7F); } return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { //Calulation of base addresses for Dinput Keyboard/Mouse byte[] bTampon = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + Controls_Ptr_Offsett, 8); _BasePtr = BitConverter.ToUInt32(bTampon, 0); Logger.WriteLog("ControlsPtr address = 0x" + _BasePtr.ToString("X8")); try { bTampon = ReadBytes(_BasePtr, 8); _KeybMouse_BaseAddress = BitConverter.ToUInt32(bTampon, 0); } catch { } Logger.WriteLog("DInput Keyboard/Mouse address = 0x" + _KeybMouse_BaseAddress.ToString("X8")); //ATRAK #2 -> 2e manette en Ptr+10 (1ere en Ptr + 8) si 2 aimtrak connectés !! Logger.WriteLog("DInput Player2 device number in the list : " + _DinputNumber + 1); try { bTampon = ReadBytes(_BasePtr + (0x8 * _DinputNumber), 8); _ATRAK_BaseAddress = BitConverter.ToUInt32(bTampon, 0); } catch { } Logger.WriteLog("DInput Device#2 address = 0x" + _ATRAK_BaseAddress.ToString("X8")); //Nops SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_KeybMouse_X_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_KeybMouse_X_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_KeybMouse_Y_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_KeybMouse_Y_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_KeybMouse_Btn); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Atrak_Axis); //Center guns data at start if (_KeybMouse_BaseAddress != 0) { WriteBytes(_KeybMouse_BaseAddress + KeybMouse_X_Offset, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); WriteBytes(_KeybMouse_BaseAddress + KeybMouse_Y_Offset, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); WriteByte(_KeybMouse_BaseAddress + KeybMouse_LBtn_Offset, 0x00); WriteByte(_KeybMouse_BaseAddress + KeybMouse_RBtn_Offset, 0x00); WriteByte(_KeybMouse_BaseAddress + KeybMouse_MBtn_Offset, 0x00); } if (_ATRAK_BaseAddress != 0) { WriteBytes(_ATRAK_BaseAddress + ATRAK_X_Offset, new byte[] { 0, 0, 0, 0 }); WriteBytes(_ATRAK_BaseAddress + ATRAK_Y_Offset, new byte[] { 0, 0, 0, 0 }); } Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1 && _KeybMouse_BaseAddress != 0) { double fX = (double)PlayerData.RIController.Computed_X / (double)1000; double fY = (double)PlayerData.RIController.Computed_Y / (double)1000; byte[] bufferX = BitConverter.GetBytes(fX); byte[] bufferY = BitConverter.GetBytes(fY); WriteBytes(_KeybMouse_BaseAddress + KeybMouse_X_Offset, bufferX); WriteBytes(_KeybMouse_BaseAddress + KeybMouse_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_KeybMouse_BaseAddress + KeybMouse_LBtn_Offset, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_KeybMouse_BaseAddress + KeybMouse_LBtn_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(_KeybMouse_BaseAddress + KeybMouse_MBtn_Offset, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(_KeybMouse_BaseAddress + KeybMouse_MBtn_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_KeybMouse_BaseAddress + KeybMouse_RBtn_Offset, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_KeybMouse_BaseAddress + KeybMouse_RBtn_Offset, 0x00); } else if (PlayerData.ID == 2 && _ATRAK_BaseAddress != 00) { //Converting 0xFF80 to 0xFFFFFF80 and so on... byte[] bufferX = { (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_X >> 8), (byte)(PlayerData.RIController.Computed_X >> 8) }; byte[] bufferY = { (byte)(PlayerData.RIController.Computed_Y & 0xFF), (byte)(PlayerData.RIController.Computed_Y >> 8), (byte)(PlayerData.RIController.Computed_Y >> 8), (byte)(PlayerData.RIController.Computed_Y >> 8) }; WriteBytes(_ATRAK_BaseAddress + ATRAK_X_Offset, bufferX); WriteBytes(_ATRAK_BaseAddress + ATRAK_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) SendKeyDown(Configurator.GetInstance().DIK_Dolphin_P2_LClick); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) SendKeyUp(Configurator.GetInstance().DIK_Dolphin_P2_LClick); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) SendKeyDown(Configurator.GetInstance().DIK_Dolphin_P2_MClick); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) SendKeyUp(Configurator.GetInstance().DIK_Dolphin_P2_MClick); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) SendKeyDown(Configurator.GetInstance().DIK_Dolphin_P2_RClick); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) SendKeyUp(Configurator.GetInstance().DIK_Dolphin_P2_RClick); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Es4PointBlankX.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooter.Games { internal class Game_Es4PointBlankX : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] Recoil = null; public byte[] StartLED = null; public byte[] PlayerLED = null; public byte[] Life = null; public int[] Ammo = null; public byte Credits = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_Es4PointBlankX(String RomName) : base(RomName, "PBX100-2-NA-MPR0-A63", "PointBlankRevival") { _KnownMd5Prints.Add("Point Blank X - ROM PBX100-2-NA-MPR0-A63 - Original", "9aea1303f133b424c661ec897c67bf9e"); _KnownMd5Prints.Add("Point Blank X - ROM PBX100-2-NA-MPR0-A63 - Patched", "70432507a3a9b66592d561259a9741ed"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && (PlayerData.ID <= MAX_PLAYERS)) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.P2_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).StartLED[0]); SetOutputValue(OutputId.P2_LmpStart, ((OutputData)_OutputData).StartLED[1]); SetOutputValue(OutputId.P1_LmpPanel, ((OutputData)_OutputData).PlayerLED[0]); SetOutputValue(OutputId.P2_LmpPanel, ((OutputData)_OutputData).PlayerLED[1]); SetOutputValue(OutputId.P1_Ammo, ((OutputData)_OutputData).Ammo[0]); SetOutputValue(OutputId.P2_Ammo, ((OutputData)_OutputData).Ammo[1]); SetOutputValue(OutputId.P1_Life, ((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P2_Life, ((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.Credits, ((OutputData)_OutputData).Credits); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_GameWaxAkuma.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_GameWaxAkuma : Game { private const String GAMEDATA_FOLDER = @"MemoryData\gamewax\akuma"; //Memory values private UInt32 _PlayersInfo_BaseOffset = 0x00190CE8; private UInt32 _Player1_X_Offset = 0x30; private UInt32 _Player1_Y_Offset = 0x32; private UInt32 _Player1_ForceNullAxis_Offset = 0x38; private UInt32 _Player1_ActivateAim_Offset = 0x39; private UInt32 _Player2_X_Offset = 0x50; private UInt32 _Player2_Y_Offset = 0x52; private UInt32 _Player2_ForceNullAxis_Offset = 0x58; private UInt32 _Player2_ActivateAim_Offset = 0x59; //private UInt32 _Buttons_Offset = 0x74; private UInt32 _Outputs_Offset = 0x78; private UInt32 _Player1_InGame_Offset = 0x7E; private UInt32 _Player2_InGame_Offset = 0x7F; //private UInt32 _RecoilEnable_Offset = 0x00190F12; private UInt32 _Player1_SHotCount_Offset = 0x5E3B48; //Also writing in 0x5E3B4C, 0x5E3B64, 0x5E3B68 private UInt32 _Player2_SHotCount_Offset = 0x5E3BDC; private UInt32 _Player1_Life_Offset = 0x5E3B08; private UInt32 _Player2_Life_Offset = 0x5E3B9C; private UInt32 _Credits_Offset = 0x00190F02; private NopStruct _Axis_Nop_Offset = new NopStruct(0x000158D4, 5); private UInt32 _P1_Trigger_Injection_Offset = 0x00015800; private UInt32 _P1_Trigger_Injection_Return_Offset = 0x00015806; private UInt32 _P1_Trigger_InitialValueArray_Offset = 0x000D93C0; private UInt32 _P2_Trigger_Injection_Offset = 0x00015831; private UInt32 _P2_Trigger_InitialValueArray_Offset = 0x000D93CC; private UInt32 _P2_Trigger_Injection_Return_Offset = 0x00015837; //Custom values private UInt32 _P1_Trigger_BankAddress = 0; private UInt32 _P2_Trigger_BankAddress = 0; private int _P1_Last_ShotCount = 0; private int _P2_Last_ShotCount = 0; /// /// Constructor /// public Game_GameWaxAkuma(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Akuma Mortis Immortal v81.07 - Original Dump", "4143b362a0cced0f6633974c710d91f9"); _KnownMd5Prints.Add("Akuma Mortis Immortal v81.07 - Boot patched v1", "a8deac2b2c90187cce90fb09f767a695"); _tProcess.Start(); Logger.WriteLog("Waiting for GameWax " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Axis values : 0x00 -> 0xFF double dMaxX = 640.0; double dMaxY = 480.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { //One of the existing package has a modified inpout32.dll acting on axis values with Mouse //Original dump does nothing //So to be sure values are clean for Demulshooter, disabling the call to the original functions filling the values SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Axis_Nop_Offset); Create_InputsDataBank(); _P1_Trigger_BankAddress = _InputsDatabank_Address; _P2_Trigger_BankAddress = _InputsDatabank_Address + 0x04; SetHack_P1Trigger(); SetHack_P2Trigger(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Usingour own flag to update the original flag during the looped procedure to update game inputs /// private void SetHack_P1Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov edx,[eax+game.exe+D93C0] CaveMemory.Write_StrBytes("8B 90"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_InitialValueArray_Offset)); //cmp eax, 90 CaveMemory.Write_StrBytes("3D 90 00 00 00"); //jne Exit CaveMemory.Write_StrBytes("0F 85 18 00 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_Trigger_BankAddress CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Trigger_BankAddress)); //test eax, eax CaveMemory.Write_StrBytes("85 C0"); //pop eax CaveMemory.Write_StrBytes("58"); //je Exit CaveMemory.Write_StrBytes("0F 84 08 00 00 00"); //or [esp+edx+18],00000080 CaveMemory.Write_StrBytes("81 4C 14 18 80 00 00 00"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Trigger_Injection_Return_Offset); Logger.WriteLog("Adding P1 Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Trigger_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Trigger_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Usingour own flag to update the original flag during the looped procedure to update game inputs /// private void SetHack_P2Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov edx,[eax+game.exe+D93CC] CaveMemory.Write_StrBytes("8B 90"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_InitialValueArray_Offset)); //cmp eax, 90 CaveMemory.Write_StrBytes("3D 90 00 00 00"); //jne Exit CaveMemory.Write_StrBytes("0F 85 18 00 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P2_Trigger_BankAddress CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Trigger_BankAddress)); //test eax, eax CaveMemory.Write_StrBytes("85 C0"); //pop eax CaveMemory.Write_StrBytes("58"); //je Exit CaveMemory.Write_StrBytes("0F 84 08 00 00 00"); //or [esp+edx+18],00000080 CaveMemory.Write_StrBytes("81 4C 14 18 80 00 00 00"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Trigger_Injection_Return_Offset); Logger.WriteLog("Adding P2 Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Trigger_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Trigger_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player1_ActivateAim_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player1_ForceNullAxis_Offset, 0x00); WriteByte(_P1_Trigger_BankAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_BankAddress, 0x00); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player2_ActivateAim_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player2_ForceNullAxis_Offset, 0x00); WriteByte(_P2_Trigger_BankAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P2_Trigger_BankAddress, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : stays activated when trigger is pulled //Gun recoil : not used ?? _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpLeft)); _Outputs.Add(new GameOutput(OutputId.LmpRight)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs byte OutputData = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, OutputData & 0x01); SetOutputValue(OutputId.P2_LmpStart, OutputData >> 1 & 0x01); SetOutputValue(OutputId.LmpLeft, OutputData >> 4 & 0x01); SetOutputValue(OutputId.LmpRight, OutputData >> 5 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, OutputData >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, OutputData >> 7 & 0x01); _P1_Life = 0; _P2_Life = 0; int P1_ShotCount = 0; int P2_ShotCount = 0; //Custom Outputs if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player1_InGame_Offset) == 1) { //Recoil byte don't move a lot (except sometimes during TEST menu) //To compute custom Recoil, we can read the shot number incresing value with each bullet P1_ShotCount = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Player1_SHotCount_Offset, 4), 0); if (P1_ShotCount > _P1_Last_ShotCount) SetOutputValue(OutputId.P1_CtmRecoil, 1); _P1_Life = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Player1_Life_Offset, 4), 0); if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersInfo_BaseOffset + _Player2_InGame_Offset) == 1) { P2_ShotCount = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Player2_SHotCount_Offset, 4), 0); if (P2_ShotCount > _P2_Last_ShotCount) SetOutputValue(OutputId.P2_CtmRecoil, 1); _P2_Life = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Player2_Life_Offset, 4), 0); if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); _P1_Last_ShotCount = P1_ShotCount; _P2_Last_ShotCount = P2_ShotCount; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.Credits, BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, 4), 0)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_GvrAliens.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_GvrAliens : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x0556AEA8; private UInt32 _P1_Y_Offset = 0x0556AEAC; private UInt32 _P1_Btn_Offset = 0x0556ACBC; private UInt32 _P1_2_Offset = 0x0556B1C0; private UInt32 _P2_Y_Offset = 0x0556B1C4; private UInt32 _P2_Btn_Offset = 0x0556AFD4; private NopStruct _Nop_X = new NopStruct(0x0002EE9C, 6); private NopStruct _Nop_Y = new NopStruct(0x0002EEA5, 6); private NopStruct _Nop_Btn_1 = new NopStruct(0x0002EE75, 6); private NopStruct _Nop_Btn_2 = new NopStruct(0x0002EE81, 6); private NopStruct _Nop_Btn_3 = new NopStruct(0x0002EE8D, 3); private NopStruct _Nop_Btn_4 = new NopStruct(0x00048825, 2); private UInt32 _OutputRecoil_Injection_Offset = 0x00048D6C; private UInt32 _OutputRecoil_Injection_Return_Offset = 0x00048D72; private UInt32 _CtmRecoil_CaveAddress; /// /// Constructor /// public Game_GvrAliens(String RomName) : base(RomName, "aliens dehasped") { _KnownMd5Prints.Add("Aliens Extermination v1.03 US - Dehasped + No StaticPath patched", "30c8725c19d07fbebb68903f8f068052"); _KnownMd5Prints.Add("Aliens Extermination v1.03 US - Dehasped", "9ed286aafe474a16c07f7d62b5c06ecb"); _tProcess.Start(); Logger.WriteLog("Waiting for Global VR " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero && _TargetProcess.MainWindowHandle != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-FFFF] = 65535 //Y => [0-FFFF] = 65535 double dMaxX = 65535.0; double dMaxY = 65535.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Easy hack, just blocking instructions to be able to inject custom values /// protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_3); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_4); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _CtmRecoil_CaveAddress = _OutputsDatabank_Address; SetHack_CustomRecoilOutput(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// This codecave will intercept the game's procedure changing the output values /// Genuine recoil is set by the game in memory (0x50909E0) with values 0x05 for P1 and 0x0A for P2. /// But I have no clue how it is unset ("Px Gun Power" setting not used) to be sure it's long enough to work, so this method will ensure a custom recoil not missed /// private void SetHack_CustomRecoilOutput() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //or [_CtmRecoil_CaveAddress], eax CaveMemory.Write_StrBytes("09 05"); byte[] b = BitConverter.GetBytes(_CtmRecoil_CaveAddress); CaveMemory.Write_Bytes(b); //mov ["aliens dehasped.exe"+4C909E0],ecx CaveMemory.Write_StrBytes("89 0D E0 09 09 05"); //return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _OutputRecoil_Injection_Return_Offset); Logger.WriteLog("Adding Custom recoil output CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _OutputRecoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _OutputRecoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Btn_Offset, 0x10); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Btn_Offset, 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Btn_Offset, 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Btn_Offset, 0xDF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Btn_Offset, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Btn_Offset, 0xBF); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_2_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Btn_Offset, 0x10); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Btn_Offset, 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Btn_Offset, 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Btn_Offset, 0xDF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Btn_Offset, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Btn_Offset, 0xBF); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun recoil : is handled by the game like it should (On/Off with every bullets) //Gun motor : is activated when player gets hit _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_LedAmmo1)); _Outputs.Add(new GameOutput(OutputId.P1_LedAmmo2)); _Outputs.Add(new GameOutput(OutputId.P2_LedAmmo1)); _Outputs.Add(new GameOutput(OutputId.P2_LedAmmo2)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LedAmmo1, ReadByte(0x0527AEA1)); SetOutputValue(OutputId.P1_LedAmmo2, ReadByte(0x0527AEA0)); SetOutputValue(OutputId.P2_LedAmmo1, ReadByte(0x0527AEA3)); SetOutputValue(OutputId.P2_LedAmmo2, ReadByte(0x0527AEA2)); //Gun recoil : values are set when bullet is fired, but I have no clue when the game is setting it back to 0 //Gun recoil force adjustment doesn't seem to play here, mean time between ON and OFF state seems to be ~30 / ~35 ms //Which may be too quick to work good with recoil... //But the Enable/Disable kickback in TEST menu will work here SetOutputValue(OutputId.P1_GunRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x04E7AEA4) >> 2 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x04E7AEA5) >> 2 & 0x01); //Custom Outputs _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 1; int P2_Clip = 1; //Game status : //[2] = Menu / Continue screen //[4] = In game //[64] = Attract demo // We will use these values to compute ourselve Recoil and P1/P2 Start Button Lights byte GameStatus = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0047C960); if (GameStatus == 4) { //Playr status: //1: GameOver //4: Ingame //8: Continue screen int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0556C844); int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0556CBD8); if (P1_Status == 4) { //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); _P1_Life = (int)(100 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x0556C884, 4), 0)); //Can't use original digits for Ammo because the game use it to display continue countdown _P1_Ammo = (int)BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x0556CB08, 4), 0); //[Clip Empty] custom Output if (ReadByte(0x0527AEA1) == 0x0A && ReadByte(0x0527AEA0) == 0x0A) P1_Clip = 0; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } if (P2_Status == 4) { //Force Start Lamp to Off SetOutputValue(OutputId.P2_CtmLmpStart, 0); _P2_Life = (int)(100 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x0556CC18, 4), 0)); //Can't use original digits for Ammo because the game use it to display continue countdown _P2_Ammo = (int)BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00556CE9C, 4), 0); //[Clip Empty] custom Output if (ReadByte(0x0527AEA3) == 0x0A && ReadByte(0x0527AEA2) == 0x0A) P2_Clip = 0; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P2_CtmLmpStart, -1); } } //Custom Gun Recoil output //Based on genuine output but sure not to be missed because of too short pulse timing if ((byte)(ReadByte(_CtmRecoil_CaveAddress) & 0x01) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); Apply_AND_ByteMask(_CtmRecoil_CaveAddress, 0x0A); } if ((byte)(ReadByte(_CtmRecoil_CaveAddress) >> 1 & 0x01) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); Apply_AND_ByteMask(_CtmRecoil_CaveAddress, 0x05); } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0046D748) + ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0046D7C0)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_GvrAliensHasp.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_GvrAliensHasp : Game { /*** MEMORY ADDRESSES **/ private const UInt32 P1_X_OFFSET = 0x0556AEA8; private const UInt32 P1_Y_OFFSET = 0x0556AEAC; private const UInt32 P1_BTN_OFFSET = 0x0556ACBC; private const UInt32 P2_X_OFFSET = 0x0556B1C0; private const UInt32 P2_Y_OFFSET = 0x0556B1C4; private const UInt32 P2_BTN_OFFSET = 0x0556AFD4; private NopStruct _Nop_X = new NopStruct(0x0002EE9C, 6); private NopStruct _Nop_Y = new NopStruct(0x0002EEA5, 6); private NopStruct _Nop_Btn_1 = new NopStruct(0x0002EE75, 6); private NopStruct _Nop_Btn_2 = new NopStruct(0x0002EE81, 6); private NopStruct _Nop_Btn_3 = new NopStruct(0x0002EE8D, 3); private NopStruct _Nop_Btn_4 = new NopStruct(0x00048825, 2); /// /// Constructor /// public Game_GvrAliensHasp(string RomName) : base(RomName, "abhRelease") { _KnownMd5Prints.Add("Aliens Extermination v1.03 US - Original", "755eea8c196592d63090bf56b4b0651b"); _tProcess.Start(); Logger.WriteLog("Waiting for Global VR " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero && _TargetProcess.MainWindowHandle != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-FFFF] = 65535 //Y => [0-FFFF] = 65535 double dMaxX = 65535.0; double dMaxY = 65535.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Easy hack, just blocking instructions to be able to inject custom values /// protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_3); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_4); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + P1_X_OFFSET, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + P1_Y_OFFSET, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P1_BTN_OFFSET, 0x10); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P1_BTN_OFFSET, 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P1_BTN_OFFSET, 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P1_BTN_OFFSET, 0xDF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P1_BTN_OFFSET, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P1_BTN_OFFSET, 0xBF); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + P2_X_OFFSET, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + P2_Y_OFFSET, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P2_BTN_OFFSET, 0x10); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P2_BTN_OFFSET, 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P2_BTN_OFFSET, 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P2_BTN_OFFSET, 0xDF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P2_BTN_OFFSET, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + P2_BTN_OFFSET, 0xBF); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_GvrFarCry.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_GvrFarCry : Game { /*** MEMORY ADDRESSES **/ private InjectionStruct _PlayerOff_InjectionStruct = new InjectionStruct(0x000CBA22, 8); private InjectionStruct _PlayerOn_InjectionStruct = new InjectionStruct(0x000CBDA3, 8); private InjectionStruct _ReadLife_InjectionStruct = new InjectionStruct(0x00125D82, 7); private InjectionStruct _ReadShots_InjectionStruct = new InjectionStruct(0x000ED237, 8); private InjectionStruct _ReadKills_InjectionStruct = new InjectionStruct(0x003AEBA4, 5); private InjectionStruct _ReadRumble_InjectionStruct = new InjectionStruct(0x003AEA6D, 7); private UInt32 _P1_Playing_CaveAddress = 0; private UInt32 _P2_Playing_CaveAddress = 0; private UInt32 _P1_Shots_CaveAddress = 0; private UInt32 _P2_Shots_CaveAddress = 0; private UInt32 _P1_Life_CaveAddress = 0; private UInt32 _P2_Life_CaveAddress = 0; private UInt32 _P1_KillsDigit1_CaveAddress = 0; private UInt32 _P1_KillsDigit2_CaveAddress = 0; private UInt32 _P2_KillsDigit1_CaveAddress = 0; private UInt32 _P2_KillsDigit2_CaveAddress = 0; private UInt32 _P1_Rumble_CaveAddress = 0; private UInt32 _P2_Rumble_CaveAddress = 0; //Unused //private UInt32 _P1_Kills_Offset = 0x00649510; //private UInt32 _P2_Kills_Offset = 0x0064955C; //Outputs private int _P1_LastShots = 0; private int _P2_LastShots = 0; /// /// Constructor /// public Game_GvrFarCry(String RomName) : base(RomName, "FarCry_r") { _KnownMd5Prints.Add("Far Cry Paradise", "263325AC3F7685CBA12B280D8E927A5D"); _KnownMd5Prints.Add("Far Cry Paradise by Mohkerz", "557d065632eaa3c8adb5764df1609976"); _KnownMd5Prints.Add("Far Cry Paradise - TeknoParrot", "87648806d0b4b5a5384e7eedf4882d7e"); _tProcess.Start(); Logger.WriteLog("Waiting for Global VR " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); /*if (!_DisableInputHack) SetHack(); else*/ Logger.WriteLog("Input Hack disabled"); Apply_OutputsMemoryHack(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_InputsDataBank(); _P1_Playing_CaveAddress = _InputsDatabank_Address; _P2_Playing_CaveAddress = _InputsDatabank_Address + 0x04; _P1_Shots_CaveAddress = _InputsDatabank_Address + 0x08; _P2_Shots_CaveAddress = _InputsDatabank_Address + 0x0C; _P1_Life_CaveAddress = _InputsDatabank_Address + 0x10; _P2_Life_CaveAddress = _InputsDatabank_Address + 0x14; _P1_KillsDigit1_CaveAddress = _InputsDatabank_Address + 0x18; _P1_KillsDigit2_CaveAddress = _InputsDatabank_Address + 0x1C; _P2_KillsDigit1_CaveAddress = _InputsDatabank_Address + 0x20; _P2_KillsDigit2_CaveAddress = _InputsDatabank_Address + 0x24; _P1_Rumble_CaveAddress = _InputsDatabank_Address + 0x28; _P2_Rumble_CaveAddress = _InputsDatabank_Address + 0x2C; SetHack_ReadPlayerPlaying(); SetHack_ReadPlayerNotPlaying(); SetHack_ReadPlayerLife(); SetHack_ReadPlayerShots(); SetHack_ReadPlayerKillsDigit(); SetHack_ReadRumble(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// There is some emory address changing from 3 to 2 when player is not playing -> playing (bit mask bit 0) /// Unfortunatelly no pointer was found, but this instruction sets the Bit 0 to 1 when player is not playing /// We will use this instruction to set our own FLAG /// EBP = 1 if it is for Player 2 /// private void SetHack_ReadPlayerNotPlaying() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,[esp+1C] CaveMemory.Write_StrBytes("8B 44 24 1C"); //or byte ptr [eax+18] CaveMemory.Write_StrBytes("80 48 18 01"); //push ebp CaveMemory.Write_StrBytes("55"); //shl ebp, 2 CaveMemory.Write_StrBytes("C1 E5 02"); //add ebp, [_P1_Playing_CaveAddress] CaveMemory.Write_StrBytes("81 C5"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Playing_CaveAddress)); //mov [ebp], 0 CaveMemory.Write_StrBytes("C7 45 00 00 00 00 00"); //pop ebp CaveMemory.Write_StrBytes("5D"); //Inject it CaveMemory.InjectToOffset(_PlayerOff_InjectionStruct, "player not playing status"); } /// /// There is some emory address changing from 3 to 2 when player is not playing -> playing (bit mask bit 0) /// Unfortunatelly no pointer was found, but this instruction sets the Bit 0 to 0 when player is playing /// We will use this instruction to set our own FLAG /// EBP = 1 if it is for Player 2 /// private void SetHack_ReadPlayerPlaying() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,[esp+1C] CaveMemory.Write_StrBytes("8B 44 24 1C"); //and byte ptr [eax+18],-02 CaveMemory.Write_StrBytes("80 60 18 FE"); //push ebp CaveMemory.Write_StrBytes("55"); //shl ebp, 2 CaveMemory.Write_StrBytes("C1 E5 02"); //add ebp, [_P1_Playing_CaveAddress] CaveMemory.Write_StrBytes("81 C5"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Playing_CaveAddress)); //mov [ebp], 1 CaveMemory.Write_StrBytes("C7 45 00 01 00 00 00"); //pop ebp CaveMemory.Write_StrBytes("5D"); //Inject it CaveMemory.InjectToOffset(_PlayerOn_InjectionStruct, "player playing status"); } /// /// This is called to compare player life, ESI is player index /// We can get the life by that instruction, as a pointer has not been found to the memory location /// private void SetHack_ReadPlayerLife() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp esi,01 CaveMemory.Write_StrBytes("83 FE 01"); //je Player2 CaveMemory.Write_StrBytes("74 08"); //mov [_P1_Life_CaveAddress],edx CaveMemory.Write_StrBytes("89 15"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Life_CaveAddress)); //jmp Exit CaveMemory.Write_StrBytes("EB 06"); //mov [_P2_Life_CaveAddress],edx CaveMemory.Write_StrBytes("89 15"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Life_CaveAddress)); //cmp edx,[edi+ecx*4+00000610] CaveMemory.Write_StrBytes("3B 94 8F 10 06 00 00"); //Inject it CaveMemory.InjectToOffset(_ReadLife_InjectionStruct, "Custom Life output"); } /// /// This instructions put the current mission fired bullet in CX /// ZF=1 if player 1 /// ZF=0 if player 2 /// We can use this count to create recoil /// private void SetHack_ReadPlayerShots() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //lea eax,[eax+ecx+28] CaveMemory.Write_StrBytes("8D 44 08 28"); //mov cx,[eax+32] CaveMemory.Write_StrBytes("66 8B 48 32"); //jne Player 2 (ZF=0) CaveMemory.Write_StrBytes("75 09"); //mov [_P1_Shots_CaveAddress], cx CaveMemory.Write_StrBytes("66 89 0D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Shots_CaveAddress)); //jmp Exit CaveMemory.Write_StrBytes("EB 07"); //Player 2 //mov [_P2_Shots_CaveAddress], cx CaveMemory.Write_StrBytes("66 89 0D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Shots_CaveAddress)); //Inject it CaveMemory.InjectToOffset(_ReadShots_InjectionStruct, "Player Shots output"); } /// /// The game is sending the KILLS number as 2 digits to the Gun device /// We intercept that WRITE call to get the data /// We can get the device index (0 or 1) at ESP+1C /// Byte Buffered Message is in [ESP+4]: 0x03, Digit1, Digit2 /// private void SetHack_ReadPlayerKillsDigit() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp eax,-01 CaveMemory.Write_StrBytes("83 F8 FF"); //pop edi CaveMemory.Write_StrBytes("5F"); //pop esi CaveMemory.Write_StrBytes("5E"); //cmp [esp+1C], 01 CaveMemory.Write_StrBytes("80 7C 24 1C 01"); //je Player2 CaveMemory.Write_StrBytes("74 16"); //mov bl, [esp+5] CaveMemory.Write_StrBytes("8A 5C 24 05"); //mov [_P1_KillsDigit1_CaveAddress], bl CaveMemory.Write_StrBytes("88 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_KillsDigit1_CaveAddress)); //mov bl, [esp+6] CaveMemory.Write_StrBytes("8A 5C 24 06"); //mov [_P1_KillsDigit2_CaveAddress], bl CaveMemory.Write_StrBytes("88 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_KillsDigit2_CaveAddress)); //jmp exit CaveMemory.Write_StrBytes("EB 14"); //Player2: //mov bl, [esp+5] CaveMemory.Write_StrBytes("8A 5C 24 05"); //mov [_P2_KillsDigit1_CaveAddress], bl CaveMemory.Write_StrBytes("88 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_KillsDigit1_CaveAddress)); //mov bl, [esp+6] CaveMemory.Write_StrBytes("8A 5C 24 06"); //mov [_P1_KillsDigit2_CaveAddress], bl CaveMemory.Write_StrBytes("88 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_KillsDigit2_CaveAddress)); //Exit: //Inject it CaveMemory.InjectToOffset(_ReadKills_InjectionStruct, "Kill digits output"); } /// // /// The game is sending the Rumble state to the Gun device /// We intercept that WRITE call to get the data /// We can get the device index (0 or 1) at ESP+10 /// Byte Buffered Message is in [ESP]: 0x02, 0x07 (second byte is using the 3 lower bits to mask data - unknowed type) /// /// private void SetHack_ReadRumble() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,[edx*4+Farcry_R.exe+649500] CaveMemory.Write_StrBytes("8B 04 95 00 95 A4 00"); //cmp [esp+10], 01 CaveMemory.Write_StrBytes("80 7C 24 10 01"); //je Player2 CaveMemory.Write_StrBytes("74 0C"); //mov [_P1_Rumble_CaveAddress],00000001 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Rumble_CaveAddress)); CaveMemory.Write_StrBytes("01 00 00 00"); //jmp exit CaveMemory.Write_StrBytes("EB 0A"); //Player2: //mov [_P2_Rumble_CaveAddress],00000001 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Rumble_CaveAddress)); CaveMemory.Write_StrBytes("01 00 00 00"); //Exit: //Inject it CaveMemory.InjectToOffset(_ReadRumble_InjectionStruct, "Rumble output"); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_LedKills1)); _Outputs.Add(new GameOutput(OutputId.P1_LedKills2)); _Outputs.Add(new GameOutput(OutputId.P2_LedKills1)); _Outputs.Add(new GameOutput(OutputId.P2_LedKills2)); //Custom Outputs _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); //_Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original outputs : //Data is Written to the gun (with WriteFile API) //Gun recoil is written @ +3AEA90 (skipped because Handle = -1, but data is in memory) //Gun is rumbling on shoot or grenade int P1_Rumble = ReadByte(_P1_Rumble_CaveAddress); int P2_Rumble = ReadByte(_P2_Rumble_CaveAddress); SetOutputValue(OutputId.P1_GunMotor, P1_Rumble); SetOutputValue(OutputId.P2_GunMotor, P2_Rumble); //Guns have 2-digits LED to display kill. This is maxed to 99 and will roll back to 0 after 100th kill (and get player a medal/bonus) //Each digits goes from 0 to 9 during gameplay, and 0xA when not playing (IOboard should handle the values to hide digits or just display - or / with 0xA ?) SetOutputValue(OutputId.P1_LedKills1, ReadByte(_P1_KillsDigit1_CaveAddress)); SetOutputValue(OutputId.P1_LedKills2, ReadByte(_P1_KillsDigit2_CaveAddress)); SetOutputValue(OutputId.P2_LedKills1, ReadByte(_P2_KillsDigit1_CaveAddress)); SetOutputValue(OutputId.P2_LedKills2, ReadByte(_P2_KillsDigit2_CaveAddress)); //Custom Outputs int P1_Life = 0; int P2_Life = 0; int P1_CurrentShots = 0; int P2_CurrentShots = 0; //Customs Outputs //Player Status : //[0] : Inactive //[1] : In-Game int P1_Status = ReadByte(_P1_Playing_CaveAddress); if (P1_Status == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); P1_Life = ReadByte(_P1_Life_CaveAddress); if (P1_Life < 0) P1_Life = 0; if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); /*P1_CurrentShots = BitConverter.ToInt32(ReadBytes(_P1_Shots_CaveAddress, 4), 0); if (P1_CurrentShots > _P1_LastShots) SetOutputValue(OutputId.P1_CtmRecoil, 1);*/ } else if (P1_Status == 0) { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } int P2_Status = ReadByte(_P2_Playing_CaveAddress); if (P2_Status == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P2_CtmLmpStart, 0); P2_Life = ReadByte(_P2_Life_CaveAddress); if (P2_Life < 0) P2_Life = 0; if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); /*P2_CurrentShots = BitConverter.ToInt32(ReadBytes(_P2_Shots_CaveAddress, 4), 0); if (P2_CurrentShots > _P2_LastShots) SetOutputValue(OutputId.P2_CtmRecoil, 1);*/ } else if (P2_Status == 0) { //Enable Start Lamp Blinking SetOutputValue(OutputId.P2_CtmLmpStart, -1); } //Custom outputs will be based on Genuine rumble if (P1_Rumble != 0) SetOutputValue(OutputId.P1_CtmRecoil, 1); if (P2_Rumble != 0) SetOutputValue(OutputId.P2_CtmRecoil, 1); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; _P1_LastShots = P1_CurrentShots; _P2_LastShots = P2_CurrentShots; WriteByte(_P1_Rumble_CaveAddress, 0x00); WriteByte(_P2_Rumble_CaveAddress, 0x00); //SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C4D758)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_GvrFearLand.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_GvrFearLand : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Axis_Injection_Offset = 0x0009EE10; private UInt32 _Axis_Injection_Return_Offset = 0x0009EE1D; private UInt32 _Buttons_Injection_Offset = 0x000B31CA; private UInt32 _Buttons_Injection_Return_Offset = 0x000B31D0; private UInt32 _GetKeyboardState_Address = 0x002D6388; private UInt32 _LpKeyState_Address = 0x00C55118; private UInt32 _ScreenWidth_Offset = 0x00498B50; private UInt32 _ScreenHeight_Offset = 0x00498B54; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _P1_Trigger_CaveAddress; private UInt32 _P2_Trigger_CaveAddress; private UInt32 _P1_Action_CaveAddress; private UInt32 _P2_Action_CaveAddress; private bool _AlternativeGameplay = false; /// /// Constructor /// public Game_GvrFearLand(String RomName, bool AlternativeGameplay) : base(RomName, "game") { _AlternativeGameplay = AlternativeGameplay; _KnownMd5Prints.Add("Haunted Museum 2 v1.01 - Original", "0320d68acfb7ee7b4784ed43b113f0a0"); _KnownMd5Prints.Add("Haunted Museum 2 v1.01 - Original + Patched Header", "06ef6c57a1200bed1c9b0fa8ad172aee"); _KnownMd5Prints.Add("Haunted Museum 2 v1.01 - Original + Patched Header + NoCrosshair", "93dcb39c43ce0906676d6673eb09c5e6"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - 'Game Full'", "d3639bf04ababc7246df5a0a0892408b"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - 'Game Full' + Patched Header", "228a3d0e81141c3850459bfaf968321a"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - 'Game Full' + Patched Header + NoCrosshair", "80e10a8e5363205b670f9366e3fe78b1"); _tProcess.Start(); Logger.WriteLog("Waiting for Global VR " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //This engine (common with other TTX shooter) is waiting for X and Y value in range [0 ; WindowSize] //BUT using the raw window size is troublesome when the game is combined with DxWnd as the //resulting real window is not the same size as the game engine parameters (SCREEN_WITH, RENDER_WIDTH, etc...) //That's why we're going to read the memory to find the INI parameter and scale the X,Y values accordingly byte[] bufferX = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenWidth_Offset, 4); double GameResX = (double)BitConverter.ToInt32(bufferX, 0); byte[] bufferY = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenHeight_Offset, 4); double GameResY = (double)BitConverter.ToInt32(bufferY, 0); Logger.WriteLog("Game engine render resolution (Px) = [ " + GameResX + "x" + GameResY + " ]"); double dMinX = 0.0; double dMaxX = GameResX; double dMinY = 0.0; double dMaxY = GameResY; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; double RatioX = GameResX / TotalResX; double RatioY = GameResY / TotalResY; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(RatioX * PlayerData.RIController.Computed_X)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(RatioY * PlayerData.RIController.Computed_Y)); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address + 0x40; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x48; _P1_Trigger_CaveAddress = _InputsDatabank_Address + 0x50; _P1_Action_CaveAddress = _InputsDatabank_Address + 0x58; _P2_X_CaveAddress = _InputsDatabank_Address + 0x60; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x68; _P2_Trigger_CaveAddress = _InputsDatabank_Address + 0x70; _P2_Action_CaveAddress = _InputsDatabank_Address + 0x78; SetHack_Axis(); SetHack_Trigger(); //Initialize values if (_AlternativeGameplay) { WriteByte(_P1_Action_CaveAddress, 0x80); WriteByte(_P2_Action_CaveAddress, 0x80); } Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// P1 and P2 share same memory values so we split them : /// Changing proc so that X and Y will be read on custom memomy values. /// We will feed it with device axis data. /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp eax,O1 CaveMemory.Write_StrBytes("83 F8 01"); //je P2X CaveMemory.Write_StrBytes("0F 84 0B 00 00 00"); //mov edx,[P1_X] byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_StrBytes("8B 15"); CaveMemory.Write_Bytes(b); //jmp CaveMemory.Write_StrBytes("E9 06 00 00 00"); //P2X: mov edx,[P2_X] b = BitConverter.GetBytes(_P2_X_CaveAddress); CaveMemory.Write_StrBytes("8B 15"); CaveMemory.Write_Bytes(b); //mov [edi],edx CaveMemory.Write_StrBytes("89 17"); //cmp eax,O1 CaveMemory.Write_StrBytes("83 F8 01"); //je P2Y CaveMemory.Write_StrBytes("0F 84 0A 00 00 00"); //mov eax,[P1_Y] b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //jmp exit CaveMemory.Write_StrBytes("E9 05 00 00 00"); //P2Y: mov eax,[P2_Y] b = BitConverter.GetBytes(_P2_Y_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Return_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// For this hack we will wait the GetKeyboardState call, /// and immediately after we will read on our custom memory storage /// to replace lpKeystate bytes for mouse buttons (see WINUSER.H for virtualkey codes) /// private void SetHack_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //call USER32.GetKEyboardState CaveMemory.Write_StrBytes("FF 15"); byte[] b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GetKeyboardState_Address); CaveMemory.Write_Bytes(b); //and [lpkeystate + 1], 0xFF00FFFF CaveMemory.Write_StrBytes("81 25"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 1); /*0x00C55119*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("00 00 FF 00"); //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P1_Action CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + 1], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 1); /*0x00C55119*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //P1_Action: //cmp [_P1_Action], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P1_Action_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Trigger CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + X], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 51); /*0x00C5514B*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //P2_Trigger: //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P2_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Action CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + 2], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 2); /*0x00C5511A*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //P2_Action: //cmp [_P2_Action], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P2_Action_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne exit CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + X], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 52); /*0x00C5514C*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if (_AlternativeGameplay) WriteByte(_P1_Action_CaveAddress, 0x00); else WriteByte(_P1_Action_CaveAddress, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { if (_AlternativeGameplay) WriteByte(_P1_Action_CaveAddress, 0x80); else WriteByte(_P1_Action_CaveAddress, 0x00); } } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if (_AlternativeGameplay) WriteByte(_P2_Action_CaveAddress, 0x00); else WriteByte(_P2_Action_CaveAddress, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { if (_AlternativeGameplay) WriteByte(_P2_Action_CaveAddress, 0x80); else WriteByte(_P2_Action_CaveAddress, 0x00); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun recoil : is handled by the game like it should (On/Off with every bullets) //Gun motor : is activated when player gets hit _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { int P1_RecoilState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140C0) & 0x01; int P1_RumbleState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B6) >> 2 & 0x01; int P2_RecoilState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140C1) & 0x01; int P2_RumbleState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B6) >> 1 & 0x01; //Orginal SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B6) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B6) >> 5 & 0x01); SetOutputValue(OutputId.P1_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B6) >> 7 & 0x01); SetOutputValue(OutputId.P1_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B6) >> 4 & 0x01); SetOutputValue(OutputId.P1_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B6) >> 3 & 0x01); SetOutputValue(OutputId.P2_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B5) >> 7 & 0x01); SetOutputValue(OutputId.P2_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B5) >> 6 & 0x01); SetOutputValue(OutputId.P2_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002B140B5) >> 5 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, P1_RecoilState); SetOutputValue(OutputId.P1_GunMotor, P1_RumbleState); SetOutputValue(OutputId.P2_GunRecoil, P2_RecoilState); SetOutputValue(OutputId.P2_GunMotor, P2_RumbleState); _P1_Life = 0; _P1_Ammo = 0; _P2_Life = 0; _P2_Ammo = 0; //Customs Outputs //Player Status : //[0] : Inactive //[1] : In-Game //[2] : Continue Screen //[3] : Game Over int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C37364); if (P1_Status == 1) { _P1_Ammo = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C39D3C, 4), 0); _P1_Life = (int)(100.0 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C39C88, 4), 0)); if (_P1_Life < 0) _P1_Life = 0; if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C3736C); if (P2_Status == 1) { _P2_Ammo = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C39DC8, 4), 0); _P2_Life = (int)(100.0 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C39C94, 4), 0)); if (_P2_Life < 0) _P2_Life = 0; if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); //Custom Recoil will simply be activated just like original Recoil SetOutputValue(OutputId.P1_CtmRecoil, P1_RecoilState); SetOutputValue(OutputId.P2_CtmRecoil, P2_RecoilState); //Custom Damaged will simply be activated just like original rumble /* De-activated : Rumble also occurs with environmental actions ! SetOutputValue(OutputId.P1_Damaged, P1_RumbleState); SetOutputValue(OutputId.P2_Damaged, P2_RumbleState); */ SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00C4D758)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_KonamiCastlevania.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_KonamiCastlevania : Game { //MultiWindow process, so we can't use MAinWindowHandle private IntPtr _hWnd_GameWindow = IntPtr.Zero; /*** Game Data for Memory Hack ***/ /*** MEMORY ADDRESSES **/ private UInt32 _ViewportWidth_Offset = 0x0032508C; private UInt32 _ViewportHeight_Offset = 0x00325090; private UInt32 _Credits_Offset = 0x003280F8; private UInt32 _Buttons_CaveAddress = 0; private UInt32 _CheckIOEmulated_Offset = 0x00083978; private UInt32 _P1_X_Offset = 0x003281A0; private UInt32 _P1_Y_Offset = 0x003281A4; private UInt32 _P2_X_Offset = 0x0032CFB4; private UInt32 _P2_Y_Offset = 0x0032CFB8; //private UInt32 _Buttons_Offset = 0x00328138; //Unused, may be used for Emulated IO private UInt32 _Buttons_Injection_Offset = 0x00080E84; private UInt32 _Buttons_Injection_Return_Offset = 0x00080E89; //by default, putting a credit does not produce sound, compared to SERVICE key (but credit does not go into BOOKEEPING log) //By doing this we can force the game to play credit sound with CREDIT KEY private NopStruct _Nop_CreditsSound = new NopStruct(0x00083065, 4); private UInt32 _CreditsSoundMod_Offset = 0x00083061; private HardwareScanCode _Test_Key = HardwareScanCode.DIK_9; private HardwareScanCode _Service_Key = HardwareScanCode.DIK_0; private HardwareScanCode _P1_Start_Key = HardwareScanCode.DIK_1; private HardwareScanCode _P2_Start_Key = HardwareScanCode.DIK_2; private HardwareScanCode _Credits_Key = HardwareScanCode.DIK_5; //Disabling adding credits while the key stays pressed private bool _IsCreditsKeyPressed = false; //Outputs private UInt32 _Outputs_Offset = 0x00331F24; private UInt32 _P1_Life_Offset = 0x00326B48; private UInt32 _P2_Life_Offset = 0x00326D08; private UInt32 _P1_Ammo_Offset = 0x00326B58; private UInt32 _P2_Ammo_Offset = 0x00326D18; /// /// Constructor /// public Game_KonamiCastlevania(String RomName) : base (RomName, "HCV") { _KnownMd5Prints.Add("Castlevania Arcade v2009-04-22 - clean dump", "8a6dd00e254df6ad68a944da8add6235"); _tProcess.Start(); Logger.WriteLog("Waiting for KONAMI " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _hWnd_GameWindow = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Contains("CROSS") || FindGameWindow_Equals("TeknoParrot - Castlevania: The Arcade")) { CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } catch (Exception) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// The process is creating 2 windows, so we need to fill the required window handle instead of the default one (not knowing which one it is) /// /// /// public override bool ClientScale(PlayerSettings PlayerData) { //Convert Screen location to Client location if (_TargetProcess != null) { //Window size Rect TotalRes = new Rect(); Win32API.GetWindowRect(_hWnd_GameWindow, ref TotalRes); Logger.WriteLog("Window position (Px) = [ " + TotalRes.Left + ";" + TotalRes.Top + " ]"); PlayerData.RIController.Computed_X = PlayerData.RIController.Computed_X - TotalRes.Left; PlayerData.RIController.Computed_Y = PlayerData.RIController.Computed_Y - TotalRes.Top; Logger.WriteLog("Onclient window position (Px) = [ " + PlayerData.RIController.Computed_X + "x" + PlayerData.RIController.Computed_Y + " ]"); } return true; } /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Reading Resolution in game's memory double dMaxX = (double)BitConverter.ToUInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ViewportWidth_Offset, 4), 0); double dMaxY = (double)BitConverter.ToUInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ViewportHeight_Offset, 4), 0); //Inverted Axis : 0 = bottom right PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region MemoryHack protected override void Apply_InputsMemoryHack() { //Replacing the check for Emulated IO so that values are generated by real board //+83978 -> JE -----> JNE WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _CheckIOEmulated_Offset, 0xEB); //Replacing the check for Emulated SENSORS so that values are generated by real board //Causes LAG (input check with hardware timeout ??) //+83963 -> JE -----> JNE //WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x83963, 0xEB); //Forcing Sound with Credits insertion SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_CreditsSound); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsSoundMod_Offset, 0x29); //ORiginal code is writing in Ptr+2A, and Service is using Ptr+29 Create_InputsDataBank(); _Buttons_CaveAddress = _InputsDatabank_Address; SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov esi, _Buttons_CaveAddress CaveMemory.Write_StrBytes("8B 35"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //mov edx, edi+48 CaveMemory.Write_StrBytes("8B 57 48"); //mov eax, edx CaveMemory.Write_StrBytes("8B C2"); //return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Input /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { byte[] bufferX = BitConverter.GetBytes((float)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { //WriteBytes(0x7281B8, bufferX); //If using Original Sensor procedure, but causes LAG //WriteBytes(0x7281BC, bufferY); //If using Original Sensor procedure, but causes LAG WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); //USed with EmulatedSensor WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); //USed with EmulatedSensor if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xDF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0x7F); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); //USed with EmulatedSensor WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); //USed with EmulatedSensor if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0xFE); } } } /// /// Low-level Keyboard hook callback. /// This is used to replace system keys /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _P1_Start_Key) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x08); } else if (s.scanCode == _P2_Start_Key) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x10); } else if (s.scanCode == _Test_Key) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); } else if (s.scanCode == _Service_Key) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); } else if (s.scanCode == _Credits_Key) { if (!_IsCreditsKeyPressed) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x04); /*int Credits = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, 4), 0); Credits++; WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, BitConverter.GetBytes(Credits));*/ WriteByte(0x72811c, 1); _IsCreditsKeyPressed = true; } } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == _P1_Start_Key) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xF7); } else if (s.scanCode == _P2_Start_Key) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xEF); } else if (s.scanCode == _Test_Key) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); } else if (s.scanCode == _Service_Key) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0x0D); } else if (s.scanCode == _Credits_Key) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFB); WriteByte(0x72811c, 0); _IsCreditsKeyPressed = false; } } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpCard_R)); _Outputs.Add(new GameOutput(OutputId.P1_LmpCard_G)); _Outputs.Add(new GameOutput(OutputId.P2_LmpCard_R)); _Outputs.Add(new GameOutput(OutputId.P2_LmpCard_G)); _Outputs.Add(new GameOutput(OutputId.P1_Whip_R)); _Outputs.Add(new GameOutput(OutputId.P1_Whip_G)); _Outputs.Add(new GameOutput(OutputId.P1_Whip_B)); _Outputs.Add(new GameOutput(OutputId.P2_Whip_R)); _Outputs.Add(new GameOutput(OutputId.P2_Whip_G)); _Outputs.Add(new GameOutput(OutputId.P2_Whip_B)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset)); SetOutputValue(OutputId.P2_LmpStart, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 4)); SetOutputValue(OutputId.P1_LmpCard_R, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 8)); SetOutputValue(OutputId.P1_LmpCard_G, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 12)); SetOutputValue(OutputId.P2_LmpCard_R, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 16)); SetOutputValue(OutputId.P2_LmpCard_G, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 20)); SetOutputValue(OutputId.P1_Whip_R, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 24)); SetOutputValue(OutputId.P1_Whip_G, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 28)); SetOutputValue(OutputId.P1_Whip_B, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 32)); SetOutputValue(OutputId.P2_Whip_R, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 36)); SetOutputValue(OutputId.P2_Whip_G, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 40)); SetOutputValue(OutputId.P2_Whip_B, GetLampAnalogValue((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 44)); //Custom Outputs _P1_Life = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset, 4), 0); _P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset); //When not playing, default value is 0x2710 (10 000) if (_P1_Life != 10000) { //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); if (_P1_Life > 0) { //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo <= 0) SetOutputValue(OutputId.P1_Clip, 0); else SetOutputValue(OutputId.P1_Clip, 1); } else { _P1_Ammo = 0; } } else { SetOutputValue(OutputId.P1_Clip, 0); _P1_Ammo = 0; _P1_Life = 0; } _P2_Life = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset, 4), 0); _P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset); //When not playing, default value is 0x2710 (10 000) if (_P2_Life != 10000) { //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); if (_P2_Life > 0) { //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo <= 0) SetOutputValue(OutputId.P2_Clip, 0); else SetOutputValue(OutputId.P2_Clip, 1); } else { _P2_Ammo = 0; } } else { SetOutputValue(OutputId.P2_Clip, 0); _P2_Ammo = 0; _P2_Life = 0; } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } //Seems like there's a memory field to set Lamp to state 0/1/2 and another one [0->0x64] to set analog intensity private byte GetLampAnalogValue(UInt32 OutputLampAddress) { if (ReadByte(OutputLampAddress) != 0) { return (ReadByte(OutputLampAddress - 0x30)); } else return 0; } #endregion } } ================================================ FILE: DemulShooter/Games/Game_KonamiCoopers9.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_KonamiCoopers9 : Game { //Outputs variable private UInt32 _Outputs_Offset = 0x01FE7F48; private UInt32 _P1_Life_Offset = 0x024FCD10; private UInt32 _P2_Life_Offset = 0x024FCDD8; private UInt32 _P1_Ammo_Offset = 0x024FCDC2; private UInt32 _P2_Ammo_Offset = 0x024FCE8A; private UInt32 _Credits_Offset = 0x01FE8188; //private UInt32 _GameScene_Offset = 0x01FE7C97; private UInt32 _PlayersStatus_Offset = 0x01FE7F4E; //<-- Injection to use if reading the outputs byte is not fast enough to get recoil change --> private InjectionStruct _RecoilTestMenu_InjectionStruct = new InjectionStruct(0x00019507, 6); private InjectionStruct _RecoilInGame_InjectionStruct = new InjectionStruct(0x00019480, 6); //Custom values private UInt32 _Recoil_CaveAddress = 0; /// /// Constructor /// public Game_KonamiCoopers9(String RomName) : base(RomName, "game") { //JConfig and Teknoparrot are sharing the same offsets, non need for memory files for now... _KnownMd5Prints.Add("Cooper9 - TeknoParrot", "541d457e167451235d521f4e3a19cdde"); _KnownMd5Prints.Add("Cooper9 - JConfig", "6d1fa58318e7fa4c26e26913e9968ca9"); _tProcess.Start(); Logger.WriteLog("Waiting for Konami " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { foreach (Process p in Process.GetProcessesByName(_Target_Process_Name)) { _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _Recoil_CaveAddress = _OutputsDatabank_Address; SetHack_Recoil(_RecoilInGame_InjectionStruct); SetHack_Recoil(_RecoilTestMenu_InjectionStruct); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// Outputs are stored in a roll buffer: /// [+0] 4 Bytes for next index /// [+0x0C] Output Indexes /// [+0x8C] Corresponding values /// /// Index: /// 0x94: Start Light (0=OFF, 1=ON, 0x2F=FLASH) /// 0x96; Tickets to send /// 0x97: Bonus value, low byte /// 0x98: Bonus value, High byte private void SetHack_Recoil(InjectionStruct Target) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,_Recoil_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); //add eax,edx CaveMemory.Write_StrBytes("01 D0"); //mov byte ptr[eax], 1 CaveMemory.Write_StrBytes("C6 00 01"); //mov al,01 CaveMemory.Write_StrBytes("B0 01"); //shl al,cl CaveMemory.Write_StrBytes("D2 E0"); //mov cl,al CaveMemory.Write_StrBytes("8A C8"); //Inject it CaveMemory.InjectToOffset(Target, "Recoil"); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset) & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset) >> 1 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset) >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset) >> 7 & 0x01); byte P1_Life = 0; byte P2_Life = 0; byte P1_Ammo = 0; byte P2_Ammo = 0; //Checking if the player is playing, to remove risks of outputs triggered during attract if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersStatus_Offset) == 1) { //Player1 P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); if (P1_Life > 0) P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset); } if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersStatus_Offset + 0x01) == 1) { //Player2 P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset); if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); if (P2_Life > 0) P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset); } SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; if (ReadByte(_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress, 0); } if (ReadByte(_Recoil_CaveAddress + 1) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 1, 0); } SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_KonamiGashaaaan2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_KonamiGashaaaan2 : Game { //MEMORY ADDRESSES private UInt32 _CreditsPtr_Offset = 0x002BB0D8; private UInt32 _ig2ACLibIOSetLamp_PtrOffset = 0x0002B5EEC; private UInt32 _ig2ACLibIOCoinBlockerOpen_PtrOffset = 0x0002B5F00; private UInt32 _ig2ACLibIOCoinBlockerClose_PtrOffset = 0x0002B5F04; private UInt32 _ig2ACLibIOSetBallSupply_PtrOffset = 0x0002B5EF0; private UInt32 _ig2ACLibIOSetBallSupplyStop_PtrOffset = 0x0002B5EF4; private UInt32 _ig2ACLibIOSetBallSupplyStopImmediate_PtrOffset = 0x0002B5EF8; //custom Values private UInt32 _Lamps_CaveAddress = 0; private UInt32 _CoinBlocker_CaveAddress = 0; private UInt32 _MotorUnits_CaveAddress = 0; /// /// Constructor /// public Game_KonamiGashaaaan2(String RomName) : base(RomName, "gasya") { _KnownMd5Prints.Add("Gashaaaan Refills v. JA-A01:2009-01-13", "db158b447371831317229ecc79920ee8"); _tProcess.Start(); Logger.WriteLog("Waiting for Konami " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { foreach (Process p in Process.GetProcessesByName(_Target_Process_Name)) { _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _Lamps_CaveAddress = _OutputsDatabank_Address; _CoinBlocker_CaveAddress = _OutputsDatabank_Address + 0x10; _MotorUnits_CaveAddress = _OutputsDatabank_Address + 0x14; SetHack_ig2ACLibIOSetLamp(); SetHack_ig2ACLibIOCoinBlockerOpen(); SetHack_ig2ACLibIOCoinBlockerClose(); SetHack_ig2ACLibIOSetBallSupply(); SetHack_ig2ACLibIOSetBallSupplyStop(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Create a simple function that gets the Lamp Id (ESP+4) and Lamp value (ESP+8) before RET /// private void SetHack_ig2ACLibIOSetLamp() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push ecx CaveMemory.Write_StrBytes("51"); //mov eax,_Lamps_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Lamps_CaveAddress)); //add eax,[esp+08] CaveMemory.Write_StrBytes("03 44 24 08"); //mov ecx,[esp+0C] CaveMemory.Write_StrBytes("8B 4C 24 0C"); //mov [eax],cl CaveMemory.Write_StrBytes("88 08"); //pop ecx CaveMemory.Write_StrBytes("59"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //ret CaveMemory.Write_StrBytes("C3"); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ig2ACLibIOSetLamp_PtrOffset, BitConverter.GetBytes(CaveMemory.CaveAddress)); } /// /// Creating a simple function to set a flag /// private void SetHack_ig2ACLibIOCoinBlockerOpen() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov byte ptr[_CoinBlocker_CaveAddress], 1 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_CoinBlocker_CaveAddress)); CaveMemory.Write_StrBytes("01"); //ret CaveMemory.Write_StrBytes("C3"); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ig2ACLibIOCoinBlockerOpen_PtrOffset, BitConverter.GetBytes(CaveMemory.CaveAddress)); } /// /// Creating a simple function to set a flag /// private void SetHack_ig2ACLibIOCoinBlockerClose() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov byte ptr[_CoinBlocker_CaveAddress], 1 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_CoinBlocker_CaveAddress)); CaveMemory.Write_StrBytes("00"); //ret CaveMemory.Write_StrBytes("C3"); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ig2ACLibIOCoinBlockerClose_PtrOffset, BitConverter.GetBytes(CaveMemory.CaveAddress)); } /// /// Creating simple functions to set a flag /// Motor ID (0/1) is in ESP+4 /// private void SetHack_ig2ACLibIOSetBallSupply() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,_Lamps_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_MotorUnits_CaveAddress)); //add eax,[esp+04] CaveMemory.Write_StrBytes("03 44 24 04"); //mov byte ptr[eax], 1 CaveMemory.Write_StrBytes("C6 00 01"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //ret CaveMemory.Write_StrBytes("C3"); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ig2ACLibIOSetBallSupply_PtrOffset, BitConverter.GetBytes(CaveMemory.CaveAddress)); } /// /// Creating simple functions to set a flag /// Motor ID (0/1) is in ESP+4 /// Applying the same function for the 2 different procedure (Stop + StopImmediate) /// private void SetHack_ig2ACLibIOSetBallSupplyStop() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,_MotorUnits_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_MotorUnits_CaveAddress)); //add eax,[esp+04 CaveMemory.Write_StrBytes("03 44 24 04"); //mov byte ptr[eax], 0 CaveMemory.Write_StrBytes("C6 00 00"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //ret CaveMemory.Write_StrBytes("C3"); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ig2ACLibIOSetBallSupplyStop_PtrOffset, BitConverter.GetBytes(CaveMemory.CaveAddress)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ig2ACLibIOSetBallSupplyStopImmediate_PtrOffset, BitConverter.GetBytes(CaveMemory.CaveAddress)); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P3_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P4_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P3_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P4_LmpFront)); _Outputs.Add(new GameOutput(OutputId.MotorUnit_1)); _Outputs.Add(new GameOutput(OutputId.MotorUnit_2)); _Outputs.Add(new GameOutput(OutputId.CoinBlocker)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Lamps_CaveAddress)); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Lamps_CaveAddress + 1)); SetOutputValue(OutputId.P3_LmpStart, ReadByte(_Lamps_CaveAddress + 2)); SetOutputValue(OutputId.P4_LmpStart, ReadByte(_Lamps_CaveAddress + 3)); SetOutputValue(OutputId.P1_LmpFront, ReadByte(_Lamps_CaveAddress + 4)); SetOutputValue(OutputId.P2_LmpFront, ReadByte(_Lamps_CaveAddress + 5)); SetOutputValue(OutputId.P3_LmpFront, ReadByte(_Lamps_CaveAddress + 6)); SetOutputValue(OutputId.P4_LmpFront, ReadByte(_Lamps_CaveAddress + 7)); SetOutputValue(OutputId.MotorUnit_1, ReadByte(_MotorUnits_CaveAddress)); SetOutputValue(OutputId.MotorUnit_2, ReadByte(_MotorUnits_CaveAddress + 1)); SetOutputValue(OutputId.CoinBlocker, ReadByte(_CoinBlocker_CaveAddress)); int Credits = (int)ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsPtr_Offset, new UInt32[] { 0x20 }); SetOutputValue(OutputId.Credits, Credits); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_KonamiLethalEnforcer3.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_KonamiLethalEnforcers3 : Game { private const String GAMEDATA_FOLDER = @"MemoryData\konami\le3"; /*** MEMORY ADDRESSES **/ private UInt32 _BaseData_PtrOffset = 0x00294150; private UInt32 _BaseData_Address = 0; private UInt32 _TEST_Offset = 0xE43C; private UInt32 _SERVICE_Offset = 0xE43F; private UInt32 _P1_X_Offset = 0xE494; private UInt32 _P1_Y_Offset = 0xE498; private UInt32 _P2_X_Offset = 0xE4A8; private UInt32 _P2_Y_Offset = 0xE4AC; private UInt32 _P1_Start_Offset = 0xE446; private UInt32 _P2_Start_Offset = 0xE447; private UInt32 _P1_Trigger_Offset = 0xE448; private UInt32 _P2_Trigger_Offset = 0xE449; private UInt32 _P1_Option_Offset = 0xE44A; private UInt32 _P2_Option_Offset = 0xE44B; //private UInt32 _CreditsToAdd_Offset = 0x374628; private UInt32 _CreditsToAdd_Ptr_Offset = 0x294150; //better : calls for sound effect and add bookkeeping private UInt32 _CreditsTotal_Offset = 0x0037462C; private NopStruct _Nop_AxisX_1 = new NopStruct(0x0011D5C4, 2); private NopStruct _Nop_AxisX_2 = new NopStruct(0x0011D66F, 2); private NopStruct _Nop_AxisX_3 = new NopStruct(0x0011D6A3, 6); private NopStruct _Nop_AxisY_1 = new NopStruct(0x0011D610, 3); private NopStruct _Nop_AxisY_2 = new NopStruct(0x0011D67D, 2); private NopStruct _Nop_AxisY_3 = new NopStruct(0x0011D6C7, 6); //Original behavior, when player points the gun out of screen, is to wait for the aim to be at least in those boundaries to get out of "Cover" state // 33 < X < 607 // 33 < Y < 448 //This is problematic for our needs as the device may not be tracked in real time, //and use of "pedal mod" would force the player to still be hidden if it's aiming at the border //For that, we will push those boundaries to the max private UInt32 _Xmin_Patch_Offset = 0x0001349D; private UInt32 _Xmax_Patch_Offset = 0x000134B6; private UInt32 _Ymin_Patch_Offset = 0x000134CD; private UInt32 _Ymax_Patch_Offset = 0x000134E4; private UInt32 _Float_640_Offset = 0x0022B218; private UInt32 _Float_480_Offset = 0x0022B214; private UInt32 _Float_0_Offset = 0x0024F92C; private HardwareScanCode _Test_Key = HardwareScanCode.DIK_9; private HardwareScanCode _Service_Key = HardwareScanCode.DIK_0; private HardwareScanCode _P1_Start_Key = HardwareScanCode.DIK_1; private HardwareScanCode _P2_Start_Key = HardwareScanCode.DIK_2; private HardwareScanCode _Credits_Key = HardwareScanCode.DIK_5; //Disabling adding credits while the key stays pressed private bool _IsCreditsKeyPressed = false; //Outputs private UInt32 _Outputs_Offset = 0xE4BC; //Based on BaseData address private UInt32 _PlayerInfo_BasePtr_Offset = 0x0029423C; private UInt32 _GameScene_Offset = 0x00294234; //17 = in game (not in attract) private UInt32 _Player1_Playing = 0x002945D0; private UInt32 _Player2_Playing = 0x002945D4; private int _P1_LastWeapon = 0; private int _P2_LastWeapon = 0; //Custom pedal-mod private bool _isPedal1_Pushed = false; private bool _isPedal2_Pushed = false; /// /// Constructor /// public Game_KonamiLethalEnforcers3(String RomName) : base (RomName, "game_patched") { _KnownMd5Prints.Add("Lethal Enforcer 3 v2005-04-15-1 - Original game.exe", "1d338c452c7b087bc7aad823a74bb023"); _KnownMd5Prints.Add("Lethal Enforcer 3 v2005-04-15-1 - DemulShooter compatible game.exe", "ce1359efe2dde0ac02280bfe8c96681d"); _tProcess.Start(); Logger.WriteLog("Waiting for KONAMI " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _BaseData_Address = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _BaseData_PtrOffset); if (_BaseData_Address != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Data Pointer Address = 0x" + _BaseData_Address.ToString("X8")); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 640.0; double dMaxY = 480.0; //If Pedal-Mod is used, we need to clmap value at +1/-1 compared to max values because of how the hack is done //to check "return value" (>0 and <640, same for Y) if ((Configurator.GetInstance().Le3_Pedal_P1_Enabled && PlayerData.ID == 1) || (Configurator.GetInstance().Le3_Pedal_P2_Enabled && PlayerData.ID == 2)) { PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 1) PlayerData.RIController.Computed_X = 1; if (PlayerData.RIController.Computed_Y < 1) PlayerData.RIController.Computed_Y = 1; if (PlayerData.RIController.Computed_X > (int)(dMaxX - 1)) PlayerData.RIController.Computed_X = (int)(dMaxX - 1); if (PlayerData.RIController.Computed_Y > (int)(dMaxY - 1)) PlayerData.RIController.Computed_Y = (int)(dMaxY - 1); } //If genuine mode, we neeed to put -1 to the axis value to enable the cover action else { PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 1) PlayerData.RIController.Computed_X = -1; if (PlayerData.RIController.Computed_Y < 1) PlayerData.RIController.Computed_Y = -1; if (PlayerData.RIController.Computed_X > (int)(dMaxX - 1)) PlayerData.RIController.Computed_X = -1; if (PlayerData.RIController.Computed_Y > (int)(dMaxY - 1)) PlayerData.RIController.Computed_Y = -1; } return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and filtering Triggers input to replace them without blocking other input /// protected override void Apply_InputsMemoryHack() { //NOPing axis proc SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisX_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisX_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisX_3); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisY_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisY_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisY_3); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Xmin_Patch_Offset, BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress +_Float_0_Offset)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Xmax_Patch_Offset, BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress +_Float_640_Offset)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Ymin_Patch_Offset, BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress +_Float_0_Offset)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Ymax_Patch_Offset, BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress +_Float_480_Offset)); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((float)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { if (Configurator.GetInstance().Le3_Pedal_P1_Enabled && !_isPedal1_Pushed && ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameScene_Offset) == 17) //Scene 17 is in-game (not blocking axis in menus) { bufferX = BitConverter.GetBytes(-1.0f); bufferY = BitConverter.GetBytes(-1.0f); } WriteBytes(_BaseData_Address + _P1_X_Offset, bufferX); WriteBytes(_BaseData_Address + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { SetButtonBistable(_P1_Trigger_Offset); WriteByte(_BaseData_Address + _P1_Trigger_Offset - 0x06, 0x01); //this byte show event in TEST mode but not used in game ?? } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_BaseData_Address + _P1_Trigger_Offset - 0x06, 0x00); //this byte show event in TEST mode but not used in game ?? if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { SetButtonBistable(_P1_Option_Offset); WriteByte(_BaseData_Address + _P1_Option_Offset - 0x06, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { WriteByte(_BaseData_Address + _P1_Option_Offset - 0x06, 0x00); } //Offsreen shoot => put the gun off screen. Let side seems to work best if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { if (!Configurator.GetInstance().Le3_Pedal_P1_Enabled) { WriteBytes(_BaseData_Address + _P1_X_Offset, BitConverter.GetBytes(-1.0f)); WriteBytes(_BaseData_Address + _P1_Y_Offset, BitConverter.GetBytes(-1.0f)); } } } else if (PlayerData.ID == 2 && ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameScene_Offset) == 17) { if (Configurator.GetInstance().Le3_Pedal_P2_Enabled && !_isPedal2_Pushed && ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameScene_Offset) == 17) //Scene 17 is in-game (not blocking axis in menus) { bufferX = BitConverter.GetBytes(-1.0f); bufferY = BitConverter.GetBytes(-1.0f); } WriteBytes(_BaseData_Address + _P1_X_Offset, bufferX); WriteBytes(_BaseData_Address + _P1_Y_Offset, bufferY); WriteBytes(_BaseData_Address + _P2_X_Offset, bufferX); WriteBytes(_BaseData_Address + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { SetButtonBistable(_P2_Trigger_Offset); WriteByte(_BaseData_Address + _P2_Trigger_Offset - 0x06, 0x01); //this byte show event in TEST mode but not used in game ?? } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_BaseData_Address + _P2_Trigger_Offset - 0x06, 0x00); //this byte show event in TEST mode but not used in game ?? if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { SetButtonBistable(_P2_Option_Offset); WriteByte(_BaseData_Address + _P2_Option_Offset - 0x06, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { WriteByte(_BaseData_Address + _P2_Option_Offset - 0x06, 0x00); } //Offsreen shoot => put the gun off screen. Let side seems to work best if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { if (!Configurator.GetInstance().Le3_Pedal_P2_Enabled) { WriteBytes(_BaseData_Address + _P2_X_Offset, BitConverter.GetBytes(-1.0f)); WriteBytes(_BaseData_Address + _P2_Y_Offset, BitConverter.GetBytes(-1.0f)); } } } } //On this game a button event changes the byte value (we can do it by cycling between 1/0) private void SetButtonBistable(UInt32 ButtonOffset) { byte ButtonValue = ReadByte(_BaseData_Address + ButtonOffset); WriteByte(_BaseData_Address + ButtonOffset, (byte)(1 - ButtonValue)); } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _P1_Start_Key) { SetButtonBistable(_P1_Start_Offset); WriteByte(_BaseData_Address + _P1_Start_Offset - 0x06, 0x01); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _P2_Start_Key) { SetButtonBistable(_P2_Start_Offset); WriteByte(_BaseData_Address + _P2_Start_Offset - 0x06, 0x01); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _Test_Key) { WriteByte(_BaseData_Address + _TEST_Offset, 0x01); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _Service_Key) { WriteByte(_BaseData_Address + _SERVICE_Offset, 0x01); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _Credits_Key) { if (!_IsCreditsKeyPressed) { WriteByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsToAdd_Ptr_Offset) + 0xE510, 0x01); _IsCreditsKeyPressed = true; } } if (s.scanCode == Configurator.GetInstance().DIK_Le3_Pedal_P1 && Configurator.GetInstance().Le3_Pedal_P1_Enabled) { _isPedal1_Pushed = true; } else if (s.scanCode == Configurator.GetInstance().DIK_Le3_Pedal_P2 && Configurator.GetInstance().Le3_Pedal_P2_Enabled) { _isPedal2_Pushed = true; } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == _P1_Start_Key) { WriteByte(_BaseData_Address + _P1_Start_Offset - 0x06, 0x00); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _P2_Start_Key) { WriteByte(_BaseData_Address + _P2_Start_Offset - 0x06, 0x00); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _Test_Key) { WriteByte(_BaseData_Address + _TEST_Offset, 0x00); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _Service_Key) { WriteByte(_BaseData_Address + _SERVICE_Offset, 0x00); //this byte show event in TEST mode but not used in game ?? } else if (s.scanCode == _Credits_Key) { _IsCreditsKeyPressed = false; } if (s.scanCode == Configurator.GetInstance().DIK_Le3_Pedal_P1 && Configurator.GetInstance().Le3_Pedal_P1_Enabled) { _isPedal1_Pushed = false; } else if (s.scanCode == Configurator.GetInstance().DIK_Le3_Pedal_P2 && Configurator.GetInstance().Le3_Pedal_P2_Enabled) { _isPedal2_Pushed = false; } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHead)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHead)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFoot)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFoot)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, (ReadByte(_BaseData_Address + _Outputs_Offset) >> 1) & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_BaseData_Address + _Outputs_Offset) & 0x01); SetOutputValue(OutputId.P1_LmpHead, (ReadByte(_BaseData_Address + _Outputs_Offset + 1) >> 1) & 0x01); SetOutputValue(OutputId.P2_LmpHead, ReadByte(_BaseData_Address + _Outputs_Offset + 1) & 0x01); SetOutputValue(OutputId.P1_LmpFoot, ReadByte(_BaseData_Address + _Outputs_Offset + 2)); SetOutputValue(OutputId.P2_LmpFoot, ReadByte(_BaseData_Address + _Outputs_Offset + 3)); SetOutputValue(OutputId.P1_LmpFront, ReadByte(_BaseData_Address + _Outputs_Offset + 12)); SetOutputValue(OutputId.P2_LmpFront, ReadByte(_BaseData_Address + _Outputs_Offset + 13)); //Custom Outputs _P1_Ammo = 0; _P2_Ammo = 0; _P1_Life = 0; _P2_Life = 0; int P1_Weapon = 0; int P2_Weapon = 0; //To be sure that outputs are not activated in Attract Mode, we can check the game scene ID and activate them only in gameplay if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameScene_Offset) == 17) { //Now, players data have some information even if they are not playing (game over or not started) if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Player1_Playing) == 1) { UInt32 P1_InfoAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayerInfo_BasePtr_Offset + 0x38C); P1_Weapon = (int)ReadPtrChain(P1_InfoAddress + 0x40, new UInt32[] { 0x15C, 0x24 }); if (P1_Weapon == _P1_LastWeapon) { _P1_Ammo = (int)ReadPtrChain(P1_InfoAddress + 0x40, new UInt32[] { 0x15C, (UInt32)(4 * P1_Weapon), 0xE8 }); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo <= 0) SetOutputValue(OutputId.P1_Clip, 0); else SetOutputValue(OutputId.P1_Clip, 1); } _P1_Life = (int)ReadPtr(P1_InfoAddress + 0x6C); //Custom Dammage if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } else { SetOutputValue(OutputId.P1_Clip, 0); } //Now, players data have some information even if they are not playing (game over or not started) if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Player2_Playing) == 1) { UInt32 P2_InfoAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayerInfo_BasePtr_Offset + 0x390); P2_Weapon = (int)ReadPtrChain(P2_InfoAddress + 0x40, new UInt32[] { 0x15C, 0x24 }); if (P2_Weapon == _P2_LastWeapon) { _P2_Ammo = (int)ReadPtrChain(P2_InfoAddress + 0x40, new UInt32[] { 0x15C, (UInt32)(4 * P1_Weapon), 0xE8 }); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo <= 0) SetOutputValue(OutputId.P2_Clip, 0); else SetOutputValue(OutputId.P2_Clip, 1); } _P2_Life = (int)ReadPtr(P2_InfoAddress + 0x6C); //Custom Dammage if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } else { SetOutputValue(OutputId.P2_Clip, 0); } } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P1_LastWeapon = P1_Weapon; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; _P2_LastWeapon = P2_Weapon; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsTotal_Offset)); } //Seems like there's a memory field to set Lamp to state 0/1/2 and another one [0->0x64] to set analog intensity private byte GetLampAnalogValue(UInt32 OutputLampAddress) { if (ReadByte(OutputLampAddress) != 0) { return (ReadByte(OutputLampAddress - 0x30)); } else return 0; } #endregion } } ================================================ FILE: DemulShooter/Games/Game_KonamiWartran.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; namespace DemulShooter { class Game_KonamiWartran : Game { //Outputs variable private UInt32 _OutputLamps_Offset = 0x0149E37C; private UInt32 _P1_WeaponStruct_Offset = 0x01475A90; private UInt32 _P2_WeaponStruct_Offset = 0x01475AB4; private UInt32 _P3_WeaponStruct_Offset = 0x01475AD8; private UInt32 _P4_WeaponStruct_Offset = 0x01475AFC; private UInt32 _P1_Life_Offset = 0x0130600C; private UInt32 _P2_Life_Offset = 0x01306208; private UInt32 _P3_Life_Offset = 0x01306404; private UInt32 _P4_Life_Offset = 0x01306600; private UInt32 _Credits_Offset = 0x001AC55C; // private byte _P1_LastWeaponType = 0; private byte _P2_LastWeaponType = 0; private byte _P3_LastWeaponType; private byte _P4_LastWeaponType; private byte _bP1_LastAmmo; private byte _bP2_LastAmmo; private byte _bP3_LastAmmo; private byte _bP4_LastAmmo; private byte _bP1_LastLife; private byte _bP2_LastLife; private byte _bP3_LastLife; private byte _bP4_LastLife; /// /// Constructor /// public Game_KonamiWartran(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Wartran Original dump", "4c987ea93e8dbd5a6aa624e504a4706c"); _tProcess.Start(); Logger.WriteLog("Waiting for Konami " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); /*if (!_DisableInputHack) SetHack(); else Logger.WriteLog("Input Hack disabled");*/ _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P3_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P4_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P3_Ammo)); _Outputs.Add(new GameOutput(OutputId.P4_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new GameOutput(OutputId.P3_Clip)); _Outputs.Add(new GameOutput(OutputId.P4_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _OutputLamps_Offset) >> 3 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _OutputLamps_Offset) >> 2 & 0x01); SetOutputValue(OutputId.P3_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _OutputLamps_Offset) >> 1 & 0x01); SetOutputValue(OutputId.P4_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _OutputLamps_Offset) & 0x01); byte P1_WeaponType = 0; byte P2_WeaponType = 0; byte P3_WeaponType = 0; byte P4_WeaponType = 0; byte P1_Life = 0; byte P2_Life = 0; byte P3_Life = 0; byte P4_Life = 0; byte P1_Ammo = 0; byte P2_Ammo = 0; byte P3_Ammo = 0; byte P4_Ammo = 0; //Player1 P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); SetOutputValue(OutputId.P1_Life, P1_Life); if (P1_Life < _bP1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); if (P1_Life > 0) { //there are a lot of weapons in the game, so we need to check if the type has changed before counting the ammo decrease P1_WeaponType = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponStruct_Offset + 0x0E); P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponStruct_Offset + 0x10 + P1_WeaponType); if (P1_WeaponType == _P1_LastWeaponType) { if (P1_Ammo < _bP1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); if (P1_Ammo > 0) SetOutputValue(OutputId.P1_Clip, 1); } } SetOutputValue(OutputId.P1_Ammo, P1_Ammo); //Player2 P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset); SetOutputValue(OutputId.P2_Life, P2_Life); if (P2_Life < _bP2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); if (P2_Life > 0) { //there are a lot of weapons in the game, so we need to check if the type has changed before counting the ammo decrease P2_WeaponType = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponStruct_Offset + 0x0E); P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponStruct_Offset + 0x10 + P2_WeaponType); if (P2_WeaponType == _P2_LastWeaponType) { if (P2_Ammo < _bP2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); if (P2_Ammo > 0) SetOutputValue(OutputId.P2_Clip, 1); } } SetOutputValue(OutputId.P2_Ammo, P2_Ammo); //Player3 P3_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P3_Life_Offset); SetOutputValue(OutputId.P3_Life, P3_Life); if (P3_Life < _bP3_LastLife) SetOutputValue(OutputId.P3_Damaged, 1); if (P3_Life > 0) { //there are a lot of weapons in the game, so we need to check if the type has changed before counting the ammo decrease P3_WeaponType = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P3_WeaponStruct_Offset + 0x0E); P3_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P3_WeaponStruct_Offset + 0x10 + P3_WeaponType); if (P3_WeaponType == _P3_LastWeaponType) { if (P3_Ammo < _bP3_LastAmmo) SetOutputValue(OutputId.P3_CtmRecoil, 1); if (P3_Ammo > 0) SetOutputValue(OutputId.P3_Clip, 1); } } SetOutputValue(OutputId.P3_Ammo, P3_Ammo); //Player4 P4_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P4_Life_Offset); SetOutputValue(OutputId.P4_Life, P4_Life); if (P4_Life < _bP4_LastLife) SetOutputValue(OutputId.P4_Damaged, 1); if (P4_Life > 0) { //there are a lot of weapons in the game, so we need to check if the type has changed before counting the ammo decrease P4_WeaponType = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P4_WeaponStruct_Offset + 0x0E); P4_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P4_WeaponStruct_Offset + 0x10 + P4_WeaponType); if (P4_WeaponType == _P4_LastWeaponType) { if (P4_Ammo < _bP4_LastAmmo) SetOutputValue(OutputId.P4_CtmRecoil, 1); if (P4_Ammo > 0) SetOutputValue(OutputId.P4_Clip, 1); } } SetOutputValue(OutputId.P4_Ammo, P4_Ammo); _bP1_LastAmmo = P1_Ammo; _bP2_LastAmmo = P2_Ammo; _bP3_LastAmmo = P3_Ammo; _bP4_LastAmmo = P4_Ammo; _bP1_LastLife = P1_Life; _bP2_LastLife = P2_Life; _bP3_LastLife = P3_Life; _bP4_LastLife = P4_Life; _P1_LastWeaponType = P1_WeaponType; _P2_LastWeaponType = P2_WeaponType; _P3_LastWeaponType = P3_WeaponType; _P4_LastWeaponType = P4_WeaponType; SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Lindbergh2spicy.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_Lindbergh2spicy : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\2spicy"; //Inputs private InjectionStruct _JvsRawAxes_InjectionStruct = new InjectionStruct(0x0831F80D, 7); //private InjectionStruct _AdjustedAxes_InjectionStruct = new InjectionStruct(????, 6); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x0831F5CC, 7); //Outputs private UInt32 _OutputsPtr_Address = 0x0A89F944; private UInt32 _Credits_Address = 0x0C8C0240; private UInt32 _PlayerStructPtr_Address = 0x0867B10C; private UInt32 _AmmoPtr_Address = 0x0888F8F8; //Custom Data private UInt32 _JvsRawAxes_CaveAddress; private UInt32 _AdjustedAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; //Check instruction for game loaded private UInt32 _RomLoaded_Check_Address = 0x082EFC63; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_Lindbergh2spicy(String RomName) : base(RomName, "LinuxLoader") { _KnownMd5Prints.Add("2 Spicy (SBMV)", "b2183415493f9901dd45197364d51ebb"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("TeknoBudgie")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0xB8, 0x30, 0x24 })) { Logger.WriteLog("2 Spicy (SBMV) binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["2 Spicy (SBMV)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0x0A - 0xF5] //Y => [0x06 - 0xFA] double dMaxX = 236.0; double dMaxY = 245.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX) + 0x0A); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY) + 0x06); if (PlayerData.RIController.Computed_X < 10) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 6) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > 245) PlayerData.RIController.Computed_X = 255; if (PlayerData.RIController.Computed_Y > 250) PlayerData.RIController.Computed_Y = 255; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _JvsRawAxes_CaveAddress = _InputsDatabank_Address; _AdjustedAxes_CaveAddress = _InputsDatabank_Address + 0x10; _Buttons_CaveAddress = _InputsDatabank_Address + 0x20; SetHack_JvsRawAxes(); //This one is different and does not have the usual lindbergh shooter function //SetHack_AdjustedAxes(); SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_JvsRawAxes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea ecx,[ebx-9] CaveMemory.Write_StrBytes("8D 4B F7"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea ebx,[ebx+esi+00000102] CaveMemory.Write_StrBytes("8D 9C 33 02 01 00 00"); //mov [ebx],cx CaveMemory.Write_StrBytes("66 89 0B"); //Inject it CaveMemory.InjectToAddress(_JvsRawAxes_InjectionStruct, "Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp esi,05 CaveMemory.Write_StrBytes("83 FE 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+esi+00000102],FC CaveMemory.Write_StrBytes("80 A4 32 02 01 00 00 FC"); //and byte ptr [edx+esi+00000103],7F CaveMemory.Write_StrBytes("80 A4 32 03 01 00 00 7F"); //movzx ecx,word ptr [esi+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8E"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 03 01 00 00"); //originalcode: //lea eax,[edx+esi+00000102] CaveMemory.Write_StrBytes("8D 84 32 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } #endregion #region Inputs public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteByte(_JvsRawAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_Y); WriteByte(_AdjustedAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_AdjustedAxes_CaveAddress + 1, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if ((byte)PlayerData.RIController.Computed_X > 0x7F) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x04); else Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x08); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xF3); //Remove both PEDAL bits if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFE); } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_LEFT) { Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x04); } else if (s.scanCode == HardwareScanCode.DIK_RIGHT) { Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x08); } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == HardwareScanCode.DIK_LEFT) { Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFB); } else if (s.scanCode == HardwareScanCode.DIK_RIGHT) { Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xF7); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun recoil : Is activated for every bullet shot _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpPanel)); _Outputs.Add(new GameOutput(OutputId.Lmp1)); _Outputs.Add(new GameOutput(OutputId.Lmp2)); _Outputs.Add(new GameOutput(OutputId.Lmp3)); _Outputs.Add(new GameOutput(OutputId.Lmp4)); _Outputs.Add(new GameOutput(OutputId.Lmp5)); _Outputs.Add(new GameOutput(OutputId.Lmp6)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs UInt32 Outputs_Address = BitConverter.ToUInt32(ReadBytes(_OutputsPtr_Address, 4), 0); int RecoilStatus = ReadByte(Outputs_Address) >> 6 & 0x01; SetOutputValue(OutputId.P1_LmpStart, ReadByte(Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.LmpPanel, ReadByte(Outputs_Address + 1) >> 7 & 0x01); SetOutputValue(OutputId.Lmp1, ReadByte(Outputs_Address + 1) >> 1 & 0x01); SetOutputValue(OutputId.Lmp2, ReadByte(Outputs_Address + 1) >> 2 & 0x01); SetOutputValue(OutputId.Lmp3, ReadByte(Outputs_Address + 1) >> 3 & 0x01); SetOutputValue(OutputId.Lmp4, ReadByte(Outputs_Address + 1) >> 4 & 0x01); SetOutputValue(OutputId.Lmp5, ReadByte(Outputs_Address + 1) >> 5 & 0x01); SetOutputValue(OutputId.Lmp6, ReadByte(Outputs_Address + 1) >> 6 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, RecoilStatus); //Custom Outputs _P1_Life = 0; _P1_Ammo = 0; int P1_Clip = 0; //Filter InGame and not in attract Demo if (ReadByte(ReadPtr(_PlayerStructPtr_Address) + 0x27) == 1 && ReadByte(ReadPtr(_PlayerStructPtr_Address) + 0x2D) == 1) { _P1_Life = ReadByte(ReadPtr(_PlayerStructPtr_Address) + 0x78); _P1_Ammo = ReadByte(ReadPtr(_AmmoPtr_Address) + 0x04); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; } _P1_LastLife = _P1_Life; _P1_LastAmmo = _P1_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); //Custom Recoil will be ctivated just ike the original one SetOutputValue(OutputId.P1_CtmRecoil, RecoilStatus); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghGsquadEvo.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_LindberghGsquadEvo : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\ghostsev"; //@80e8780 -> copy source in multiple places. desactivating copy stops input to be updated... //Inputs private UInt32 _ComputedAxes_BaseAddress = 0x086553AC; //-> store float [-1;1] in +C8, +CC, +D0,+D4, but still offset from calibration private NopStruct _Nop_ComputedAxes = new NopStruct(0x80E86CA, 9); private InjectionStruct _JvsRawAxes_InjectionStruct = new InjectionStruct(0x08185951, 7); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x08185710, 7); //MEMORY ADDRESSES //private UInt32 _GameMode_Address = 0x08660420; private UInt32 _P1_GameState_Address = 0x086617E8; private UInt32 _P2_GameState_Address = 0x8661994; private UInt32 _P1_LifePtr_Address = 0x086618D4; private UInt32 _P2_LifePtr_Address = 0x08661A80; private UInt32 _Outputs_Address = 0x08656330; private UInt32 _Credits_Address = 0x00AE9FBA0; private InjectionStruct _PlayerDamage_Injection = new InjectionStruct(0x810C9D9, 6); //Custom Data private UInt32 _JvsRawAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; private UInt32 _Damage_CaveAddress; private UInt32 _RomLoaded_Check_Address = 0x0807C9A0; /// /// Constructor /// public Game_LindberghGsquadEvo(String RomName) : base(RomName, "linuxloader") { _KnownMd5Prints.Add("Ghost Squad Evolution (SBNJ) (Rev.A)", "bf8fcf04d6d90f9f5dd33ca76f1b670a"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("TeknoBudgie") && !FindGameWindow_Contains("FREEGLUT")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0xA1, 0x90, 0x05 })) { Logger.WriteLog("Ghost Squad Evolution (Rev .A) binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Ghost Squad Evolution (SBNJ) (Rev.A)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y axis => 0x00 - 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _JvsRawAxes_CaveAddress = _InputsDatabank_Address; _Buttons_CaveAddress = _InputsDatabank_Address + 0x10; SetHack_Axes(); SetHack_Buttons(); //Removing post-calibration storage of float data //SetNops(0, _Nop_ComputedAxes); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_Axes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea ecx,[ebx-B] CaveMemory.Write_StrBytes("8D 4B F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea ebx,[ebx+esi+00000102] CaveMemory.Write_StrBytes("8D 9C 33 02 01 00 00"); //mov [ebx],cx CaveMemory.Write_StrBytes("66 89 0B"); //Inject it CaveMemory.InjectToAddress(_JvsRawAxes_InjectionStruct, "Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp esi,05 CaveMemory.Write_StrBytes("83 FE 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+esi+00000102],FC CaveMemory.Write_StrBytes("80 A4 32 02 01 00 00 FC"); //and byte ptr [edx+esi+00000103],7F CaveMemory.Write_StrBytes("80 A4 32 03 01 00 00 7F"); //movzx ecx,word ptr [esi+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8E"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 03 01 00 00"); //originalcode: //lea eax,[edx+esi+00000102] CaveMemory.Write_StrBytes("8D 84 32 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } #region Outputs Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _Damage_CaveAddress = _OutputsDatabank_Address; SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } //Intercept a call to set_player_damage_internal() function to get dammage event //Player Index is in edx private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[ebp+08] CaveMemory.Write_StrBytes("8B 45 08"); //add eax,_Damage_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Damage_CaveAddress)); //mov byte ptr [eax],01 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //mov [ebx+000000B8],edx CaveMemory.Write_StrBytes("89 93 B8 00 00 00"); //Inject it it CaveMemory.InjectToAddress(_PlayerDamage_Injection, "Damage"); } #endregion #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { float X_Value = (2.0f * (float)PlayerData.RIController.Computed_X / 255.0f) - 1.0f; float Y_Value = (2.0f * (float)PlayerData.RIController.Computed_Y / 255.0f) - 1.0f; if (PlayerData.ID == 1) { WriteByte(_JvsRawAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_Y); //WriteBytes(_ComputedAxes_BaseAddress + 0xC8, BitConverter.GetBytes(X_Value)); //WriteBytes(_ComputedAxes_BaseAddress + 0xCC, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x80); //ACTION Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x40); //CHANGE WEAPON } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0x7F); Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0xBF); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFE); } else if (PlayerData.ID == 2) { WriteByte(_JvsRawAxes_CaveAddress + 0x04, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x06, (byte)PlayerData.RIController.Computed_Y); // WriteBytes(_ComputedAxes_BaseAddress + 0xD0, BitConverter.GetBytes(X_Value)); //WriteBytes(_ComputedAxes_BaseAddress + 0xD4, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 9, 0x80); //ACTION Apply_OR_ByteMask(_Buttons_CaveAddress + 9, 0x40); //CHANGE WEAPON } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 9, 0x7F); Apply_AND_ByteMask(_Buttons_CaveAddress + 9, 0xBF); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFE); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Outputs_Address) >> 4 & 0x01); SetOutputValue(OutputId.P1_LmpHolder, ReadByte(_Outputs_Address) >> 5 & 0x01); SetOutputValue(OutputId.P2_LmpHolder, ReadByte(_Outputs_Address) >> 2 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, ReadByte(_Outputs_Address) >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, ReadByte(_Outputs_Address) >> 3 & 0x01); //Custom Outputs _P1_Life = 0; _P2_Life = 0; // Checking the Player state to not trigger Damage event during Attract mode: // 0x00 = Not playing // 0x02 = In-Game // 0x08 = Continue // 0x10 = Game-Over // 0x80 = Attract Mode if (ReadByte(_P1_GameState_Address) == 2) { _P1_Life = ReadByte(ReadPtr(_P1_LifePtr_Address) + 0xB8); //P1_Ammo = ReadByte(P1_StructAddress + 0x400); //[Damaged] custom Output if (ReadByte(_Damage_CaveAddress) == 1) SetOutputValue(OutputId.P1_Damaged, 1); } WriteByte(_Damage_CaveAddress, 0); // Checking the Player state to not trigger Damage event during Attract mode: // 0x00 = Not playing // 0x02 = In-Game // 0x08 = Continue // 0x10 = Game-Over // 0x80 = Attract Mode if (ReadByte(_P2_GameState_Address) == 2) { _P2_Life = ReadByte(ReadPtr(_P2_LifePtr_Address) + 0xB8); //P1_Ammo = ReadByte(P1_StructAddress + 0x400); //[Damaged] custom Output if (ReadByte(_Damage_CaveAddress + 1) == 1) SetOutputValue(OutputId.P2_Damaged, 1); } WriteByte(_Damage_CaveAddress + 1, 0); //Custom recoil will be recoil just like the original one SetOutputValue(OutputId.P1_CtmRecoil, ReadByte(_Outputs_Address) >> 6 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, ReadByte(_Outputs_Address) >> 3 & 0x01); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, (int)(ReadByte(_Credits_Address))); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghHotd4.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_LindberghHotd4 : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\hotd4"; //Inputs private InjectionStruct _JvsRawAxes_InjectionStruct = new InjectionStruct(0x0831E5A8, 7); private InjectionStruct _AdjustedAxes_InjectionStruct = new InjectionStruct(0x81538D8, 6); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x0831E3C1, 7); //Outputs Address private UInt32 _JvsMgrPtr_Address = 0x0A6F2754; private UInt32 _Credits_Address = 0x0A715CC0; private UInt32 _GunMgrPtr_Address = 0x0A6F27A8; private UInt32 _PlayerMgrPtr_Address = 0x0A6F27F8; //Custom Data private UInt32 _JvsRawAxes_CaveAddress; private UInt32 _AdjustedAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; //Rom loaded + Rom version check private UInt32 _RomLoaded_Check_Address = 0x08373BD8; /// /// Constructor /// public Game_LindberghHotd4(String RomName) : base(RomName, "linuxloader") { _KnownMd5Prints.Add("House of The Dead 4 (SBLC) (Rev.A)", "afc1c946193c2533eb131a259c9cdb66"); _KnownMd5Prints.Add("House of The Dead 4 (SBLC) (Rev.B)", "619511471598b5a1e89f3976c76ec83b"); _KnownMd5Prints.Add("House of The Dead 4 (SBLC) (Rev.C)", "036408020B362255455B84028618352B"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("TeknoBudgie")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0x04, 0xDE, 0xC1 })) { Logger.WriteLog("House Of The Dead 4 - Rev. A binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["House of The Dead 4 (SBLC) (Rev.A)"]; } else if (buffer.SequenceEqual(new byte[] { 0x11, 0x0F, 0x57 })) { Logger.WriteLog("House Of The Dead 4 - Rev. B binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["House of The Dead 4 (SBLC) (Rev.B)"]; } else if (buffer.SequenceEqual(new byte[] { 0x83, 0xEC, 0x14 })) { Logger.WriteLog("House Of The Dead 4 - Rev. C binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["House of The Dead 4 (SBLC) (Rev.C)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y axis => 0x00 - 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _JvsRawAxes_CaveAddress = _InputsDatabank_Address; _AdjustedAxes_CaveAddress = _InputsDatabank_Address + 0x10; _Buttons_CaveAddress = _InputsDatabank_Address + 0x20; SetHack_Axes(); SetHack_AdjustedAxes(); SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_Axes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); if (_TargetProcess_Md5Hash.Equals(_KnownMd5Prints["House of The Dead 4 (SBLC) (Rev.A)"])) { //lea ecx,[eax-B] CaveMemory.Write_StrBytes("8D 48 F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea eax,[eax+edx+00000102] CaveMemory.Write_StrBytes("8D 84 10 02 01 00 00"); //mov [eax],cx CaveMemory.Write_StrBytes("66 89 08"); } else { //lea ecx,[eax-B] CaveMemory.Write_StrBytes("8D 48 F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea eax,[eax+esi+00000102] CaveMemory.Write_StrBytes("8D 84 30 02 01 00 00"); //mov [eax],cx CaveMemory.Write_StrBytes("66 89 08"); } //Inject it CaveMemory.InjectToAddress(_JvsRawAxes_InjectionStruct, "Axes"); } /// /// Replacing GetAdjstGunPos() content /// private void SetHack_AdjustedAxes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ebx,eax CaveMemory.Write_StrBytes("8B D8"); //mov eax,[esp+18] CaveMemory.Write_StrBytes("8B 44 24 18"); //shl eax,1 CaveMemory.Write_StrBytes("D1 E0"); //movzx eax,byte ptr [eax+_Buttons_CaveAddress + 6] CaveMemory.Write_StrBytes("0F B6 80"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress + 6)); //and al,1 CaveMemory.Write_StrBytes("24 01"); //test al,al CaveMemory.Write_StrBytes("84 C0"); //je OnScreen CaveMemory.Write_StrBytes("74 0F"); //mov [ebx],000000FF CaveMemory.Write_StrBytes("C7 03 FF 00 00 00"); //mov [edx],000000FF CaveMemory.Write_StrBytes("C7 02 FF 00 00 00"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //ret CaveMemory.Write_StrBytes("C3"); //OnScreen: //mov eax,[esp+18] CaveMemory.Write_StrBytes("8B 44 24 18"); //shl eax,1 CaveMemory.Write_StrBytes("D1 E0"); //add eax,_AdjustedAxes_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_AdjustedAxes_CaveAddress)); //movzx eax,word ptr [eax] CaveMemory.Write_StrBytes("0F B7 00"); //mov [ebx],00000000 CaveMemory.Write_StrBytes("C7 03 00 00 00 00"); //mov [edx],00000000 CaveMemory.Write_StrBytes("C7 02 00 00 00 00"); //mov [ebx],al CaveMemory.Write_StrBytes("88 03"); //shr eax, 8 CaveMemory.Write_StrBytes("C1 E8 08"); //mov [edx],al CaveMemory.Write_StrBytes("88 02"); //mov eax,00000001 CaveMemory.Write_StrBytes("B8 01 00 00 00"); //ret CaveMemory.Write_StrBytes("C3"); //Inject it CaveMemory.InjectToAddress(_AdjustedAxes_InjectionStruct, "Adjusted Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp ebx,05 CaveMemory.Write_StrBytes("83 FB 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+ebx+00000102], CaveMemory.Write_StrBytes("80 A4 1A 02 01 00 00 FC"); //and byte ptr [edx+ebx+00000103],7F CaveMemory.Write_StrBytes("80 A4 1A 03 01 00 00 7F"); //movzx ecx,word ptr [ebx+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8B"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+ebx+00000102],cl CaveMemory.Write_StrBytes("08 8C 1A 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+ebx+00000103],cl CaveMemory.Write_StrBytes("08 8C 1A 03 01 00 00"); //originalcode: //lea eax,[edx+ebx+00000102] CaveMemory.Write_StrBytes("8D 84 1A 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { //First channels of analog inputs in JVS are RawPosition (0x00-0xFF) for each players (Bytes 0-7) WriteByte(_JvsRawAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_Y); //Nexts channels (Bytes 8-15) have gun acceleration analog data. Default is 0x8000 when not moving, then goes up or down according to the acceleration direction for each axis //Not setting these will stop gun shaking from moving //For now on, shaking will be activated by shooting of screen until I find a way to perperly compute it WriteBytes(_JvsRawAxes_CaveAddress + 0x08, new byte[] { 0x80, 0x00}); WriteBytes(_JvsRawAxes_CaveAddress + 0x0A, new byte[] { 0x80, 0x00 }); WriteByte(_AdjustedAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_AdjustedAxes_CaveAddress + 1, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteByte(_JvsRawAxes_CaveAddress + 0x08, 0x00); Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte(_JvsRawAxes_CaveAddress + 0x08, 0x80); Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFE); } } else if (PlayerData.ID == 2) { WriteByte(_JvsRawAxes_CaveAddress + 0x04, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x06, (byte)PlayerData.RIController.Computed_Y); WriteBytes(_JvsRawAxes_CaveAddress + 0x0C, new byte[] { 0x80, 0x00 }); WriteBytes(_JvsRawAxes_CaveAddress + 0x0E, new byte[] { 0x80, 0x00 }); WriteByte(_AdjustedAxes_CaveAddress + 2, (byte)PlayerData.RIController.Computed_X); WriteByte(_AdjustedAxes_CaveAddress + 3, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 9, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 9, 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteByte(_JvsRawAxes_CaveAddress + 0x0C, 0x00); Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte(_JvsRawAxes_CaveAddress + 0x0C, 0x80); Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFE); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs UInt32 Outputs_Address = BitConverter.ToUInt32(ReadBytes(_JvsMgrPtr_Address, 4), 0); int P1_Motor_Status = ReadByte(Outputs_Address) >> 6 & 0x01; int P2_Motor_Status = ReadByte(Outputs_Address) >> 3 & 0x01; SetOutputValue(OutputId.P1_LmpStart, ReadByte(Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(Outputs_Address) >> 4 & 0x01); SetOutputValue(OutputId.P1_GunMotor, P1_Motor_Status); SetOutputValue(OutputId.P2_GunMotor, P2_Motor_Status); //Custom Outputs UInt32 GameModePtr = BitConverter.ToUInt32(ReadBytes(ReadPtr(_GunMgrPtr_Address) + 0x2C, 4), 0); int GameMode = ReadByte(GameModePtr + 0x38); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (GameMode == 8) { UInt32 PlayersPtr_BaseAddress = ReadPtr(_PlayerMgrPtr_Address); if (PlayersPtr_BaseAddress != 0) { UInt32 P1_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x34); UInt32 P2_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x38); //PlayerMode: //4: CutScene //3: InGame //9, 11: Continue //0: GameOver int P1_Mode = ReadByte(P1_StructAddress + 0x38); int P2_Mode = ReadByte(P2_StructAddress + 0x38); if (P1_Mode == 3 || P1_Mode == 4) { _P1_Life = ReadByte(P1_StructAddress + 0x3C); _P1_Ammo = ReadByte(P1_StructAddress + 0x274); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Recoil] custom output : //Generate new event for each bullet fired, only while original motor event is ON //(this way, no recoil during attract mode) if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); } if (P2_Mode == 3 || P2_Mode == 4) { _P2_Life = ReadByte(P2_StructAddress + 0x3C); _P2_Ammo = ReadByte(P2_StructAddress + 0x274); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); //[Clip] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Recoil] custom output : //Generate new event for each bullet fired, only while original motor event is ON //(this way, no recoil during attract mode) if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); } } } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghHotd4Ex.cs ================================================ using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; using System; using System.Collections.Generic; using System.Diagnostics; namespace DemulShooter { class Game_LindberghHotd4Ex : Game { //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction = 0x0815C65A; //Intruction writing Lamps //Outputs private UInt32 _OutputsPtr_Address = 0x0A9DFEBC; private UInt32 _Outputs_Address; private UInt32 _GameMgr_Pointer_Address = 0xA9DFE98; private UInt32 _Credits_Address = 0xAA3DD80; private UInt32 _Freeplay_Address = 0xAA3DD67; private int _P1_Last_Life = 0; private int _P2_Last_Life = 0; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_LindberghHotd4Ex(String RomName, bool DisableInputHack, bool Verbose) : base(RomName, "BudgieLoader", DisableInputHack, Verbose) { _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting byte[] buffer = ReadBytes(_RomLoaded_check_Instruction, 5); if (buffer[0] == 0x8B && buffer[1] == 0x15 && buffer[2] == 0xBC && buffer[3] == 0xFE && buffer[4] == 0x9D) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game not Loaded, waiting..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputDesciption.P1_LmpStart, OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputDesciption.P2_LmpStart, OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputDesciption.LmpPanel, OutputId.LmpPanel)); _Outputs.Add(new GameOutput(OutputDesciption.Lmp1, OutputId.Lmp1)); _Outputs.Add(new GameOutput(OutputDesciption.Lmp2, OutputId.Lmp2)); _Outputs.Add(new GameOutput(OutputDesciption.Lmp3, OutputId.Lmp3)); _Outputs.Add(new GameOutput(OutputDesciption.Lmp4, OutputId.Lmp4)); _Outputs.Add(new GameOutput(OutputDesciption.Lmp5, OutputId.Lmp5)); _Outputs.Add(new GameOutput(OutputDesciption.Lmp6, OutputId.Lmp6)); _Outputs.Add(new GameOutput(OutputDesciption.P1_LmpFoot, OutputId.P1_LmpFoot)); _Outputs.Add(new GameOutput(OutputDesciption.P2_LmpFoot, OutputId.P2_LmpFoot)); _Outputs.Add(new GameOutput(OutputDesciption.P1_Life, OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputDesciption.P2_Life, OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputDesciption.P1_Damaged, OutputId.P1_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputDesciption.P2_Damaged, OutputId.P2_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0)); /* _Outputs.Add(new AsyncGameOutput(OutputDesciption.P1_CtmRecoil, OutputId.P1_CtmRecoil, MameOutputHelper.CustomRecoilOnDelay, MameOutputHelper.CustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputDesciption.P2_CtmRecoil, OutputId.P2_CtmRecoil, MameOutputHelper.CustomRecoilOnDelay, MameOutputHelper.CustomRecoilOffDelay, 0)); */ _Outputs.Add(new GameOutput(OutputDesciption.Credits, OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs _Outputs_Address = BitConverter.ToUInt32(ReadBytes(_OutputsPtr_Address, 4), 0); SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Outputs_Address) >> 4 & 0x01); SetOutputValue(OutputId.LmpPanel, ReadByte(_Outputs_Address + 1) >> 7 & 0x01); SetOutputValue(OutputId.Lmp1, ReadByte(_Outputs_Address + 1) >> 6 & 0x01); SetOutputValue(OutputId.Lmp2, ReadByte(_Outputs_Address + 1) >> 5 & 0x01); SetOutputValue(OutputId.Lmp3, ReadByte(_Outputs_Address + 1) >> 4 & 0x01); SetOutputValue(OutputId.Lmp4, ReadByte(_Outputs_Address + 1) >> 3 & 0x01); SetOutputValue(OutputId.Lmp5, ReadByte(_Outputs_Address + 1) >> 2 & 0x01); SetOutputValue(OutputId.Lmp6, ReadByte(_Outputs_Address + 1) >> 1 & 0x01); SetOutputValue(OutputId.P1_LmpFoot, ReadByte(_Outputs_Address) >> 5 & 0x01); SetOutputValue(OutputId.P2_LmpFoot, ReadByte(_Outputs_Address) >> 4 & 0x01); //Custom Outputs int P1_Life = 0; int P2_Life = 0; UInt32 GameManager_Address = ReadPtr(_GameMgr_Pointer_Address); if (GameManager_Address != 0) { P1_Life = ReadByte(GameManager_Address + 0x48); if (P1_Life < _P1_Last_Life) SetOutputValue(OutputId.P1_Damaged, 1); P2_Life = ReadByte(GameManager_Address + 0x4C); if (P2_Life < _P2_Last_Life) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_Last_Life = P1_Life; _P2_Last_Life = P2_Life; /*_P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (ReadPtr(_GameInfos_Address) != 0) { _Life = ReadByte(ReadPtr(_GameInfos_Address) + 0x40); //[Damaged] custom Output if (_Life < _LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_Ammo = ReadByte(ReadPtrChain(_GameInfos_Address, new UInt32[] { 0x34 }) + 0x2D4); //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //No attract mode so we can just reuse the original recoil flag SetOutputValue(OutputId.P1_CtmRecoil, P1_Motor_Status); _P2_Ammo = ReadByte(ReadPtrChain(_GameInfos_Address, new UInt32[] { 0x38 }) + 0x2D4); //[Clip] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //No attract mode so we can just reuse the original recoil flag SetOutputValue(OutputId.P2_CtmRecoil, P2_Motor_Status); } _LastLife = _Life; _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _Life);*/ SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Displaying Credits if no FREEPLAY enabled if (ReadByte(_Freeplay_Address) == 0) SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); else SetOutputValue(OutputId.Credits, 0); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghHotd4Sp.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_LindberghHotd4Sp : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\hotd4sp"; //Inputs private InjectionStruct _Axes_InjectionStruct = new InjectionStruct(0x08366CDC, 7); private InjectionStruct _AdjustedAxes_InjectionStruct = new InjectionStruct(0x08179DAC, 6); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x08366AF5, 7); //Outputs Address private UInt32 _Outputs_Address = 0x084C35CB; private UInt32 _Outputs2_Address = 0x0A69F8BC; private UInt32 _GameInfos_Address = 0x0A69F92C; private UInt32 _Credits_Address = 0x0A6AEFA4; //also 0x0A6C2E40 //Custom Data private UInt32 _JvsRawAxes_CaveAddress; private UInt32 _AdjustedAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; //Rom loaded + Rom version check private UInt32 _RomLoaded_Check_Address = 0x080505A2; private int _LastLife = 0; private int _Life = 0; /// /// Constructor /// public Game_LindberghHotd4Sp(String RomName) : base(RomName, "linuxloader") { _KnownMd5Prints.Add("House of The Dead 4 Special (SBLS) (Rev.B)", "74baffc7429973f3df8902a31c5164a0"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("TeknoBudgie")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0x0D, 0x80, 0x00 })) { Logger.WriteLog("House Of The Dead 4 Special - Rev. B binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["House of The Dead 4 Special (SBLS) (Rev.B)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y axis => 0x00 - 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _JvsRawAxes_CaveAddress = _InputsDatabank_Address; _AdjustedAxes_CaveAddress = _InputsDatabank_Address + 0x10; _Buttons_CaveAddress = _InputsDatabank_Address + 0x20; SetHack_Axes(); //Hacking into the GetAdjustPos() disable gun firing or misplace axis somewhere ? //For now the default calibration is good enough to not use this, and the game still has bugs....be back at it later //SetHack_AdjustedAxes(); SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_Axes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea ecx,[eax-B] CaveMemory.Write_StrBytes("8D 48 F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea eax,[eax+esi+00000102] CaveMemory.Write_StrBytes("8D 84 30 02 01 00 00"); //mov [eax],cx CaveMemory.Write_StrBytes("66 89 08"); //Inject it CaveMemory.InjectToAddress(_Axes_InjectionStruct, "Axes"); } /// /// Replacing GetAdjstGunPos() content /// private void SetHack_AdjustedAxes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ebx,eax CaveMemory.Write_StrBytes("8B D8"); //mov eax,[esp+18] CaveMemory.Write_StrBytes("8B 44 24 18"); //shl eax,1 CaveMemory.Write_StrBytes("D1 E0"); //movzx eax,byte ptr [eax+_Buttons_CaveAddress + 6] CaveMemory.Write_StrBytes("0F B6 80"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress + 6)); //and al,1 CaveMemory.Write_StrBytes("24 01"); //test al,al CaveMemory.Write_StrBytes("84 C0"); //je OnScreen CaveMemory.Write_StrBytes("74 0F"); //mov [ebx],000000FF CaveMemory.Write_StrBytes("C7 03 FF 00 00 00"); //mov [edx],000000FF CaveMemory.Write_StrBytes("C7 02 FF 00 00 00"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //ret CaveMemory.Write_StrBytes("C3"); //OnScreen: //mov eax,[esp+18] CaveMemory.Write_StrBytes("8B 44 24 18"); //shl eax,1 CaveMemory.Write_StrBytes("D1 E0"); //add eax,_AdjustedAxes_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_AdjustedAxes_CaveAddress)); //movzx eax,word ptr [eax] CaveMemory.Write_StrBytes("0F B7 00"); //mov [ebx],00000000 CaveMemory.Write_StrBytes("C7 03 00 00 00 00"); //mov [edx],00000000 CaveMemory.Write_StrBytes("C7 02 00 00 00 00"); //mov [ebx],al CaveMemory.Write_StrBytes("88 03"); //shr eax, 8 CaveMemory.Write_StrBytes("C1 E8 08"); //mov [edx],al CaveMemory.Write_StrBytes("88 02"); //mov eax,00000001 CaveMemory.Write_StrBytes("B8 01 00 00 00"); //ret CaveMemory.Write_StrBytes("C3"); //Inject it CaveMemory.InjectToAddress(_AdjustedAxes_InjectionStruct, "Adjusted Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp ebx,05 CaveMemory.Write_StrBytes("83 FB 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+ebx+00000102], CaveMemory.Write_StrBytes("80 A4 1A 02 01 00 00 FC"); //and byte ptr [edx+ebx+00000103],7F CaveMemory.Write_StrBytes("80 A4 1A 03 01 00 00 7F"); //movzx ecx,word ptr [ebx+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8B"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+ebx+00000102],cl CaveMemory.Write_StrBytes("08 8C 1A 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+ebx+00000103],cl CaveMemory.Write_StrBytes("08 8C 1A 03 01 00 00"); //originalcode: //lea eax,[edx+ebx+00000102] CaveMemory.Write_StrBytes("8D 84 1A 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteByte(_JvsRawAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_Y); WriteByte(_AdjustedAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_AdjustedAxes_CaveAddress + 1, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFE); } else if (PlayerData.ID == 2) { WriteByte(_JvsRawAxes_CaveAddress + 0x04, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x06, (byte)PlayerData.RIController.Computed_Y); WriteByte(_AdjustedAxes_CaveAddress + 2, (byte)PlayerData.RIController.Computed_X); WriteByte(_AdjustedAxes_CaveAddress + 3, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 9, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 9, 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFE); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_AirFront)); _Outputs.Add(new GameOutput(OutputId.P1_AirRear)); _Outputs.Add(new GameOutput(OutputId.P2_AirFront)); _Outputs.Add(new GameOutput(OutputId.P2_AirRear)); _Outputs.Add(new GameOutput(OutputId.LmpStop)); _Outputs.Add(new GameOutput(OutputId.LmpAction)); _Outputs.Add(new GameOutput(OutputId.LmpReset)); _Outputs.Add(new GameOutput(OutputId.LmpError)); _Outputs.Add(new GameOutput(OutputId.LmpSafety)); _Outputs.Add(new GameOutput(OutputId.LmpFloor)); _Outputs.Add(new GameOutput(OutputId.LmpSpot)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs int P1_Motor_Status = ReadByte(_Outputs_Address) >> 4 & 0x01; int P2_Motor_Status = ReadByte(_Outputs_Address) >> 5 & 0x01; //SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P1_GunMotor, P1_Motor_Status); SetOutputValue(OutputId.P2_GunMotor, P2_Motor_Status); SetOutputValue(OutputId.P1_AirFront, ReadByte(_Outputs_Address) & 0x01); SetOutputValue(OutputId.P2_AirFront, ReadByte(_Outputs_Address) >> 1 & 0x01); SetOutputValue(OutputId.P1_AirRear, ReadByte(_Outputs_Address) >> 2 & 0x01); SetOutputValue(OutputId.P2_AirRear, ReadByte(_Outputs_Address) >> 3 & 0x01); SetOutputValue(OutputId.LmpStop, ReadByte(_Outputs_Address + 2) >> 3 & 0x01); SetOutputValue(OutputId.LmpReset, ReadByte(_Outputs_Address + 2) & 0x01); SetOutputValue(OutputId.LmpError, ReadByte(_Outputs_Address + 2) >> 1 & 0x01); SetOutputValue(OutputId.LmpSafety, ReadByte(_Outputs_Address + 2) >> 2 & 0x01); SetOutputValue(OutputId.LmpFloor, ReadByte(_Outputs_Address + 2) >> 4 & 0x01); SetOutputValue(OutputId.LmpSpot, ReadByte(_Outputs_Address + 2) >> 5 & 0x01); //Billboard : 0x80=RED, 0x81=GREEN, 0x82=BLUE int BillBoardOn = ReadByte(_Outputs_Address + 1) >> 7 & 0x01; if (BillBoardOn != 0) SetOutputValue(OutputId.LmpBillboard, ReadByte(_Outputs_Address + 1)); else SetOutputValue(OutputId.LmpBillboard, 0); //Start and Action Buttons are on another memory location Byte bOutputs2 = ReadByte(ReadPtr(_Outputs2_Address)); SetOutputValue(OutputId.LmpAction, bOutputs2 >> 4 & 0x01); SetOutputValue(OutputId.P1_LmpStart, bOutputs2 >> 7 & 0x01); //Custom Outputs _Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (ReadPtr(_GameInfos_Address) != 0) { _Life = ReadByte(ReadPtr(_GameInfos_Address) + 0x40); //[Damaged] custom Output if (_Life < _LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_Ammo = ReadByte(ReadPtrChain(_GameInfos_Address, new UInt32[] { 0x34 }) + 0x2D4); //No attract mode so we can just reuse the original recoil flag SetOutputValue(OutputId.P1_CtmRecoil, P1_Motor_Status); _P2_Ammo = ReadByte(ReadPtrChain(_GameInfos_Address, new UInt32[] { 0x38 }) + 0x2D4); //No attract mode so we can just reuse the original recoil flag SetOutputValue(OutputId.P2_CtmRecoil, P2_Motor_Status); } _LastLife = _Life; _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _Life); SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghHotdEx.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_LindberghHotdEx : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\hotdex"; //Inputs private InjectionStruct _JvsRawAxes_InjectionStruct = new InjectionStruct(0x084BD186, 7); private InjectionStruct _AdjustedAxes_InjectionStruct = new InjectionStruct(0x81039C1, 7); //Not Called ! private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x084BCF45, 7); //Outputs private UInt32 _JvsMgrPtr_Address = 0x0A9DFEBC; private UInt32 _GameMgrPtr_Address = 0xA9DFE98; private UInt32 _Credits_Address = 0xAA3DD80; private UInt32 _Freeplay_Address = 0xAA3DD67; //Custom Data private UInt32 _JvsRawAxes_CaveAddress; private UInt32 _AdjustedAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; //Rom loaded + Rom version check private UInt32 _RomLoaded_Check_Address = 0x0815C65A; //Intruction writing Lamps /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_LindberghHotdEx(String RomName) : base(RomName, "BudgieLoader") { _KnownMd5Prints.Add("House of The Dead EX (SBRC)", "69a85f6f2ba82e014cf7e3f65f7b3d2a"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("HoD4")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0x8B, 0x15, 0xBC })) { Logger.WriteLog("House Of The Dead EX (SBRC) binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["House of The Dead EX (SBRC)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Outputs #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y axis => 0x00 - 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _JvsRawAxes_CaveAddress = _InputsDatabank_Address; _AdjustedAxes_CaveAddress = _InputsDatabank_Address + 0x10; _Buttons_CaveAddress = _InputsDatabank_Address + 0x20; SetHack_Axes(); //SetHack_AdjustedAxes(); SetHack_Buttons(); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_Axes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea ecx,[ebx-B] CaveMemory.Write_StrBytes("8D 4B F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea ebx,[ebx+esi+00000102] CaveMemory.Write_StrBytes("8D 9C 33 02 01 00 00"); //mov [ebx],cx CaveMemory.Write_StrBytes("66 89 0B"); //Inject it CaveMemory.InjectToAddress(_JvsRawAxes_InjectionStruct, "Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp esi,05 CaveMemory.Write_StrBytes("83 FE 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+esi+00000102],FC CaveMemory.Write_StrBytes("80 A4 32 02 01 00 00 FC"); //and byte ptr [edx+esi+00000103],7F CaveMemory.Write_StrBytes("80 A4 32 03 01 00 00 7F"); //movzx ecx,word ptr [esi+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8E"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 03 01 00 00"); //originalcode: //lea eax,[edx+esi+00000102] CaveMemory.Write_StrBytes("8D 84 32 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteByte(_JvsRawAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_Y); //WriteBytes(_ComputedAxes_BaseAddress + 0xC8, BitConverter.GetBytes(X_Value)); //WriteBytes(_ComputedAxes_BaseAddress + 0xCC, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x08); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xF7); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFE); } else if (PlayerData.ID == 2) { WriteByte(_JvsRawAxes_CaveAddress + 0x04, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x06, (byte)PlayerData.RIController.Computed_Y); // WriteBytes(_ComputedAxes_BaseAddress + 0xD0, BitConverter.GetBytes(X_Value)); //WriteBytes(_ComputedAxes_BaseAddress + 0xD4, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x04); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFB); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFE); } } #endregion /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpPanel)); _Outputs.Add(new GameOutput(OutputId.Lmp1)); _Outputs.Add(new GameOutput(OutputId.Lmp2)); _Outputs.Add(new GameOutput(OutputId.Lmp3)); _Outputs.Add(new GameOutput(OutputId.Lmp4)); _Outputs.Add(new GameOutput(OutputId.Lmp5)); _Outputs.Add(new GameOutput(OutputId.Lmp6)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFoot)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFoot)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); /* _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, MameOutputHelper.CustomRecoilOnDelay, MameOutputHelper.CustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, MameOutputHelper.CustomRecoilOnDelay, MameOutputHelper.CustomRecoilOffDelay, 0)); */ _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs UInt32 Outputs_Address = BitConverter.ToUInt32(ReadBytes(_JvsMgrPtr_Address, 4), 0); SetOutputValue(OutputId.P1_LmpStart, ReadByte(Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(Outputs_Address) >> 4 & 0x01); SetOutputValue(OutputId.LmpPanel, ReadByte(Outputs_Address + 1) >> 7 & 0x01); SetOutputValue(OutputId.Lmp1, ReadByte(Outputs_Address + 1) >> 6 & 0x01); SetOutputValue(OutputId.Lmp2, ReadByte(Outputs_Address + 1) >> 5 & 0x01); SetOutputValue(OutputId.Lmp3, ReadByte(Outputs_Address + 1) >> 4 & 0x01); SetOutputValue(OutputId.Lmp4, ReadByte(Outputs_Address + 1) >> 3 & 0x01); SetOutputValue(OutputId.Lmp5, ReadByte(Outputs_Address + 1) >> 2 & 0x01); SetOutputValue(OutputId.Lmp6, ReadByte(Outputs_Address + 1) >> 1 & 0x01); SetOutputValue(OutputId.P1_LmpFoot, ReadByte(Outputs_Address) >> 5 & 0x01); SetOutputValue(OutputId.P2_LmpFoot, ReadByte(Outputs_Address) >> 4 & 0x01); //Custom Outputs int P1_Life = 0; int P2_Life = 0; UInt32 GameManager_Address = ReadPtr(_GameMgrPtr_Address); if (GameManager_Address != 0) { P1_Life = ReadByte(GameManager_Address + 0x48); if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); P2_Life = ReadByte(GameManager_Address + 0x4C); if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; /*_P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (ReadPtr(_GameInfos_Address) != 0) { _Life = ReadByte(ReadPtr(_GameInfos_Address) + 0x40); //[Damaged] custom Output if (_Life < _LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_Ammo = ReadByte(ReadPtrChain(_GameInfos_Address, new UInt32[] { 0x34 }) + 0x2D4); //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //No attract mode so we can just reuse the original recoil flag SetOutputValue(OutputId.P1_CtmRecoil, P1_Motor_Status); _P2_Ammo = ReadByte(ReadPtrChain(_GameInfos_Address, new UInt32[] { 0x38 }) + 0x2D4); //[Clip] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //No attract mode so we can just reuse the original recoil flag SetOutputValue(OutputId.P2_CtmRecoil, P2_Motor_Status); } _LastLife = _Life; _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _Life);*/ SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Displaying Credits if no FREEPLAY enabled if (ReadByte(_Freeplay_Address) == 0) SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); else SetOutputValue(OutputId.Credits, 0); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghLgj.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_LindberghLgj : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\lgj"; //Inputs private InjectionStruct _JvsRawAxes_InjectionStruct = new InjectionStruct(0x084E8CDC, 7); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x084E8AF5, 7); //Game is using complex calibration procedure, overriding float values after this : private NopStruct _Nop_AdjustedAxis_X = new NopStruct(0x080A9AD2, 8); private NopStruct _Nop_AdjustedAxis_Y = new NopStruct(0x080A97FE, 8); //INPUT_STRUCT offset in game private UInt32 _Player1_InputPtr_Address = 0x087D3BB0; private UInt32 _Player2_InputPtr_Address = 0x087D3BAC; private const UInt32 INPUT_X_OFFSET = 0x134; private const UInt32 INPUT_Y_OFFSET = 0x138; private UInt32 _NoCrosshair_Patch_Address = 0x080B0D06; //Outputs private UInt32 _JvsOutput_Address = 0x087D186D; private UInt32 _Credits_Address = 0x08C08420; private UInt32 _PlayersStatus_Address = 0x87D3DC0; private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x080AFA1C, 6); //Custom Data private UInt32 _RawJvsAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; private UInt32 _CustomRecoil_CaveAddress = 0; //Check instruction for game loaded private UInt32 _RomLoaded_Check_Address = 0x0807925B; /// /// Constructor /// public Game_LindberghLgj(String RomName) : base(RomName, "linuxloader") { _KnownMd5Prints.Add("Let's Go Jungle (SBLC)", "37b0239d4fbc6256c65dd70beb087a34"); _KnownMd5Prints.Add("Let's Go Jungle (SBLC) (Rev.A)", "875486650a16abce5b1901fd319001c0"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("TeknoBudgie")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0x8B, 0x1D, 0xB0 })) { Logger.WriteLog("Let's Go Jungle! binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Let's Go Jungle (SBLC)"]; } else if (buffer.SequenceEqual(new byte[] { 0x00, 0x00, 0x8B })) { Logger.WriteLog("Let's Go Jungle! - Rev .A binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Let's Go Jungle (SBLC) (Rev.A)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch (Exception Ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); Logger.WriteLog(Ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y axis => 0x00 - 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX - dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY - dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _RawJvsAxes_CaveAddress = _InputsDatabank_Address; _Buttons_CaveAddress = _InputsDatabank_Address + 0x10; SetHack_Axes(); SetHack_Buttons(); // Noping X,Y axis values update in the following procedure, after adjustment is made : // acpPlayer::input() SetNops(0, _Nop_AdjustedAxis_X); SetNops(0, _Nop_AdjustedAxis_Y); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_Axes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea ecx,[eax-B] CaveMemory.Write_StrBytes("8D 48 F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_RawJvsAxes_CaveAddress)); //lea eax,[eax+esi+00000102] CaveMemory.Write_StrBytes("8D 84 30 02 01 00 00"); //mov [eax],cx CaveMemory.Write_StrBytes("66 89 08"); //Inject it CaveMemory.InjectToAddress(_JvsRawAxes_InjectionStruct, "Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp ebx,05 CaveMemory.Write_StrBytes("83 FB 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+ebx+00000102], CaveMemory.Write_StrBytes("80 A4 1A 02 01 00 00 FC"); //and byte ptr [edx+ebx+00000103],7F CaveMemory.Write_StrBytes("80 A4 1A 03 01 00 00 7F"); //movzx ecx,word ptr [ebx+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8B"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+ebx+00000102],cl CaveMemory.Write_StrBytes("08 8C 1A 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+ebx+00000103],cl CaveMemory.Write_StrBytes("08 8C 1A 03 01 00 00"); //originalcode: //lea eax,[edx+ebx+00000102] CaveMemory.Write_StrBytes("8D 84 1A 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } //Original game is simply setting a Motor to vibrate, so simply using this data to create or pulsed custom recoil will not be synchronized with bullets shot //as the pulses lenght and spaceing will depend on DemulShooter output pulse config data. //To synch recoil pulse with projectiles, this hack allows to intercept the code increasing the "PlayerShotCounter" variable protected override void Apply_OutputsMemoryHack() { //Create Databak to store our value Create_OutputsDataBank(); _CustomRecoil_CaveAddress = _OutputsDatabank_Address; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercepting the bullet count increase call to create our own recoil /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //lea edx,[edi+01] CaveMemory.Write_StrBytes("8D 57 01"); //mov [esi+14],edi CaveMemory.Write_StrBytes("89 7E 14"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,DWORD PTR [ebx+0x118] CaveMemory.Write_StrBytes("8B 83 18 01 00 00"); //mov byte ptr [eax+_CustomRecoil_CaveAddress],01 CaveMemory.Write_StrBytes("C6 80"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_CustomRecoil_CaveAddress)); CaveMemory.Write_StrBytes("01"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToAddress(_Recoil_InjectionStruct, "Recoil"); } /// /// acpPlayer::calc() calls AbkTrunk::StartBranch() a 2 different places, depending on the crosshair displayed (no shoot / shoot) /// Changing the last parameter from 1 to 0 when the needed cursor is "shoot" will mask it /// protected override void Apply_NoCrosshairMemoryHack() { WriteByte(_NoCrosshair_Patch_Address, 0x00); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { float X_Value = 1.0f - (2.0f * (float)PlayerData.RIController.Computed_X / 255.0f); float Y_Value = (2.0f * (float)PlayerData.RIController.Computed_Y / 255.0f) - 1.0f; if (PlayerData.ID == 1) { WriteByte(_RawJvsAxes_CaveAddress, (byte)PlayerData.RIController.Computed_Y); WriteByte(_RawJvsAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_X); WriteBytes(ReadPtr(_Player1_InputPtr_Address) + INPUT_X_OFFSET, BitConverter.GetBytes(X_Value)); WriteBytes(ReadPtr(_Player1_InputPtr_Address) + INPUT_Y_OFFSET, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); } else if (PlayerData.ID == 2) { WriteByte(_RawJvsAxes_CaveAddress + 0x04, (byte)PlayerData.RIController.Computed_Y); WriteByte(_RawJvsAxes_CaveAddress + 0x06, (byte)PlayerData.RIController.Computed_X); WriteBytes(ReadPtr(_Player2_InputPtr_Address) + INPUT_X_OFFSET, BitConverter.GetBytes(X_Value)); WriteBytes(ReadPtr(_Player2_InputPtr_Address) + INPUT_Y_OFFSET, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpRoom)); _Outputs.Add(new GameOutput(OutputId.LmpCoin)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_JvsOutput_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_JvsOutput_Address) >> 4 & 0x01); SetOutputValue(OutputId.LmpRoom, ReadByte(_JvsOutput_Address) >> 5 & 0x01); SetOutputValue(OutputId.LmpCoin, ReadByte(_JvsOutput_Address) >> 2 & 0x01); SetOutputValue(OutputId.P1_GunMotor, ReadByte(_JvsOutput_Address) >> 3 & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte(_JvsOutput_Address) >> 6 & 0x01); //Custom Outputs UInt32 P1_StructAddress = ReadPtr(_Player1_InputPtr_Address); UInt32 P2_StructAddress = ReadPtr(_Player2_InputPtr_Address); _P1_Life = 0; _P2_Life = 0; //Checking this byte (player playing ?) otherwise the game is still enabing rumble during Attract Mode if (ReadByte(_PlayersStatus_Address) == 1) { if (P1_StructAddress != 0) { _P1_Life = (int)BitConverter.ToSingle(ReadBytes(P1_StructAddress + 0x4BC, 4), 0); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); if (ReadByte(_CustomRecoil_CaveAddress) == 1 && (ReadByte(_JvsOutput_Address) >> 3 & 0x01) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_CustomRecoil_CaveAddress, 0); } } } //Checking this byte (player playing ?) otherwise the game is still enabing rumble during Attract Mode if (ReadByte(_PlayersStatus_Address + 4) == 1) { if (P2_StructAddress != 0) { _P2_Life = (int)BitConverter.ToSingle(ReadBytes(P2_StructAddress + 0x4BC, 4), 0); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); if (ReadByte(_CustomRecoil_CaveAddress + 1) == 1 && (ReadByte(_JvsOutput_Address) >> 6 & 0x01) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_CustomRecoil_CaveAddress + 1, 0); } } } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghLgjsp.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_LindberghLgjsp : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\lgjsp"; //Inputs private InjectionStruct _JvsRawAxes_InjectionStruct = new InjectionStruct(0x0851412C, 7); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x08513F45, 7); //Game is using complex calibration procedure, overriding float values after this : private NopStruct _Nop_AdjustedAxis_X = new NopStruct(0x080AFB1E, 8); private NopStruct _Nop_AdjustedAxis_Y = new NopStruct(0x080AFB26, 8); //INPUT_STRUCT offset in game private UInt32 _Player1_InputPtr_Address = 0x08810B38; private UInt32 _Player2_InputPtr_Address = 0x08810B34; private const UInt32 INPUT_X_OFFSET = 0x134; private const UInt32 INPUT_Y_OFFSET = 0x138; private UInt32 _NoCrosshair_Patch_Address = 0x080B7C36; //Outputs private UInt32 _JvsOutput_Address = 0x0880E5F5; private UInt32 _Credits_Address = 0x08C45460; private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x080B653C, 6); //Custom Data private UInt32 _JvsRawAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; private UInt32 _CustomRecoil_CaveAddress; //Check instruction for game loaded private UInt32 _RomLoaded_Check_Address = 0x0807A810; /// /// Constructor /// public Game_LindberghLgjsp(String RomName) : base(RomName, "linuxloader") { _KnownMd5Prints.Add("Let's Go Jungle Special (SBNR) (Rev.A)", "b95a4cb3fe5a0d3d25484e52e16d2b7a"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("TeknoBudgie")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0x8B, 0x1D, 0x38 })) { Logger.WriteLog("Let's Go Jungle Special (Rev .A) binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Let's Go Jungle Special (SBNR) (Rev.A)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y axis => 0x00 - 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX - dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY - dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _JvsRawAxes_CaveAddress = _InputsDatabank_Address; _Buttons_CaveAddress = _InputsDatabank_Address + 0x10; SetHack_Axes(); SetHack_Buttons(); // Noping X,Y axis values update in the following procedure : // acpPlayer::input() SetNops(0, _Nop_AdjustedAxis_X); SetNops(0, _Nop_AdjustedAxis_Y); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_Axes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea ecx,[eax-B] CaveMemory.Write_StrBytes("8D 48 F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea eax,[eax+esi+00000102] CaveMemory.Write_StrBytes("8D 84 30 02 01 00 00"); //mov [eax],cx CaveMemory.Write_StrBytes("66 89 08"); //Inject it CaveMemory.InjectToAddress(_JvsRawAxes_InjectionStruct, "Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp ebx,05 CaveMemory.Write_StrBytes("83 FB 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+ebx+00000102], CaveMemory.Write_StrBytes("80 A4 1A 02 01 00 00 FC"); //and byte ptr [edx+ebx+00000103],7F CaveMemory.Write_StrBytes("80 A4 1A 03 01 00 00 7F"); //movzx ecx,word ptr [ebx+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8B"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+ebx+00000102],cl CaveMemory.Write_StrBytes("08 8C 1A 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+ebx+00000103],cl CaveMemory.Write_StrBytes("08 8C 1A 03 01 00 00"); //originalcode: //lea eax,[edx+ebx+00000102] CaveMemory.Write_StrBytes("8D 84 1A 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } //Original game is simply setting a Motor to vibrate, so simply using this data to create or pulsed custom recoil will not be synchronized with bullets shot //as the pulses lenght and spaceing will depend on DemulShooter output pulse config data. //To synch recoil pulse with projectiles, this hack allows to intercept the code increasing the "PlayerShotCounter" variable protected override void Apply_OutputsMemoryHack() { //Create Databak to store our value Create_OutputsDataBank(); _CustomRecoil_CaveAddress = _OutputsDatabank_Address; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercepting the bullet count increase call to create our own recoil /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //lea ecx,[edi+01] CaveMemory.Write_StrBytes("8D 4A 01"); //mov [esi+14],edx CaveMemory.Write_StrBytes("89 56 14"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,DWORD PTR [ebx+0x118] CaveMemory.Write_StrBytes("8B 83 18 01 00 00"); //mov byte ptr [eax+_CustomRecoil_CaveAddress],01 CaveMemory.Write_StrBytes("C6 80"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_CustomRecoil_CaveAddress)); CaveMemory.Write_StrBytes("01"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToAddress(_Recoil_InjectionStruct, "Recoil"); } /// /// acpPlayer::calc() calls AbkTrunk::StartBranch() a 2 different places, depending on the crosshair displayed (no shoot / shoot) /// Changing the last parameter from 1 to 0 when the needed cursor is "shoot" will mask it /// protected override void Apply_NoCrosshairMemoryHack() { WriteByte(_NoCrosshair_Patch_Address, 0x00); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { float X_Value = 1.0f - (2.0f * (float)PlayerData.RIController.Computed_X / 255.0f); float Y_Value = (2.0f * (float)PlayerData.RIController.Computed_Y / 255.0f) - 1.0f; if (PlayerData.ID == 1) { WriteByte(_JvsRawAxes_CaveAddress, (byte)PlayerData.RIController.Computed_Y); WriteByte(_JvsRawAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_X); WriteBytes(ReadPtr(_Player1_InputPtr_Address) + INPUT_X_OFFSET, BitConverter.GetBytes(X_Value)); WriteBytes(ReadPtr(_Player1_InputPtr_Address) + INPUT_Y_OFFSET, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); } else if (PlayerData.ID == 2) { WriteByte(_JvsRawAxes_CaveAddress + 0x04, (byte)PlayerData.RIController.Computed_Y); WriteByte(_JvsRawAxes_CaveAddress + 0x06, (byte)PlayerData.RIController.Computed_X); WriteBytes(ReadPtr(_Player2_InputPtr_Address) + INPUT_X_OFFSET, BitConverter.GetBytes(X_Value)); WriteBytes(ReadPtr(_Player2_InputPtr_Address) + INPUT_Y_OFFSET, BitConverter.GetBytes(Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpRoom)); _Outputs.Add(new GameOutput(OutputId.LmpCoin)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_JvsOutput_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_JvsOutput_Address) >> 4 & 0x01); SetOutputValue(OutputId.LmpRoom, ReadByte(_JvsOutput_Address) >> 5 & 0x01); SetOutputValue(OutputId.LmpCoin, ReadByte(_JvsOutput_Address) >> 2 & 0x01); SetOutputValue(OutputId.P1_GunMotor, ReadByte(_JvsOutput_Address) >> 3 & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte(_JvsOutput_Address) >> 6 & 0x01); //Custom Outputs //Unused ?? UInt32 P1_StructAddress = ReadPtr(0x08810B38); UInt32 P2_StructAddress = ReadPtr(0x08810B34); _P1_Life = 0; _P2_Life = 0; //[Damaged] custom Output _P1_Life = (int)BitConverter.ToSingle(ReadBytes(0x08810AA8, 4), 0); if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P2_Life = (int)BitConverter.ToSingle(ReadBytes(0x08810AAC, 4), 0); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //The game does not seem to have an attract mode ? //No filtering for now... if (ReadByte(_CustomRecoil_CaveAddress) == 1 && (ReadByte(_JvsOutput_Address) >> 3 & 0x01) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_CustomRecoil_CaveAddress, 0); } if (ReadByte(_CustomRecoil_CaveAddress + 1) == 1 && (ReadByte(_JvsOutput_Address) >> 6 & 0x01) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_CustomRecoil_CaveAddress + 1, 0); } SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_LindberghRambo.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter.Games { public class Game_LindberghRambo : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\rambo"; //Inputs private InjectionStruct _JvsRawAxes_InjectionStruct = new InjectionStruct(0x083D696A, 7); private InjectionStruct _AdjustedAxes_InjectionStruct = new InjectionStruct(0x08075288, 6); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x083D6729, 7); //Outputs private UInt32 _JvsMgrPtr_Address = 0x085DD158; private UInt32 _CreditsMgrPtr_Address = 0x085DC2B0; private UInt32 _PlayerMgrPtr_Address = 0x85CE9B0; //Show crosshair flag private UInt32 _DrawSightFlag_Address = 0x85A3BE8; //Custom Data private UInt32 _JvsRawAxes_CaveAddress; private UInt32 _AdjustedAxes_CaveAddress; private UInt32 _Buttons_CaveAddress; private UInt32 _RomLoaded_Check_Address = 0x08073BC7; /// /// Constructor /// public Game_LindberghRambo(String RomName) : base(RomName, "linuxloader") { _KnownMd5Prints.Add("ramboD.elf (SBQL)", "cad71f7fa562b285feee195ee1ff32bf"); _KnownMd5Prints.Add("ramboM.elf (SBQL)", "e3ce5cd7aa18be0c0a3b9d6a7928b122"); _KnownMd5Prints.Add("ramboM.elf (SBSS)", "4590a750488712001d230daf69dc7fab"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { if (_Target_Process_Name.ToLower().Contains("budgieloader")) { if (!FindGameWindow_Contains("RAMBO")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } else if (_Target_Process_Name.ToLower().Contains("linuxloader")) { if (!FindGameWindow_Contains("FPS")) { Logger.WriteLog("Game Window not found, waiting..."); return; } } //To make sure LinuxLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is Rev.A or Rev.B or Rev.C binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_Check_Address, 3); if (buffer.SequenceEqual(new byte[] { 0xE8, 0x76, 0x01 })) { Logger.WriteLog("ramboD.elf (SBQL) binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["ramboD.elf (SBQL)"]; } else if (buffer.SequenceEqual(new byte[] { 0x89, 0x1C, 0x24 })) { Logger.WriteLog("ramboM.elf (SBQL) binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["ramboM.elf (SBQL)"]; } else if (buffer.SequenceEqual(new byte[] { 0xE9, 0x86, 0xEF })) { Logger.WriteLog("ramboM.elf (SBSS) binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["ramboM.elf (SBSS)"]; } else { Logger.WriteLog("Game not Loaded, waiting..."); return; } Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y axis => 0x00 - 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _JvsRawAxes_CaveAddress = _InputsDatabank_Address; _AdjustedAxes_CaveAddress = _InputsDatabank_Address + 0x10; _Buttons_CaveAddress = _InputsDatabank_Address + 0x20; SetHack_JvsRawAxes(); SetHack_AdjustedAxes(); SetHack_Buttons(); } /// /// At the end of amJvspAckAnalogInput(), replacing Axis value before memory copy /// private void SetHack_JvsRawAxes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea ecx,[ebx-B] CaveMemory.Write_StrBytes("8D 4B F5"); //movzx ecx,word ptr [ecx+_Axes_CaveAddress] CaveMemory.Write_StrBytes("0F B6 89"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_JvsRawAxes_CaveAddress)); //lea ebx,[ebx+esi+00000102] CaveMemory.Write_StrBytes("8D 9C 33 02 01 00 00"); //mov [ebx],cx CaveMemory.Write_StrBytes("66 89 0B"); //Inject it CaveMemory.InjectToAddress(_JvsRawAxes_InjectionStruct, "Axes"); } /// /// Replacing GetAdjstGunPos() content /// private void SetHack_AdjustedAxes() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[esp+18] CaveMemory.Write_StrBytes("8B 44 24 18"); //shl eax,1 CaveMemory.Write_StrBytes("D1 E0"); //movzx eax,byte ptr [eax+_Buttons_CaveAddress + 6] CaveMemory.Write_StrBytes("0F B6 80"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress + 6)); //and al,1 CaveMemory.Write_StrBytes("24 01"); //test al,al CaveMemory.Write_StrBytes("84 C0"); //je OnScreen CaveMemory.Write_StrBytes("74 11"); //mov eax,[esp+04] CaveMemory.Write_StrBytes("8B 44 24 04"); //mov byte ptr [eax], CaveMemory.Write_StrBytes("C6 00 FF"); //mov eax,[esp+08] CaveMemory.Write_StrBytes("8B 44 24 08"); //mov byte ptr [eax], CaveMemory.Write_StrBytes("C6 00 FF"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //ret CaveMemory.Write_StrBytes("C3"); //OnScreen: //mov eax,[esp+18] CaveMemory.Write_StrBytes("8B 44 24 18"); //shl eax,1 CaveMemory.Write_StrBytes("D1 E0"); //add eax,_AdjustedAxes_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_AdjustedAxes_CaveAddress)); //movzx eax,word ptr [eax] CaveMemory.Write_StrBytes("0F B7 00"); //mov edx,[esp+04] CaveMemory.Write_StrBytes("8B 54 24 04"); //mov byte ptr[edx],al CaveMemory.Write_StrBytes("88 02"); //mov edx,[esp+08] CaveMemory.Write_StrBytes("8B 54 24 08"); //shr eax, 8 CaveMemory.Write_StrBytes("C1 E8 08"); //mov byte ptr[edx],al CaveMemory.Write_StrBytes("88 02"); //mov eax,00000001 CaveMemory.Write_StrBytes("B8 01 00 00 00"); //ret CaveMemory.Write_StrBytes("C3"); //Inject it CaveMemory.InjectToAddress(_AdjustedAxes_InjectionStruct, "Adjusted Axes"); } /// /// At the end of amJvspAckSwInput(), removing the wanted buttons bit states from the source memory (Trigger, Reload, and Grenade) /// and changing the bits with custom values before memorycopy /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp esi,05 CaveMemory.Write_StrBytes("83 FE 05"); //je originalcode CaveMemory.Write_StrBytes("74 28"); //and byte ptr [edx+esi+00000102],FC CaveMemory.Write_StrBytes("80 A4 32 02 01 00 00 FC"); //and byte ptr [edx+esi+00000103],7F CaveMemory.Write_StrBytes("80 A4 32 03 01 00 00 7F"); //movzx ecx,word ptr [esi+_Buttons_CaveAddress] CaveMemory.Write_StrBytes("0F B7 8E"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 02 01 00 00"); //shr ecx,08 CaveMemory.Write_StrBytes("C1 E9 08"); //or [edx+esi+00000102],cl CaveMemory.Write_StrBytes("08 8C 32 03 01 00 00"); //originalcode: //lea eax,[edx+esi+00000102] CaveMemory.Write_StrBytes("8D 84 32 02 01 00 00"); //Inject it CaveMemory.InjectToAddress(_Buttons_InjectionStruct, "Buttons"); } /// /// ramboD.elf has a switch to draw sight, but ramboM does not /// protected override void Apply_NoCrosshairMemoryHack() { if (_TargetProcess_Md5Hash.Equals(_KnownMd5Prints["ramboD.elf (SBQL)"])) WriteByte(_DrawSightFlag_Address, 0); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteByte(_JvsRawAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x02, (byte)PlayerData.RIController.Computed_Y); WriteByte(_AdjustedAxes_CaveAddress, (byte)PlayerData.RIController.Computed_X); WriteByte(_AdjustedAxes_CaveAddress + 1, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 7, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 7, 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 6, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 6, 0xFE); } else if (PlayerData.ID == 2) { WriteByte(_JvsRawAxes_CaveAddress + 0x04, (byte)PlayerData.RIController.Computed_X); WriteByte(_JvsRawAxes_CaveAddress + 0x06, (byte)PlayerData.RIController.Computed_Y); WriteByte(_AdjustedAxes_CaveAddress + 2, (byte)PlayerData.RIController.Computed_X); WriteByte(_AdjustedAxes_CaveAddress + 3, (byte)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 9, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 9, 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 8, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 8, 0xFE); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs UInt32 Outputs_Address = ReadPtr(_JvsMgrPtr_Address); SetOutputValue(OutputId.P1_LmpStart, ReadByte(Outputs_Address) & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(Outputs_Address) >> 1 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, ReadByte(Outputs_Address) >> 2 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, ReadByte(Outputs_Address) >> 3 & 0x01); //Custom Outputs UInt32 PlayersPtr_BaseAddress = ReadPtr(_PlayerMgrPtr_Address); int P1_Ammo = 0; int P2_Ammo = 0; _P1_Life = 0; _P2_Life = 0; if (PlayersPtr_BaseAddress != 0) { UInt32 P1_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x34); UInt32 P2_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x38); //PlayerMode: //0: Standby //3: Game //4: Hold //A, C: Continue int P1_Status = ReadByte(P1_StructAddress + 0x38); int P2_Status = ReadByte(P2_StructAddress + 0x38); if (P1_Status == 3 || P1_Status == 4) { _P1_Life = ReadByte(P1_StructAddress + 0x3C); P1_Ammo = ReadByte(P1_StructAddress + 0x400); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 3 || P2_Status == 4) { _P2_Life = ReadByte(P2_StructAddress + 0x3C); P2_Ammo = ReadByte(P2_StructAddress + 0x400); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); //Custom recoil will be recoil just like the original one SetOutputValue(OutputId.P1_CtmRecoil, ReadByte(Outputs_Address) >> 2 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, ReadByte(Outputs_Address) >> 3 & 0x01); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); UInt32 CreditsMgr = ReadPtr(_CreditsMgrPtr_Address); SetOutputValue(OutputId.Credits, (int)(ReadByte(CreditsMgr + 0x38))); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Model2Bel.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooter { class Game_Model2Bel : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Axis_Injection_Offset = 0x000C8937; private UInt32 _Buttons_Injection_Offset = 0x000C88F0; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _Buttons_CaveAddress; //Outputs //private UInt32 _OutputsPtr_Offset = 0x001AA730; private UInt32 _PlayerStructPtr_Offset = 0x001AA8F4; private UInt32 _Outputs_BaseAddress; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_Model2Bel(String RomName) : base(RomName, "emulator") { _KnownMd5Prints.Add("Model2Emulator 1.1a", "26bd488f9a391dcac1c5099014aa1c9e"); _KnownMd5Prints.Add("Model2Emulator 1.1a multicpu", "ac59ce7cfb95d6d639c0f0d1afba1192"); _tProcess.Start(); Logger.WriteLog("Waiting for Model2 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { if (p.ProcessName.Equals("EMULATOR") || p.ProcessName.Equals("emulator_multicpu")) { _Target_Process_Name = p.ProcessName; _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y => [0x00 - > 0xFE] double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); //At runtime, the game is reading 4 bytes with players axis values in that order : _P1_X_CaveAddress = _InputsDatabank_Address; _P2_X_CaveAddress = _InputsDatabank_Address + 1; _P1_Y_CaveAddress = _InputsDatabank_Address + 2; _P2_Y_CaveAddress = _InputsDatabank_Address + 3; //And for buttons _Buttons_CaveAddress = _InputsDatabank_Address + 8; //Buttons : The game is reading the Byte containing Buttons info. Replacing the real address with our own byte[] b = BitConverter.GetBytes(_Buttons_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset + 1, b); //Axis : Same Thing b = BitConverter.GetBytes(_P1_X_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Injection_Offset + 2, b); //Initial values WriteBytes(_P1_X_CaveAddress, new byte[] { 0x55, 0xAA, 0x7F, 0x7F }); WriteByte(_Buttons_CaveAddress, 0xFF); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteByte(_P1_X_CaveAddress, bufferX[0]); WriteByte(_P1_Y_CaveAddress, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x10); } else if (PlayerData.ID == 2) { WriteByte(_P2_X_CaveAddress, bufferX[0]); WriteByte(_P2_Y_CaveAddress, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xDF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x20); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { /*SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Outputs_Address) & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Outputs_Address) >> 1 & 0x01); SetOutputValue(OutputId.P1_GunMotor, ReadByte(_Outputs_Address) >> 2 & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte(_Outputs_Address) >> 3 & 0x01);*/ //custom Outputs UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayerStructPtr_Offset); _Outputs_BaseAddress = ReadPtr(iTemp + 0x540); _P1_Ammo = BitConverter.ToInt16(ReadBytes(_Outputs_BaseAddress + 0x005B4954, 2), 0); if (_P1_Ammo <= 0) _P1_Ammo = 0; _P2_Ammo = BitConverter.ToInt16(ReadBytes(_Outputs_BaseAddress + 0x005B49D8, 2), 0); if (_P2_Ammo <= 0) _P2_Ammo = 0; _P1_Life = BitConverter.ToUInt16(ReadBytes(_Outputs_BaseAddress + 0x005B4996, 2), 0); _P2_Life = BitConverter.ToUInt16(ReadBytes(_Outputs_BaseAddress + 0x005B4A1E, 2), 0); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo <= 0) SetOutputValue(OutputId.P1_Clip, 0); else SetOutputValue(OutputId.P1_Clip, 1); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo <= 0) SetOutputValue(OutputId.P2_Clip, 0); else SetOutputValue(OutputId.P2_Clip, 1); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.Credits, ReadByte(_Outputs_BaseAddress + 0x005A87F0)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Model2Gunblade.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooter { class Game_Model2Gunblade : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Axis_Injection_Offset = 0x000C8937; private UInt32 _Buttons_Injection_Offset = 0x000C88F0; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _Buttons_CaveAddress; //Outputs private UInt32 _Outputs_Offset = 0x00174CF0; private UInt32 _CreditsPtr_Offset = 0x001AA71C; private UInt32 _GameInfoPtr_Offset = 0x001AA730; private UInt32 _GameInfo_BaseAddress; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_Model2Gunblade(String RomName) : base(RomName, "emulator") { _KnownMd5Prints.Add("Model2Emulator 1.1a", "26bd488f9a391dcac1c5099014aa1c9e"); _KnownMd5Prints.Add("Model2Emulator 1.1a multicpu", "ac59ce7cfb95d6d639c0f0d1afba1192"); _tProcess.Start(); Logger.WriteLog("Waiting for Model2 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { if (p.ProcessName.Equals("EMULATOR") || p.ProcessName.Equals("emulator_multicpu")) { _Target_Process_Name = p.ProcessName; _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X and Y => [0x00 - > 0xFE] double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); //At runtime, the game is reading 4 bytes with players axis values in that order : _P1_X_CaveAddress = _InputsDatabank_Address; _P2_X_CaveAddress = _InputsDatabank_Address + 1; _P1_Y_CaveAddress = _InputsDatabank_Address + 2; _P2_Y_CaveAddress = _InputsDatabank_Address + 3; //And for buttons _Buttons_CaveAddress = _InputsDatabank_Address + 8; //Buttons : The game is reading the Byte containing Buttons info. Replacing the real address with our own byte[] b = BitConverter.GetBytes(_Buttons_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset + 1, b); //Axis : Same Thing b = BitConverter.GetBytes(_P1_X_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Injection_Offset + 2, b); //Initial values WriteBytes(_P1_X_CaveAddress, new byte[] { 0x55, 0xAA, 0x7F, 0x7F }); WriteByte(_Buttons_CaveAddress, 0xFF); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteByte(_P1_X_CaveAddress, bufferX[0]); WriteByte(_P1_Y_CaveAddress, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); } else if (PlayerData.ID == 2) { WriteByte(_P2_X_CaveAddress, bufferX[0]); WriteByte(_P2_Y_CaveAddress, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun Motor : enabled non-stop while trigger is pulled _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, bOutput >> 2 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bOutput >> 3 & 0x01); SetOutputValue(OutputId.P1_GunMotor, bOutput >> 4 & 0x01); SetOutputValue(OutputId.P2_GunMotor, bOutput >> 5 & 0x01); //Custom Outputs UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _GameInfoPtr_Offset); _GameInfo_BaseAddress = ReadPtr(iTemp + 0x630); //Player status in game : //[1] : P1 Playing //[2] : P2 Playing //[0] : Game Over int P1_Status = ReadByte(_GameInfo_BaseAddress + 0x0053FFC0) & 0x01; int P2_Status = ReadByte(_GameInfo_BaseAddress + 0x0053FFC0) >> 1 & 0x01; _P1_Life = ReadByte(_GameInfo_BaseAddress + 0x00540114); _P2_Life = ReadByte(_GameInfo_BaseAddress + 0x00540154); //Ammo are number of shot fired, used to trigger recoil with any newer shot _P1_Ammo = (int)BitConverter.ToSingle(ReadBytes(_GameInfo_BaseAddress + 0x00540104, 4), 0); _P2_Ammo = (int)BitConverter.ToSingle(ReadBytes(_GameInfo_BaseAddress + 0x00540144, 4), 0); if (P1_Status != 0) { //Custom Recoil if (_P1_Ammo > _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_LastLife = _P1_Life; } else { _P1_Life = 0; _P1_LastLife = 0; } if (P2_Status != 0) { //Custom Recoil if (_P2_Ammo > _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); _P2_LastLife = _P2_Life; } else { _P2_Life = 0; _P2_LastLife = 0; } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsPtr_Offset) + 0x22)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Model2Hotd.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_Model2Hotd : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Buttons_Injection_Offset = 0x000C88F0; private UInt32 _Reload_Injection_Offset = 0x000C89B8; private NopStruct _Nop_P1_X = new NopStruct(0x000CAB13, 6); private NopStruct _Nop_P1_Y = new NopStruct(0x000CAB02, 6); private NopStruct _Nop_P2_X = new NopStruct(0x000CAB37, 6); private NopStruct _Nop_P2_Y = new NopStruct(0x000CAB3D, 6); private UInt32 _P1_X_Offset = 0x00174CF8; private UInt32 _P1_Y_Offset = 0x00174CFC; private UInt32 _P2_X_Offset = 0x00174D00; private UInt32 _P2_Y_Offset = 0x00174D04; private UInt32 _Buttons_CaveAddress; private UInt32 _Reload_CaveAddress; //Outputs private UInt32 _CreditsPtr_Offset = 0x001AA71C; private UInt32 _Outputs_Offset = 0x000174CF0; private UInt32 _GameInfoPtr_Offset = 0x001AA730; private UInt32 _GameInfo_BaseAddress; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_Model2Hotd(String RomName) : base(RomName, "EMULATOR") { _KnownMd5Prints.Add("Model2Emulator 1.1a", "26bd488f9a391dcac1c5099014aa1c9e"); _KnownMd5Prints.Add("Model2Emulator 1.1a multicpu", "ac59ce7cfb95d6d639c0f0d1afba1192"); _tProcess.Start(); Logger.WriteLog("Waiting for Model2 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { if (p.ProcessName.Equals("EMULATOR") || p.ProcessName.Equals("emulator_multicpu")) { _Target_Process_Name = p.ProcessName; _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0x0000 - > 0x01EF] //Y => [0x0000 - > 0x017F] double dMaxX = 496.0; double dMaxY = 384.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _Buttons_CaveAddress = _InputsDatabank_Address + 8; _Reload_CaveAddress = _InputsDatabank_Address + 9; //Buttons : The game is reading the Byte containing Buttons info. Replacing the real address with our own byte[] b = BitConverter.GetBytes(_Buttons_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset + 1, b); //Same thing for Reload flag b = BitConverter.GetBytes(_Reload_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Reload_Injection_Offset + 1, b); //Axis SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Y); //Initial values WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, BitConverter.GetBytes((Int32)0xA5)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, BitConverter.GetBytes((Int32)192)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, BitConverter.GetBytes((Int32)0x14A)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, BitConverter.GetBytes((Int32)192)); WriteByte(_Buttons_CaveAddress, 0xFF); WriteByte(_Reload_CaveAddress, 0x03); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int32)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_AND_ByteMask(_Reload_CaveAddress, 0xFE); Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); Apply_OR_ByteMask(_Reload_CaveAddress, 0x01); } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_AND_ByteMask(_Reload_CaveAddress, 0xFD); Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); Apply_OR_ByteMask(_Reload_CaveAddress, 0x02); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, bOutput >> 2 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bOutput >> 3 & 0x01); //Custom Outputs UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _GameInfoPtr_Offset); _GameInfo_BaseAddress = ReadPtr(iTemp + 0x630); //Player status in game : //[5] : Playing //[4] : Continue Screen //[9] : Game Over int P1_Status = ReadByte(_GameInfo_BaseAddress + 0x0051EC04); int P2_Status = ReadByte(_GameInfo_BaseAddress + 0x0051ECD0); _P1_Life = ReadByte(_GameInfo_BaseAddress + 0x0051EC10); _P2_Life = ReadByte(_GameInfo_BaseAddress + 0x0051ECDC); _P1_Ammo = ReadByte(_GameInfo_BaseAddress + 0x0051EC41); _P2_Ammo = ReadByte(_GameInfo_BaseAddress + 0x0051ED0D); if (P1_Status == 5) { //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo <= 0) SetOutputValue(OutputId.P1_Clip, 0); else SetOutputValue(OutputId.P1_Clip, 1); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; } else { SetOutputValue(OutputId.P1_Clip, 0); _P1_Ammo = 0; _P1_LastAmmo = 0; _P1_Life = 0; _P1_LastLife = 0; } if (P2_Status == 5) { //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo <= 0) SetOutputValue(OutputId.P2_Clip, 0); else SetOutputValue(OutputId.P2_Clip, 1); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; } else { SetOutputValue(OutputId.P2_Clip, 0); _P2_Ammo = 0; _P2_LastAmmo = 0; _P2_Life = 0; _P2_LastLife = 0; } SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.Credits, ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsPtr_Offset) + 0x5E)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Model2Rchase2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooter { class Game_Model2Rchase2 : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Axis_Injection_Offset = 0x000C8937; private UInt32 _Buttons_Injection_Offset = 0x000C88F0; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _Buttons_CaveAddress; //Outputs private UInt32 _Outputs_Offset = 0x00174CF0; private UInt32 _CreditsPtr_Offset = 0x001AA71C; private UInt32 _GameInfoPtr_Offset = 0x001AA730; private UInt32 _GameInfo_BaseAddress; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_Model2Rchase2(String RomName) : base(RomName, "EMULATOR") { _KnownMd5Prints.Add("Model2Emulator 1.1a", "26bd488f9a391dcac1c5099014aa1c9e"); _KnownMd5Prints.Add("Model2Emulator 1.1a multicpu", "ac59ce7cfb95d6d639c0f0d1afba1192"); _tProcess.Start(); Logger.WriteLog("Waiting for Model2 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { if (p.ProcessName.Equals("EMULATOR") || p.ProcessName.Equals("emulator_multicpu")) { _Target_Process_Name = p.ProcessName; _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0xBF -> 0x3E] //X and Y => [0xBF -> 0x40] double dMaxX = 130.0; double dMaxY = 128.0; PlayerData.RIController.Computed_X = Convert.ToInt16(dMaxX - Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)) + 0x3E; PlayerData.RIController.Computed_Y = Convert.ToInt16(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)) + 0x40; if (PlayerData.RIController.Computed_X < 0x40) PlayerData.RIController.Computed_X = 0x40; if (PlayerData.RIController.Computed_Y < 0x40) PlayerData.RIController.Computed_Y = 0x40; if (PlayerData.RIController.Computed_X > 0xBF) PlayerData.RIController.Computed_X = 0xBF; if (PlayerData.RIController.Computed_Y > 0xBF) PlayerData.RIController.Computed_Y = 0xBF; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Creating a custom memory bank to store our data /// protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); //At runtime, the game is reading 4 bytes with players axis values in that order : _P2_X_CaveAddress = _InputsDatabank_Address; _P1_X_CaveAddress = _InputsDatabank_Address + 1; _P2_Y_CaveAddress = _InputsDatabank_Address + 2; _P1_Y_CaveAddress = _InputsDatabank_Address + 3; //And for buttons _Buttons_CaveAddress = _InputsDatabank_Address + 8; //Buttons : The game is reading the Byte containing Buttons info. Replacing the real address with our own byte[] b = BitConverter.GetBytes(_Buttons_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset + 1, b); //Axis : Same Thing b = BitConverter.GetBytes(_P2_X_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Injection_Offset + 2, b); //Initial values WriteBytes(_P2_X_CaveAddress, new byte[] { 0x70, 0x89, 0x6F, 0x6F }); WriteByte(_Buttons_CaveAddress, 0xFF); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteByte(_P1_X_CaveAddress, bufferX[0]); WriteByte(_P1_Y_CaveAddress, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); } else if (PlayerData.ID == 2) { WriteByte(_P2_X_CaveAddress, bufferX[0]); WriteByte(_P2_Y_CaveAddress, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, bOutput >> 2 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bOutput >> 3 & 0x01); //Custom Outputs UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _GameInfoPtr_Offset); _GameInfo_BaseAddress = ReadPtr(iTemp + 0x550); //Player status in game : //[1] : P1 Playing //[2] : P2 Playing //[0] : Game Over int P1_Status = ReadByte(_GameInfo_BaseAddress + 0x00501D88) & 0x01; int P2_Status = ReadByte(_GameInfo_BaseAddress + 0x00501D88) >> 1 & 0x01; _P1_Life = ReadByte(_GameInfo_BaseAddress + 0x00501E70); _P2_Life = ReadByte(_GameInfo_BaseAddress + 0x00501E72); if (P1_Status != 0) { //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_LastLife = _P1_Life; } else { _P1_Life = 0; _P1_LastLife = 0; } if (P2_Status != 0) { //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); _P2_LastLife = _P2_Life; } else { _P2_Life = 0; _P2_LastLife = 0; } SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsPtr_Offset) + 0x22)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Model2Vcop.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_Model2Vcop : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Buttons_Injection_Offset = 0x000CBA64; private UInt32 _Reload_Injection_Offset = 0x000C8A3E; private UInt32 _P1_Axis_Injection_Offset_1 = 0x000CAAF5; private UInt32 _P2_Axis_Injection_Offset_1 = 0x000CAB2F; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _Buttons_CaveAddress; private UInt32 _Reload_CaveAddress; //Outputs private UInt32 _OutputsPtr_Offset = 0x001AA730; private UInt32 _Outputs_BaseAddress; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_Model2Vcop(String RomName) : base(RomName, "EMULATOR") { _KnownMd5Prints.Add("Model2Emulator 1.1a", "26bd488f9a391dcac1c5099014aa1c9e"); _KnownMd5Prints.Add("Model2Emulator 1.1a multicpu", "ac59ce7cfb95d6d639c0f0d1afba1192"); _tProcess.Start(); Logger.WriteLog("Waiting for Model2 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { if (p.ProcessName.Equals("EMULATOR") || p.ProcessName.Equals("emulator_multicpu")) { _Target_Process_Name = p.ProcessName; _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X [0x0000 -> 0x01EF] //Y [0x0000 -> 0x017F] double dMaxX = 496.0; double dMaxY = 384.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x04; _P2_X_CaveAddress = _InputsDatabank_Address + 0x08; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x0C; _Buttons_CaveAddress = _InputsDatabank_Address + 0x10; _Reload_CaveAddress = _InputsDatabank_Address + 0x11; //Buttons : The game is reading the Byte containing Buttons info. Replacing the real address with our own byte[] b = BitConverter.GetBytes(_Buttons_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset + 2, b); //Same thing for Reload flag b = BitConverter.GetBytes(_Reload_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Reload_Injection_Offset + 1, b); //Axis SetHack_P1Axis(_P1_Axis_Injection_Offset_1, 8); SetHack_P2Axis(_P2_Axis_Injection_Offset_1, 8); //Initial values WriteBytes(_P1_X_CaveAddress, BitConverter.GetBytes((Int32)0xA5)); WriteBytes(_P1_Y_CaveAddress, BitConverter.GetBytes((Int32)0xC0)); WriteBytes(_P2_X_CaveAddress, BitConverter.GetBytes((Int32)0x14A)); WriteBytes(_P2_Y_CaveAddress, BitConverter.GetBytes((Int32)0xC0)); WriteByte(_Buttons_CaveAddress, 0xFF); WriteByte(_Reload_CaveAddress, 0x00); Logger.WriteLog("nputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_P1Axis(UInt32 InjectionOffset, UInt32 Length) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ecx,[_P1_X_CaveAddress] CaveMemory.Write_StrBytes("8B 0D"); byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_Bytes(b); //mov edx,[_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 15"); b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_Bytes(b); //return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + InjectionOffset + Length); Logger.WriteLog("Adding P1Axis CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + InjectionOffset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + InjectionOffset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P2Axis(UInt32 InjectionOffset, UInt32 Length) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ecx,[_P1_X_CaveAddress] CaveMemory.Write_StrBytes("8B 0D"); byte[] b = BitConverter.GetBytes(_P2_X_CaveAddress); CaveMemory.Write_Bytes(b); //mov edx,[_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 15"); b = BitConverter.GetBytes(_P2_Y_CaveAddress); CaveMemory.Write_Bytes(b); //return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + InjectionOffset + Length); Logger.WriteLog("Adding P2Axis CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + InjectionOffset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + InjectionOffset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Reload_CaveAddress, 0x01); Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); Apply_AND_ByteMask(_Reload_CaveAddress, 0xFE); } } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Reload_CaveAddress, 0x2); Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); Apply_AND_ByteMask(_Reload_CaveAddress, 0xFD); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _OutputsPtr_Offset); _Outputs_BaseAddress = ReadPtr(iTemp + 0x630); SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Outputs_BaseAddress + 0x0050169C) >> 2 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Outputs_BaseAddress + 0x0050169C) >> 3 & 0x01); //custom Outputs int P1_Status = ReadByte(_Outputs_BaseAddress + 0x0050EE30); int P2_Status = ReadByte(_Outputs_BaseAddress + 0x0050EE34); _P1_Life = ReadByte(_Outputs_BaseAddress + 0x0050EE70); _P2_Life = ReadByte(_Outputs_BaseAddress + 0x0050EE74); _P1_Ammo = ReadByte(_Outputs_BaseAddress + 0x0050EE38); _P2_Ammo = ReadByte(_Outputs_BaseAddress + 0x0050EE3C); if (P1_Status == 1) { //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo <= 0) SetOutputValue(OutputId.P1_Clip, 0); else SetOutputValue(OutputId.P1_Clip, 1); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; } else { SetOutputValue(OutputId.P1_Clip, 0); _P1_Ammo = 0; _P1_LastAmmo = 0; _P1_Life = 0; _P1_LastLife = 0; } if (P2_Status == 1) { //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo <= 0) SetOutputValue(OutputId.P2_Clip, 0); else SetOutputValue(OutputId.P2_Clip, 1); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; } else { SetOutputValue(OutputId.P2_Clip, 0); _P2_Ammo = 0; _P2_LastAmmo = 0; _P2_Life = 0; _P2_LastLife = 0; } SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.Credits, ReadByte(_Outputs_BaseAddress + 0x00559270)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Model2Vcop2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_Model2Vcop2 : Game { /*** MEMORY ADDRESSES **/ private UInt32 _Buttons_Injection_Offset = 0x000C88F0; private UInt32 _Reload_Injection_Offset = 0x000C89B8; private InjectionStruct _P1_Axis_InjectionStruct = new InjectionStruct(0x000CAAF5, 8); private InjectionStruct _P2_Axis_InjectionStruct = new InjectionStruct(0x000CAB2F, 8); private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _Buttons_CaveAddress; private UInt32 _Reload_CaveAddress; //Outputs private UInt32 _CreditsPtr_Offset = 0x001AA71C; private UInt32 _Outputs_Offset = 0x00174CF0; private UInt32 _GameInfoPtr_Offset = 0x001AA730; private UInt32 _GameInfo_BaseAddress; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_Model2Vcop2(String RomName) : base(RomName, "EMULATOR") { _KnownMd5Prints.Add("Model2Emulator 1.1a", "26bd488f9a391dcac1c5099014aa1c9e"); _KnownMd5Prints.Add("Model2Emulator 1.1a multicpu", "ac59ce7cfb95d6d639c0f0d1afba1192"); _tProcess.Start(); Logger.WriteLog("Waiting for Model2 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { if (p.ProcessName.Equals("EMULATOR") || p.ProcessName.Equals("emulator_multicpu")) { _Target_Process_Name = p.ProcessName; _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X [0x0000 -> 0x01EF] //Y [0x0000 -> 0x017F] double dMaxX = 496.0; double dMaxY = 384.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x04; _P2_X_CaveAddress = _InputsDatabank_Address + 0x08; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x0C; _Buttons_CaveAddress = _InputsDatabank_Address + 0x10; _Reload_CaveAddress = _InputsDatabank_Address + 0x11; //Buttons : The game is reading the Byte containing Buttons info. Replacing the real address with our own byte[] b = BitConverter.GetBytes(_Buttons_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset + 1, b); //Same thing for Reload flag b = BitConverter.GetBytes(_Reload_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Reload_Injection_Offset + 1, b); //Axis SetHack_P1Axis(); SetHack_P2Axis(); //Initial values WriteBytes(_P1_X_CaveAddress, BitConverter.GetBytes((Int32)0xA5)); WriteBytes(_P1_Y_CaveAddress, BitConverter.GetBytes((Int32)0xC0)); WriteBytes(_P2_X_CaveAddress, BitConverter.GetBytes((Int32)0x14A)); WriteBytes(_P2_Y_CaveAddress, BitConverter.GetBytes((Int32)0xC0)); WriteByte(_Buttons_CaveAddress, 0xFF); WriteByte(_Reload_CaveAddress, 0x03); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_P1Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ecx,[_P1_X_CaveAddress] CaveMemory.Write_StrBytes("8B 0D"); byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_Bytes(b); //mov edx,[_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 15"); b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_Bytes(b); //Inject it CaveMemory.InjectToOffset(_P1_Axis_InjectionStruct, "P1 Axis"); } private void SetHack_P2Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ecx,[_P1_X_CaveAddress] CaveMemory.Write_StrBytes("8B 0D"); byte[] b = BitConverter.GetBytes(_P2_X_CaveAddress); CaveMemory.Write_Bytes(b); //mov edx,[_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 15"); b = BitConverter.GetBytes(_P2_Y_CaveAddress); CaveMemory.Write_Bytes(b); //Inject it CaveMemory.InjectToOffset(_P2_Axis_InjectionStruct, "P2 Axis"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_AND_ByteMask(_Reload_CaveAddress, 0xFE); Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); Apply_OR_ByteMask(_Reload_CaveAddress, 0x01); } } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_AND_ByteMask(_Reload_CaveAddress, 0xFD); Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); Apply_OR_ByteMask(_Reload_CaveAddress, 0x02); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, bOutput >> 2 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bOutput >> 3 & 0x01); //Custom Outputs UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _GameInfoPtr_Offset); _GameInfo_BaseAddress = ReadPtr(iTemp + 0x550); int GameStatus = ReadByte(_GameInfo_BaseAddress + 0x0050003D); int P1_Status = ReadByte(_GameInfo_BaseAddress + 0x00502088); int P2_Status = ReadByte(_GameInfo_BaseAddress + 0x0050208C); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (GameStatus == 1) { if (P1_Status == 1) { _P1_Life = ReadByte(_GameInfo_BaseAddress + 0x00502090); _P1_Ammo = ReadByte(_GameInfo_BaseAddress + 0x00507260); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; } if (P2_Status == 1) { _P2_Life = ReadByte(_GameInfo_BaseAddress + 0x00502094); _P2_Ammo = ReadByte(_GameInfo_BaseAddress + 0x00507264); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo <= 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; } } SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P1_Clip, P2_Clip); SetOutputValue(OutputId.Credits, ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsPtr_Offset) + 0x128)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_PpmPoliceTrainer2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.Win32; namespace DemulShooter { class Game_PpmPoliceTrainer2 : Game { //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction_v001g11 = 0x0806C18D; //Set life when player join //Outputs Address private UInt32 _P1_Life_Address = 0x08236C80; private UInt32 _P1_Ammo_Address = 0x08236C88; private UInt32 _P2_Life_Address = 0x08236F08; private UInt32 _P2_Ammo_Address = 0x08236F10; private UInt32 _Lamps_Address = 0x82307F0; private UInt32 _Credits_Address = 0x08230864; //private UInt32 _Buttons_Address = 0x082307C8; private InjectionStruct _P1Recoil_InjectionStruct = new InjectionStruct(0x08056454, 6); private InjectionStruct _P2Recoil_InjectionStruct = new InjectionStruct(0x08056482, 6); //custom values private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_PpmPoliceTrainer2(String RomName) : base(RomName, "BudgieLoader") { _KnownMd5Prints.Add("Police Trainer 2 - 0.0.1g11 - Original dump", "e40bd5c6a7f2c3a84281e115c25d3f20"); _tProcess.Start(); Logger.WriteLog("Waiting for Raw Thrill " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { //Looks like that game is opening a couple of BudgieLoader processes, we need to find the good one... foreach (Process p in processes) { _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting byte[] buffer = ReadBytes(_RomLoaded_check_Instruction_v001g11, 3); if (buffer[0] == 0x89 && buffer[1] == 0x03 && buffer[2] == 0x83) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Police Trainer 2 - 0.0.1g11 binary detected"); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } else { Logger.WriteLog("Game not Loaded, waiting..."); } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 4; SetHack_Recoil_P1(); SetHack_Recoil_P2(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The game is stacking Trigger press on a counter, but both P1 & P2 are stored in the same one /// We can intercept the counter update to split it into 2 different ones, to detect trigger press /// private void SetHack_Recoil_P1() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp dword ptr [_P1_Life_Address],00 CaveMemory.Write_StrBytes("83 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Life_Address)); CaveMemory.Write_StrBytes("00"); //js Next CaveMemory.Write_StrBytes("78 0A"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,_P1_RecoilStatus_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress)); //mov byte ptr[eax], 1 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //Next: //mov [08230900],edx CaveMemory.Write_StrBytes("89 15 00 09 23 08"); //return CaveMemory.Write_jmp(_P1Recoil_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding P1 Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress -_P1Recoil_InjectionStruct.InjectionOffset - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); for (int i = 0; i < _P1Recoil_InjectionStruct.NeededNops; i++) { Buffer.Add(0x90); } Win32API.WriteProcessMemory(ProcessHandle, _P1Recoil_InjectionStruct.InjectionOffset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// The game is stacking Trigger press on a counter, but both P1 & P2 are stored in the same one /// We can intercept the counter update to split it into 2 different ones, to detect trigger press /// private void SetHack_Recoil_P2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp dword ptr [_P2_Life_Address],00 CaveMemory.Write_StrBytes("83 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Life_Address)); CaveMemory.Write_StrBytes("00"); //js Next CaveMemory.Write_StrBytes("78 0A"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,_P2_RecoilStatus_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_RecoilStatus_CaveAddress)); //mov byte ptr[eax], 1 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //Next: //mov [08230900],edx CaveMemory.Write_StrBytes("89 15 00 09 23 08"); //return CaveMemory.Write_jmp(_P2Recoil_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding P2 Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - _P2Recoil_InjectionStruct.InjectionOffset - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); for (int i = 0; i < _P2Recoil_InjectionStruct.NeededNops; i++) { Buffer.Add(0x90); } Win32API.WriteProcessMemory(ProcessHandle, _P2Recoil_InjectionStruct.InjectionOffset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpFront, ReadByte(_Lamps_Address) >> 6 & 0x01 ); SetOutputValue(OutputId.P2_LmpFront, ReadByte(_Lamps_Address) >> 7 & 0x01 ); //Custom Outputs _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Check if the Player is currently playing if (BitConverter.ToInt32(ReadBytes(_P1_Life_Address, 4), 0) > 0) //Life = -1 when player not playing { _P1_Ammo = BitConverter.ToInt32(ReadBytes(_P1_Ammo_Address, 4), 0); if (_P1_Ammo < 0) _P1_Ammo = 0; //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; _P1_Life = BitConverter.ToInt32(ReadBytes(_P1_Life_Address, 4), 0); if (_P1_Life < 0) _P1_Life = 0; if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } //Check if the Player is currently playing if (BitConverter.ToInt32(ReadBytes(_P2_Life_Address, 4), 0) > 0) //Life = -1 when player not playing { _P2_Ammo = BitConverter.ToInt32(ReadBytes(_P2_Ammo_Address, 4), 0); if (_P2_Ammo < 0) _P2_Ammo = 0; //[Clip] custom Output if (_P2_Ammo > 0) P2_Clip = 1; _P2_Life = BitConverter.ToInt32(ReadBytes(_P2_Life_Address, 4), 0); if (_P2_Life < 0) _P2_Life = 0; if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Reading our own flags for damage and recoil and resetting them if (ReadByte(_P1_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilStatus_CaveAddress, 0); } if (ReadByte(_P2_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilStatus_CaveAddress, 0); } SetOutputValue(OutputId.Credits, BitConverter.ToInt32(ReadBytes(_Credits_Address, 4), 0)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_Re2Transformers2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_Re2Transformers2 : Game { //Memory values private UInt32 _pPlayerManager_Address = 0x200F7CD8; private UInt32 _pCreditsManager_Address = 0x200E9750; private UInt32 _CPlayer1_Offset = 0x34; private UInt32 _CPlayer2_Offset = 0x38; private UInt32 _Cplayer_ID_Offset = 0x60; private UInt32 _Cplayer_Mode_Offset = 0x64; private UInt32 _Cplayer_Life_Offset = 0x68; private UInt32 _CreditMgr_IncCoin_Function_Offset = 0x00066DE0; private UInt32 _FrameRateMgr_ExecServer_Function_Offset = 0x00D70D0; private UInt32 _GmMgr_GetGameMode_Function_Offset = 0x000E43C0; private UInt32 _GetResolution_Function_Offset = 0x002608B0; private UInt32 _PlMgr_IsHoldMode_FunctionOffset = 0x0024DDE0; private UInt32 _LaserSight_Patch_Offset = 0x002E558A; private UInt32 _PlayerInputMode_Patch_Offset = 0x000F751A; private InjectionStruct _Axis_InjectionStruct = new InjectionStruct(0x000F75CD, 6); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x000F787F, 5); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x000F6DB0, 5); private InjectionStruct _FixCrosshair_InjectionStruct = new InjectionStruct(0x000F6DB5, 7); private InjectionStruct _FixEnnemyTarget_InjectionStruct = new InjectionStruct(0x004EEF9D, 5); private InjectionStruct _Credits_InjectionStruct = new InjectionStruct(0x00240BCB, 5); private InjectionStruct _StartLamps_InjectionStruct = new InjectionStruct(0x00067159, 5); private InjectionStruct _GunLamps_InjectionStruct = new InjectionStruct(0x000FC124, 5); private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x0024AAFF, 6); private InjectionStruct _Damage_InjectionStruct = new InjectionStruct(0x00246C69, 6); //Custom Input Address private UInt32 _P1_StartOn_Address; private UInt32 _P1_StartPress_Address; private UInt32 _P2_StartOn_Address; private UInt32 _P2_StartPress_Address; private UInt32 _P1_TriggerOn_Address; private UInt32 _P1_TriggerPress_Address; private UInt32 _P2_TriggerOn_Address; private UInt32 _P2_TriggerPress_Address; private UInt32 _LeverFrontOn_Address; private UInt32 _LeverFrontPress_Address; private UInt32 _LeverBackOn_Address; private UInt32 _LeverBackPress_Address; private UInt32 _P1_X_Address; private UInt32 _P1_Y_Address; private UInt32 _P2_X_Address; private UInt32 _P2_Y_Address; private UInt32 _AddCredit_Address; private UInt32 _P1_LmpStart_Address; private UInt32 _P2_LmpStart_Address; private UInt32 _P1_LmpGun_Address; private UInt32 _P2_LmpGun_Address; private UInt32 _P1_Recoil_Address; private UInt32 _P2_Recoil_Address; private UInt32 _P1_Damage_Address; private UInt32 _P2_Damage_Address; /// /// Constructor /// public Game_Re2Transformers2(String RomName) : base(RomName, "Transformers2") { _KnownMd5Prints.Add("Transformers Shadow Rising v180605 - Original Dump", "b3b1f4ad6408d6ee946761a00f761455"); _tProcess.Start(); Logger.WriteLog("Waiting for RingEdge2 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; //Running DemulShooter before the game can cause it to find an empty window at start if (_GameWindowHandle == IntPtr.Zero) return; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("GameWindow Title = " + Get_GameWindowTitle()); CheckExeMd5(); //ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Axis values : 0x00 -> 0xFF double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and filtering Triggers input to replace them without blocking other input /// protected override void Apply_InputsMemoryHack() { // This will be used to store custom input values // Games has 2 distinct requets for button : "press" and "on" Create_InputsDataBank(); _P1_StartOn_Address = _InputsDatabank_Address; _P2_StartOn_Address = _InputsDatabank_Address + 0x04; _P1_StartPress_Address = _InputsDatabank_Address + 0x08; _P2_StartPress_Address = _InputsDatabank_Address + 0x0C; _P1_TriggerOn_Address = _InputsDatabank_Address + 0x10; _P2_TriggerOn_Address = _InputsDatabank_Address + 0x14; _P1_TriggerPress_Address = _InputsDatabank_Address + 0x18; _P2_TriggerPress_Address = _InputsDatabank_Address + 0x1C; _LeverFrontOn_Address = _InputsDatabank_Address + 0x20; _LeverFrontPress_Address = _InputsDatabank_Address + 0x24; _LeverBackOn_Address = _InputsDatabank_Address + 0x28; _LeverBackPress_Address = _InputsDatabank_Address + 0x2C; _P1_X_Address = _InputsDatabank_Address + 0x30; _P1_Y_Address = _InputsDatabank_Address + 0x34; _P2_X_Address = _InputsDatabank_Address + 0x38; _P2_Y_Address = _InputsDatabank_Address + 0x3C; _AddCredit_Address = _InputsDatabank_Address + 0x40; SetHack_Axis(); SetHack_Buttons(); SetHack_Credits(); SetHack_CorrectReticlePosition(); SetHack_CorrectEnnemyTarget(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// ///Hacking Axis proc : ///In CGunMgr::MainProc() ///Input type beeing force to "Mouse", the hack will separate P1/P2 proc (EBX value) and put values in ShotX and ShotX members of the INPUT struct ///At the end : ESI+4 receives X, and ESI+5 for Y (one byte each) /// > private void SetHack_Axis() { //Forcing The InputMode to "1" (mouse) in the switch check in CGunMgr::MainProc loop WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayerInputMode_Patch_Offset, new byte[] { 0xB8, 0x01, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90 }); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //sar eax, 8 CaveMemory.Write_StrBytes("C1 F8 08"); //test ebx,ebx CaveMemory.Write_StrBytes("85 DB"); //jne Player2 CaveMemory.Write_StrBytes("75 14"); //mov eax,[_P1_X_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_Address)); //mov [esi+4], al CaveMemory.Write_StrBytes("88 46 04"); //mov eax,[_P1_Y_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Y_Address)); //mov [esi+5], al CaveMemory.Write_StrBytes("88 46 05"); //jmp exit CaveMemory.Write_StrBytes("EB 12"); //mov eax,[_P2_X_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_X_Address)); //mov [esi+4], al CaveMemory.Write_StrBytes("88 46 04"); //mov eax,[_P2_Y_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Y_Address)); //mov [esi+5], al CaveMemory.Write_StrBytes("88 46 05"); //Inject it CaveMemory.InjectToOffset(_Axis_InjectionStruct, "Axis"); } /// /// Ath the end of the CGun::MainProc we are overwritting buttons values with data read on our custom Databank /// Looks like putting 1 in the Button_On byte is not necesary for gameplay (autofire still on) and causes repeated inputs on Name entry screen /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push edi CaveMemory.Write_StrBytes("57"); //shl ebx, 2 CaveMemory.Write_StrBytes("C1 E3 02"); //mov eax, _P1_TriggerPress_Address CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_TriggerPress_Address)); //add eax, ebx CaveMemory.Write_StrBytes("01 D8"); //mov edi, [eax] CaveMemory.Write_StrBytes("8B 38"); //mov [esi+08],edi CaveMemory.Write_StrBytes("89 7E 08"); ////mov eax, _P1_TriggerOn_Address //CaveMemory.Write_StrBytes("B8"); //CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_TriggerOn_Address)); ////add eax, ebx //CaveMemory.Write_StrBytes("01 D8"); ////mov edi, [eax] //CaveMemory.Write_StrBytes("8B 38"); ////mov [esi+0C],edi //CaveMemory.Write_StrBytes("89 7E 0C"); //mov eax, _P1_StartPress_Address CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_StartPress_Address)); //add eax, ebx CaveMemory.Write_StrBytes("01 D8"); //mov edi, [eax] CaveMemory.Write_StrBytes("8B 38"); //mov [esi+28],edi CaveMemory.Write_StrBytes("89 7E 28"); ////mov eax, _P1_StartOn_Address //CaveMemory.Write_StrBytes("B8"); //CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_StartOn_Address)); ////add eax, ebx //CaveMemory.Write_StrBytes("01 D8"); ////mov edi, [eax] //CaveMemory.Write_StrBytes("8B 38"); ////mov [esi+2C],edi //CaveMemory.Write_StrBytes("89 7E 2C"); //mov eax, _LeverFrontPress_Address CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_LeverFrontPress_Address)); //mov edi, [eax] CaveMemory.Write_StrBytes("8B 38"); //mov [esi+30],edi CaveMemory.Write_StrBytes("89 7E 30"); //mov eax, _LeverFrontOn_Address CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_LeverFrontOn_Address)); //mov edi, [eax] CaveMemory.Write_StrBytes("8B 38"); //mov [esi+34],edi CaveMemory.Write_StrBytes("89 7E 34"); //mov eax, _LeverBackPress_Address CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_LeverBackPress_Address)); //mov edi, [eax] CaveMemory.Write_StrBytes("8B 38"); //mov [esi+38],edi CaveMemory.Write_StrBytes("89 7E 38"); //mov eax, _LeverBackOn_Address CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_LeverBackOn_Address)); //mov edi, [eax] CaveMemory.Write_StrBytes("8B 38"); //mov [esi+3C],edi CaveMemory.Write_StrBytes("89 7E 3C"); //shr ebx, 2 CaveMemory.Write_StrBytes("C1 EB 02"); //pop edi CaveMemory.Write_StrBytes("5F"); //xor eax, eax CaveMemory.Write_StrBytes("31 C0"); //Inject it CaveMemory.InjectToOffset(_Buttons_InjectionStruct, "Buttons"); } /// /// Adding a byte check on a custom "Set Credits" flag in the App::Main loop /// That way we can call an existing function to add credits and play sound /// Changing the credits value is quicker but sound will not be played.... /// private void SetHack_Credits() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp dword ptr [_AddCredit_Address],00 CaveMemory.Write_StrBytes("83 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_AddCredit_Address)); CaveMemory.Write_StrBytes("00"); //je originalcode CaveMemory.Write_StrBytes("0F 84 14 00 00 00"); //mov [_AddCredit_Address],00 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_AddCredit_Address)); CaveMemory.Write_StrBytes("00 00 00 00"); //push 00 CaveMemory.Write_StrBytes("6A 00"); //call CreditMgr_IncCoin() CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _CreditMgr_IncCoin_Function_Offset); //add esp, 4 CaveMemory.Write_StrBytes("83 C4 04"); //Originalcode //call _FrameRateMgr_ExecServer_Function_Offset() CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _FrameRateMgr_ExecServer_Function_Offset); //Inject it CaveMemory.InjectToOffset(_Credits_InjectionStruct, "Credits"); } /// /// Reticle seems to be misaligned if resolution is not 1920x1080 /// private void SetHack_CorrectReticlePosition() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [esp+14],00000780 CaveMemory.Write_StrBytes("C7 44 24 14 80 07 00 00"); //mov [esp+10],00000438 CaveMemory.Write_StrBytes("C7 44 24 10 38 04 00 00"); //mov eax,[ebp-04] CaveMemory.Write_StrBytes("8B 45 FC"); //movd xmm0,eax CaveMemory.Write_StrBytes("66 0F 6E C0"); //Inject it CaveMemory.InjectToOffset(_FixCrosshair_InjectionStruct, "FixCrosshair"); } /// /// On a different resolution than 1920x1080, ennemy targets on screen are mislocated /// The game is measuring window width and window height but even with this, it's buggy /// Forcing window dimension to 1920p fix the issue (strangely ?) /// For that, the game calls to get center values /// Then the game calls for WindowSize /// = Forcing Center and max values /// private void SetHack_CorrectEnnemyTarget() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [ebp-4], 44700000 CaveMemory.Write_StrBytes("C7 45 FC 00 00 70 44"); //mov [ebp-8], 44070000 CaveMemory.Write_StrBytes("C7 45 F8 00 00 07 44"); //mov [ebp-18], 44870000 CaveMemory.Write_StrBytes("C7 45 E8 00 00 87 44"); //mov [ebp-10], 44f00000 CaveMemory.Write_StrBytes("C7 45 F0 00 00 F0 44"); //movss xmm0,[ebp-18] CaveMemory.Write_StrBytes("F3 0F 10 45 E8"); //Inject it CaveMemory.InjectToOffset(_FixEnnemyTarget_InjectionStruct, "FixEnnemyTarget"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_LmpStart_Address = _OutputsDatabank_Address; _P2_LmpStart_Address = _OutputsDatabank_Address + 0x04; _P1_LmpGun_Address = _OutputsDatabank_Address + 0x08; _P2_LmpGun_Address = _OutputsDatabank_Address + 0x0C; _P1_Recoil_Address = _OutputsDatabank_Address + 0x10; _P2_Recoil_Address = _OutputsDatabank_Address + 0x14; _P1_Damage_Address = _OutputsDatabank_Address + 0x18; _P2_Damage_Address = _OutputsDatabank_Address + 0x1C; SetHack_Output_StartLamp(); SetHack_Output_GunLamp(); SetHack_Recoil(); SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// CCreditMgr::ExecServer() ends it's loop by calling a blanked function to send Lamp Data /// Intercepting the call will allow to read the values /// private void SetHack_Output_StartLamp() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [_P1_LmpStart_Address], edi CaveMemory.Write_StrBytes("89 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_LmpStart_Address)); //mov [_P2_LmpStart_Address], ebx CaveMemory.Write_StrBytes("89 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_LmpStart_Address)); //Inject it CaveMemory.InjectToOffset(_StartLamps_InjectionStruct, "StartLamps"); } /// /// CCreditMgr::ExecServer() ends it's loop by calling a blanked function to send Lamp Data /// Intercepting the call will allow to read the values /// private void SetHack_Output_GunLamp() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [esp+4] CaveMemory.Write_StrBytes("8B 44 24 04"); //mov [_P1_LmpGun_Address], eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_LmpGun_Address)); //mov eax, [esp+8] CaveMemory.Write_StrBytes("8B 44 24 08"); //mov [_P2_LmpGun_Address], eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_LmpGun_Address)); //Inject it CaveMemory.InjectToOffset(_GunLamps_InjectionStruct, "GunLamps"); } /// /// When a bullet is fired, the game sends a message to the Shell executable and increment a bullet counter /// Intercepting the call allow us to put our own flag /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //movsx eax,byte ptr [esi+60] CaveMemory.Write_StrBytes("0F BE 46 60"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_Recoil_Address CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Recoil_Address)); //mov [eax], 1 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //movsx eax,byte ptr [esi+60] CaveMemory.Write_StrBytes("0F BE 46 60"); //push 00 CaveMemory.Write_StrBytes("6A 00"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// When a player takes dammage, the game sends a command to the Shell executable. /// Intercepting the call allow us to put our own flag /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [ebp+15],al CaveMemory.Write_StrBytes("88 45 15"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //mov al,[edi+60] CaveMemory.Write_StrBytes("8A 47 60"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_Damage_Address CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Damage_Address)); //mov [eax], 1 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //lea eax,[ebp+14] CaveMemory.Write_StrBytes("8D 45 14"); //Inject it CaveMemory.InjectToOffset(_Damage_InjectionStruct, "Dammage"); } /// /// The game is checking if Recoil is enabled for each bullet fired. /// Using this request call, we can generate the start of our own CustomRecoil output event /// private void SetHack_RecoilP1() { } private void SetHack_RecoilP2() { } /// /// Removing all UI targetting display : /// - Reticle (?) /// - Laser /// - Gun Model (deactivated when KID MODE is on) /// protected override void Apply_NoCrosshairMemoryHack() { if (_HideCrosshair) { // PlayerGunObj::UpdateLaserSight(PlayerGunObj *this) // Check if laser is on -> force JmMP to remove sight WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _LaserSight_Patch_Offset, 0xEB); } Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //call GmMgr_GetGameMode() CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _GmMgr_GetGameMode_Function_Offset); //cmp eax,06 CaveMemory.Write_StrBytes("83 F8 06"); //jb original code CaveMemory.Write_StrBytes("72 1A"); //cmp eax,07 CaveMemory.Write_StrBytes("83 F8 07"); //ja original code CaveMemory.Write_StrBytes("77 15"); //call _PlMgr_IsHoldMode CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _PlMgr_IsHoldMode_FunctionOffset); //test eax, eax CaveMemory.Write_StrBytes("85 C0"); //jne originalcode CaveMemory.Write_StrBytes("75 0C"); //mov [esi],C1F00000 CaveMemory.Write_StrBytes("C7 06 00 00 F0 C1"); //mov [edi],C1F00000 CaveMemory.Write_StrBytes("C7 07 00 00 F0 C1"); //Originalcode: //pop eax CaveMemory.Write_StrBytes("58"); //call GetResolution() CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _GetResolution_Function_Offset); //Inject it CaveMemory.InjectToOffset(_NoCrosshair_InjectionStruct, "No Crosshair"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes(PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes(PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_Address, bufferX); WriteBytes(_P1_Y_Address, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte(_P1_TriggerOn_Address, 0x01); WriteByte(_P1_TriggerPress_Address, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte(_P1_TriggerOn_Address, 0x00); WriteByte(_P1_TriggerPress_Address, 0x00); } } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_Address, bufferX); WriteBytes(_P2_Y_Address, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte(_P2_TriggerOn_Address, 0x01); WriteByte(_P2_TriggerPress_Address, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P2_TriggerPress_Address, 0x00); } } public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == Configurator.GetInstance().DIK_Tsr_Start_P1) { WriteByte(_P1_StartOn_Address, 0x01); WriteByte(_P1_StartPress_Address, 0x01); } else if (s.scanCode == Configurator.GetInstance().DIK_Tsr_Start_P2) { WriteByte(_P2_StartOn_Address, 0x01); WriteByte(_P2_StartPress_Address, 0x01); } else if (s.scanCode == Configurator.GetInstance().DIK_Tsr_LeverFront) { WriteByte(_LeverFrontOn_Address, 0x01); WriteByte(_LeverFrontPress_Address, 0x01); } else if (s.scanCode == Configurator.GetInstance().DIK_Tsr_LeverBack) { WriteByte(_LeverBackOn_Address, 0x01); WriteByte(_LeverBackPress_Address, 0x01); } else if (s.scanCode == Configurator.GetInstance().DIK_Tsr_Credits) WriteByte(_AddCredit_Address, 0x01); } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == Configurator.GetInstance().DIK_Tsr_Start_P1) { WriteByte(_P1_StartOn_Address, 0x00); WriteByte(_P1_StartPress_Address, 0x00); } else if (s.scanCode == Configurator.GetInstance().DIK_Tsr_Start_P2) { WriteByte(_P2_StartOn_Address, 0x00); WriteByte(_P2_StartPress_Address, 0x00); } else if (s.scanCode == Configurator.GetInstance().DIK_Tsr_LeverFront) { WriteByte(_LeverFrontOn_Address, 0x00); WriteByte(_LeverFrontPress_Address, 0x00); } else if (s.scanCode == Configurator.GetInstance().DIK_Tsr_LeverBack) { WriteByte(_LeverBackOn_Address, 0x00); WriteByte(_LeverBackPress_Address, 0x00); } } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : stays activated when trigger is pulled //Gun recoil : not used ?? _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.LmpRoom)); _Outputs.Add(new GameOutput(OutputId.LmpLever)); _Outputs.Add(new GameOutput(OutputId.Lmp_Downlight)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_P1_LmpStart_Address)); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_P2_LmpStart_Address)); SetOutputValue(OutputId.P1_LmpGun, ReadByte(_P1_LmpGun_Address)); SetOutputValue(OutputId.P2_LmpGun, ReadByte(_P2_LmpGun_Address)); //To-do : //Lamp side, Lever and recoil ?? //Custom Outputs _P1_Life = 0; _P2_Life = 0; int Credits = 0; //Life is read in a struct that is only created when a game has started UInt32 PlayerManager = ReadPtr(_pPlayerManager_Address); if (PlayerManager != 0) { UInt32 CPlayer1 = ReadPtr(PlayerManager + _CPlayer1_Offset); if (CPlayer1 != 0) { if (ReadByte(CPlayer1 + _Cplayer_Mode_Offset) == 3 && ReadByte(CPlayer1 + _Cplayer_ID_Offset) == 0) { _P1_Life = BitConverter.ToInt32(ReadBytes(CPlayer1 + _Cplayer_Life_Offset, 4), 0); } } UInt32 CPlayer2 = ReadPtr(PlayerManager + _CPlayer2_Offset); if (CPlayer2 != 0) { if (ReadByte(CPlayer2 + _Cplayer_Mode_Offset) == 3 && ReadByte(CPlayer2 + _Cplayer_ID_Offset) == 1) { _P2_Life = BitConverter.ToInt32(ReadBytes(CPlayer2 + _Cplayer_Life_Offset, 4), 0); } } } //Reading our own flags and resetting them if (ReadByte(_P1_Recoil_Address) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_Recoil_Address, 0); } if (ReadByte(_P2_Recoil_Address) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_Recoil_Address, 0); } if (ReadByte(_P1_Damage_Address) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_Damage_Address, 0); } if (ReadByte(_P2_Damage_Address) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_Damage_Address, 0); } SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); UInt32 CCreditsManager = ReadPtr(_pCreditsManager_Address); if (CCreditsManager != 0) { Credits = ReadByte(CCreditsManager + 0x38); } SetOutputValue(OutputId.Credits, Credits); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RtAliensArmageddon.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_RtAliensArmageddon : Game { //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction_v390 = 0x08088211; private InjectionStruct _P1_NoCrosshair_InjectionStruct = new InjectionStruct(0x0807109C, 6); private InjectionStruct _P2_NoCrosshair_InjectionStruct = new InjectionStruct(0x0807111A, 6); //Outputs Address private UInt32 _P1_Struct_Address = 0x08DAB920; private UInt32 _P2_Struct_Address = 0x08DABAF0; private UInt32 _Lamp_Address = 0x8ECEC5C; private UInt32 _RecoilStatus_CaveAddress; private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x080883B5, 11); private int _P1_Last_Weapon = 0; private int _P2_Last_Weapon = 0; /// /// Constructor /// public Game_RtAliensArmageddon(String RomName) : base(RomName, "BudgieLoader") { _KnownMd5Prints.Add("Aliens Armageddon - 03.90 USA", "fe95d8a34331b95d14f788220e6b8fed"); _tProcess.Start(); Logger.WriteLog("Waiting for Raw Thrill " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting byte[] buffer = ReadBytes(_RomLoaded_check_Instruction_v390, 3); if (buffer[0] == 0x83 && buffer[1] == 0xEA && buffer[2] == 0x01) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Aliens Arageddon - 03.90 USA binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Aliens Armageddon - 03.90 USA"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game not Loaded, waiting..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack /// /// At that place, game acknoledged a gun0 or gun1 fired /// [ESP+4D] has a pointer to the struct containing the weapon's name, so it's possible to exclude flamethrower from custom recoil /// Names can be assault_rifle, shotgun, minigun, turret, fthrower, sniper, gunpod_2, launcher /// protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _RecoilStatus_CaveAddress = _OutputsDatabank_Address; Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[esp+000000D4] CaveMemory.Write_StrBytes("8B 84 24 D4 00 00 00"); //add eax,04 CaveMemory.Write_StrBytes("83 C0 04"); //cmp [eax],72687466 CaveMemory.Write_StrBytes("81 38 66 74 68 72"); //HEX equivalent of "fthr to exclude flamethrower (quicket than strcmp) //je originalcode CaveMemory.Write_StrBytes("74 07"); //mov byte ptr [ebx+_RecoilStatus_CaveAddress],01 CaveMemory.Write_StrBytes("C6 83"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_RecoilStatus_CaveAddress)); CaveMemory.Write_StrBytes("01"); //Originalcode: //mov [esp+04],edi CaveMemory.Write_StrBytes("89 7C 24 04"); //mov [esp],00000000 CaveMemory.Write_StrBytes("C7 04 24 00 00 00 00"); //Inject it CaveMemory.InjectToAddress(_Recoil_InjectionStruct, "Recoil"); } /// /// To remove crosshair, we will change the cursor drawinf location to -1.0 to hide it. /// Small filtering added : looking at the player "playing" flag so that crosshair will still be visible on TEST menu /// protected override void Apply_NoCrosshairMemoryHack() { Apply_P1NoCrosshair_Hack(); Apply_P2NoCrosshair_Hack(); } private void Apply_P1NoCrosshair_Hack() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp dword ptr [0890B2B0] CaveMemory.Write_StrBytes("D9 1D B0 B2 90 08"); //cmp byte ptr [08DAB921],01 CaveMemory.Write_StrBytes("80 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Struct_Address + 1)); CaveMemory.Write_StrBytes("01"); //jne exit CaveMemory.Write_StrBytes("75 0A"); //mov [0890B2B0],BF800000 CaveMemory.Write_StrBytes("C7 05 B0 B2 90 08 00 00 80 BF"); //Inject it CaveMemory.InjectToAddress(_P1_NoCrosshair_InjectionStruct, "P1 NoCrosshair"); } private void Apply_P2NoCrosshair_Hack() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp dword ptr [0890B2B4] CaveMemory.Write_StrBytes("D9 1D B4 B2 90 08"); //cmp byte ptr [0x08DABAF1],01 CaveMemory.Write_StrBytes("80 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Struct_Address + 1)); CaveMemory.Write_StrBytes("01"); //jne exit CaveMemory.Write_StrBytes("75 0A"); //mov [0890B2B4],BF800000 CaveMemory.Write_StrBytes("C7 05 B4 B2 90 08 00 00 80 BF"); //InjectIt CaveMemory.InjectToAddress(_P2_NoCrosshair_InjectionStruct, "P2 NoCrosshair"); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGunGrenadeBtn)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGunMolding)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGunGrenadeBtn)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGunMolding)); _Outputs.Add(new GameOutput(OutputId.LmpSpeaker)); _Outputs.Add(new GameOutput(OutputId.LmpMarqueeBacklight)); _Outputs.Add(new GameOutput(OutputId.LmpMarqueeUplight)); _Outputs.Add(new GameOutput(OutputId.LmpUpperCtrlPanel)); _Outputs.Add(new GameOutput(OutputId.LmpLowerCtrlPanel)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); //In Teknoparrot, Guns hardware is not emulated, so the game is not running original gun recoil procedures _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); //_Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x24, 4), 0)); SetOutputValue(OutputId.P1_LmpHolder, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x3C, 4), 0)); SetOutputValue(OutputId.P1_LmpGun, ReadByte(_Lamp_Address)); SetOutputValue(OutputId.P1_LmpGunGrenadeBtn, ReadByte(_Lamp_Address + 0x10)); SetOutputValue(OutputId.P1_LmpGunMolding, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x48, 4), 0)); SetOutputValue(OutputId.P2_LmpStart, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x28, 4), 0)); SetOutputValue(OutputId.P2_LmpHolder, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x40, 4), 0)); SetOutputValue(OutputId.P2_LmpGun, ReadByte(_Lamp_Address + 0x0C)); SetOutputValue(OutputId.P2_LmpGunGrenadeBtn, ReadByte(_Lamp_Address + 0x14)); SetOutputValue(OutputId.P2_LmpGunMolding, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x4C, 4), 0)); SetOutputValue(OutputId.LmpSpeaker, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x50, 4), 0)); SetOutputValue(OutputId.LmpBillboard, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x38, 4), 0)); SetOutputValue(OutputId.LmpMarqueeBacklight, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x34, 4), 0)); SetOutputValue(OutputId.LmpMarqueeUplight, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x44, 4), 0)); SetOutputValue(OutputId.LmpUpperCtrlPanel, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x2c, 4), 0)); SetOutputValue(OutputId.LmpLowerCtrlPanel, BitConverter.ToInt32(ReadBytes(_Lamp_Address + 0x30, 4), 0)); //Custom Outputs _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Check if the Player is currently playing if (ReadByte(_P1_Struct_Address + 0x01) == 1) { _P1_Ammo = BitConverter.ToInt32(ReadBytes(_P1_Struct_Address + 0x68, 4), 0); if (_P1_Ammo < 0) _P1_Ammo = 0; //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; _P1_Life = BitConverter.ToInt32(ReadBytes(_P1_Struct_Address + 0x10, 4), 0); if (_P1_Life < 0) _P1_Life = 0; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); //Custom recoil by reading and resetting FLAG if (ReadByte(_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_RecoilStatus_CaveAddress, 0); } } //Check if the Player is currently playing if (ReadByte(_P2_Struct_Address + 0x01) == 1) { _P2_Ammo = BitConverter.ToInt32(ReadBytes(_P2_Struct_Address + 0x68, 4), 0); if (_P2_Ammo < 0) _P2_Ammo = 0; //[Clip] custom Output if (_P2_Ammo > 0) P2_Clip = 1; _P2_Life = BitConverter.ToInt32(ReadBytes(_P2_Struct_Address + 0x10, 4), 0); if (_P2_Life < 0) _P2_Life = 0; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); //Custom recoil by reading and resetting FLAG if (ReadByte(_RecoilStatus_CaveAddress + 1) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_RecoilStatus_CaveAddress + 1, 0); } } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Recoil : reading updated value from the game //SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RtJurassicPark.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.Win32; namespace DemulShooter { class Game_RtJurassicPark : Game { private const String GAMEDATA_FOLDER = @"MemoryData\lindbergh\jpark"; //Outputs Address private UInt32 _ReticleShowODR_Call_Address = 0x0819295A; private UInt32 _Lasers_Injection_Address = 0x0817FEF9; private UInt32 _Lasers_Injection_Return_Address = 0x0817FEFF; private UInt32 _Damage_Injection_Addres = 0x08174C6D; private UInt32 _Damage_Injection_Return_Addres = 0x08174C73; private UInt32 _Recoil01_Injection_Address = 0x081778C3; private UInt32 _Recoil01_Injection_Return_Address = 0x081778C9; private UInt32 _Recoil02_Injection_Address = 0x0817AB08; private UInt32 _Recoil02_Injection_Return_Address = 0x0817AB10; private UInt32 _gGameRecord_Address = 0x08D1F720; private UInt32 _gLampsVal_Address = 0x08B4CF60; private UInt32 P1_Weapon_Address = 0x8D20560; private UInt32 P2_Weapon_Address = 0x8D20564; private UInt32 P1_AmmoElectro_Address = 0x8D1F8A0; private UInt32 P2_AmmoElectro_Address = 0x8D1F8A4; private UInt32 P1_AmmoShotGun_Address = 0x8B4BF64; private UInt32 P2_AmmoShotGun_Address = 0x8B4BF88; private UInt32 P1_AmmoFreezeRay_Address = 0x8D1FAA8; private UInt32 P2_AmmoFreezeRay_Address = 0x8D1FAAC; private UInt32 P1_AmmoMinoGun_Address = 0x8B4BE44; private UInt32 P2_AmmoMinoGun_Address = 0x8B4BE74; private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; private UInt32 _P1_DamageStatus_CaveAddress = 0; private UInt32 _P2_DamageStatus_CaveAddress = 0; //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction_v108 = 0x08188F10; //private UInt32 _RomLoaded_check_Instruction_v133 = 0x08153065; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_RtJurassicPark(String RomName) : base(RomName, "BudgieLoader") { //Only for documentation, version check is done by reading code position in memory, as we don't have access to the ELF path _KnownMd5Prints.Add("Jurassic Park - v1.08 (unpatched ?)", "f24794f1bc8bf93031206578e4bdabf5"); _KnownMd5Prints.Add("Jurassic Park - v1.08", "c62483935c2ea3c8387f33b3c8b89c6b"); _tProcess.Start(); Logger.WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); } // /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting //And this instruction is also helping us detecting whether the game file is v1.08 or v1.33 binary, to call the corresponding hack byte[] buffer = ReadBytes(_RomLoaded_check_Instruction_v108, 5); if (buffer[0] == 0x83 && buffer[1] == 0xEC && buffer[2] == 0x0C && buffer[3] == 0x8B && buffer[4] == 0x4C) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Jurassic Park - v1.08 binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Jurassic Park - v1.08"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { /*buffer = ReadBytes(_RomLoaded_check_Instruction_RevC, 5); if (buffer[0] == 0xE8 && buffer[1] == 0x42 && buffer[2] == 0x0D && buffer[3] == 0x00 && buffer[4] == 0x00) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("House Of The Dead 4 - Rev.C binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["House of The Dead 4 - Rev.C"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); if (!_DisableInputHack) SetHack(); else Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game not Loaded, waiting..."); }*/ } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 4; _P1_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x10; _P2_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x14; SetHack_Damage(); SetHack_Recoil1(); SetHack_Recoil2(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Remove Laser aiming display /// private void SetHack_Lasers() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [esp+20],00000000 CaveMemory.Write_StrBytes("C7 44 24 20 00 00 00 00"); //mov [esp+24],00000000 CaveMemory.Write_StrBytes("C7 44 24 24 00 00 00 00"); //mov eax,[ebx+00000228] CaveMemory.Write_StrBytes("8B 83 28 02 00 00"); CaveMemory.Write_jmp(_Lasers_Injection_Return_Address); Logger.WriteLog("Adding Lasers Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - _Lasers_Injection_Address - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, _Lasers_Injection_Address, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Intercep call to log damage taken by a player to set a custom flag to "1" /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push ebx CaveMemory.Write_StrBytes("53"); //shl ebx,02 CaveMemory.Write_StrBytes("C1 E3 02"); //add abx, _P1_DamageStatus_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_DamageStatus_CaveAddress)); //mov [ebx],00000001 CaveMemory.Write_StrBytes("C7 03 01 00 00 00"); //pop ebx CaveMemory.Write_StrBytes("5B"); //fld dword ptr [08D1F798] CaveMemory.Write_StrBytes("D9 05 98 F7 D1 08"); CaveMemory.Write_jmp(_Damage_Injection_Return_Addres); Logger.WriteLog("Adding Damage Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - _Damage_Injection_Addres - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, _Damage_Injection_Addres, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Intercept call for FFPulse() function to create our own flag /// Called when following weapon starts shooting : /// - Default Gun /// - Jeep Gun /// - Shotgun /// - FreezeGun /// - Minigun /// /// Does not work for ElectroGun /// private void SetHack_Recoil1() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,[esp+10] CaveMemory.Write_StrBytes("8B 44 24 10"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_RecoilStatus_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress)); //mov [eax],00000001 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //sub esp,00000330 CaveMemory.Write_StrBytes("81 EC 30 03 00 00"); CaveMemory.Write_jmp(_Recoil01_Injection_Return_Address); Logger.WriteLog("Adding FFPulse Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - _Recoil01_Injection_Address - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, _Recoil01_Injection_Address, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Intercept call for Start SHoot for ElectroGun /// private void SetHack_Recoil2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push ebx CaveMemory.Write_StrBytes("53"); //shl ebx,02 CaveMemory.Write_StrBytes("C1 E3 02"); //add abx, _P1_RecoilStatus_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress)); //mov [ebx],00000001 CaveMemory.Write_StrBytes("C7 03 01 00 00 00"); //pop ebx CaveMemory.Write_StrBytes("5B"); //mov [esp+04],00000004 CaveMemory.Write_StrBytes("C7 44 24 04 04 00 00 00"); CaveMemory.Write_jmp(_Recoil02_Injection_Return_Address); Logger.WriteLog("Adding ElectroGun Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - _Recoil02_Injection_Address - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, _Recoil02_Injection_Address, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } protected override void Apply_NoCrosshairMemoryHack() { if (_HideCrosshair) { //Replacing call to mShowODR() for reticle object by a call to mHideODR() WriteBytes(_ReticleShowODR_Call_Address + 1, new byte[] { 0x11, 0x86 }); //Codecave to remove Lasers SetHack_Lasers(); } else { //Setting it back to mShowODR() if it was changed by a previous instance of DemulSHooter WriteBytes(_ReticleShowODR_Call_Address + 1, new byte[] { 0xF1, 0x85 }); //Again, putting back original code for lasers instead of Codecave WriteBytes(_Lasers_Injection_Address, new byte[] { 0x8B, 0x83, 0x28, 0x02, 0x00, 0x00 }); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpDinoHead)); _Outputs.Add(new GameOutput(OutputId.LmpLogo)); _Outputs.Add(new GameOutput(OutputId.LmpDinoEyes)); _Outputs.Add(new GameOutput(OutputId.LmpRoof)); _Outputs.Add(new GameOutput(OutputId.LmpMarquee)); _Outputs.Add(new GameOutput(OutputId.LmpDash)); _Outputs.Add(new GameOutput(OutputId.LmpFoliage)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_R)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_G)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_B)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun_R)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun_G)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun_B)); _Outputs.Add(new GameOutput(OutputId.LmpSeat_R)); _Outputs.Add(new GameOutput(OutputId.LmpSeat_G)); _Outputs.Add(new GameOutput(OutputId.LmpSeat_B)); _Outputs.Add(new GameOutput(OutputId.LmpBenchLogo)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.LmpSeatBase)); _Outputs.Add(new GameOutput(OutputId.LmpEstop)); _Outputs.Add(new GameOutput(OutputId.LmpCompressor)); /*_Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor));*/ //Custom Outputs _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { SetOutputValue(OutputId.P1_LmpStart, GetLampValueAsInt(0)); SetOutputValue(OutputId.P2_LmpStart, GetLampValueAsInt(1)); SetOutputValue(OutputId.LmpDinoHead, GetLampValueAsInt(2)); SetOutputValue(OutputId.LmpLogo, GetLampValueAsInt(3)); SetOutputValue(OutputId.LmpDinoEyes, GetLampValueAsInt(4)); SetOutputValue(OutputId.LmpRoof, GetLampValueAsInt(5)); SetOutputValue(OutputId.LmpMarquee, GetLampValueAsInt(6)); SetOutputValue(OutputId.LmpDash, GetLampValueAsInt(7)); SetOutputValue(OutputId.LmpFoliage, GetLampValueAsInt(8)); SetOutputValue(OutputId.P1_LmpGun_R, GetLampValueAsInt(9)); SetOutputValue(OutputId.P1_LmpGun_G, GetLampValueAsInt(10)); SetOutputValue(OutputId.P1_LmpGun_B, GetLampValueAsInt(11)); SetOutputValue(OutputId.P2_LmpGun_R, GetLampValueAsInt(12)); SetOutputValue(OutputId.P2_LmpGun_G, GetLampValueAsInt(13)); SetOutputValue(OutputId.P2_LmpGun_B, GetLampValueAsInt(14)); SetOutputValue(OutputId.LmpSeat_R, GetLampValueAsInt(15)); SetOutputValue(OutputId.LmpSeat_G, GetLampValueAsInt(16)); SetOutputValue(OutputId.LmpSeat_B, GetLampValueAsInt(17)); SetOutputValue(OutputId.LmpBenchLogo, GetLampValueAsInt(18)); SetOutputValue(OutputId.P1_LmpHolder, GetLampValueAsInt(19)); SetOutputValue(OutputId.P2_LmpHolder, GetLampValueAsInt(20)); SetOutputValue(OutputId.LmpSeatBase, GetLampValueAsInt(21)); SetOutputValue(OutputId.LmpEstop, GetLampValueAsInt(22)); SetOutputValue(OutputId.LmpCompressor, GetLampValueAsInt(23)); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; if (GameFlow_Get_PlayerActive(0)) { switch(ReadByte(P1_Weapon_Address)) { //default Gun / Jeep Gun case 0: { _P1_Ammo = 99; }break; //Electro Gun case 4: { _P1_Ammo = (int)ReadPtr(P1_AmmoElectro_Address); }break; //Shotgun case 6: { _P1_Ammo = (int)ReadPtr(P1_AmmoShotGun_Address); } break; //FreezeRay case 7: { _P1_Ammo = (int)ReadPtr(P1_AmmoFreezeRay_Address); } break; //Minigun case 8: { _P1_Ammo = (int)ReadPtr(P1_AmmoMinoGun_Address); } break; } UInt32 PlayerRecord_Address = GameFlow_Get_PlayerRecord(0); if (PlayerRecord_Address != 0) { _P1_Life = (int)BitConverter.ToSingle(ReadBytes(PlayerRecord_Address + 0xC, 4), 0); } //[Damaged] custom Output if (ReadByte(_P1_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); } //[Recoil] custom Output if (ReadByte(_P1_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); } } if (GameFlow_Get_PlayerActive(1)) { switch (ReadByte(P2_Weapon_Address)) { //default Gun case 0: { _P2_Ammo = 0; } break; //Electro Gun case 4: { _P2_Ammo = (int)ReadPtr(P2_AmmoElectro_Address); } break; //Shotgun case 6: { _P2_Ammo = (int)ReadPtr(P2_AmmoShotGun_Address); } break; //FreezeRay case 7: { _P2_Ammo = (int)ReadPtr(P2_AmmoFreezeRay_Address); } break; //Minigun case 8: { _P2_Ammo = (int)ReadPtr(P2_AmmoMinoGun_Address); } break; } UInt32 PlayerRecord_Address = GameFlow_Get_PlayerRecord(1); if (PlayerRecord_Address != 0) { _P2_Life = (int)BitConverter.ToSingle(ReadBytes(PlayerRecord_Address + 0xC, 4), 0); } //[Damaged] custom Output if (ReadByte(_P2_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); } //[Recoil] custom Output if (ReadByte(_P2_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); } } //Reset custom Outputs, even if the player is not active. //If not, the value would stay UP if player not active and upper loop not entered WriteByte(_P1_DamageStatus_CaveAddress, 0x00); WriteByte(_P1_RecoilStatus_CaveAddress, 0x00); WriteByte(_P2_DamageStatus_CaveAddress, 0x00); WriteByte(_P2_RecoilStatus_CaveAddress, 0x00); // SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); } private int GetLampValueAsInt(int LmpId) { float fLmpValue = BitConverter.ToSingle(ReadBytes(_gLampsVal_Address + (uint)(4 * LmpId), 4), 0); int iLmpValue = (int)(fLmpValue * 100); return iLmpValue; } /// /// Redoing the original function in order to get the pointer struct info for a player /// The game initially stores data in a chained list, meaning that the first player ti enter the game is in the first item, etc.... /// So item 0 can be either P1 or P2, and we can't just read a byte to know /// /// private UInt32 GameFlow_Get_PlayerRecord(int PlayerId) { UInt32 Buffer1 = ReadPtr(_gGameRecord_Address); UInt32 Buffer2 = 0; if (Buffer1 != 0) { while (true) { Buffer2 = ReadPtr(Buffer1 + 0x8); if ((int)ReadByte(Buffer2) == PlayerId) { if (ReadByte(Buffer2 + 0x10) != 0) break; } Buffer1 = ReadPtr(Buffer1); if (Buffer1 == 0) return 0; } return Buffer2; } else { return 0; } } /// /// Redoing the original function in order to know if a player is playing /// The game initially stores data in a chained list, meaning that the first player ti enter the game is in the first item, etc.... /// So item 0 can be either P1 or P2, and we can't just read a byte to know /// /// private bool GameFlow_Get_PlayerActive(int PlayerId) { UInt32 Buffer1 = ReadPtr(_gGameRecord_Address); UInt32 Buffer2 = 0; if (Buffer1 != 0) { while (true) { Buffer2 = ReadPtr(Buffer1 + 0x8); if ((int)ReadByte(Buffer2) == PlayerId) { if (ReadByte(Buffer2 + 0x10) != 0) break; } Buffer1 = ReadPtr(Buffer1); if (Buffer1 == 0) return false; } return true; } else { return false; } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RtTargetTerror.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_RtTargetTerror : Game { //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction_v212 = 0x080C73CB; //Memory value private UInt32 _gPlayers_Address = 0x081DDF00; private UInt32 _Player_State_Offset = 0x18; private UInt32 _Player_Life_Offset = 0x30; private UInt32 _Player_Weapon_Offset = 0x34; private UInt32 _Player_Ammo_Offset = 0x3A; private UInt32 _GS_PLAYING_minigame_loaded_Address = 0x8134AD8; //private UInt32 _CoinsNumber_Address = 0x081DFCE5; //private UInt32 _CoinValue_Address = 0x081DC304; private InjectionStruct _MiniGameRecoil_InjectionStruct = new InjectionStruct(0x080628D0, 6); //Custom data private UInt32 _P1_CustomRecoil_CaveAddress = 0; private UInt32 _P2_CustomRecoil_CaveAddress = 0; private int _P1_Last_Weapon = 0; private int _P2_Last_Weapon = 0; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_RtTargetTerror(String RomName) : base(RomName, "BudgieLoader") { _KnownMd5Prints.Add("Target Terror Gold - 2.12", "ff96481d27c47424bd7759f0213faf8f"); _tProcess.Start(); Logger.WriteLog("Waiting for Raw Thrill " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting byte[] buffer = ReadBytes(_RomLoaded_check_Instruction_v212, 5); if (buffer[0] == 0xC7 && buffer[1] == 0x05 && buffer[2] == 0xE5 && buffer[3] == 0xFC && buffer[4] == 0x1D) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Target Terror Gold - 2.12 binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Target Terror Gold - 2.12"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game not Loaded, waiting..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack //Teknoparrot does not emulate the gun board, so the game is not proceding to the real hardware motor engine //To get recoil, we cannot compute Ammo difference, because some weapons have infinite ammo //This game's procedure is called everytime a bullet is fire to choose what the game is going to do according to the current weapon. //Player index is in ESI protected override void Apply_OutputsMemoryHack() { //Create Databak to store our value Create_OutputsDataBank(); _P1_CustomRecoil_CaveAddress = _OutputsDatabank_Address; _P2_CustomRecoil_CaveAddress = _OutputsDatabank_Address + 0x04; SetHack_MiniGameRecoil(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// There a a few different minigames, and usual weapon/ammo stuf can't be unsed in them /// On top of that, each one of them has it's own structure, scriipt and way of handling Trigger /// Most common point is the call to FBShot_Add(), we can intercept the call and use that Flag later /// private void SetHack_MiniGameRecoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx,[esp+08] CaveMemory.Write_StrBytes("8B 4C 24 08"); //shl ecx,02 CaveMemory.Write_StrBytes("C1 E1 02"); //add ecx, _P1_CustomRecoil_CaveAddress CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_CustomRecoil_CaveAddress)); //mov byte ptr[ecx], 1 CaveMemory.Write_StrBytes("C6 01 01"); //mov edx,[0812F864] CaveMemory.Write_StrBytes("8B 15 64 F8 12 08"); //Inject it CaveMemory.InjectToAddress(_MiniGameRecoil_InjectionStruct, "Recoil"); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); //_Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Custom Outputs _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Gamestate: // 02 = Title screen / Attract DEMO // 04 = Level Select // 05 = InGame // 08 = Game Over screen // 12 = Test Mode UInt32 P1_Address = _gPlayers_Address; UInt32 P2_Address = _gPlayers_Address + 0x558; //Check Player Status: //0 = Playing, dead //1 = Playing, alive //2 = Continue screen //3 = Title/Attract if (ReadByte(P1_Address + _Player_State_Offset) == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); //If a mini game is loaded, using the custom recoil flag if (ReadByte(_GS_PLAYING_minigame_loaded_Address) == 01) { if (ReadByte(_P1_CustomRecoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_CustomRecoil_CaveAddress, 0); } } else { _P1_Ammo = ReadByte(P1_Address + _Player_Ammo_Offset); if (_P1_Ammo < 0) _P1_Ammo = 0; _P1_Life = ReadByte(P1_Address + _Player_Life_Offset); if (_P1_Life < 0) _P1_Life = 0; //To get recoil, we"ll check what kind of weapon is used and calculate accordingly //Some weapons have standard recoil based on Ammo, other don't (Flame Thrower) // 1 = Beretta // 2 = Machinegun // 3 = Shotgun // 4 = Grenade Launcher // 5 = RPP // 6 = Flame Thrower // 7 - Shocker // 8 - FreezeGun // - NOTE - Recoil in Hunt game (bonus game) is not activated here. Look at 0x080B4390 for ammo decrease (need to verify for P2, also) //Maybe check game mode ??? int P1_Weapon = ReadByte(P1_Address + _Player_Weapon_Offset); if (P1_Weapon != 6) { if (_P1_Last_Weapon == P1_Weapon) { if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); } } _P1_Last_Weapon = P1_Weapon; //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } //Same thing for P2 if (ReadByte(P2_Address + _Player_State_Offset) == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P2_CtmLmpStart, 0); //If a mini game is loaded, using the custom recoil flag if (ReadByte(_GS_PLAYING_minigame_loaded_Address) == 01) { if (ReadByte(_P2_CustomRecoil_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_CustomRecoil_CaveAddress, 0); } } else { _P2_Ammo = ReadByte(P2_Address + _Player_Ammo_Offset); if (_P2_Ammo < 0) _P2_Ammo = 0; _P2_Life = ReadByte(P2_Address + _Player_Life_Offset); if (_P2_Life < 0) _P2_Life = 0; int P2_Weapon = ReadByte(P2_Address + _Player_Weapon_Offset); if (P2_Weapon != 6) { if (_P2_Last_Weapon == P2_Weapon) { if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); } } _P2_Last_Weapon = P2_Weapon; //[Clip] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P2_CtmLmpStart, -1); } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); // For Credits, we need to compute the coin number * coin value /*float fCoinValue = BitConverter.ToSingle(ReadBytes(_CoinValue_Address, 4), 0); UInt32 fCoinNumber = BitConverter.ToUInt32(ReadBytes(_CoinsNumber_Address, 4), 0); float fCredits = fCoinValue * (float)fCoinNumber; SetOutputValue(OutputId.Credits, (int)fCredits);*/ } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RtTerminatorSalvation.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.Win32; namespace DemulShooter { class Game_RtTerminatorSalvation : Game { //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction_v125 = 0x0811F6A0; //Outputs Address private UInt32 _P1_Ammo_Address = 0x88DBF68; private UInt32 _P2_Ammo_Address = 0x88DBF7C; private UInt32 _P1_WeaponNumber_Address = 0x88A5F80; private UInt32 _P2_WeaponNumber_Address = 0x88A5FE0; private UInt32 _Lamp_Address = 0x88AE748; private UInt32 _GameState_Address = 0x088CD028; private UInt32 _P1_PlayerStruct_Address = 0x088CC240; private UInt32 _P2_PlayerStruct_Address = 0x088CC2BC; private UInt32 _Recoil_Injection_Address = 0x0811FCF2; private UInt32 _Recoil_Injection_ReturnAddress = 0x0811FCFC; private UInt32 _P1_CustomRecoil_CaveAddress = 0; private UInt32 _P2_CustomRecoil_CaveAddress = 0; private int _P1_Last_Weapon = 0; private int _P2_Last_Weapon = 0; /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_RtTerminatorSalvation(String RomName) : base(RomName, "BudgieLoader") { _KnownMd5Prints.Add("Terminator Salvation - 01.25 USA", "b1e68e0f4dc1db9ec04a1c0e83c9913e"); _tProcess.Start(); Logger.WriteLog("Waiting for Raw Thrill " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting byte[] buffer = ReadBytes(_RomLoaded_check_Instruction_v125, 5); if (buffer[0] == 0x83 && buffer[1] == 0x2C && buffer[2] == 0xB5 && buffer[3] == 0x68 && buffer[4] == 0xBF) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Terminator Salvation - 01.25 USA binary detected"); _TargetProcess_Md5Hash = _KnownMd5Prints["Terminator Salvation - 01.25 USA"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game not Loaded, waiting..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack //Teknoparrot does not emulate the gun board, so the game is not proceding to the real hardware motor engine //To get recoil, we cannot compute Ammo difference, because some weapons have infinite ammo //This game's procedure is called everytime a bullet is fire to choose what the game is going to do according to the current weapon. //Player index is in ESI protected override void Apply_OutputsMemoryHack() { //Create Databak to store our value Create_OutputsDataBank(); _P1_CustomRecoil_CaveAddress = _OutputsDatabank_Address; _P2_CustomRecoil_CaveAddress = _OutputsDatabank_Address + 0x04; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[_P1_CustomRecoil_CaveAddress] byte[] b = BitConverter.GetBytes(_P1_CustomRecoil_CaveAddress); CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(b); //mov [eax+esi*4], 1 CaveMemory.Write_StrBytes("C7 04 B0 01 00 00 00"); //pop eax" CaveMemory.Write_StrBytes("58"); //cmp dword ptr[edi+10], 09 CaveMemory.Write_StrBytes("83 7F 10 09"); //ja 0811FF5E CaveMemory.Write_ja(0x0811FF5E); CaveMemory.Write_jmp(_Recoil_Injection_ReturnAddress); Logger.WriteLog("Adding Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - _Recoil_Injection_Address - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, _Recoil_Injection_Address, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_B)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGunGrenadeBtn)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun_B)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGunGrenadeBtn)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); //In Teknoparrot, Guns hardware is not emulated, so the game is not running original gun recoil procedures /*_Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor));*/ _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); //_Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Lamp_Address + 0x20) & 0x01); SetOutputValue(OutputId.P1_LmpHolder, ReadByte(_Lamp_Address + 0x08) & 0x01); SetOutputValue(OutputId.P1_LmpGun_B, ReadByte(_Lamp_Address + 0x60) & 0x01); SetOutputValue(OutputId.P1_LmpGunGrenadeBtn, ReadByte(_Lamp_Address + 0x70) & 0x01); SetOutputValue(OutputId.P1_LmpGun, ReadByte(_Lamp_Address + 0x68) & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Lamp_Address + 0x30) & 0x01); SetOutputValue(OutputId.P2_LmpHolder, ReadByte(_Lamp_Address + 0x10) & 0x01); SetOutputValue(OutputId.P2_LmpGun_B, ReadByte(_Lamp_Address + 0x48) & 0x01); SetOutputValue(OutputId.P2_LmpGunGrenadeBtn, ReadByte(_Lamp_Address + 0x58) & 0x01); SetOutputValue(OutputId.P2_LmpGun, ReadByte(_Lamp_Address + 0x50) & 0x01); SetOutputValue(OutputId.LmpBillboard, ReadByte(_Lamp_Address) & 0x01); //Custom Outputs _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Check GameState : //2 = Intro //4 = Title //3 = Chapter Select //1 = Play int GameState = BitConverter.ToInt32(ReadBytes(_GameState_Address, 4), 0); if (GameState == 1) { //Check Player Status: //0 = Not Playing //3 = Playing int P1_Status = BitConverter.ToInt32(ReadBytes(_P1_PlayerStruct_Address, 4), 0); if (P1_Status == 3) { _P1_Ammo = BitConverter.ToInt32(ReadBytes(_P1_Ammo_Address, 4), 0); if (_P1_Ammo < 0) _P1_Ammo = 0; _P1_Life = BitConverter.ToInt32(ReadBytes(_P1_PlayerStruct_Address + 0x58, 4), 0); if (_P1_Life < 0) _P1_Life = 0; //To get recoil, we can't use original mechanism as Teknoparrot is not emulating the gun board //and the game is not doing the outputs //Getting custom recoil by checking ammunition has one drawback this infinite ammo weapons //Se we"ll check what kind of weapon is used and calculate accordingly int P1_Weapon = BitConverter.ToInt32(ReadBytes(_P1_WeaponNumber_Address, 4), 0); //For infinite ammo weapon, using memory hack to get data if (P1_Weapon == 5 || P1_Weapon == 8 || P1_Weapon == 9) { if (ReadByte(_P1_CustomRecoil_CaveAddress) == 1) SetOutputValue(OutputId.P1_CtmRecoil, 1); } //For other weapon, just check difference between ammo, and make sure that this smaller value is not due to a gun change //0 = Shotgun //1 = Regular gun //2 = Gaitlin //3-4 Grenade ? else { if (_P1_Last_Weapon == P1_Weapon) { if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); } } //Clearing memory hack flag, even if not used WriteByte(_P1_CustomRecoil_CaveAddress, 0); _P1_Last_Weapon = P1_Weapon; //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } int P2_Status = BitConverter.ToInt32(ReadBytes(_P2_PlayerStruct_Address, 4), 0); if (P2_Status == 3) { _P2_Ammo = BitConverter.ToInt32(ReadBytes(_P2_Ammo_Address, 4), 0); if (_P2_Ammo < 0) _P2_Ammo = 0; _P2_Life = BitConverter.ToInt32(ReadBytes(_P2_PlayerStruct_Address + 0x58, 4), 0); if (_P2_Life < 0) _P2_Life = 0; //To get recoil, we can't use original mechanism as Teknoparrot is not emulating the gun board //and the game is not doing the outputs //Getting custom recoil by checking ammunition has one drawback this infinite ammo weapons //Se we"ll check what kind of weapon is used and calculate accordingly int P2_Weapon = BitConverter.ToInt32(ReadBytes(_P2_WeaponNumber_Address, 4), 0); //For infinite ammo weapon, using memory hack to get data if (P2_Weapon == 5 || P2_Weapon == 8 || P2_Weapon == 9) { if (ReadByte(_P2_CustomRecoil_CaveAddress) == 1) SetOutputValue(OutputId.P2_CtmRecoil, 1); } //For other weapon, just check difference between ammo //0 = Shotgun //1 = Regular gun //2 = Gaitlin //3-4 Grenade ? else { if (_P2_Last_Weapon == P2_Weapon) { if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); } } //Clearing memory hack flag, even if not used WriteByte(_P2_CustomRecoil_CaveAddress, 0); _P2_Last_Weapon = P2_Weapon; //[Clip]Custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RtWalkingDead.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_RtWalkingDead : Game { //Rom loaded + Rom version check private UInt32 _RomLoaded_check_Instruction_v105 = 0x0819DE0B; //Check ForceFeedback setting //Outputs Address private UInt32 _P1_Struct_Address = 0x090100A0; private UInt32 _P2_Struct_Address = 0x09010274; /*private UInt32 _P1_Life_Address = 0x090100B0; private UInt32 _P2_Life_Address = 0x09010284; private UInt32 _P1_Ammo_Address = 0x0901010C; private UInt32 _P2_Ammo_Address = 0x090102E0;*/ private UInt32 _Lamp_Address = 0x08AC2188; //Custom Values private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; private UInt32 _P1_DamageStatus_CaveAddress = 0; private UInt32 _P2_DamageStatus_CaveAddress = 0; /*** MEMORY ADDRESSES **/ private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x0808B1D9, 7); private InjectionStruct _Damage_InjectionStruct = new InjectionStruct(0x080762D6, 8); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x0806DF47, 8); /// /// Constructor /// /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) public Game_RtWalkingDead(String RomName) : base(RomName, "BudgieLoader") { _KnownMd5Prints.Add("Walking Dead - 01.05 - Teknoparrot Patched", "5158b185b977b38749845be958caddb6"); _KnownMd5Prints.Add("Walking Dead - 01.05 - Original dump", "6951619cd907173862c5eb337e263af5"); _tProcess.Start(); Logger.WriteLog("Waiting for Raw Thrill " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //To make sure BurgieLoader has loaded the rom entirely, we're looking for some random instruction to be present in memory before starting byte[] buffer = ReadBytes(_RomLoaded_check_Instruction_v105, 3); if (buffer[0] == 0x8B && buffer[1] == 0x40 && buffer[2] == 0x48) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Walking Dead - 01.05 binary detected"); //Check would be needed on the game MD5 to find if it is the original or patched version..... //_TargetProcess_Md5Hash = _KnownMd5Prints["Walking Dead - 01.05 - Teknoparrot Patched"]; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game not Loaded, waiting..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 4; _P1_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x10; _P2_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x14; SetHack_Damage(); SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercep call to log damage taken by a player to set a custom flag to "1" /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push edi CaveMemory.Write_StrBytes("57"); //sub edi,01 CaveMemory.Write_StrBytes("83 EF 01"); //shl edi,02 CaveMemory.Write_StrBytes("C1 E7 02"); //add edi,BudgieLoader.exe+56789 CaveMemory.Write_StrBytes("81 C7"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_DamageStatus_CaveAddress)); //mov [edi],00000001 CaveMemory.Write_StrBytes("C7 07 01 00 00 00"); //pop edi CaveMemory.Write_StrBytes("5F"); //mov [esp+04],00000040 CaveMemory.Write_StrBytes("C7 44 24 04 40 00 00 00"); //Inject it CaveMemory.InjectToAddress(_Damage_InjectionStruct, "Damage"); } /// /// Intercept call for checking ForceFeedback enabled setting on shoot /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push ebx CaveMemory.Write_StrBytes("53"); //shl ebx,02 CaveMemory.Write_StrBytes("C1 E3 02"); //add ebx, _P1_RecoilStatus_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress)); //mov [ebx],00000001 CaveMemory.Write_StrBytes("C7 03 01 00 00 00"); //pop ebx CaveMemory.Write_StrBytes("5B"); //mov [esp],088C735C CaveMemory.Write_StrBytes("C7 04 24 5C 73 8C 08"); //Inject it CaveMemory.InjectToAddress(_Recoil_InjectionStruct, "Recoil"); } /// /// Originally, changing the fld value loaded with our own -1.0f stored in databank /// Changing the parameter in the called function a bit further allow to do a simple Injection without custom stored value /// protected override void Apply_NoCrosshairMemoryHack() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea edx,[esp+18] CaveMemory.Write_StrBytes("8D 54 24 18"); //mov [edx],BF800000 CaveMemory.Write_StrBytes("C7 02 00 00 80 BF"); //mov [esp+04],edx CaveMemory.Write_StrBytes("89 54 24 04"); //InjectIt CaveMemory.InjectToAddress(_NoCrosshair_InjectionStruct, "NoCrosshair"); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated permanently while trigger is pressed _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P1_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.P2_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.LmpRoof_R)); _Outputs.Add(new GameOutput(OutputId.LmpRoof_G)); _Outputs.Add(new GameOutput(OutputId.LmpRoof_B)); _Outputs.Add(new GameOutput(OutputId.LmpLgMarquee)); _Outputs.Add(new GameOutput(OutputId.LmpSqMarquee)); _Outputs.Add(new GameOutput(OutputId.LmpWalker_R)); _Outputs.Add(new GameOutput(OutputId.LmpWalker_G)); _Outputs.Add(new GameOutput(OutputId.LmpWalker_B)); _Outputs.Add(new GameOutput(OutputId.LmpWalkerEyes)); _Outputs.Add(new GameOutput(OutputId.LmpWalkerCeiling)); _Outputs.Add(new GameOutput(OutputId.LmpPosts)); _Outputs.Add(new GameOutput(OutputId.LmpRear_R)); //In Teknoparrot, Guns hardware is not emulated, so the game is not running original gun recoil procedures /*_Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor));*/ _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); //_Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Lamp_Address + 0x18)); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Lamp_Address + 0x1C)); SetOutputValue(OutputId.P1_LmpGun, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address, 4), 0) * 20)); SetOutputValue(OutputId.P2_LmpGun, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0x04, 4), 0) * 200)); SetOutputValue(OutputId.P1_LmpHolder, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0x84, 4), 0) * 200)); SetOutputValue(OutputId.P2_LmpHolder, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0xB4, 4), 0) * 200)); SetOutputValue(OutputId.P1_LmpPanel, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0x7C, 4), 0) * 200)); SetOutputValue(OutputId.P2_LmpPanel, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0x80, 4), 0) * 200)); SetOutputValue(OutputId.LmpRoof_R, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0x88, 4), 0) * 200)); SetOutputValue(OutputId.LmpRoof_G, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0x8C, 4), 0) * 200)); SetOutputValue(OutputId.LmpRoof_B, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0x90, 4), 0) * 200)); //Long Marquee uses 4 outputs, so we will add them to be sure it won't be missed if the game only triggers a part of them byte LongMarqueeStatus = 0; for (uint i = 0; i < 3; i++) { if (ReadByte(_Lamp_Address + 0x94 + 4 * i + 3) != 0x00) LongMarqueeStatus |= 1; } SetOutputValue(OutputId.LmpLgMarquee, LongMarqueeStatus); SetOutputValue(OutputId.LmpSqMarquee, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0xA4, 4), 0) * 200)); SetOutputValue(OutputId.LmpWalker_R, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0xA8, 4), 0) * 200)); SetOutputValue(OutputId.LmpWalker_G, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0xAC, 4), 0) * 200)); SetOutputValue(OutputId.LmpWalker_B, (int)(BitConverter.ToSingle(ReadBytes(_Lamp_Address + 0xB0, 4), 0) * 200)); SetOutputValue(OutputId.LmpWalkerEyes, ReadByte(_Lamp_Address + 0x2C)); SetOutputValue(OutputId.LmpWalkerCeiling, ReadByte(_Lamp_Address + 0x28)); SetOutputValue(OutputId.LmpPosts, ReadByte(_Lamp_Address + 0x20)); SetOutputValue(OutputId.LmpRear_R, ReadByte(_Lamp_Address + 0x24)); //Custom Outputs _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Check if the Player is currently playing if (ReadByte(_P1_Struct_Address + 0x01) == 1) { _P1_Ammo = BitConverter.ToInt32(ReadBytes(_P1_Struct_Address + 0x6C, 4), 0); if (_P1_Ammo < 0) _P1_Ammo = 0; //[Clip] custom Output if (_P1_Ammo > 0) P1_Clip = 1; _P1_Life = BitConverter.ToInt32(ReadBytes(_P1_Struct_Address + 0x10, 4), 0); if (_P1_Life < 0) _P1_Life = 0; } //Check if the Player is currently playing if (ReadByte(_P2_Struct_Address + 0x01) == 1) { _P2_Ammo = BitConverter.ToInt32(ReadBytes(_P2_Struct_Address + 0x6C, 4), 0); if (_P2_Ammo < 0) _P2_Ammo = 0; //[Clip] custom Output if (_P2_Ammo > 0) P2_Clip = 1; _P2_Life = BitConverter.ToInt32(ReadBytes(_P2_Struct_Address + 0x10, 4), 0); if (_P2_Life < 0) _P2_Life = 0; } SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Reading our own flags for damage and recoil and resetting them if (ReadByte(_P1_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilStatus_CaveAddress, 0); } if (ReadByte(_P2_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilStatus_CaveAddress, 0); } if (ReadByte(_P1_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_DamageStatus_CaveAddress, 0); } if (ReadByte(_P2_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_DamageStatus_CaveAddress, 0); } //SetOutputValue(OutputId.Credits, ReadByte(_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwGunman.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_RwGunman : Game { private const String GAMEDATA_FOLDER = @"MemoryData\ringwide\mng"; /*** MEMORY ADDRESSES **/ private UInt32 _Credits_Offset = 0x006CBB40; private UInt32 _pGunCylinderMgr_Offset = 0x006C9B30; private InjectionStruct _Axis_InjectionStruct = new InjectionStruct(0x00142447, 9); private InjectionStruct _Lamp_InjectionStruct = new InjectionStruct(0x001DC8C1, 5); private UInt32 _P1_TriggerPatch_Offset = 0x0034A1DD; private UInt32 _P1_OtherClickPatch_Offset = 0x0034A21E; //equivalent of right-click, used in TEST menu only to navigate private UInt32 _P1_ReloadPatch_Offset = 0x0034A25F; private InjectionStruct _P2_Buttons_InjectionStruct = new InjectionStruct(0x349622, 6); private InjectionStruct _P1_NoCrosshair_InjectionStruct = new InjectionStruct(0x0014386C, 5); private InjectionStruct _P2_NoCrosshair_InjectionStruct = new InjectionStruct(0x001436C6, 5); //Custom Input Address private UInt32 _P1_X_Address; private UInt32 _P1_Y_Address; private UInt32 _P2_X_Address; private UInt32 _P2_Y_Address; private UInt32 _P1_Trigger_Address; private UInt32 _P1_Reload_Address; private UInt32 _P1_Other_Address; private UInt32 _P2_Trigger_Address; private UInt32 _P2_Reload_Address; private UInt32 _P2_Other_Address; //custom Outputs Address private UInt32 _P1_CustomRecoil_CaveAddress; private UInt32 _P2_CustomRecoil_CaveAddress; private UInt32 _CustomLamps_CaveAddress; /// /// Constructor /// public Game_RwGunman(String RomName) : base(RomName, "gunman_dxF") { //_KnownMd5Prints.Add("gunman_dxD.exe - build 8796", "304bdb086204d6fa53eb65ad7073a2e0"); // Different code ! _KnownMd5Prints.Add("gunman_dxF.exe - build 8796", "ffd6e3c06a2bf4a0abbe0961589432cc"); _KnownMd5Prints.Add("gunman_dxR.exe - build 8796", "12666f3917779bf2bda624224a2dd346"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 1023.0; double dMaxY = 767.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them /// Reverse back to it when DumbJVSCommand will be working with ParrotLoader, without DumbJVSManager /// protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_Address = _InputsDatabank_Address; _P1_Y_Address = _InputsDatabank_Address + 0x04; _P2_X_Address = _InputsDatabank_Address + 0x08; _P2_Y_Address = _InputsDatabank_Address + 0x0C; _P1_Trigger_Address = _InputsDatabank_Address + 0x10; _P1_Reload_Address = _InputsDatabank_Address + 0x11; _P1_Other_Address = _InputsDatabank_Address + 0x12; _P2_Trigger_Address = _InputsDatabank_Address + 0x14; _P2_Reload_Address = _InputsDatabank_Address + 0x15; _P2_Other_Address = _InputsDatabank_Address + 0x16; SetHack_Axis(); SetHack_Buttons(); Logger.WriteLog("Inputs memory Hack complete !"); Logger.WriteLog("-"); } /// /// Game is using mouse coordinates to set P1 values /// And also for P2 if a command line option is set, or otherwise uses some other data /// Changing the values in memory for both after the whole procedure allow us to set data whatever mode is choosen /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx,[ebp-2C] CaveMemory.Write_StrBytes("8B 4D D4"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[_P1_X_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_Address)); //mov [ecx+000000A8],eax CaveMemory.Write_StrBytes("89 81 A8 00 00 00"); //mov eax,[_P1_Y_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Y_Address)); //mov [ecx+000000AC],eax CaveMemory.Write_StrBytes("89 81 AC 00 00 00"); //mov eax,[_P2_X_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_X_Address)); //mov [ecx+000000B0],eax CaveMemory.Write_StrBytes("89 81 B0 00 00 00"); //mov eax,[_P2_Y_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Y_Address)); //mov [ecx+000000B4],eax CaveMemory.Write_StrBytes("89 81 B4 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //add ecx,000000A8 CaveMemory.Write_StrBytes("81 C1 A8 00 00 00"); //Inject it CaveMemory.InjectToOffset(_Axis_InjectionStruct, "Axis"); } /// /// Replacing the address of the buttons status loaded in the procedure to get trigger status /// by our own address /// private void SetHack_Buttons() { //P1: //Replace Trigger flag test with our own value WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerPatch_Offset, new byte[] { 0x0F, 0xB6, 0x15 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerPatch_Offset + 3, BitConverter.GetBytes(_P1_Trigger_Address)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerPatch_Offset + 7, new byte[] { 0x80, 0xE2, 0x80 }); //Replace Reload flag test with our own value WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_OtherClickPatch_Offset, new byte[] { 0x0F, 0xB6, 0x15 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_OtherClickPatch_Offset + 3, BitConverter.GetBytes(_P1_Reload_Address)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_OtherClickPatch_Offset + 7, new byte[] { 0x80, 0xE2, 0x80 }); //Replace flag test with our own value WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_ReloadPatch_Offset, new byte[] { 0x0F, 0xB6, 0x15 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_ReloadPatch_Offset + 3, BitConverter.GetBytes(_P1_Other_Address)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_ReloadPatch_Offset + 7, new byte[] { 0x80, 0xE2, 0x80 }); //P2: Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,[_P2_Trigger_Address] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Trigger_Address)); //mov [ecx+48],eax CaveMemory.Write_StrBytes("89 41 48"); //mov [ecx+4C],edx CaveMemory.Write_StrBytes("89 51 4C"); //Inject it CaveMemory.InjectToOffset(_P2_Buttons_InjectionStruct, "P2 Buttons"); } protected override void Apply_OutputsMemoryHack() { //Create Databak to store our value Create_OutputsDataBank(); _P1_CustomRecoil_CaveAddress = _OutputsDatabank_Address; _P2_CustomRecoil_CaveAddress = _OutputsDatabank_Address + 0x04; _CustomLamps_CaveAddress = _OutputsDatabank_Address + 0x10; SetHack_Lamp(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercepting the call to the Lamp-related functions so that we can read the parameters (Lamp ID and status) /// Unfortunatelly, the game does not call that function once in-game (only in test-menu) /// private void SetHack_Lamp() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ebp,[esp+0C] CaveMemory.Write_StrBytes("8B 6C 24 0C"); //add ebp,_CustomLamps_CaveAddress CaveMemory.Write_StrBytes("81 C5"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_CustomLamps_CaveAddress)); //mov eax,[esp+10] CaveMemory.Write_StrBytes("8B 44 24 10"); //mov [ebp+00],al CaveMemory.Write_StrBytes("88 45 00"); //mov ebp,esp CaveMemory.Write_StrBytes("8B EC"); //mov eax,[ebp+08] CaveMemory.Write_StrBytes("8B 45 08"); //Inject it CaveMemory.InjectToOffset(_Lamp_InjectionStruct, "Lamp"); } /// /// protected override void Apply_NoCrosshairMemoryHack() { if (_HideCrosshair) { SetHack_NoCrosshair(_P1_NoCrosshair_InjectionStruct, "P1 Nocrosshair"); SetHack_NoCrosshair(_P2_NoCrosshair_InjectionStruct, "P2 Nocrosshair"); } } /// /// Forcing float value [-100.0, -100.0] for both player reticle display /// private void SetHack_NoCrosshair(InjectionStruct PlayerInjectionStruct, String InjectionName) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov edx,C2C80000 CaveMemory.Write_StrBytes("BA 00 00 C8 C2"); //mov eax,C2C80000 CaveMemory.Write_StrBytes("B8 00 00 C8 C2"); //Inject it CaveMemory.InjectToOffset(PlayerInjectionStruct, InjectionName); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { float x = (float)PlayerData.RIController.Computed_X; byte[] bufferX = BitConverter.GetBytes(x); byte[] bufferY = BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_Address, bufferX); WriteBytes(_P1_Y_Address, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P1_Trigger_Address, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_Address, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_P1_Reload_Address, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_P1_Reload_Address, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(_P1_Other_Address, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(_P1_Other_Address, 0x00); } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_Address, bufferX); WriteBytes(_P2_Y_Address, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P2_Trigger_Address, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P2_Trigger_Address, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_P2_Reload_Address, 0x04); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_P2_Reload_Address, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(0x11c0100, 1); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(0x11c0100, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : stays activated when trigger is pulled //Gun recoil : not used ?? _Outputs = new List(); /*_Outputs.Add(new GameOutput(OutputId.Lmp_Horn_R)); _Outputs.Add(new GameOutput(OutputId.Lmp_Horn_G)); _Outputs.Add(new GameOutput(OutputId.Lmp_Horn_B)); _Outputs.Add(new GameOutput(OutputId.Lmp_LeftBulletMark)); _Outputs.Add(new GameOutput(OutputId.Lmp_RightBulletMark)); _Outputs.Add(new GameOutput(OutputId.Lmp_W)); _Outputs.Add(new GameOutput(OutputId.Lmp_A)); _Outputs.Add(new GameOutput(OutputId.Lmp_N)); _Outputs.Add(new GameOutput(OutputId.Lmp_T)); _Outputs.Add(new GameOutput(OutputId.Lmp_E)); _Outputs.Add(new GameOutput(OutputId.Lmp_D)); _Outputs.Add(new GameOutput(OutputId.Lmp_LeftReload)); _Outputs.Add(new GameOutput(OutputId.Lmp_RightReload)); _Outputs.Add(new GameOutput(OutputId.Lmp_Payout));*/ _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs /*SetOutputValue(OutputId.Lmp_Horn_R, ReadByte(_CustomLamps_CaveAddress + 0x3)); SetOutputValue(OutputId.Lmp_Horn_G, ReadByte(_CustomLamps_CaveAddress + 0x4)); SetOutputValue(OutputId.Lmp_Horn_B, ReadByte(_CustomLamps_CaveAddress + 0x5)); SetOutputValue(OutputId.Lmp_LeftBulletMark, ReadByte(_CustomLamps_CaveAddress + 0x8)); SetOutputValue(OutputId.Lmp_RightBulletMark, ReadByte(_CustomLamps_CaveAddress + 0x9)); SetOutputValue(OutputId.Lmp_W, ReadByte(_CustomLamps_CaveAddress + 0xA)); SetOutputValue(OutputId.Lmp_A, ReadByte(_CustomLamps_CaveAddress + 0xB)); SetOutputValue(OutputId.Lmp_N, ReadByte(_CustomLamps_CaveAddress + 0xC)); SetOutputValue(OutputId.Lmp_T, ReadByte(_CustomLamps_CaveAddress + 0xD)); SetOutputValue(OutputId.Lmp_E, ReadByte(_CustomLamps_CaveAddress + 0xE)); SetOutputValue(OutputId.Lmp_D, ReadByte(_CustomLamps_CaveAddress + 0xF)); SetOutputValue(OutputId.Lmp_LeftReload, ReadByte(_CustomLamps_CaveAddress + 0x0)); SetOutputValue(OutputId.Lmp_RightReload, ReadByte(_CustomLamps_CaveAddress + 0x1)); SetOutputValue(OutputId.Lmp_Payout, ReadByte(_CustomLamps_CaveAddress + 0x2));*/ //Custom Outputs _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; int Credits = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, 4), 0); //Credits are needed to play (=reload bullets) so it can be used as a filter, as there is no "life" if (Credits > 0) { _P1_Ammo = ReadByte(ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _pGunCylinderMgr_Offset, new UInt32[] { 0x34 }) + 0x1C8); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; _P2_Ammo = ReadByte(ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _pGunCylinderMgr_Offset, new UInt32[] { 0x38 }) + 0x1C8); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.Credits, Credits); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwLGI.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_RwLGI : Game { private const String GAMEDATA_FOLDER = @"MemoryData\ringwide\lgi"; /*** MEMORY ADDRESSES **/ protected UInt32 _Data_Base_Address; private UInt32 _Data_Base_Address_Ptr_Offset = 0x0080C88C; protected UInt32 _P1_X_Offset = 0x00000011; protected UInt32 _P1_Y_Offset = 0x0000000F; protected UInt32 _P1_Buttons_Offset = 0x00000005; protected UInt32 _P2_X_Offset = 0x00000015; protected UInt32 _P2_Y_Offset = 0x00000013; protected UInt32 _P2_Buttons_Offset = 0x00000009; protected NopStruct _Nop_Axis = new NopStruct(0x002F0BEC, 3); protected UInt32 _Buttons_Injection_Offset = 0x002F0B72; protected UInt32 _Buttons_Injection_Return_Offset = 0x002F0B7A; protected UInt32 _CalibrationValues_Injection_Offset = 0x00484427; protected UInt32 _CalibrationValues_Injection_Return_Offset = 0x0048442D; //Outputs private UInt32 _OutputsPtr_Offset = 0x0063BF5C; private UInt32 _PlayersStructPtr_Offset = 0x00820020; //Custom recoil injection private UInt32 _Recoil_Injection_Offset = 0x0033DA91; private UInt32 _Recoil_Injection_Return_Offset = 0x0033DA96; private UInt32 _P1_CustomRecoil_CaveAddress = 0; private UInt32 _P2_CustomRecoil_CaveAddress = 0; /// /// Constructor /// public Game_RwLGI(String RomName) : base(RomName, "LGI_RingW_F_Safe") { _KnownMd5Prints.Add("Let's Go Island - For TeknoParrot", "b782c82a8ccb87c97d77441505e17a26"); _KnownMd5Prints.Add("Let's Go Island - For JConfig", "aec871535341123a0899e5850b64cb85"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Data_Base_Address_Ptr_Offset, 4); UInt32 Calc_Addr1 = BitConverter.ToUInt32(buffer, 0); if (Calc_Addr1 != 0) { buffer = ReadBytes(Calc_Addr1, 4); _Data_Base_Address = BitConverter.ToUInt32(buffer, 0); if (_Data_Base_Address != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("Data base adddress = 0x" + _Data_Base_Address.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //We can't access the TEST menu to do calibration //Choosen solution is to force Calibration Values for Min-Max axis to [0x00-0xFF] when we write axis values in memory //So we can safely use full range of values now : //Axes inversés : 0 = Bas et Droite double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt32(dMaxX - Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them /// Reverse back to it when DumbJVSCommand will be working with ParrotLoader, without DumbJVSManager /// protected override void Apply_InputsMemoryHack() { //NOPing axis proc SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis); SetHack_Buttons(); SetHack_Calibration(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// ///Hacking buttons proc : ///Same byte is used for both triggers, start and service (for each player) ///0b10000000 is start ///0b01000000 is Px Service ///0b00000001 is TriggerL ///0b00000010 is TriggerR ///So we need to make a mask to accept Start button moodification and block other so we can inject /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push ecx CaveMemory.Write_StrBytes("51"); //and ecx,00000080 CaveMemory.Write_StrBytes("81 E1 80 00 00 00"); //cmp ecx,00 CaveMemory.Write_StrBytes("83 F9 00"); //jg @ => if Start PRessed CaveMemory.Write_StrBytes("0F 8F 09 00 00 00"); //and dword ptr [ebx-01],7F => Putting the start bit to 0 CaveMemory.Write_StrBytes("83 63 FF 7F"); //jmp @ CaveMemory.Write_StrBytes("E9 07 00 00 00"); //or [ebx-01],00000080 ==> start is pressed, putting bit to 1 CaveMemory.Write_StrBytes("81 4B FF 80 00 00 00"); //pop ecx CaveMemory.Write_StrBytes("59"); //and ecx,00000040 CaveMemory.Write_StrBytes("83 E1 40"); //cmp ecx,00 CaveMemory.Write_StrBytes("83 F9 00"); //jg @ => if Service PRessed CaveMemory.Write_StrBytes("0F 8F 0C 00 00 00"); //and dword ptr [ebx-01],BF => Putting the Service bit to 0 CaveMemory.Write_StrBytes("81 63 FF BF 00 00 00"); //jmp @ CaveMemory.Write_StrBytes("E9 04 00 00 00"); //or [ebx-01],00000040 ==> Service is pressed, putting bit to 1 CaveMemory.Write_StrBytes("83 4B FF 40"); //movzx ecx,byte ptr [esp+13] CaveMemory.Write_StrBytes("0F B6 4C 24 13"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Buttons CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// ///To bypass the need of Calibration (calculated at each level entry), we can force the game to use max range (0x00-0xFF) for JVS data ///The game uses these values as Min values (we can force them to 0): ///00C17D29 (p1 min x) ///00C17D2D (p1 min y) ///00C17D31 (p2 min x) ///00C17D35 (p2 min y) ///For max value, the game is using complicated FloatingPoint/Double operations with a kind of ratio values. ///Forcing the following values enable the use of 0x00-0xFF full range values to match with (0.0 - 1024.0) calculated values. /// private void SetHack_Calibration() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //xor edi, edi CaveMemory.Write_StrBytes("31 FF"); //xor ebx,ebx CaveMemory.Write_StrBytes("31 DB"); //xor ebp, ebp CaveMemory.Write_StrBytes("31 ED"); //xor ecx, ecx CaveMemory.Write_StrBytes("31 C9"); //mov [esp+10], 0x00000000 CaveMemory.Write_StrBytes("C7 44 24 10 00 00 00 00"); //mov [esp+14], 0x0000FF00 CaveMemory.Write_StrBytes("C7 44 24 14 00 FF 00 00"); //mov [esp+18], 0x0000FF00 CaveMemory.Write_StrBytes("C7 44 24 18 00 FF 00 00"); //mov [esp+1C], 0x0000FF00 CaveMemory.Write_StrBytes("C7 44 24 1C 00 FF 00 00"); //mov esi, 0x0000FF00 CaveMemory.Write_StrBytes("BE 00 FF 00 00"); //mov dword_C17D28, edi CaveMemory.Write_StrBytes("89 3D 28 7D C1 00"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _CalibrationValues_Injection_Return_Offset); Logger.WriteLog("Adding Calibration CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _CalibrationValues_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _CalibrationValues_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //Original game is simply setting a Motor to vibrate, so simply using this data to create or pulsed custom recoil will not be synchronized with bullets shot //as the pulses lenght and spaceing will depend on DemulShooter output pulse config data. //To synch recoil pulse with projectiles, this hack allows to intercept the code shooting the actual projectile to generate the pulse protected override void Apply_OutputsMemoryHack() { //Create Databak to store our value Create_OutputsDataBank(); _P1_CustomRecoil_CaveAddress = _OutputsDatabank_Address; _P2_CustomRecoil_CaveAddress = _OutputsDatabank_Address + 0x04; Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx,[edi+04] CaveMemory.Write_StrBytes("8B 4F 04"); //mov eax,ecx CaveMemory.Write_StrBytes("8B C1"); //shl eax, 2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_CustomRecoil_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_CustomRecoil_CaveAddress)); //mov [eax], 1 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //xor al,al CaveMemory.Write_StrBytes("31 C0"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Recoil_Injection_Return_Offset); Logger.WriteLog("Adding Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Recoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Recoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteByte(_Data_Base_Address + _P1_X_Offset, bufferX[0]); WriteByte(_Data_Base_Address + _P1_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0xFE); } else if (PlayerData.ID == 2) { WriteByte(_Data_Base_Address + _P2_X_Offset, bufferX[0]); WriteByte(_Data_Base_Address + _P2_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0xFE); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : stays activated when trigger is pulled //Gun recoil : not used ?? _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpRear_R)); _Outputs.Add(new GameOutput(OutputId.LmpRear_G)); _Outputs.Add(new GameOutput(OutputId.LmpRear_B)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _OutputsPtr_Offset, 4); byte OutputData = ReadByte(BitConverter.ToUInt32(buffer, 0) + 0x44); SetOutputValue(OutputId.P1_LmpStart, OutputData >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, OutputData >> 4 & 0x01); SetOutputValue(OutputId.LmpRear_R, 0); SetOutputValue(OutputId.LmpRear_G, 0); SetOutputValue(OutputId.LmpRear_B, 0); SetOutputValue(OutputId.P1_GunRecoil, OutputData >> 5 & 0x01); SetOutputValue(OutputId.P1_GunMotor, OutputData >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, OutputData >> 2 & 0x01); SetOutputValue(OutputId.P2_GunMotor, OutputData >> 3 & 0x01); //Custom Outputs UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersStructPtr_Offset); UInt32 P1_Strutc_Address = ReadPtr(iTemp + 0x08); UInt32 P2_Strutc_Address = ReadPtr(iTemp + 0x0C); P1_Strutc_Address = ReadPtr(P1_Strutc_Address + 0x14); P2_Strutc_Address = ReadPtr(P2_Strutc_Address + 0x14); int P1_Status = ReadByte(P1_Strutc_Address + 0x30); int P2_Status = ReadByte(P2_Strutc_Address + 0x30); _P1_Life = 0; _P2_Life = 0; if (P1_Status == 1) { _P1_Life = (int)BitConverter.ToSingle(ReadBytes(P1_Strutc_Address + 0x18, 4), 0); if (_P1_Life < 0) _P1_Life = 0; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 1) { _P2_Life = (int)BitConverter.ToSingle(ReadBytes(P2_Strutc_Address + 0x18, 4), 0); if (_P2_Life < 0) _P2_Life = 0; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Using constant "ON" value from motor to create asynch outputs for recoil //Pulses are not sync with bullets, but with DemulShooter output on/off timings only /*SetOutputValue(OutputId.P1_CtmRecoil, OutputData1 >> 6 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, OutputData1 >> 3 & 0x01);*/ //New method : reading intercepted value for bullet fired, so that recoil is sync with bullets //Need to filter this with MOTOR_ON, if not : recoil will be activated when bullets are fired during attract if (ReadByte(_P1_CustomRecoil_CaveAddress) == 1 && (OutputData >> 6 & 0x01) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_CustomRecoil_CaveAddress, 0); } if (ReadByte(_P2_CustomRecoil_CaveAddress) == 1 && (OutputData >> 3 & 0x01) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_CustomRecoil_CaveAddress, 0); } SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0080CBE0)); //0x008200B8 also possible } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwLGI3D.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_RwLGI3D : Game { private const String GAMEDATA_FOLDER = @"MemoryData\ringwide\lgi3d"; /*** MEMORY ADDRESSES **/ private UInt32 _Data_Base_Address; private UInt32 _Data_Base_Address_Ptr_Offset = 0x0082E34C; private UInt32 _P1_X_Offset = 0x00000011; private UInt32 _P1_Y_Offset = 0x0000000F; private UInt32 _P1_Buttons_Offset = 0x00000005; private UInt32 _P2_X_Offset = 0x00000015; private UInt32 _P2_Y_Offset = 0x00000013; private UInt32 _P2_Buttons_Offset = 0x00000009; private NopStruct _Nop_Axis = new NopStruct(0x002FE60C, 3); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x002FE592, 8); private InjectionStruct _CalibrationValues_InjectionStruct = new InjectionStruct(0x0049DC27, 6); private UInt32 _StrlenFunction_Offset = 0x002FF3EC; //Outputs private UInt32 _OutputsPtr_Offset = 0x0065DA20; private UInt32 _PlayersStructPtr_Offset = 0x008429F0; //Custom injection private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x003518F1, 5); private InjectionStruct _CurrentSequence_InjectionStruct = new InjectionStruct(0x0002615BF, 6); //Custom Data private UInt32 _P1_CustomRecoil_CaveAddress = 0; private UInt32 _P2_CustomRecoil_CaveAddress = 0; private UInt32 _CurrentSequenceStringLength = 0; private UInt32 _CurrentSequenceString = 0; private bool _IsAttractMode = true; /// /// Constructor /// public Game_RwLGI3D(String RomName) : base(RomName, "LGI") { _KnownMd5Prints.Add("Let's Go Island - For TeknoParrot", "ef9e3625684e0d52eab5bc1f0c68c7c3"); _KnownMd5Prints.Add("Let's Go Island - For JConfig", "49c5de5df60f475a965b2d894b3477c6"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Data_Base_Address_Ptr_Offset, 4); UInt32 Calc_Addr1 = BitConverter.ToUInt32(buffer, 0); Logger.WriteLog("CalcAddrr1 = 0x" + Calc_Addr1.ToString("X4")); if (Calc_Addr1 != 0) { buffer = ReadBytes(Calc_Addr1, 4); _Data_Base_Address = BitConverter.ToUInt32(buffer, 0); if (_Data_Base_Address != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("Data base adddress = 0x" + _Data_Base_Address.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //We can't access the TEST menu to do calibration //Choosen solution is to force Calibration Values for Min-Max axis to [0x00-0xFF] when we write axis values in memory //So we can safely use full range of values now : //Axes inversés : 0 = Bas et Droite double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt32(dMaxX - Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them /// Reverse back to it when DumbJVSCommand will be working with ParrotLoader, without DumbJVSManager /// protected override void Apply_InputsMemoryHack() { //NOPing axis proc SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis); SetHack_Buttons(); SetHack_Calibration(); Logger.WriteLog("Inputs memory Hack complete !"); Logger.WriteLog("-"); } /// ///Hacking buttons proc : ///Same byte is used for both triggers, start and service (for each player) ///0b10000000 is start ///0b01000000 is Px Service ///0b00000001 is TriggerL ///0b00000010 is TriggerR ///So we need to make a mask to accept Start button moodification and block other so we can inject /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push ecx CaveMemory.Write_StrBytes("51"); //and ecx,00000080 CaveMemory.Write_StrBytes("81 E1 80 00 00 00"); //cmp ecx,00 CaveMemory.Write_StrBytes("83 F9 00"); //jg @ => if Start PRessed CaveMemory.Write_StrBytes("0F 8F 09 00 00 00"); //and dword ptr [ebx-01],7F => Putting the start bit to 0 CaveMemory.Write_StrBytes("83 63 FF 7F"); //jmp @ CaveMemory.Write_StrBytes("E9 07 00 00 00"); //or [ebx-01],00000080 ==> start is pressed, putting bit to 1 CaveMemory.Write_StrBytes("81 4B FF 80 00 00 00"); //pop ecx CaveMemory.Write_StrBytes("59"); //and ecx,00000040 CaveMemory.Write_StrBytes("83 E1 40"); //cmp ecx,00 CaveMemory.Write_StrBytes("83 F9 00"); //jg @ => if Service PRessed CaveMemory.Write_StrBytes("0F 8F 0C 00 00 00"); //and dword ptr [ebx-01],BF => Putting the Service bit to 0 CaveMemory.Write_StrBytes("81 63 FF BF 00 00 00"); //jmp @ CaveMemory.Write_StrBytes("E9 04 00 00 00"); //or [ebx-01],00000040 ==> Service is pressed, putting bit to 1 CaveMemory.Write_StrBytes("83 4B FF 40"); //movzx ecx,byte ptr [esp+13] CaveMemory.Write_StrBytes("0F B6 4C 24 13"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_InjectionStruct.InjectionReturnOffset); //Inject it CaveMemory.InjectToOffset(_Buttons_InjectionStruct, "Buttons"); } /// ///To bypass the need of Calibration (calculated at each level entry), we can force the game to use max range (0x00-0xFF) for JVS data ///The game uses these values as Min values (we can force them to 0): ///00C17D29 (p1 min x) ///00C17D2D (p1 min y) ///00C17D31 (p2 min x) ///00C17D35 (p2 min y) ///For max value, the game is using complicated FloatingPoint/Double operations with a kind of ratio values. ///Forcing the following values enable the use of 0x00-0xFF full range values to match with (0.0 - 1024.0) calculated values. /// private void SetHack_Calibration() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //xor edi, edi CaveMemory.Write_StrBytes("31 FF"); //xor ebx,ebx CaveMemory.Write_StrBytes("31 DB"); //xor ebp, ebp CaveMemory.Write_StrBytes("31 ED"); //xor ecx, ecx CaveMemory.Write_StrBytes("31 C9"); //mov [esp+10], 0x00000000 CaveMemory.Write_StrBytes("C7 44 24 10 00 00 00 00"); //mov [esp+14], 0x0000FF00 CaveMemory.Write_StrBytes("C7 44 24 14 00 FF 00 00"); //mov [esp+18], 0x0000FF00 CaveMemory.Write_StrBytes("C7 44 24 18 00 FF 00 00"); //mov [esp+1C], 0x0000FF00 CaveMemory.Write_StrBytes("C7 44 24 1C 00 FF 00 00"); //mov esi, 0x0000FF00 CaveMemory.Write_StrBytes("BE 00 FF 00 00"); //mov dword_C17D28, edi CaveMemory.Write_StrBytes("89 3D 28 7D C1 00"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _CalibrationValues_InjectionStruct.InjectionReturnOffset); //Inject it CaveMemory.InjectToOffset(_CalibrationValues_InjectionStruct, "Calibration Values"); } protected override void Apply_OutputsMemoryHack() { //Create Databak to store our value Create_OutputsDataBank(); _P1_CustomRecoil_CaveAddress = _OutputsDatabank_Address; _P2_CustomRecoil_CaveAddress = _OutputsDatabank_Address + 0x04; _CurrentSequenceStringLength = _OutputsDatabank_Address + 0x10; _CurrentSequenceString = _OutputsDatabank_Address + 0x14; SetHack_Recoil(); SetHack_GetCurrentSequence(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } //Original game is simply setting a Motor to vibrate, so simply using this data to create or pulsed custom recoil will not be synchronized with bullets shot //as the pulses lenght and spaceing will depend on DemulShooter output pulse config data. //To synch recoil pulse with projectiles, this hack allows to intercept the code shooting the actual projectile to generate the pulse private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx,[edi+04] CaveMemory.Write_StrBytes("8B 4F 04"); //mov eax,ecx CaveMemory.Write_StrBytes("8B C1"); //shl eax, 2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_CustomRecoil_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_CustomRecoil_CaveAddress)); //mov [eax], 1 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //xor al,al CaveMemory.Write_StrBytes("30 C0"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Recoil_InjectionStruct.InjectionReturnOffset); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// Get the current sequence string, this will be used later to filter when the game is in "Advertise" mode /// private void SetHack_GetCurrentSequence() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov mov eax,[esp+54] CaveMemory.Write_StrBytes("8B 44 24 54"); //test eax, eax CaveMemory.Write_StrBytes("85 C0"); //je Exit CaveMemory.Write_StrBytes("74 2A"); //push eax CaveMemory.Write_StrBytes("50"); //call strlen() CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _StrlenFunction_Offset); //add esp, 4 CaveMemory.Write_StrBytes("83 C4 04"); //push ecx CaveMemory.Write_StrBytes("51"); //push esi CaveMemory.Write_StrBytes("56"); //push edi CaveMemory.Write_StrBytes("57"); //mov ecx, _CurrentSequenceStringLength CaveMemory.Write_StrBytes("B9"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_CurrentSequenceStringLength)); //mov [ecx], eax CaveMemory.Write_StrBytes("89 01"); //mov ecx,eax CaveMemory.Write_StrBytes("8B C8"); //inc ecx CaveMemory.Write_StrBytes("41"); //mov mov esi,[esp+60] CaveMemory.Write_StrBytes("8B 74 24 60"); //mov edi, _CurrentSequenceString CaveMemory.Write_StrBytes("BF"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_CurrentSequenceString)); //repe movsb CaveMemory.Write_StrBytes("F3 A4"); //pop edi CaveMemory.Write_StrBytes("5F"); //pop esi CaveMemory.Write_StrBytes("5E"); //pop ecx CaveMemory.Write_StrBytes("59"); //mov mov eax,[esp+54] CaveMemory.Write_StrBytes("8B 44 24 54"); //test eax,eax CaveMemory.Write_StrBytes("85 C0"); //Inject it CaveMemory.InjectToOffset(_CurrentSequence_InjectionStruct, "Current Sequence"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteByte(_Data_Base_Address + _P1_X_Offset, bufferX[0]); WriteByte(_Data_Base_Address + _P1_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P1_Buttons_Offset, 0xFE); } else if (PlayerData.ID == 2) { WriteByte(_Data_Base_Address + _P2_X_Offset, bufferX[0]); WriteByte(_Data_Base_Address + _P2_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Data_Base_Address + _P2_Buttons_Offset, 0xFE); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : stays activated when trigger is pulled //Gun recoil : not used ?? _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.Lmp2D3D)); _Outputs.Add(new GameOutput(OutputId.P1_AirFront)); _Outputs.Add(new GameOutput(OutputId.P2_AirFront)); _Outputs.Add(new GameOutput(OutputId.P1_AirRear)); _Outputs.Add(new GameOutput(OutputId.P2_AirRear)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _OutputsPtr_Offset, 4); byte OutputData1 = ReadByte(BitConverter.ToUInt32(buffer, 0) + 0x44); byte OutputData2 = ReadByte(BitConverter.ToUInt32(buffer, 0) + 0x45); SetOutputValue(OutputId.P1_LmpStart, OutputData1 >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, OutputData1 >> 4 & 0x01); SetOutputValue(OutputId.Lmp2D3D, OutputData1 >> 1 & 0x01); SetOutputValue(OutputId.P1_AirFront, OutputData2 >> 7 & 0x01); SetOutputValue(OutputId.P2_AirFront, OutputData2 >> 6 & 0x01); SetOutputValue(OutputId.P1_AirRear, OutputData2 >> 5 & 0x01); SetOutputValue(OutputId.P2_AirRear, OutputData2 >> 4 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, OutputData1 >> 5 & 0x01); SetOutputValue(OutputId.P1_GunMotor, OutputData1 >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, OutputData1 >> 2 & 0x01); SetOutputValue(OutputId.P2_GunMotor, OutputData1 >> 3 & 0x01); //Custom Outputs UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersStructPtr_Offset); UInt32 P1_Strutc_Address = ReadPtr(iTemp + 0x08); UInt32 P2_Strutc_Address = ReadPtr(iTemp + 0x0C); P1_Strutc_Address = ReadPtr(P1_Strutc_Address + 0x14); P2_Strutc_Address = ReadPtr(P2_Strutc_Address + 0x14); int P1_Status = ReadByte(P1_Strutc_Address + 0x38); int P2_Status = ReadByte(P2_Strutc_Address + 0x38); _P1_Life = 0; _P2_Life = 0; UInt32 StrLength = BitConverter.ToUInt32(ReadBytes(_CurrentSequenceStringLength, 4), 0); byte[] bCurrentSeq = ReadBytes(_CurrentSequenceString, StrLength); string sCurrentSeq = System.Text.ASCIIEncoding.ASCII.GetString(bCurrentSeq); if (sCurrentSeq.StartsWith("Adv")) _IsAttractMode = true; if (sCurrentSeq.StartsWith("GameSeq")) _IsAttractMode = false; if (!_IsAttractMode) { _P1_Life = (int)BitConverter.ToSingle(ReadBytes(P1_Strutc_Address + 0x20, 4), 0); if (_P1_Life < 0) _P1_Life = 0; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); _P2_Life = (int)BitConverter.ToSingle(ReadBytes(P2_Strutc_Address + 0x20, 4), 0); if (_P2_Life < 0) _P2_Life = 0; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Using constant "ON" value from motor to create asynch outputs for recoil //Pulses are not sync with bullets, but with DemulShooter output on/off timings only /*SetOutputValue(OutputId.P1_CtmRecoil, OutputData1 >> 6 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, OutputData1 >> 3 & 0x01);*/ //New method : reading intercepted value for bullet fired, so that recoil is sync with bullets //Need to filter this with MOTOR_ON, if not : recoil will be activated when bullets are fired during attract if (ReadByte(_P1_CustomRecoil_CaveAddress) == 1 && (OutputData1 >> 6 & 0x01) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_CustomRecoil_CaveAddress, 0); } if (ReadByte(_P2_CustomRecoil_CaveAddress) == 1 && (OutputData1 >> 3 & 0x01) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_CustomRecoil_CaveAddress, 0); } SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0082E6A0)); //0x00842A88 also possible } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwOpGhost.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_RwOpGhost : Game { private const string GAMEDATA_FOLDER = @"MemoryData\ringwide\op"; /*** MEMORY ADDRESSES **/ private UInt32 _JvsEnabled_Offset = 0x23D2E7; private UInt32 _AmLibData_Ptr_Offset = 0x026662C0; private UInt32 _AmLibData_BaseAddress = 0; private UInt32 _GetKeyboardState_Function_Offset = 0x001DF304; //JVS emulation mode (TEKNOPARROT + Jconfig) //JVS checksum @5992D7 = SUM(64681F:64683E) -> LowNibble in 64683F //Buttons injection will be made at the source of JVS data and need to remove checksum //Axis injection will be made after in-game calculation, as we can't access/save calibration from test menu private UInt32 _Buttons_CaveAddress; private UInt32 _JvsRemoveChecksum_Offset = 0x001992F2; private UInt32 _Jvs_ButtonsInjection_Offset = 0x001AF1E0; private NopStruct _Nop_Axix_X_1 = new NopStruct(0x0009DE8F, 3); private NopStruct _Nop_Axix_X_2 = new NopStruct(0x0009DEBD, 3); private NopStruct _Nop_Axix_Y_1 = new NopStruct(0x0009DF2D, 3); private NopStruct _Nop_Axix_Y_2 = new NopStruct(0x0009DEF7, 3); private UInt32 _Jvs_Data_Ptr_Offset = 0x0265C208; //For P2, use [265C208]+0x74, or real thing is [265C20C]+0x18 private UInt32 _Jvs_Data_BaseAddress = 0; //Hardware hardcoded keys to emulate TEST and SERVICE buttons to move in TestMode, although the game won't save changes private HardwareScanCode _TestButton = HardwareScanCode.DIK_8; private HardwareScanCode _ServiceButton = HardwareScanCode.DIK_9; //For Non JVS axis with the same axis hack, add : private NopStruct _Nop_Axix_X_3 = new NopStruct(0x0009DF47, 3); private NopStruct _Nop_Axix_Y_3 = new NopStruct(0x0009DF4A, 3); //DirectInput mode (no JVS emulation) private UInt32 _Axis_Address_Ptr_Offset = 0x0265C20C; private UInt32 _P2_X_Address; private UInt32 _P2_Y_Address; private NopStruct _Nop_Axis_X = new NopStruct(0x0009E0A4, 3); private NopStruct _Nop_Axis_Y = new NopStruct(0x0009E082, 3); private UInt32 _Buttons_Injection_Offset = 0x0009EF26; private UInt32 _Buttons_Injection_Return_Offset = 0x0009EF2C; private UInt32 _Axis_Injection_Offset = 0x0009DF7E; private UInt32 _Axis_Injection_Return_Offset = 0x0009DF84; //Outputs private UInt32 _JVS_Outputs_Offset = 0x00246428; private UInt32 _InternalLedOutputs_Ptr_Offset = 0x0065C1F4; private InjectionStruct _InternalRecoil_InjectionStruct = new InjectionStruct(0x00030A10, 5); //Custom Data private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P1_Trigger_CaveAddress; private UInt32 _P1_Action_CaveAddress; private UInt32 _P1_Change_CaveAddress; private UInt32 _P1_Reload_CaveAddress; private UInt32 _Recoil_CaveAddress; //Keys (no JVS emulation) //START_P2 = NumPad + //START_P1 = ENTER //Service = Y private VirtualKeyCode _P2_Trigger_VK = VirtualKeyCode.VK_NUMPAD5; private VirtualKeyCode _P2_Reload_VK = VirtualKeyCode.VK_NUMPAD0; private VirtualKeyCode _P2_Change_VK = VirtualKeyCode.VK_DECIMAL; private VirtualKeyCode _P2_Action_VK = VirtualKeyCode.VK_SUBSTRACT; // Test private bool _P2OutOfScreen = false; //JVS emulation detection private bool _IsJvsEnabled = false; //Credits settings (these are defaults values) private int _Credits_Freeplay = 0; //0 or 1 /// /// Constructor /// public Game_RwOpGhost(String RomName) : base(RomName, "gs2") { if (Configurator.GetInstance().OpGhost_EnableFreeplay) _Credits_Freeplay = 1; _KnownMd5Prints.Add("Operation Ghost - For TeknoParrot", "40f795933abc4f441c98acc778610aa2"); _KnownMd5Prints.Add("Operation Ghost - For JConfig", "19a949581145ed8478637d286a4b85a0"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; _AmLibData_BaseAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _AmLibData_Ptr_Offset); //Modifying Credits parameters : WriteBytes(_AmLibData_BaseAddress + 0x1C8, BitConverter.GetBytes(_Credits_Freeplay)); WriteBytes(_AmLibData_BaseAddress + 0x1CC, BitConverter.GetBytes(Configurator.GetInstance().OpGhost_CreditsToStart)); WriteBytes(_AmLibData_BaseAddress + 0x1D0, BitConverter.GetBytes(Configurator.GetInstance().OpGhost_CreditsToContinue)); WriteBytes(_AmLibData_BaseAddress + 0x1D4, BitConverter.GetBytes(Configurator.GetInstance().OpGhost_CoinsPerCredits)); if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero && _AmLibData_BaseAddress != 0) { if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsEnabled_Offset) == 1) { _IsJvsEnabled = true; _Jvs_Data_BaseAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _Jvs_Data_Ptr_Offset); if (_Jvs_Data_BaseAddress != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("AmLib data base address = 0x" + _AmLibData_BaseAddress.ToString("X8")); Logger.WriteLog("JVS emulation detected"); Logger.WriteLog("JVS axis data pointer base address = 0x" + _Jvs_Data_BaseAddress.ToString("X8")); CheckExeMd5(); if (!_DisableInputHack) SetHack_Jvs(); else Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } } else { byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Address_Ptr_Offset, 4); UInt32 Calc_Addr = BitConverter.ToUInt32(buffer, 0); if (Calc_Addr != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; _P2_X_Address = Calc_Addr + 0x28; _P2_Y_Address = Calc_Addr + 0x2C; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("AmLib data base address = 0x" + _AmLibData_BaseAddress.ToString("X8")); //Logger.WriteLog("P1_X adddress = 0x" + _P1_X_Address.ToString("X8")); //Logger.WriteLog("P1_Y adddress = 0x" + _P1_Y_Address.ToString("X8")); Logger.WriteLog("P2_X adddress = 0x" + _P2_X_Address.ToString("X8")); Logger.WriteLog("P2_Y adddress = 0x" + _P2_Y_Address.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-1024] //Y => [0-600] double dMaxX = 1024.0; double dMaxY = 600.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Trigger_CaveAddress = _InputsDatabank_Address; _P1_Reload_CaveAddress = _InputsDatabank_Address + 0x10; _P1_Change_CaveAddress = _InputsDatabank_Address + 0x01; _P1_Action_CaveAddress = _InputsDatabank_Address + 0x03; _P1_X_CaveAddress = _InputsDatabank_Address + 0x20; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x24; SetHack_Buttons(); SetHack_Axis(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// For this hack we will wait the GetKeyboardState call /// And immediately after we will read on our custom memory storage /// to replace lpKeystate bytes for mouse buttons (see WINUSER.H for virtualkey codes) /// then the game will continue... /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //call USER32.GetKEyboardState CaveMemory.Write_StrBytes("FF 15"); byte[] b = BitConverter.GetBytes((int)_TargetProcess_MemoryBaseAddress + _GetKeyboardState_Function_Offset); CaveMemory.Write_Bytes(b); //lpkeystate is in ESP register at that point : //and [esp + 1], 0x00FF0000 CaveMemory.Write_StrBytes("81 64 24 01 00 00 FF 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [_P1_Trigger_Address] CaveMemory.Write_StrBytes("A1"); b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); //We pushed eax so ESP was changed, so now lpkeystate is in. ESP+1+4 //or [esp + 5], eax CaveMemory.Write_StrBytes("09 44 24 05"); //pop eax CaveMemory.Write_StrBytes("58"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// For this hack we will override the writing of X and Y data issued from /// the legit ScrenToClient call, with our own calculated values /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx, [_P1_X_Address] CaveMemory.Write_StrBytes("8B 0D"); byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_Bytes(b); //mov edx, [_P1_Y_Address] CaveMemory.Write_StrBytes("8B 15"); b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Return_Offset); Logger.WriteLog("Adding Axis CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Noping procedures for P2 SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis_Y); //Center Crosshair at start byte[] bufferX = { 0x00, 0x02, 0, 0 }; //512 byte[] bufferY = { 0x2C, 0x01, 0, 0 }; //300 WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); WriteBytes(_P2_X_Address, bufferX); WriteBytes(_P2_Y_Address, bufferY); //Win32.keybd_event(Win32.VK_NUMLOCK, 0x45, Win32.KEYEVENTF_EXTENDEDKEY | 0, 0); } /// /// OutputsHack will only be needed when JVS is disabled to get recoil from internal calls /// protected override void Apply_OutputsMemoryHack() { if (!_IsJvsEnabled) { //Create Databak to store our value Create_OutputsDataBank(); _Recoil_CaveAddress = _OutputsDatabank_Address; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } } /// /// In that function 'RequestRecoil()' the game calls a procedure to get the WeaponManager corresponding to the event /// When JVS emulation is enabled, it returns 3(P1) or 4(P2) /// When JVS emulation is disabled, it returns 1(P1) or 2(P2) /// When JVS emulation is enabled, output can be obtained by the JVS report bytes, so this injection will only be needed for non-JVS /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //push eax CaveMemory.Write_StrBytes("50"); //sub eax,01 CaveMemory.Write_StrBytes("83 E8 01"); //add eax,_Recoil_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); //mov byte ptr [eax],01 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //sub eax,03 CaveMemory.Write_StrBytes("83 E8 03"); //Inject it CaveMemory.InjectToOffset(_InternalRecoil_InjectionStruct, "Recoil"); } #endregion #region Memory Hack for JVS private void SetHack_Jvs() { CreateDataBank_Jvs(); //NOPing axis instructions SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axix_X_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axix_X_2); //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axix_X_3); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axix_Y_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axix_Y_2); //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axix_Y_3); //Hacking buttons values in JVS data source //Seems like there is a checksum verification for JVS data integrity, so we will remove that WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _JvsRemoveChecksum_Offset, new byte[] { 0xEB, 0x11, 0x90, 0x90, 0x90, 0x90, 0x90 }); //Now we can filter and modify buttons values according to what we need to send Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //and [646829], 00800080 CaveMemory.Write_StrBytes("81 25 29 68 64 00 80 00 80 00"); //mov eax, [_Buttons] CaveMemory.Write_StrBytes("A1"); byte[] b = BitConverter.GetBytes(_Buttons_CaveAddress); CaveMemory.Write_Bytes(b); //or [646829], eax CaveMemory.Write_StrBytes("09 05 29 68 64 00"); //mov eax [_ButtonsTestAddress] CaveMemory.Write_StrBytes("A1"); b = BitConverter.GetBytes(_Buttons_CaveAddress + 4); CaveMemory.Write_Bytes(b); //or byte ptr[646828], al CaveMemory.Write_StrBytes("08 05 28 68 64 00"); //pop eax CaveMemory.Write_StrBytes("58"); //jmp dword ptr [edx*4+gs2.exe+1AF274] CaveMemory.Write_StrBytes("FF 24 95 74 F2 5A 00"); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Jvs_ButtonsInjection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Jvs_ButtonsInjection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// 1st Memory created to store custom buttons data /// This memory will be read by the codecave to overwrite the original data read from EAX /// private void CreateDataBank_Jvs() { Codecave InputMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); InputMemory.Open(); InputMemory.Alloc(0x800); _Buttons_CaveAddress = InputMemory.CaveAddress; Logger.WriteLog("Custom JVS Buttons data will be stored at : 0x" + _P1_X_CaveAddress.ToString("X8")); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { if (_IsJvsEnabled) { WriteBytes(_Jvs_Data_BaseAddress + 0x18, bufferX); WriteBytes(_Jvs_Data_BaseAddress + 0x1C, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0x7F); } } else { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(_P1_Change_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(_P1_Change_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { if (!Configurator.GetInstance().OpGhost_SeparateButtons) WriteByte(_P1_Action_CaveAddress, 0x80); PlayerData.RIController.Computed_X = 2000; byte[] bufferX_R = { (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_X >> 8), 0, 0 }; WriteBytes(_P1_X_CaveAddress, bufferX_R); System.Threading.Thread.Sleep(20); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { if (!Configurator.GetInstance().OpGhost_SeparateButtons) WriteByte(_P1_Action_CaveAddress, 0x00); } } } else if (PlayerData.ID == 2) { if (_IsJvsEnabled) { //JVS Axis WriteBytes(_Jvs_Data_BaseAddress + 0x74, bufferX); WriteBytes(_Jvs_Data_BaseAddress + 0x78, bufferY); //JVS Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 2, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 2, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 2, 0x01); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 2, 0xFE); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0x7F); } } else { WriteBytes(_P2_X_Address, bufferX); WriteBytes(_P2_Y_Address, bufferY); //P2 uses keyboard so no autoreload when out of screen, so we add: if (PlayerData.RIController.Computed_X <= 1 || PlayerData.RIController.Computed_X >= 1022 || PlayerData.RIController.Computed_Y <= 1 || PlayerData.RIController.Computed_Y >= 596) { if (!_P2OutOfScreen) { Send_VK_KeyDown(_P2_Reload_VK); _P2OutOfScreen = true; } } else { if (_P2OutOfScreen) { Send_VK_KeyUp(_P2_Reload_VK); _P2OutOfScreen = false; } } //Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Send_VK_KeyDown(_P2_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Send_VK_KeyUp(_P2_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Send_VK_KeyDown(_P2_Change_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Send_VK_KeyUp(_P2_Change_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Send_VK_KeyDown(_P2_Reload_VK); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Send_VK_KeyDown(_P2_Action_VK); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Send_VK_KeyUp(_P2_Reload_VK); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Send_VK_KeyUp(_P2_Action_VK); } } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (_IsJvsEnabled) { if (s.scanCode == _TestButton) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x40); } else if (s.scanCode == _ServiceButton) { Apply_OR_ByteMask(_Buttons_CaveAddress + 4, 0x80); } if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x80); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x80); } } } else { if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { WriteByte(_P1_Action_CaveAddress, 0x80); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Send_VK_KeyDown(_P2_Action_VK); } } } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (_IsJvsEnabled) { if (s.scanCode == _TestButton) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xBF); } else if (s.scanCode == _ServiceButton) { Apply_AND_ByteMask(_Buttons_CaveAddress + 4, 0x7F); } if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0x7F); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0x7F); } } } else { if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { WriteByte(_P1_Action_CaveAddress, 0x00); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Send_VK_KeyUp(_P2_Action_VK); } } } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : stays activated when trigger is pulled //Gun recoil : not used ?? _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (_IsJvsEnabled) { byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, bOutput >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bOutput >> 4 & 0x01); SetOutputValue(OutputId.LmpBillboard, bOutput >> 5 & 0x01); SetOutputValue(OutputId.P1_LmpHolder, bOutput >> 1 & 0x01); SetOutputValue(OutputId.P2_LmpHolder, bOutput & 0x01); SetOutputValue(OutputId.P1_GunRecoil, bOutput >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, bOutput >> 3 & 0x01); //Custom recoil will be enabled just like original recoil SetOutputValue(OutputId.P1_CtmRecoil, bOutput >> 6 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, bOutput >> 3 & 0x01); } else { //ReadPtr() + 15E0 = Outputs //1 Byte per output (0 / 1) // +0 = Billboard // +1 = P1 Holder // +2 = P2 Holder // +3 = Unused -- // +4 = P1 START // +5 = P2 START UInt32 LedStatusAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _InternalLedOutputs_Ptr_Offset) + 0x15E0; SetOutputValue(OutputId.P1_LmpStart, ReadByte(LedStatusAddress + 4)); SetOutputValue(OutputId.P2_LmpStart, ReadByte(LedStatusAddress + 5)); SetOutputValue(OutputId.LmpBillboard, ReadByte(LedStatusAddress)); SetOutputValue(OutputId.P1_LmpHolder, ReadByte(LedStatusAddress + 1)); SetOutputValue(OutputId.P2_LmpHolder, ReadByte(LedStatusAddress + 2)); if (ReadByte(_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress, 0x00); } if (ReadByte(_Recoil_CaveAddress + 1) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 1, 0x00); } } //Credits will be calculated by using the formula : Credits = Coins / CoinsByCredits //Warning : Need to handle "Divide by 0" error if game is closed brutally ! int Credits = 0; try { Credits = ReadByte((UInt32)_AmLibData_BaseAddress + 0x1E0) / ReadByte((UInt32)_AmLibData_BaseAddress + 0x1D4); } catch { } SetOutputValue(OutputId.Credits, Credits); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwSDR.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_RwSDR : Game { private const string GAMEDATA_FOLDER = @"MemoryData\ringwide\sdr"; /*** MEMORY ADDRESSES **/ private UInt32 _Controls_Base_Address; private UInt32 _Data_Base_Address_Ptr_Offset = 0x0308A0C4; private UInt32 _P1_X_Offset = 0x00000017; private UInt32 _P1_Y_Offset = 0x00000015; private UInt32 _P1_Buttons_Offset = 0x00000008; private UInt32 _P2_X_Offset = 0x0000001B; private UInt32 _P2_Y_Offset = 0x00000019; private UInt32 _P2_Buttons_Offset = 0x0000000A; private NopStruct _Nop_Axis = new NopStruct(0x000DB450, 5); private UInt32 _Buttons_Injection_Offset = 0x000DB3EC; private UInt32 _Buttons_Injection_Return_Offset = 0x000DB3F1; //Outputs private UInt32 _Outputs_Offset = 0x0308A0A4; private UInt32 _P1_Status_Offset = 0x0118FF60; private UInt32 _P1_Life_Offset = 0x0118FECC; private UInt32 _P2_Status_Offset = 0x0118FFFC; private UInt32 _P2_Life_Offset = 0x0118FEE0; /// /// Constructor /// public Game_RwSDR(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Sega Dream Raider - Unpatched Header", "abedceb2135ab50c1182dda31153667687f53469"); _KnownMd5Prints.Add("Sega Dream Raider - Patched Header", "ee96b126534355d96c12449291bdc44bb2d13e29"); _KnownMd5Prints.Add("Sega Dream Raider - For TeknoParrot", "4264540b2a24f3359a3deb5f1e95e392"); _KnownMd5Prints.Add("Sega Dream Raider - For JConfig", "da993b12f7572f828c578c9eb73b3111"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Data_Base_Address_Ptr_Offset, 4); _Controls_Base_Address = BitConverter.ToUInt32(buffer, 0); if (_Controls_Base_Address != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("Controls base address = 0x" + _Controls_Base_Address.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [30-C0] = 145 //Y => [30-C0] = 145 //Axes inversés : 0 = Bas et Droite double dMinX = 48.0; double dMinY = 48.0; double dMaxX = 192.0; double dMaxY = 192.0; double DeltaX = dMaxX - dMinX; double DeltaY = dMaxY - dMinY; PlayerData.RIController.Computed_X = Convert.ToInt32(DeltaX - Math.Round(DeltaX * PlayerData.RIController.Computed_X / TotalResX) + dMinX); PlayerData.RIController.Computed_Y = Convert.ToInt32(DeltaY - Math.Round(DeltaY * PlayerData.RIController.Computed_Y / TotalResY) + dMinY); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them /// Reverse back to it when DumbJVSCommand will be working with ParrotLoader, without DumbJVSManager /// protected override void Apply_InputsMemoryHack() { //NOPing axis proc SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis); //Hacking buttons proc : //Same byte is used for both triggers, start and service (for each player) //0b10000000 is Start //0b01000000 is Service //0b00000010 is Trigger //So we need to make a mask to accept Start button moodification and block other so we can inject Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push esi CaveMemory.Write_StrBytes("56"); //and esi,00000080 CaveMemory.Write_StrBytes("81 E6 80 00 00 00"); //cmp esi,00 CaveMemory.Write_StrBytes("83 FE 00"); //jg @ => if Start PRessed CaveMemory.Write_StrBytes("0F 8F 0D 00 00 00"); //and dword ptr [ebp+ecx*2+08],7F => Putting the start bit to 0 CaveMemory.Write_StrBytes("81 64 4D 08 7F FF FF FF"); //jmp @ CaveMemory.Write_StrBytes("E9 08 00 00 00"); //or [ebp+ecx*2+08],00000080 ==> start is pressed, putting bit to 1 CaveMemory.Write_StrBytes("81 4C 4D 08 80 00 00 00"); //pop esi CaveMemory.Write_StrBytes("5E"); //and esi,00000040 CaveMemory.Write_StrBytes("83 E6 40"); //cmp esi,00 CaveMemory.Write_StrBytes("83 FE 00"); //jg @ => if Service PRessed CaveMemory.Write_StrBytes("0F 8F 0A 00 00 00"); //and [ebp+ecx*2+08],000000BF => Putting the Service bit to 0 CaveMemory.Write_StrBytes("83 64 4D 08 BF"); //jmp @ CaveMemory.Write_StrBytes("E9 05 00 00 00"); //or dword ptr [ebp+ecx*2+08],40 ==> Service is pressed, putting bit to 1 CaveMemory.Write_StrBytes("83 4C 4D 08 40"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteByte(_Controls_Base_Address + _P1_X_Offset, bufferX[0]); WriteByte(_Controls_Base_Address + _P1_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Controls_Base_Address + _P1_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Controls_Base_Address + _P1_Buttons_Offset, 0xFD); } else if (PlayerData.ID == 2) { WriteByte(_Controls_Base_Address + _P2_X_Offset, bufferX[0]); WriteByte(_Controls_Base_Address + _P2_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Controls_Base_Address + _P2_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Controls_Base_Address + _P2_Buttons_Offset, 0xFD); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : ?? _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.LmpRear_R)); _Outputs.Add(new GameOutput(OutputId.LmpRear_G)); _Outputs.Add(new GameOutput(OutputId.LmpRear_B)); _Outputs.Add(new GameOutput(OutputId.Blower_Level)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte OutputData1 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset); byte OutputData2 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 1); byte OutputData3 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2); SetOutputValue(OutputId.P1_LmpStart, OutputData1 >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, OutputData1 >> 4 & 0x01); SetOutputValue(OutputId.P1_Lmp_R, OutputData2 >> 7 & 0x01); SetOutputValue(OutputId.P1_Lmp_G, OutputData2 >> 6 & 0x01); SetOutputValue(OutputId.P1_Lmp_B, OutputData2 >> 5 & 0x01); SetOutputValue(OutputId.P2_Lmp_R, OutputData2 >> 4 & 0x01); SetOutputValue(OutputId.P2_Lmp_G, OutputData2 >> 3 & 0x01); SetOutputValue(OutputId.P2_Lmp_B, OutputData2 >> 2 & 0x01); SetOutputValue(OutputId.LmpRear_R, OutputData2 >> 1 & 0x01); SetOutputValue(OutputId.LmpRear_G, OutputData2 & 0x01); SetOutputValue(OutputId.LmpRear_B, OutputData3 >> 7 & 0x01); SetOutputValue(OutputId.Blower_Level, (OutputData1 & 0x03) + (OutputData3 >> 4 & 0x04)); SetOutputValue(OutputId.P1_GunMotor, OutputData1 >> 6 & 0x01); SetOutputValue(OutputId.P2_GunMotor, OutputData1 >> 3 & 0x01); //Custom Outputs int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset); int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Status_Offset); _P1_Life = 0; _P2_Life = 0; if (P1_Status == 1) { _P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 1) { _P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Using constant "ON" value from motor to create asynch outputs for recoil SetOutputValue(OutputId.P1_CtmRecoil, OutputData1 >> 6 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, OutputData1 >> 3 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00607FF0)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwSGG.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_RwSGG : Game { private const string GAMEDATA_FOLDER = @"MemoryData\ringwide\sgg"; /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x00478F9D; private UInt32 _P1_Y_Offset = 0x00478FA3; private UInt32 _P2_X_Offset = 0x00478FA9; private UInt32 _P2_Y_Offset = 0x00478FAF; private NopStruct _Nop_Axis = new NopStruct(0x0018B0B9, 3); private UInt32 _Buttons_Injection_Offset = 0x0018B031; private UInt32 _Buttons_Injection_Return_Offset = 0x0018B036; private UInt32 _GameTestMenuSettings_Offsets = 0x00681D38; private UInt32 _P1_Buttons_CaveAddress; private UInt32 _P2_Buttons_CaveAddress; //Those 3 are used to store : // - The number of credit added since the game has started, // - The max value of credits we can add in a game // - The max value of credits at a given time during the game //These settings are hardcoded (MemoryBaseAddress + 0x000602E9, MemoryBaseAddress + 0x000602EB) //and can be changed by hex-editing the binary. //In order to let binaries as untouched as possible, DemulShooter will use one of the Codecave to reset the value stored in memory private UInt32 _HardcodedNumberOfCredits_SinceBeginning_Offset = 0x0065C60F; //private UInt32 _HardcoredMaxNumberOfCreditsToAdd = 0x0065C608; private UInt32 _HardcodedMaxNumberOfCredits = 0x0065C609; //Outputs private UInt32 _OutputsPtr_Offset = 0x027034A8; private UInt32 _Outputs_Address = 0; private UInt32 _GameStatusPtr_Offset = 0x027034BC; private UInt32 _Credits_Offset = 0x0065C410; private UInt32 _PlayersPtr_Offset = 0x027034E4; private UInt32 _P1_CustomRecoil_Address = 0; private UInt32 _P2_CustomRecoil_Address = 0; private UInt32 _P1_Recoil_Injection_Offset = 0x00075B60; private UInt32 _P1_Recoil_Injection_Return_Offset = 0x00075B65; private UInt32 _P2_Recoil_Injection_Offset = 0x00075B82; private UInt32 _P2_Recoil_Injection_Return_Offset = 0x00075B87; private Timer _Tmr_NoAutofireP1; private Timer _Tmr_NoAutofireP2; /// /// Constructor /// public Game_RwSGG(String RomName) : base(RomName, "RingGunR_RingWide") { if (_NoAutoFire) { _Tmr_NoAutofireP1 = new Timer(); _Tmr_NoAutofireP1.Interval = 20; _Tmr_NoAutofireP1.Enabled = true; _Tmr_NoAutofireP1.Tick +=new EventHandler(_Tmr_NoAutofireP1_Tick); _Tmr_NoAutofireP2 = new Timer(); _Tmr_NoAutofireP2.Interval = 20; _Tmr_NoAutofireP2.Enabled = true; _Tmr_NoAutofireP2.Tick += new EventHandler(_Tmr_NoAutofireP2_Tick); } _KnownMd5Prints.Add("GoldenGun - For TeknoParrot", "9a94458ca852b8b33d8b17b2cfdd663d"); _KnownMd5Prints.Add("GoldenGun - For JConfig", "338efedcffdfead481dc5ff51d25f570"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //Wait for game to load settings if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets) != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; //Force Calibration values to get fixed and maximum range WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets + 0x44, new byte[] { 0x00, 0x06, 0x00, 0x0A }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets + 0x4C, new byte[] { 0x00, 0xFA, 0x00, 0xF6 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets + 0x54, new byte[] { 0x00, 0x06, 0x00, 0x0A }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets + 0x5C, new byte[] { 0x00, 0xFA, 0x00, 0xF6 }); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch (Exception ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); Logger.WriteLog(ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //We can't access the TEST menu to do calibration //Choosen solution is to force Calibration Values for Min-Max axis to [0x00-0xFF] when we write axis values in memory //So we can safely use full range of values now : double dMaxX = 255.0; double dMaxY = 255.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P2_Buttons_CaveAddress = _InputsDatabank_Address; _P1_Buttons_CaveAddress = _InputsDatabank_Address + 0x04; SetHack_Axis(); SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Axis() { //NOPing Axis proc SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis); //Centering Crosshair at start WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, 0x80); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, 0x80); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, 0x80); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, 0x80); } /// /// START, SERVICE and TRIGGER/RELOAD are on the same byte /// This hack will just block update of the concerned bits so that we can injct our own value /// That way, other buttons will work as usual /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //This is not related to input hack, this is to remove the credits limitation : //mov byte ptr[_HardcodedNumberOfCredits_SinceBeginning], 0 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _HardcodedNumberOfCredits_SinceBeginning_Offset)); CaveMemory.Write_StrBytes("00"); //mov byte ptr[_HardcodedMaxNumberOfCredits], 0 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _HardcodedMaxNumberOfCredits)); CaveMemory.Write_StrBytes("FF"); List Buffer = new List(); //mov dl, [esp+esi+14] CaveMemory.Write_StrBytes("8A 54 34 14"); //cmp esi, 0 CaveMemory.Write_StrBytes("83 FE 00"); //jne exit code CaveMemory.Write_StrBytes("0F 85 0F 00 00 00"); //shl ebx, 1 CaveMemory.Write_StrBytes("D1 E3"); //add ebx, _P2_Buttons_CaveAddress byte[] b = BitConverter.GetBytes(_P2_Buttons_CaveAddress); CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(b); //mov ebx, [ebx] CaveMemory.Write_StrBytes("8B 1B"); //and dl, 0xFC CaveMemory.Write_StrBytes("80 E2 FC"); //or dl, bl CaveMemory.Write_StrBytes("08 DA"); //Exit: //mov bl, al CaveMemory.Write_StrBytes("88 C3"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// HAcks for recoil output : /// Data is available on the Output offset but the game is resetting the value too quickly for DemulShooter to get it everytime /// So we are using a custom flag in memory, and will put it to 0 or 1 when the game is changing original recoil state. /// protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_CustomRecoil_Address = _OutputsDatabank_Address; _P2_CustomRecoil_Address = _OutputsDatabank_Address + 0x04; SetHack_Recoil_P1(); SetHack_Recoil_P2(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Recoil_P1() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //xor ecx,ecx CaveMemory.Write_StrBytes("31 C9"); //or ebx,40 CaveMemory.Write_StrBytes("83 CB 40"); //mov [_P1_CustomRecoil_Address], 1 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_CustomRecoil_Address)); CaveMemory.Write_StrBytes("01 00 00 00"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Recoil_Injection_Return_Offset); Logger.WriteLog("Adding P1 Recoil output CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Recoil_Injection_Offset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Recoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_Recoil_P2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ecx, 1 CaveMemory.Write_StrBytes("B9 01 00 00 00"); //mov [_P2_CustomRecoil_Address], 1 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_CustomRecoil_Address)); CaveMemory.Write_StrBytes("01 00 00 00"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Recoil_Injection_Return_Offset); Logger.WriteLog("Adding P2 Recoil output CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Recoil_Injection_Offset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Recoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX[0]); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x02); //Force single shot instead of Auto-fire mode if (_NoAutoFire) _Tmr_NoAutofireP1.Start(); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFE); } else if (PlayerData.ID == 2) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX[0]); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY[0]); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x02); //Force single shot instead of Auto-fire mode if (_NoAutoFire) _Tmr_NoAutofireP2.Start(); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFE); } } private void _Tmr_NoAutofireP1_Tick(Object Sender, EventArgs e) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFD); _Tmr_NoAutofireP1.Stop(); } private void _Tmr_NoAutofireP2_Tick(Object Sender, EventArgs e) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFD); _Tmr_NoAutofireP2.Stop(); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.P2_LmpPanel)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { _Outputs_Address = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _OutputsPtr_Offset) + 0x08; SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Outputs_Address) >> 4 & 0x01); //According to the manual, those 2 Outputs are mulicolor LEDs drawing a "X" or a "+" on the main panel //Hard to get them exactly without a GameTest binary SetOutputValue(OutputId.P1_LmpPanel, 0); SetOutputValue(OutputId.P2_LmpPanel, 0); SetOutputValue(OutputId.P1_GunRecoil, ReadByte(_Outputs_Address) >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, ReadByte(_Outputs_Address) >> 3 & 0x01); int P1_Clip = 0; int P2_Clip = 0; //Game Status : //2,3,4: Logos at start //5: Attract video //6: Title Menu //9: Demo instructions //10: Attract gameplay video //12: Playing //11: Ranking UInt32 GameStatus = ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _GameStatusPtr_Offset) + 0x34); if (GameStatus == 12) { UInt32 PlayersPtr_BaseAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersPtr_Offset); if (PlayersPtr_BaseAddress != 0) { UInt32 P1_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x34); UInt32 P2_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x38); //Player Status: //0: Not Playing //3: InGame //4: CutScene //5: Bonus select //12/14: Continu Screen int P1_Status = ReadByte(P1_StructAddress + 0x38); int P2_Status = ReadByte(P2_StructAddress + 0x38); if (P1_Status == 03 || P1_Status == 04) { _P1_Life = ReadByte(P1_StructAddress + 0x3C); _P1_Ammo = ReadByte(P1_StructAddress + 0x38C); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 03 || P2_Status == 04) { _P2_Life = ReadByte(P2_StructAddress + 0x3C); _P2_Ammo = ReadByte(P2_StructAddress + 0x38C); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } } //Custom Recoil will be read by our own codecave if (ReadByte(_P1_CustomRecoil_Address) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_CustomRecoil_Address, 0); } if (ReadByte(_P2_CustomRecoil_Address) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_CustomRecoil_Address, 0); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwTargetBravo.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_RwTargetBravo : Game { private const string GAMEDATA_FOLDER = @"MemoryData\ringwide\tb"; /*** MEMORY ADDRESSES **/ private UInt32 _JvsEnabled_Offset = 0x247344; private UInt32 _AmLibData_Ptr_Offset = 0x02671580; private UInt32 _AmLibData_BaseAddress = 0; private UInt32 _GetKeyboardState_Function_Offset = 0x001E735C; //JVS emulation mode (TEKNOPARROT + Jconfig) //JVS checksum @5992D7 = SUM(64681F:64683E) -> LowNibble in 64683F //Buttons injection will be made at the source of JVS data and need to remove checksum //Axis injection will be made after in-game calculation, as we can't access/save calibration from test menu private UInt32 _Buttons_CaveAddress; private UInt32 _JvsRemoveChecksum_Offset = 0x001A1262; private UInt32 _Jvs_ButtonsInjection_Offset = 0x001B72C0; private UInt32 _Jvs_Data_Ptr_Offset = 0x026669C0; //For P2, use [265C208]+0x74, or real thing is [265C20C]+0x18 private UInt32 _Jvs_Data_BaseAddress = 0; private NopStruct _Nop_Axix_X_1 = new NopStruct(0x000A186F, 3); private NopStruct _Nop_Axix_X_2 = new NopStruct(0x000A189D, 3); private NopStruct _Nop_Axix_Y_1 = new NopStruct(0x000A190D, 3); private NopStruct _Nop_Axix_Y_2 = new NopStruct(0x000A18D7, 3); //Hardware hardcoded keys to emulate TEST and SERVICE buttons to move in TestMode, although the game won't save changes private HardwareScanCode _TestButton = HardwareScanCode.DIK_8; private HardwareScanCode _ServiceButton = HardwareScanCode.DIK_9; //For Non JVS axis with the same axis hack, add : private NopStruct _Nop_Axix_X_3 = new NopStruct(0x000A1927, 3); private NopStruct _Nop_Axix_Y_3 = new NopStruct(0x000A192A, 3); //DirectInput mode (no JVS emulation) private UInt32 _Axis_Address_Ptr_Offset = 0x026669C4; private UInt32 _P2_X_Address; private UInt32 _P2_Y_Address; private NopStruct _Nop_Axis_X = new NopStruct(0x000A1A84, 3); private NopStruct _Nop_Axis_Y = new NopStruct(0x000A1A62, 3); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x000A2906, 6); private InjectionStruct _Axis_InjectionStruct = new InjectionStruct(0x000A195E, 6); //Outputs private UInt32 _JVS_Outputs_Offset = 0x00250B38; private UInt32 _InternalLedOutputs_Ptr_Offset = 0x006669AC; private InjectionStruct _InternalRecoil_InjectionStruct = new InjectionStruct(0x000318D0, 5); //Custom Data private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P1_Trigger_CaveAddress; private UInt32 _P1_Action_CaveAddress; private UInt32 _P1_Change_CaveAddress; private UInt32 _P1_Reload_CaveAddress; private UInt32 _Recoil_CaveAddress; //Keys (no JVS emulation) //START_P2 = NumPad + //START_P1 = ENTER //Service = Y private VirtualKeyCode _P2_Trigger_VK = VirtualKeyCode.VK_NUMPAD5; private VirtualKeyCode _P2_Reload_VK = VirtualKeyCode.VK_NUMPAD0; private VirtualKeyCode _P2_Change_VK = VirtualKeyCode.VK_DECIMAL; private VirtualKeyCode _P2_Action_VK = VirtualKeyCode.VK_SUBSTRACT; // Test private bool _P2OutOfScreen = false; //JVS emulation detection private bool _IsJvsEnabled = false; //Credits settings (these are defaults values) private int _Credits_Freeplay = 0; //0 or 1 /// /// Constructor /// public Game_RwTargetBravo(String RomName) : base(RomName, "gs2") { if (Configurator.GetInstance().OpGhost_EnableFreeplay) _Credits_Freeplay = 1; _KnownMd5Prints.Add("Target Bravo - original", "a93befe8fa764059b96296f2d40bb5db"); _KnownMd5Prints.Add("Target Bravo - 1080p patched", "3b508229088a65baf0329469e1aedab3"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; _AmLibData_BaseAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _AmLibData_Ptr_Offset); //Modifying Credits parameters : WriteBytes(_AmLibData_BaseAddress + 0x1C8, BitConverter.GetBytes(_Credits_Freeplay)); WriteBytes(_AmLibData_BaseAddress + 0x1CC, BitConverter.GetBytes(Configurator.GetInstance().OpGhost_CreditsToStart)); WriteBytes(_AmLibData_BaseAddress + 0x1D0, BitConverter.GetBytes(Configurator.GetInstance().OpGhost_CreditsToContinue)); WriteBytes(_AmLibData_BaseAddress + 0x1D4, BitConverter.GetBytes(Configurator.GetInstance().OpGhost_CoinsPerCredits)); if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero && _AmLibData_BaseAddress != 0) { if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JvsEnabled_Offset) == 1) { _IsJvsEnabled = true; _Jvs_Data_BaseAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _Jvs_Data_Ptr_Offset); if (_Jvs_Data_BaseAddress != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("AmLib data base address = 0x" + _AmLibData_BaseAddress.ToString("X8")); Logger.WriteLog("JVS emulation detected"); Logger.WriteLog("JVS axis data pointer base address = 0x" + _Jvs_Data_BaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); //if (!_DisableInputHack) // SetHack_Jvs(); //else // Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } } else { byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Address_Ptr_Offset, 4); UInt32 Calc_Addr = BitConverter.ToUInt32(buffer, 0); if (Calc_Addr != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; _P2_X_Address = Calc_Addr + 0x28; _P2_Y_Address = Calc_Addr + 0x2C; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("AmLib data base address = 0x" + _AmLibData_BaseAddress.ToString("X8")); //Logger.WriteLog("P1_X adddress = 0x" + _P1_X_Address.ToString("X8")); //Logger.WriteLog("P1_Y adddress = 0x" + _P1_Y_Address.ToString("X8")); Logger.WriteLog("P2_X adddress = 0x" + _P2_X_Address.ToString("X8")); Logger.WriteLog("P2_Y adddress = 0x" + _P2_Y_Address.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-1024] //Y => [0-600] double dMaxX = 1024.0; double dMaxY = 600.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Trigger_CaveAddress = _InputsDatabank_Address; _P1_Reload_CaveAddress = _InputsDatabank_Address + 0x10; _P1_Change_CaveAddress = _InputsDatabank_Address + 0x01; _P1_Action_CaveAddress = _InputsDatabank_Address + 0x03; _P1_X_CaveAddress = _InputsDatabank_Address + 0x20; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x24; SetHack_Buttons(); SetHack_Axis(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// For this hack we will wait the GetKeyboardState call /// And immediately after we will read on our custom memory storage /// to replace lpKeystate bytes for mouse buttons (see WINUSER.H for virtualkey codes) /// then the game will continue... /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //call USER32.GetKEyboardState CaveMemory.Write_StrBytes("FF 15"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GetKeyboardState_Function_Offset)); //lpkeystate is in ESP register at that point : //and [esp + 1], 0x00FF0000 CaveMemory.Write_StrBytes("81 64 24 01 00 00 FF 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [_P1_Trigger_Address] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Trigger_CaveAddress)); //We pushed eax so ESP was changed, so now lpkeystate is in. ESP+1+4 //or [esp + 5], eax CaveMemory.Write_StrBytes("09 44 24 05"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Buttons_InjectionStruct, "Trigger"); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); } /// /// For this hack we will override the writing of X and Y data issued from /// the legit ScrenToClient call, with our own calculated values /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx, [_P1_X_Address] CaveMemory.Write_StrBytes("8B 0D"); byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_Bytes(b); //mov edx, [_P1_Y_Address] CaveMemory.Write_StrBytes("8B 15"); b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_Bytes(b); //Inject it CaveMemory.InjectToOffset(_Axis_InjectionStruct, "Axis"); //Noping procedures for P2 SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis_Y); //Center Crosshair at start byte[] bufferX = { 0x00, 0x02, 0, 0 }; //512 byte[] bufferY = { 0x2C, 0x01, 0, 0 }; //300 WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); WriteBytes(_P2_X_Address, bufferX); WriteBytes(_P2_Y_Address, bufferY); } /// /// OutputsHack will only be needed when JVS is disabled to get recoil from internal calls /// protected override void Apply_OutputsMemoryHack() { if (!_IsJvsEnabled) { //Create Databak to store our value Create_OutputsDataBank(); _Recoil_CaveAddress = _OutputsDatabank_Address; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } } /// /// In that function 'RequestRecoil()' the game calls a procedure to get the WeaponManager corresponding to the event /// When JVS emulation is enabled, it returns 3(P1) or 4(P2) /// When JVS emulation is disabled, it returns 1(P1) or 2(P2) /// When JVS emulation is enabled, output can be obtained by the JVS report bytes, so this injection will only be needed for non-JVS /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //push eax CaveMemory.Write_StrBytes("50"); //sub eax,01 CaveMemory.Write_StrBytes("83 E8 01"); //add eax,_Recoil_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); //mov byte ptr [eax],01 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //sub eax,03 CaveMemory.Write_StrBytes("83 E8 03"); //Inject it CaveMemory.InjectToOffset(_InternalRecoil_InjectionStruct, "Recoil"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { if (_IsJvsEnabled) { WriteBytes(_Jvs_Data_BaseAddress + 0x18, bufferX); WriteBytes(_Jvs_Data_BaseAddress + 0x1C, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x01); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xFE); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0x7F); } } else { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(_P1_Change_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(_P1_Change_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { if (!Configurator.GetInstance().OpGhost_SeparateButtons) WriteByte(_P1_Action_CaveAddress, 0x80); PlayerData.RIController.Computed_X = 2000; byte[] bufferX_R = { (byte)(PlayerData.RIController.Computed_X & 0xFF), (byte)(PlayerData.RIController.Computed_X >> 8), 0, 0 }; WriteBytes(_P1_X_CaveAddress, bufferX_R); System.Threading.Thread.Sleep(20); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { if (!Configurator.GetInstance().OpGhost_SeparateButtons) WriteByte(_P1_Action_CaveAddress, 0x00); } } } else if (PlayerData.ID == 2) { if (_IsJvsEnabled) { //JVS Axis WriteBytes(_Jvs_Data_BaseAddress + 0x74, bufferX); WriteBytes(_Jvs_Data_BaseAddress + 0x78, bufferY); //JVS Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 2, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 2, 0xFD); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_Buttons_CaveAddress + 2, 0x01); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_Buttons_CaveAddress + 2, 0xFE); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0x7F); } } else { WriteBytes(_P2_X_Address, bufferX); WriteBytes(_P2_Y_Address, bufferY); //P2 uses keyboard so no autoreload when out of screen, so we add: if (PlayerData.RIController.Computed_X <= 1 || PlayerData.RIController.Computed_X >= 1022 || PlayerData.RIController.Computed_Y <= 1 || PlayerData.RIController.Computed_Y >= 596) { if (!_P2OutOfScreen) { Send_VK_KeyDown(_P2_Reload_VK); _P2OutOfScreen = true; } } else { if (_P2OutOfScreen) { Send_VK_KeyUp(_P2_Reload_VK); _P2OutOfScreen = false; } } //Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Send_VK_KeyDown(_P2_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Send_VK_KeyUp(_P2_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Send_VK_KeyDown(_P2_Change_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Send_VK_KeyUp(_P2_Change_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Send_VK_KeyDown(_P2_Reload_VK); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Send_VK_KeyDown(_P2_Action_VK); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Send_VK_KeyUp(_P2_Reload_VK); if (!Configurator.GetInstance().OpGhost_SeparateButtons) Send_VK_KeyUp(_P2_Action_VK); } } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (_IsJvsEnabled) { if (s.scanCode == _TestButton) { Apply_OR_ByteMask(_Buttons_CaveAddress, 0x40); } else if (s.scanCode == _ServiceButton) { Apply_OR_ByteMask(_Buttons_CaveAddress + 4, 0x80); } if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { Apply_OR_ByteMask(_Buttons_CaveAddress + 1, 0x80); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Apply_OR_ByteMask(_Buttons_CaveAddress + 3, 0x80); } } } else { if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { WriteByte(_P1_Action_CaveAddress, 0x80); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Send_VK_KeyDown(_P2_Action_VK); } } } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (_IsJvsEnabled) { if (s.scanCode == _TestButton) { Apply_AND_ByteMask(_Buttons_CaveAddress, 0xBF); } else if (s.scanCode == _ServiceButton) { Apply_AND_ByteMask(_Buttons_CaveAddress + 4, 0x7F); } if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { Apply_AND_ByteMask(_Buttons_CaveAddress + 1, 0x7F); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Apply_AND_ByteMask(_Buttons_CaveAddress + 3, 0x7F); } } } else { if (Configurator.GetInstance().OpGhost_SeparateButtons) { if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P1) { WriteByte(_P1_Action_CaveAddress, 0x00); } else if (s.scanCode == Configurator.GetInstance().DIK_OpGhost_Action_P2) { Send_VK_KeyUp(_P2_Action_VK); } } } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : stays activated when trigger is pulled //Gun recoil : not used ?? _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); _Outputs.Add(new GameOutput(OutputId.P1_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P2_LmpHolder)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (_IsJvsEnabled) { byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Outputs_Offset); SetOutputValue(OutputId.P1_LmpStart, bOutput >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bOutput >> 4 & 0x01); SetOutputValue(OutputId.LmpBillboard, bOutput >> 5 & 0x01); SetOutputValue(OutputId.P1_LmpHolder, bOutput >> 1 & 0x01); SetOutputValue(OutputId.P2_LmpHolder, bOutput & 0x01); SetOutputValue(OutputId.P1_GunRecoil, bOutput >> 6 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, bOutput >> 3 & 0x01); //Custom recoil will be enabled just like original recoil SetOutputValue(OutputId.P1_CtmRecoil, bOutput >> 6 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, bOutput >> 3 & 0x01); } else { //ReadPtr() + 15E0 = Outputs //1 Byte per output (0 / 1) // +0 = Billboard // +1 = P1 Holder // +2 = P2 Holder // +3 = Unused -- // +4 = P1 START // +5 = P2 START UInt32 LedStatusAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _InternalLedOutputs_Ptr_Offset) + 0x15E0; SetOutputValue(OutputId.P1_LmpStart, ReadByte(LedStatusAddress + 4)); SetOutputValue(OutputId.P2_LmpStart, ReadByte(LedStatusAddress + 5)); SetOutputValue(OutputId.LmpBillboard, ReadByte(LedStatusAddress)); SetOutputValue(OutputId.P1_LmpHolder, ReadByte(LedStatusAddress + 1)); SetOutputValue(OutputId.P2_LmpHolder, ReadByte(LedStatusAddress + 2)); if (ReadByte(_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress, 0x00); } if (ReadByte(_Recoil_CaveAddress + 1) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 1, 0x00); } } //Credits will be calculated by using the formula : Credits = Coins / CoinsByCredits //Warning : Need to handle "Divide by 0" error if game is closed brutally ! int Credits = 0; try { Credits = ReadByte((UInt32)_AmLibData_BaseAddress + 0x1E0) / ReadByte((UInt32)_AmLibData_BaseAddress + 0x1D4); } catch { } SetOutputValue(OutputId.Credits, Credits); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_RwTransformers.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_RwTransformers : Game { private const String GAMEDATA_FOLDER = @"MemoryData\ringwide\tha"; /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x0098CCBB; private UInt32 _P1_Y_Offset = 0x0098CCB5; private UInt32 _P1_Buttons_Offset = 0x0098CC9C; private UInt32 _P2_X_Offset = 0x0098CCC7; private UInt32 _P2_Y_Offset = 0x0098CCC1; private UInt32 _P2_Buttons_Offset = 0x0098CCA8; private UInt32 _Buttons_Injection_Offset = 0x00419FDB; private UInt32 _Buttons_Injection_Return_Offset = 0x00419FE0; private NopStruct _Nop_Axis_1 = new NopStruct(0x0041A059, 3); private NopStruct _Nop_Axis_2 = new NopStruct(0x0041A05F, 4); private UInt32 _GameTestMenuSettings_Offsets = 0x009BB838; //For custom recoil output private UInt32 _P1_RecoilState_Address = 0; private UInt32 _P2_RecoilState_Address = 0; private UInt32 _P1_Recoil_Injection_Offset = 0x0002D518; private UInt32 _P1_Recoil_Injection_Return_Offset = 0x0002D51D; private UInt32 _P2_Recoil_Injection_Offset = 0x0002D548; private UInt32 _P2_Recoil_Injection_Return_Offset = 0x0002D54D; //Those 3 are used to store : // - The number of credit added since the game has started, // - The max value of credits we can add in a game // - The max value of credits at a given time during the game //These settings are hardcoded (MemoryBaseAddress + 0x000602E9, MemoryBaseAddress + 0x000602EB) //and can be changed by hex-editing the binary. //In order to let binaries as untouched as possible, DemulShooter will use one of the Codecave to reset the value stored in memory private UInt32 _HardcodedNumberOfCredits_SinceBeginning_Offset = 0x009650FB; //private UInt32 _HardcoredMaxNumberOfCreditsToAdd = 0x009650F5; private UInt32 _HardcodedMaxNumberOfCredits = 0x009650F4; //Outputs private UInt32 _OutputsPtr_Offset = 0x1A3CBA0; private UInt32 _Outputs_Address = 0; private UInt32 _Credits_Offset = 0x00964E60; private UInt32 _PlayersPtr_Offset = 0x1A3CC1C; /// /// Constructor /// public Game_RwTransformers(String RomName) : base(RomName, "TF_Gun_R_Ring_dumped") { _KnownMd5Prints.Add("Transformers Final - For TeknoParrot", "7e11f7e78ed566a277edba1a8aab0749"); _KnownMd5Prints.Add("Transformers Final - For JConfig", "0d23fead523ea91eaea5047e652dff69"); _tProcess.Start(); Logger.WriteLog("Waiting for RingWide " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { //Wait for game to load settings if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets) != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Player 1 and 2 axis limits are different, and we can't access the TEST menu to do calibration //Choosen solution is to force Calibration Values for Min-Max axis to [0x00-0xFF] when we write axis values in memory //So we can safely use full range of values now : double dMaxX = 255.0; double dMaxY = 255.0; //Inverted Axis : 0 = bottom right PlayerData.RIController.Computed_X = Convert.ToInt32(dMaxX - Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and filtering Triggers input to replace them without blocking other input /// protected override void Apply_InputsMemoryHack() { //Force Calibration values WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets + 0x58, new byte[] { 0x00, 0xFF, 0x00, 0xFF }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameTestMenuSettings_Offsets + 0x60, new byte[] { 0x00, 0xFF, 0x00, 0xFF }); //NOPing axis proc SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Axis_1); SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// ///Hacking buttons proc : ///Same byte is used for both triggers, start and service (for each player) ///0b10000000 is start ///0b01000000 is Px Service ///0b00000001 is TriggerL ///0b00000010 is TriggerR ///So we need to make a mask to accept Start button moodification and block other so we can inject /// > private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //This is not related to input hack, this is to remove the credits limitation : //mov byte ptr[_HardcodedNumberOfCredits_SinceBeginning], 0 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _HardcodedNumberOfCredits_SinceBeginning_Offset)); CaveMemory.Write_StrBytes("00"); //mov byte ptr[_HardcodedMaxNumberOfCredits], 0 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _HardcodedMaxNumberOfCredits)); CaveMemory.Write_StrBytes("FF"); //Start of input hack : //push eax CaveMemory.Write_StrBytes("50"); //mov al,[ecx-01] CaveMemory.Write_StrBytes("8A 41 FF"); //and al,03 CaveMemory.Write_StrBytes("24 03"); //and dl,C0 CaveMemory.Write_StrBytes("80 E2 C0"); //add dl,al CaveMemory.Write_StrBytes("00 C2"); //pop eax CaveMemory.Write_StrBytes("58"); //mov [ecx-01],dl CaveMemory.Write_StrBytes("88 51 FF"); //not dl CaveMemory.Write_StrBytes("F6 D2"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } protected override void Apply_OutputsMemoryHack() { //Output hack Create_OutputsDataBank(); _P1_RecoilState_Address = _OutputsDatabank_Address; _P2_RecoilState_Address = _OutputsDatabank_Address + 0x04; SetHack_RecoilP1(); SetHack_RecoilP2(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The game is checking if Recoil is enabled for each bullet fired. /// Using this request call, we can generate the start of our own CustomRecoil output event /// private void SetHack_RecoilP1() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov byte ptr[_P1_RecoilState_Address], 1 CaveMemory.Write_StrBytes("C6 05"); byte[] b = BitConverter.GetBytes(_P1_RecoilState_Address); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("01"); //mov eax,[ecx+34] CaveMemory.Write_StrBytes("8B 41 34"); //test eax, eax CaveMemory.Write_StrBytes("85 C0"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Recoil_Injection_Return_Offset); Logger.WriteLog("Adding P1_Recoil CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Recoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Recoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_RecoilP2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov byte ptr[_P2_RecoilState_Address], 1 CaveMemory.Write_StrBytes("C6 05"); byte[] b = BitConverter.GetBytes(_P2_RecoilState_Address); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("01"); //mov eax,[ecx+38] CaveMemory.Write_StrBytes("8B 41 38"); //test eax, eax CaveMemory.Write_StrBytes("85 C0"); //Jump back CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Recoil_Injection_Return_Offset); Logger.WriteLog("Adding P2_Recoil CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Recoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Recoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes(PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes(PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0xFD); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset, 0xFE); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset, 0xFD); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); _Outputs.Add(new GameOutput(OutputId.P1_LmpBreak)); _Outputs.Add(new GameOutput(OutputId.P2_LmpBreak)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { _Outputs_Address = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _OutputsPtr_Offset) + 0x08; SetOutputValue(OutputId.P1_LmpStart, ReadByte(_Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte(_Outputs_Address) >> 4 & 0x01); SetOutputValue(OutputId.P1_LmpGun, ReadByte(_Outputs_Address) >> 1 & 0x01); SetOutputValue(OutputId.P2_LmpGun, ReadByte(_Outputs_Address) & 0x01); SetOutputValue(OutputId.LmpBillboard, ReadByte(_Outputs_Address + 1) >> 2 & 0x01); SetOutputValue(OutputId.P1_LmpBreak, ReadByte(_Outputs_Address + 1) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpBreak, ReadByte(_Outputs_Address + 1) >> 6 & 0x01); //There are others lamps (Logo, Sides ? RGB Leds ?) coded on [Outputs_Address + 1] 0x20, 0x10, 0x08 //But without Test menu I can't know how these one are working //Besides, lamps name are different on the TestMode binary than on any Arcade Manual I could find... SetOutputValue(OutputId.P1_GunMotor, ReadByte(_Outputs_Address) >> 6 & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte(_Outputs_Address) >> 3 & 0x01); //custom Outputs _P1_Life = 0; _P2_Life = 0; UInt32 PlayersPtr_BaseAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersPtr_Offset); if (PlayersPtr_BaseAddress != 0) { UInt32 P1_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x34); UInt32 P2_StructAddress = ReadPtr(PlayersPtr_BaseAddress + 0x38); //Player Status : //3: Game //4: Cutscene //15: Contnue int P1_Status = ReadByte(P1_StructAddress + 0x5C); int P2_Status = ReadByte(P1_StructAddress + 0x5C); if (P1_Status == 3 || P1_Status == 4) { _P1_Life = ReadByte(P1_StructAddress + 0x60); //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 3 || P2_Status == 4) { _P2_Life = ReadByte(P2_StructAddress + 0x60); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; //Custom Recoil will simply be read on memory and reset //the codecave injected will update it for the "ON" state byte P1_RecoilState = ReadByte(_P1_RecoilState_Address); SetOutputValue(OutputId.P1_CtmRecoil, P1_RecoilState); if (P1_RecoilState == 1) WriteByte(_P1_RecoilState_Address, 0x00); byte P2_RecoilState = ReadByte(_P2_RecoilState_Address); SetOutputValue(OutputId.P2_CtmRecoil, P2_RecoilState); if (P2_RecoilState == 1) WriteByte(_P2_RecoilState_Address, 0x00); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxBlockKingBallShooter.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxBlockKingBallShooter : Game { /*** MEMORY ADDRESSES **/ private UInt32 _AxisX_Offset = 0x005473D0; private UInt32 _AxisY_Offset = 0x005473D4; private UInt32 _ScreenTouched_Offset = 0x00546A19; private NopStruct _Nop_AxisX = new NopStruct(0x002597FF, 6); private NopStruct _Nop_AxisY = new NopStruct(0x002597E9, 5); private NopStruct _Nop_BtnTriggerReset = new NopStruct(0x00259852, 6); //private NopStruct _Nop_BtnTrigger = new NopStruct(0x06368C806, 4); private UInt32 _OutputDamage_Injection_Offset = 0x00241490; private UInt32 _OutputDamage_Injection_Return_Offset = 0x00241497; private UInt32 _CtmDamage_CaveAddress; /// /// Constructor /// public Game_TtxBlockKingBallShooter(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Block King Ball Shooter v1.05 - Original", "42b7ab17909d13ff096f2b08ece6bf2a"); _KnownMd5Prints.Add("Block King Ball Shooter v1.05 - For JConfig", "7e2fae81627c05a836033918e01046c6"); _tProcess.Start(); Logger.WriteLog("Waiting for TTX " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0 ; 0xFFFF] => 65536 //Y => [0 ; 0xFFFF] => 65536 double dMinX = 0.0; double dMaxX = 65535.0; double dMinY = 0.0; double dMaxY = 65535.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; //In that game trim is needed before, for min/max values of UInt32, so we use X,Y variable at first. //Otherwise, in windowed mode, there are issues with negative or over 65535 values if cursor is out of window //when we convert it to UInt32 in next step double X = 0.0; double Y = 0.0; X = Math.Round(dRangeX * PlayerData.RIController.Computed_X / TotalResX); Y = Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY); if (X < 0) X = 0; if (Y < 0) Y = 0; if (X > dMaxX) X = dMaxX; if (Y > dMaxY) Y = dMaxY; //Now that we have trimmed 0x0000-0xFFFF values, we can convert to Int16 Logger.WriteLog("Raw Game Format (Dec) = [ " + X.ToString() + " ; " + Y.ToString() + " ]"); PlayerData.RIController.Computed_X = Convert.ToUInt16(X); PlayerData.RIController.Computed_Y = Convert.ToUInt16(Y); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { //Blocking input from Jconfig SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisX); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisY); //Blocking "Screen touched clear to 0" loop //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_BtnTriggerReset); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _CtmDamage_CaveAddress = _OutputsDatabank_Address; SetHack_CustomDamageOutput(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// This codecave will intercept the game's procedure to decrease timer due to a hit (= damaged) /// private void SetHack_CustomDamageOutput() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [_CtmDamage_CaveAddress], 1 CaveMemory.Write_StrBytes("C7 05"); byte[] b = BitConverter.GetBytes(_CtmDamage_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("01 00 00 00"); //fld dword ptr [esp+04] CaveMemory.Write_StrBytes("D9 44 24 04"); //fadd dword ptr [ecx+0C] CaveMemory.Write_StrBytes("D8 41 0C"); //return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _OutputDamage_Injection_Return_Offset); Logger.WriteLog("Adding Custom Damage output CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _OutputDamage_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _OutputDamage_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _AxisX_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _AxisY_Offset, bufferY); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenTouched_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenTouched_Offset, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpCannonBtn)); _Outputs.Add(new GameOutput(OutputId.LmpCannon_R)); _Outputs.Add(new GameOutput(OutputId.LmpCannon_G)); _Outputs.Add(new GameOutput(OutputId.LmpCannon_B)); //Need to separate Attract from game //_Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0)); //_Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00546B43)); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00546B49)); SetOutputValue(OutputId.LmpCannonBtn, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00546B45)); SetOutputValue(OutputId.LmpCannon_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00546B4B)); SetOutputValue(OutputId.LmpCannon_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00546B47)); SetOutputValue(OutputId.LmpCannon_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00546B4D)); //Custom Outputs: //[Damaged] custom Output /*if (ReadByte(_CtmDamage_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_CtmDamage_CaveAddress, 0x00); }*/ //Credits UInt32 CreditsPtr = BitConverter.ToUInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x5469E0, 4), 0); SetOutputValue(OutputId.Credits, ReadByte(CreditsPtr + 4)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxEadp.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxEadp : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x00201BEC; private UInt32 _P1_Y_Offset = 0x00201BEE; private UInt32 _P1_Trigger_Offset = 0x00201BF0; private UInt32 _P1_Grenade_Offset = 0x00212235; private UInt32 _P1_Out_Offset = 0x00201BEA; private UInt32 _P2_X_Offset = 0x00201BF6; private UInt32 _P2_Y_Offset = 0x00201BF8; private UInt32 _P2_Trigger_Offset = 0x00201BFA; private UInt32 _P2_Grenade_Offset = 0x0021228D; private UInt32 _P2_Out_Offset = 0x00201BF4; private NopStruct _Nop_P1_X = new NopStruct(0x0014F583, 7); private NopStruct _Nop_P1_Y = new NopStruct(0x0014F5A8, 7); private NopStruct _Nop_P1_Trigger = new NopStruct(0x0014F653, 7); private NopStruct _Nop_P1_Out_1 = new NopStruct(0x00014F60C, 7); private NopStruct _Nop_P1_Out_2 = new NopStruct(0x00014F613, 7); private NopStruct _Nop_P2_X = new NopStruct(0x0014F5C5, 7); private NopStruct _Nop_P2_Y = new NopStruct(0x0014F5EA, 7); private NopStruct _Nop_P2_Trigger = new NopStruct(0x0014F66C, 7); private NopStruct _Nop_P2_Out_1 = new NopStruct(0x0014F633, 7); private NopStruct _Nop_P2_Out_2 = new NopStruct(0x0014F63A, 7); private NopStruct _Nop_Grenade_1 = new NopStruct(0x000C509A, 3); private NopStruct _Nop_Grenade_2 = new NopStruct(0x000C50AB, 3); //Outputs private UInt32 _CustomRecoil_Injection_Offset = 0x0003ED23; private UInt32 _CustomRecoil_InjectionReturn_Offset = 0x0003ED28; private UInt32 _CustomDamage_Injection_Offset = 0x0006E29A; private UInt32 _CustomDamage_InjectionReturn_Offset = 0x0006E29F; private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; private UInt32 _P1_DamageStatus_CaveAddress = 0; private UInt32 _P2_DamageStatus_CaveAddress = 0; /// /// Constructor /// public Game_TtxEadp(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Elevator Action Dead Parade - Original", "59dbaf264bb96323cb3127dee0f809ca"); _KnownMd5Prints.Add("Elevator Action Dead Parade - For JConfig", "1bc36915ac4a4658feeca80ca1b6ca10"); _tProcess.Start(); Logger.WriteLog("Waiting for Taito Type X " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0x0000 ; 0x4000] = 16384 //Y => [0x0000 ; 0x4000] = 16384 double dMinX = 0.0; double dMaxX = 16384.0; double dMinY = 0.0; double dMaxY = 16384.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dRangeX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dRangeY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them. /// protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Trigger); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Out_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Out_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Trigger); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Out_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Out_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Grenade_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Grenade_2); //Set P1 & P2 IN_SCREEN WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, new byte[] { 0x00, 0x00 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, new byte[] { 0x00, 0x00 }); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 0x04; _P1_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x10; _P2_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x14; SetHack_Damage(); SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Code injection where the game is calling for rumble because of damage. /// That way we can known when a player is damaged and make our own output. /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push edx CaveMemory.Write_StrBytes("52"); //shl edx, 2 CaveMemory.Write_StrBytes("C1 E2 02"); //add edx, _P1_DamageStatus_CaveAddress byte[] b = BitConverter.GetBytes(_P1_DamageStatus_CaveAddress); CaveMemory.Write_StrBytes("81 C2"); CaveMemory.Write_Bytes(b); //mov [edx], 1 CaveMemory.Write_StrBytes("C7 02 01 00 00 00"); //pop edx CaveMemory.Write_StrBytes("5A"); //push 14 CaveMemory.Write_StrBytes("6A 14"); //push 01 CaveMemory.Write_StrBytes("6A 01"); //push edx CaveMemory.Write_StrBytes("52"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _CustomDamage_InjectionReturn_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _CustomDamage_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _CustomDamage_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Code injection where the game is calling for rumble because of Recoil /// That way we can knwo when a bullet is fired and create our own output /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //shl edx, 2 CaveMemory.Write_StrBytes("C1 E2 02"); //add edx, _P1_RecoilStatus_CaveAddress byte[] b = BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress); CaveMemory.Write_StrBytes("81 C2"); CaveMemory.Write_Bytes(b); //mov [edx], 1 CaveMemory.Write_StrBytes("C7 02 01 00 00 00"); CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + 0xC56E0); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _CustomRecoil_InjectionReturn_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _CustomRecoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _CustomRecoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); //Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //Set out of screen Byte WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, new byte[] { 0x01, 0x01 }); //Trigger a shoot to reload !! WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x00); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, new byte[] { 0x00, 0x00 }); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Grenade_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Grenade_Offset, 0x00); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); //Inputs if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //Set out of screen Byte WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, new byte[] { 0x01, 0x01 }); //Trigger a shoot to reload !! WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x00); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, new byte[] { 0x00, 0x00 }); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Grenade_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Grenade_Offset, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //GunMotor : Activated on every bullet + On damage _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.LmpSide_R)); _Outputs.Add(new GameOutput(OutputId.LmpSide_G)); _Outputs.Add(new GameOutput(OutputId.LmpSide_B)); _Outputs.Add(new GameOutput(OutputId.LmpUpBtn)); _Outputs.Add(new GameOutput(OutputId.LmpDownBtn)); _Outputs.Add(new GameOutput(OutputId.LmpCloseBtn)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte Outputs1 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00204EBC); byte Outputs2 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00204EBE); byte Outputs3 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00204EBB); //Original Outputs SetOutputValue(OutputId.LmpSide_R, Outputs2 >> 6 & 0x01); SetOutputValue(OutputId.LmpSide_G, Outputs2 >> 5 & 0x01); SetOutputValue(OutputId.LmpSide_B, Outputs2 >> 4 & 0x01); SetOutputValue(OutputId.LmpUpBtn, Outputs1 >> 6 & 0x01); SetOutputValue(OutputId.LmpDownBtn, Outputs1 >> 5 & 0x01); SetOutputValue(OutputId.LmpCloseBtn, Outputs1 >> 4 & 0x01); SetOutputValue(OutputId.P1_GunMotor, Outputs3 >> 4 & 0x01); SetOutputValue(OutputId.P2_GunMotor, Outputs3 >> 3 & 0x01); //Custom Outputs if (ReadByte(_P1_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_DamageStatus_CaveAddress, 0x00); } if (ReadByte(_P2_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_DamageStatus_CaveAddress, 0x00); } if (ReadByte(_P1_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilStatus_CaveAddress, 0x00); } if (ReadByte(_P2_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilStatus_CaveAddress, 0x00); } //Ammo + Life UInt32 PlayersStatusPtr_Offset = 0x00212CD4; UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + PlayersStatusPtr_Offset); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; if (iTemp != 0) { //Status Byte, 0x01 if playing, else 0x00 if (ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + 0x210A80) + 0x1794C) != 0) { _P1_Ammo = ReadByte(ReadPtr(iTemp + 0x54) + 0x150); _P1_Life = ReadByte(ReadPtr(iTemp + 0x4C) + 0x150); } if (ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + 0x210A80) + 0x17A38) != 0) { _P2_Ammo = ReadByte(ReadPtr(iTemp + 0x58) + 0x150); _P2_Life = ReadByte(ReadPtr(iTemp + 0x50) + 0x150); } } SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P2_Life, _P2_Life); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxGaiaAttack4.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxGaiaAttack4 : Game { /*** MEMORY ADDRESSES **/ private const UInt32 AXIS_INJECTION_OFFSET = 0x00114C99; private const UInt32 AXIS_INJECTION_RETURN_OFFSET = 0x00114CA6; private const UInt32 BUTTONS_INJECTION_OFFSET = 0x000012FB; private const UInt32 BUTTONS_INJECTION_RETURN_OFFSET = 0x00001300; private const UInt32 _ScreenWidth_Offset = 0x0032FC10; private const UInt32 _ScreenHeight_Offset = 0x0032FC14; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _P3_X_CaveAddress; private UInt32 _P3_Y_CaveAddress; private UInt32 _P4_X_CaveAddress; private UInt32 _P4_Y_CaveAddress; private UInt32 _P1_Trigger_CaveAddress; private UInt32 _P2_Trigger_CaveAddress; private UInt32 _P3_Trigger_CaveAddress; private UInt32 _P4_Trigger_CaveAddress; //Outputs private UInt32 _CustomRecoil_Injection_Offset = 0x00073BDA; private UInt32 _CustomRecoil_InjectionReturn_Offset = 0x00073BDF; private UInt32 _CustomDamage_Injection_Offset_1 = 0x00076658; private UInt32 _CustomDamage_InjectionReturn_Offset_1 = 0x0007665D; private UInt32 _CustomDamage_Injection_Offset_2 = 0x000764A0; private UInt32 _CustomDamage_InjectionReturn_Offset_2 = 0x000764A5; private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; private UInt32 _P3_RecoilStatus_CaveAddress = 0; private UInt32 _P4_RecoilStatus_CaveAddress = 0; private UInt32 _P1_DamageStatus_CaveAddress = 0; private UInt32 _P2_DamageStatus_CaveAddress = 0; private UInt32 _P3_DamageStatus_CaveAddress = 0; private UInt32 _P4_DamageStatus_CaveAddress = 0; /// /// Constructor /// public Game_TtxGaiaAttack4(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Gaia Attack 4 v1.02 - Original", "f2d8e8f7d3a9a29d4804ff7cb29aa8a2"); _KnownMd5Prints.Add("Gaia Attack 4 v1.02 - For JConfig", "94fe4e73f6fd915ffb10885dc091d5cb"); _tProcess.Start(); Logger.WriteLog("Waiting for TTX " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //This engine (common with other TTX shooter) is waiting for X and Y value in range [0 ; WindowSize] //BUT using the raw window size is troublesome when the game is combined with DxWnd as the //resulting real window is not the same size as the game engine parameters (SCREEN_WITH, RENDER_WIDTH, etc...) //That's why we're going to read the memory to find the INI parameter and scale the X,Y values accordingly byte[] bufferX = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenWidth_Offset, 4); double GameResX = (double)BitConverter.ToUInt32(bufferX, 0); byte[] bufferY = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenHeight_Offset, 4); double GameResY = (double)BitConverter.ToInt32(bufferY, 0); Logger.WriteLog("Game engine render resolution (Px) = [ " + GameResX + "x" + GameResY + " ]"); double RatioX = GameResX / TotalResX; double RatioY = GameResY / TotalResY; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(RatioX * PlayerData.RIController.Computed_X)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(RatioY * PlayerData.RIController.Computed_Y)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)GameResX) PlayerData.RIController.Computed_X = (int)GameResX; if (PlayerData.RIController.Computed_Y > (int)GameResY) PlayerData.RIController.Computed_Y = (int)GameResY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address + 0x00; _P2_X_CaveAddress = _InputsDatabank_Address + 0x04; _P3_X_CaveAddress = _InputsDatabank_Address + 0x08; _P4_X_CaveAddress = _InputsDatabank_Address + 0x0C; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x10; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x14; _P3_Y_CaveAddress = _InputsDatabank_Address + 0x18; _P4_Y_CaveAddress = _InputsDatabank_Address + 0x1C; _P1_Trigger_CaveAddress = _InputsDatabank_Address + 0x20; _P2_Trigger_CaveAddress = _InputsDatabank_Address + 0x28; _P3_Trigger_CaveAddress = _InputsDatabank_Address + 0x30; _P4_Trigger_CaveAddress = _InputsDatabank_Address + 0x38; SetHack_Axis(); SetHack_Trigger(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// With debug controls, all players share same memory values so we split them : /// Changing proc so that X and Y will be read on custom memomy values /// We will feed it with devices axis data /// Player index is stored on ESI ( from 0 to 3) so basically we will use some indexing to get values : /// _P1_X_CaveAddress[4*ESI] and _P1_Y_CaveAddress[4*ESI] /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); // //FIRST CODECAVE : SPLIT P1/ P2 /P3 / P4 AXIS // List Buffer = new List(); //push ecx CaveMemory.Write_StrBytes("51"); //mov eax, 04 CaveMemory.Write_StrBytes("B8 04 00 00 00"); //mul esi CaveMemory.Write_StrBytes("F7 E6"); //mov ecx, eax CaveMemory.Write_StrBytes("8B C8"); //mov eax, P1_X_CaveAddress byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(b); //add eax, ecx CaveMemory.Write_StrBytes("01 C8"); //mov edx, [eax] CaveMemory.Write_StrBytes("8B 10"); //mov edi, [edx] CaveMemory.Write_StrBytes("89 17"); //mov eax, P1_Y_CaveAddress b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(b); //add eax, ecx CaveMemory.Write_StrBytes("01 C8"); //mov eax, [eax] CaveMemory.Write_StrBytes("8B 00"); //pop ecx CaveMemory.Write_StrBytes("59"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + AXIS_INJECTION_RETURN_OFFSET); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + AXIS_INJECTION_OFFSET) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + AXIS_INJECTION_OFFSET, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// For this hack we will wait the GetKeyboardState call /// And immediately after we will read on our custom memory storage /// to replace lpKeystate bytes for mouse buttons (see WINUSER.H for virtualkey codes) /// then the game will continue... /// private void SetHack_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //(lpKeystate+0x100) address is in ESI register //and [esi-100], 0xFF0000FF CaveMemory.Write_StrBytes("81 A6 00 FF FF FF FF 00 00 FF"); //and [esi-9d], 0xFFFF0000 CaveMemory.Write_StrBytes("81 A6 63 FF FF FF 00 00 FF FF"); //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); byte[] b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Trigger CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [esi-FF], 80 CaveMemory.Write_StrBytes("81 8E 01 FF FF FF 80 00 00 00"); //P2_Trigger: //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P2_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne originalcode CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [esi-FF], 80 CaveMemory.Write_StrBytes("81 8E 02 FF FF FF 80 00 00 00"); //cmp [_P3_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P3_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Trigger CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [esi-9D], 80 CaveMemory.Write_StrBytes("81 8E 63 FF FF FF 80 00 00 00"); //cmp [_P4_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P4_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Trigger CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [esi-9C], 80 CaveMemory.Write_StrBytes("81 8E 64 FF FF FF 80 00 00 00"); //OriginalCode //call game.exe+12F250 CaveMemory.Write_call((UInt32)_TargetProcess.MainModule.BaseAddress + 0x12F250); //return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + BUTTONS_INJECTION_RETURN_OFFSET); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + BUTTONS_INJECTION_OFFSET) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + BUTTONS_INJECTION_OFFSET, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 0x00; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 0x04; _P3_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 0x08; _P4_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 0x0C; _P1_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x10; _P2_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x14; _P3_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x18; _P4_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x1C; SetHack_Damage(_CustomDamage_Injection_Offset_1, _CustomDamage_InjectionReturn_Offset_1); SetHack_Damage(_CustomDamage_Injection_Offset_2, _CustomDamage_InjectionReturn_Offset_2); SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Code injection where the game is calling for rumble because of Recoil /// That way we can knwo when a bullet is fired and create our own output /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); byte[] b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16E34); //cmp dword ptr [ecx*8+game.exe+B16E34], 00 CaveMemory.Write_StrBytes("83 3C CD"); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("00"); //jne originalCode CaveMemory.Write_StrBytes("0F 85 11 00 00 00"); //push ecx CaveMemory.Write_StrBytes("51"); //shl ecx, 2 CaveMemory.Write_StrBytes("C1 E1 02"); //add ecx, _P1_RecoilStatus_CaveAddress b = BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress); CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(b); //mov [ecx], 1 CaveMemory.Write_StrBytes("C7 01 01 00 00 00"); //pop edx CaveMemory.Write_StrBytes("59"); //OriginalCode: //call game.exe+74200 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + 0x00074200); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _CustomRecoil_InjectionReturn_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _CustomRecoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _CustomRecoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Code injection where the game is calling for rumble because of damage. /// That way we can known when a player is damaged and make our own output. /// private void SetHack_Damage(UInt32 InjectionOffset, UInt32 InjectionReturnOffset) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); byte[] b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16E34); //cmp dword ptr [ecx*8+game.exe+B16E34], 00 CaveMemory.Write_StrBytes("83 3C CD"); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("00"); //jne originalCode CaveMemory.Write_StrBytes("0F 85 11 00 00 00"); //push ecx CaveMemory.Write_StrBytes("51"); //shl ecx, 2 CaveMemory.Write_StrBytes("C1 E1 02"); //add ecx, _P1_DammageStatus_CaveAddress b = BitConverter.GetBytes(_P1_DamageStatus_CaveAddress); CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(b); //mov [ecx], 1 CaveMemory.Write_StrBytes("C7 01 01 00 00 00"); //pop edx CaveMemory.Write_StrBytes("59"); //OriginalCode: //call game.exe+74200 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + 0x00074200); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + InjectionReturnOffset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + InjectionOffset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + InjectionOffset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x00); } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x00); } else if (PlayerData.ID == 3) { WriteBytes(_P3_X_CaveAddress, bufferX); WriteBytes(_P3_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P3_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P3_Trigger_CaveAddress, 0x00); } else if (PlayerData.ID == 4) { WriteBytes(_P4_X_CaveAddress, bufferX); WriteBytes(_P4_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P4_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P4_Trigger_CaveAddress, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P3_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P4_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P3_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P4_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7202) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7202) >> 5 & 0x01); SetOutputValue(OutputId.P3_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7200) >> 5 & 0x01); SetOutputValue(OutputId.P4_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7200) >> 4 & 0x01); SetOutputValue(OutputId.P1_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7202) >> 7 & 0x01); SetOutputValue(OutputId.P1_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7202) >> 4 & 0x01); SetOutputValue(OutputId.P1_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7202) >> 3 & 0x01); SetOutputValue(OutputId.P2_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7201) >> 7 & 0x01); SetOutputValue(OutputId.P2_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7201) >> 6 & 0x01); SetOutputValue(OutputId.P2_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7201) >> 5 & 0x01); SetOutputValue(OutputId.P1_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7202) >> 2 & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7202) >> 1 & 0x01); SetOutputValue(OutputId.P3_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7200) >> 3 & 0x01); SetOutputValue(OutputId.P4_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01FE7200) >> 2 & 0x01); //Custom Outputs: //[Damaged] custom Output if (ReadByte(_P1_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_DamageStatus_CaveAddress, 0x00); } if (ReadByte(_P2_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_DamageStatus_CaveAddress, 0x00); } if (ReadByte(_P3_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P3_Damaged, 1); WriteByte(_P3_DamageStatus_CaveAddress, 0x00); } if (ReadByte(_P4_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P4_Damaged, 1); WriteByte(_P4_DamageStatus_CaveAddress, 0x00); } //[Recoil] custom Output if (ReadByte(_P1_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilStatus_CaveAddress, 0x00); } if (ReadByte(_P2_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilStatus_CaveAddress, 0x00); } if (ReadByte(_P3_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P3_CtmRecoil, 1); WriteByte(_P3_RecoilStatus_CaveAddress, 0x00); } if (ReadByte(_P4_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P4_CtmRecoil, 1); WriteByte(_P4_RecoilStatus_CaveAddress, 0x00); } //Life if player is InGame //Player Status : //[0] : Inactive //[1] : In-Game //[2] : Continue Screen //[3] : Game Over int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16A98); int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16AA0); int P3_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16AA8); int P4_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16AB0); if (P1_Status == 1) SetOutputValue(OutputId.P1_Life, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16E60)); else SetOutputValue(OutputId.P1_Life, 0); if (P2_Status == 1) SetOutputValue(OutputId.P2_Life, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16E68)); else SetOutputValue(OutputId.P2_Life, 0); if (P3_Status == 1) SetOutputValue(OutputId.P3_Life, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16E70)); else SetOutputValue(OutputId.P3_Life, 0); if (P4_Status == 1) SetOutputValue(OutputId.P4_Life, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B16E78)); else SetOutputValue(OutputId.P4_Life, 0); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00B3B7C8)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxGundam.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxGundam : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x0026F990; private UInt32 _P1_Y_Offset = 0x0026F992; private UInt32 _P1_TriggerDown_Offset = 0x0026F995; private UInt32 _P1_TriggerStatus_Offset = 0x0026F996; private UInt32 _P1_TriggerUp_Offset = 0x0026F997; private UInt32 _P1_WeaponChangeDown_Offset = 0x0026F998; private UInt32 _P1_WeaponChangeStatus_Offset = 0x0026F999; private UInt32 _P1_WeaponChangeUp_Offset = 0x0026F99A; private UInt32 _P2_X_Offset = 0x0026F9A0; private UInt32 _P2_Y_Offset = 0x0026F9A2; private UInt32 _P2_TriggerDown_Offset = 0x0026F9A5; private UInt32 _P2_TriggerStatus_Offset = 0x0026F9A6; private UInt32 _P2_TriggerUp_Offset = 0x0026F9A7; private UInt32 _P2_WeaponChangeDown_Offset = 0x0026F9A8; private UInt32 _P2_WeaponChangeStatus_Offset = 0x0026F9A9; private UInt32 _P2_WeaponChangeUp_Offset = 0x0026F9AA; private UInt32 _Border_Check1_Injection_Offset = 0x000A7A50; private UInt32 _Border_Check1_Injection_Return_Offset = 0x000A7A7E; private UInt32 _Border_Check2_Injection_Offset = 0x000A7AEE; private UInt32 _Border_Check2_Injection_Return_Offset = 0x000A7B0C; private NopStruct _Nop_P1_X_1 = new NopStruct(0x000D038C, 7); private NopStruct _Nop_P1_Y = new NopStruct(0x000D0374, 7); private NopStruct _Nop_Btn_Down_1 = new NopStruct(0x000D0302, 12); private NopStruct _Nop_Btn_Down_2 = new NopStruct(0x000D030F, 12); private NopStruct _Nop_Btn_Up_1 = new NopStruct(0x000D032F, 13); private NopStruct _Nop_Btn_Up_2 = new NopStruct(0x000D033D, 13); private NopStruct _Nop_Btn_Reset = new NopStruct(0x000D1262, 3); // This one is tricky ! Only used when on 2P game.exe (when mouse X > 640) and is screwing 1P X axis : private NopStruct _Nop_P1_X_2 = new NopStruct(0x000D0383, 6); private bool _Pedal1_Enable; private HardwareScanCode _Pedal1_Key; private bool _isPedal1_Pushed = false; private bool _Pedal2_Enable; private HardwareScanCode _Pedal2_Key; private bool _isPedal2_Pushed = false; private Codecave _Cave_Check1, _Cave_Check2; /*** Outputs ***/ private UInt32 _P1_GunMotorOffset = 0x0026FB1C; private UInt32 _P2_GunMotorOffset = 0x0026FB20; //dword_66FB24 also set to 0 and 1 at the same time than motors sometimes ?? private UInt32 _P1_Playing_Offset = 0x0026FB9C; private UInt32 _P2_Playing_Offset = 0x0026FB9D; private UInt32 _Ammo_Injection_Offset = 0x000A7BA3; private UInt32 _Ammo_Injection_ReturnOffset = 0x000A7BA9; private UInt32 _Life_Injection_Offset = 0x00099964; private UInt32 _Life_Injection_ReturnOffset = 0x0009996A; //Sets value to 1 when active, 0 un-initialized, 2 otherwise private UInt32 _SetRumbleGunShot_Offset = 0x000A60FF; private UInt32 _SetRumbleCanonShot_Offset = 0x000A7649; private UInt32 _SetRumbleOnHit_Offset = 0x009B06B; private UInt32 _SetRumbleUnkownReason01_Offset = 0x000A70F3; private UInt32 _SetRumbleUnkownReason02_Offset = 0x000E19CA; private UInt32 _SetRumbleUnkownReason03_Offset = 0x000E1A31; private UInt32 _SetRumbleStateFuntionOffset = 0x000D0B40; private UInt32 _P1_RecoilEnabled_CustomAddress; private UInt32 _P2_RecoilEnabled_CustomAddress; private UInt32 _P1_DamagedEnabled_CustomAddress; private UInt32 _P2_DamagedEnabled_CustomAddress; private UInt32 _P1_Ammo_CustomAddress; private UInt32 _P2_Ammo_CustomAddress; private UInt32 _P1_Life_CustomAddress; private UInt32 _P2_Life_CustomAddress; /// /// Constructor /// public Game_TtxGundam(String RomName, bool Pedal1_Enable, HardwareScanCode Pedal1_Key, bool Pedal2_Enable, HardwareScanCode Pedal2_Key) : base(RomName, "game") { _Pedal1_Enable = Pedal1_Enable; _Pedal1_Key = Pedal1_Key; _Pedal2_Enable = Pedal2_Enable; _Pedal2_Key = Pedal2_Key; _KnownMd5Prints.Add("Gundam : SoZ v1.01 - Dual Player, Unpatched I/O", "70af03a21a42d9042065fc65b7eb56f9"); _KnownMd5Prints.Add("Gundam : SoZ v1.01 - Single Player, Pathched I/O", "d8cd539967cc3c23f620139ab4669d30"); _KnownMd5Prints.Add("Gundam : SoZ v1.01 - Dual Player, Patched I/O", "c31187a57fd5ab6864b78eaa755ae3f0"); _tProcess.Start(); Logger.WriteLog("Waiting for Taito Type X " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); CheckExeMd5(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen public override bool ClientScale(PlayerSettings PayerData) { /************************************************************************************************* * Screen has a weird behavior with this game and the background dosbox activated to launch it. * Process mainhandle is giving DOS windows size... * Foreground Windows sometimes fail on top of where the DOS box is... * With Game All RH launcher, fullscreen is an obligation so the game res = screen res, * so in-game cursor position = screen cursor position . * This is preventing bugs on axis positionning ************************************************************************************************/ return true; } /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double dMinX = 0.0; double dMaxX = 640.0; double dMinY = 0.0; double dMaxY = 480.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; if (_RomName.Equals("gsoz2p")) { dMaxX = 1280.0; dMaxY = 480.0; //Side by Side dual screen, each one 640x480 max coordonates //So total X is 1280 PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / _screenWidth)); //For Y we need to change reference from Screen[X, Y] to real Game [X, Y] with black border; double Ratio = _screenWidth / dMaxX; Logger.WriteLog("Ratio = " + Ratio.ToString()); double GameHeight = Ratio * dMaxY; Logger.WriteLog("Real game height (px) = " + GameHeight); double Border = (_screenHeight - GameHeight) / 2.0; Logger.WriteLog("Black boder top and bottom (px) = " + Border.ToString()); double y = PlayerData.RIController.Computed_Y - Border; double percent = y * 100.0 / GameHeight; PlayerData.RIController.Computed_Y = (int)(percent * dMaxY / 100.0); //Player One will have X value cut-off to [0-639] next //For player 2 we first shift value to the left if (PlayerData.ID == 2) PlayerData.RIController.Computed_X -= 640; } else if (_RomName.Equals("gsoz")) { //Single screen, each one 640x480 max coordonates //So total Y is 480 PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / _screenHeight)); //For X we need to change reference from Screen[X, Y] to real Game [X, Y] with black border dMaxX = 640.0; double Ratio = (double)_screenHeight / dMaxY; Logger.WriteLog("Ratio = " + Ratio.ToString()); double GameWidth = Ratio * dMaxX; Logger.WriteLog("Real game width (px) = " + GameWidth); double Border = (_screenWidth - GameWidth) / 2.0; Logger.WriteLog("Black boder left and right (px) = " + Border.ToString()); double x = PlayerData.RIController.Computed_X - Border; double percent = x * 100.0 / GameWidth; PlayerData.RIController.Computed_X = (int)(percent * dMaxX / 100.0); //In case of forced scren ration /*if (_ForcedXratio > 0) { Logger.WriteLog("Forcing X Ratio to = " + _ForcedXratio.ToString()); double ViewportHeight = GameResY; double ViewportWidth = GameResY * _ForcedXratio; double SideBarsWidth = (GameResX - ViewportWidth) / 2; Logger.WriteLog("Game Viewport size (Px) = [ " + ViewportWidth + "x" + ViewportHeight + " ]"); Logger.WriteLog("SideBars Width (px) = " + SideBarsWidth.ToString()); dRangeX = dRangeX + SideBarsWidth * 2; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / GameResX) - SideBarsWidth); } else PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / GameResX)); */} if (PlayerData.RIController.Computed_X < 1) PlayerData.RIController.Computed_X = 1; if (PlayerData.RIController.Computed_X > 639) PlayerData.RIController.Computed_X = 639; if (PlayerData.RIController.Computed_Y < 1) PlayerData.RIController.Computed_Y = 1; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_X_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_X_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_Down_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_Down_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_Up_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_Up_2); //Reset Buttons should be left but this does not work, so we have to handle the reset too and NOP this one SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Btn_Reset); /*** * If neither Pedal1 nor Pedal2 are enabled, no need for a codecave (default game). * Else, We need to make 2 codecave for the 2 checking procedure (X-Y min-max) * Each one will have to be split for P1 and P2 separatelly ***/ if (_Pedal1_Enable || _Pedal2_Enable) { _Cave_Check1 = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); _Cave_Check1.Open(); _Cave_Check1.Alloc(0x800); //cmp [esi+1C], 0 _Cave_Check1.Write_StrBytes("83 7E 1C 00"); //je @player1 _Cave_Check1.Write_StrBytes("0F 84 0A 00 00 00"); //cmp [esi+1C], 1 _Cave_Check1.Write_StrBytes("83 7E 1C 01"); //je @player2 _Cave_Check1.Write_StrBytes("0F 84 33 00 00 00"); //player1: if (_Pedal1_Enable) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check1.Write_StrBytes("66 83 FF 10"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (_Pedal1_Enable) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check1.Write_StrBytes("66 81 FF 70 02"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //mov eax, edi _Cave_Check1.Write_StrBytes("8B C7"); //shr eax, 10 _Cave_Check1.Write_StrBytes("C1 E8 10"); if (_Pedal1_Enable) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check1.Write_StrBytes("66 3D 10 00"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (_Pedal1_Enable) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D E0 01"); else //cmp ax, 01D0 _Cave_Check1.Write_StrBytes("66 3D D0 01"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check1.Write_StrBytes("E9 2E 00 00 00"); //player2: if (_Pedal2_Enable) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check1.Write_StrBytes("66 83 FF 10"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (_Pedal2_Enable) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check1.Write_StrBytes("66 81 FF 70 02"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //mov eax, edi _Cave_Check1.Write_StrBytes("8B C7"); //shr eax, 10 _Cave_Check1.Write_StrBytes("C1 E8 10"); if (_Pedal2_Enable) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check1.Write_StrBytes("66 3D 10 00"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (_Pedal2_Enable) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D E0 01"); else //cmp ax, 01D0 _Cave_Check1.Write_StrBytes("66 3D D0 01"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check1.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check1_Injection_Return_Offset); Logger.WriteLog("Adding check1 CodeCave at : 0x" + _Cave_Check1.CaveAddress.ToString("X8")); IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = _Cave_Check1.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check1_Injection_Offset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check1_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); _Cave_Check2 = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); _Cave_Check2.Open(); _Cave_Check2.Alloc(0x800); //cmp [esi+1C], 0 _Cave_Check2.Write_StrBytes("83 7E 1C 00"); //je @player1 _Cave_Check2.Write_StrBytes("0F 84 0A 00 00 00"); //cmp [esi+1C], 1 _Cave_Check2.Write_StrBytes("83 7E 1C 01"); //je @player2 _Cave_Check2.Write_StrBytes("0F 84 33 00 00 00"); //player1: if (_Pedal1_Enable) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check2.Write_StrBytes("66 83 FF 10"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (_Pedal1_Enable) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check2.Write_StrBytes("66 81 FF 70 02"); //jg game.exe+A7B85 _Cave_Check2.Write_jg((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); //mov eax, [esp+26] _Cave_Check2.Write_StrBytes("66 8B 44 24 26"); if (_Pedal1_Enable) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check2.Write_StrBytes("66 3D 10 00"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (_Pedal1_Enable) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D E0 01"); else //cmp ax, 01D0 _Cave_Check2.Write_StrBytes("66 3D D0 01"); //jle game.exe+A7B85 _Cave_Check2.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check2.Write_StrBytes("E9 2E 00 00 00"); //player2: if (_Pedal2_Enable) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check2.Write_StrBytes("66 83 FF 10"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (_Pedal2_Enable) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check2.Write_StrBytes("66 81 FF 70 02"); //jg game.exe+A7B85 _Cave_Check2.Write_jg((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); //mov eax, [esp+26] _Cave_Check2.Write_StrBytes("66 8B 44 24 26"); if (_Pedal2_Enable) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check2.Write_StrBytes("66 3D 10 00"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (_Pedal2_Enable) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D E0 01"); else //cmp ax_Cave_Check2 01D0 _Cave_Check2.Write_StrBytes("66 3D D0 01"); //jle game.exe+A7B85 _Cave_Check2.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check2.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check2_Injection_Return_Offset); Logger.WriteLog("Adding check2 CodeCave at : 0x" + _Cave_Check2.CaveAddress.ToString("X8")); bytesWritten = 0; jumpTo = _Cave_Check2.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check2_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check2_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //Initializing values byte[] initX = { 0x10, 0 }; byte[] initY = { 0x10, 0 }; //Write Axis WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, initX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, initY); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, initX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, initY); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilEnabled_CustomAddress = _OutputsDatabank_Address; _P2_RecoilEnabled_CustomAddress = _OutputsDatabank_Address + 0x04; _P1_DamagedEnabled_CustomAddress = _OutputsDatabank_Address + 0x08; _P2_DamagedEnabled_CustomAddress = _OutputsDatabank_Address + 0x0C; _P1_Ammo_CustomAddress = _OutputsDatabank_Address + 0x10; _P2_Ammo_CustomAddress = _OutputsDatabank_Address + 0x14; _P1_Life_CustomAddress = _OutputsDatabank_Address + 0x18; _P2_Life_CustomAddress = _OutputsDatabank_Address + 0x1C; GetRecoilEvent(_SetRumbleGunShot_Offset); GetRecoilEvent(_SetRumbleCanonShot_Offset); GetDamagedEvent(_SetRumbleOnHit_Offset); //Not knowing when these these part of code are called to vibrate, we will put it in the "Damage" output (less likely to be used than an unwanted recoil) GetDamagedEvent(_SetRumbleUnkownReason01_Offset); GetDamagedEvent(_SetRumbleUnkownReason02_Offset); GetDamagedEvent(_SetRumbleUnkownReason03_Offset); Hack_GetAmmo(); Hack_GetLife(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The function to set Rumble state Up is called at many places /// These one will catch when the Rumble is called because of gun fire, or cannon fire /// and put a flag in out data bank private void GetRecoilEvent(UInt32 Injection_Offset) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [esp+4] CaveMemory.Write_StrBytes("8B 44 24 04"); //shl eax, 2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_RecoilEnabled_customAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilEnabled_CustomAddress)); //mov [eax],00000001 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //call game.exe+D0B40 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _SetRumbleStateFuntionOffset); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset + 5); Logger.WriteLog("Adding Recoil Detetcion Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Same thing but injected at the moment the game is setting rumble on Damage Received private void GetDamagedEvent(UInt32 Injection_Offset) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [esp+4] CaveMemory.Write_StrBytes("8B 44 24 04"); //shl eax, 2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_RecoilEnabled_customAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_DamagedEnabled_CustomAddress)); //mov [eax],00000001 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //call game.exe+D0B40 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _SetRumbleStateFuntionOffset); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset + 5); Logger.WriteLog("Adding Damage Detetcion Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //Weapon Struct: //+ 0x08 -> Weapon Type (0 = Gaitling, 1 = Canon, 2 = Mines) //+ 0x2C -> Gaitling Ammo //+ 0x30 -> Canon Ammo //+ 0x34 -> Mines Ammo //+ 0xC0 -> Player ID //We will read values at a time when the Struct Pointer created and is used by the game during playthrough private void Hack_GetAmmo() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx,[esi+54] CaveMemory.Write_StrBytes("8B 4E 54"); //mov [edx+0C],eax CaveMemory.Write_StrBytes("89 42 0C"); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //eax,[ecx+000000C0] CaveMemory.Write_StrBytes("8B 81 C0 00 00 00"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_Ammo_CustomAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Ammo_CustomAddress)); //mov ebx,[ecx+8] CaveMemory.Write_StrBytes("8B 59 08"); //shl ebx, 2 CaveMemory.Write_StrBytes("C1 E3 02"); //add ebx, 2C CaveMemory.Write_StrBytes("83 C3 2C"); //add ebx, ecx CaveMemory.Write_StrBytes("01 CB"); //mov ebx, [ebx] CaveMemory.Write_StrBytes("8B 1B"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Ammo_Injection_ReturnOffset); Logger.WriteLog("Adding Ammo Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Ammo_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Ammo_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //Struct : //+ 0x1C -> Player ID //+ 0x4C -> Life private void Hack_GetLife() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,[esi+4C] CaveMemory.Write_StrBytes("8B 46 4C"); //ecx,[esi+1C] CaveMemory.Write_StrBytes("8B 4E 1C"); //shl ecx,02 CaveMemory.Write_StrBytes("C1 E1 02"); //add ecx, _P1_Life_CustomAddress CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Life_CustomAddress)); //mov [ecx], eax CaveMemory.Write_StrBytes("89 01"); //mov ecx,[esi+50] CaveMemory.Write_StrBytes("8B 4E 50"); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Life_Injection_ReturnOffset); Logger.WriteLog("Adding Life Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Life_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Life_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerDown_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerUp_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerStatus_Offset, 0x00); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerUp_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponChangeDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponChangeStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponChangeDown_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponChangeUp_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponChangeStatus_Offset, 0x00); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_WeaponChangeUp_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //With "Pedal mode", enable right-click to shoot only when hiding if(_Pedal1_Enable) { if (!_isPedal1_Pushed) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerDown_Offset, 0x00); } } else { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerDown_Offset, 0x00); } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerUp_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_TriggerStatus_Offset, 0x00); System.Threading.Thread.Sleep(20); } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerDown_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerUp_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerStatus_Offset, 0x00); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerUp_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponChangeDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponChangeStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponChangeDown_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponChangeUp_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponChangeStatus_Offset, 0x00); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_WeaponChangeUp_Offset, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //With "Pedal mode", enable right-click to shoot only when hiding if (_Pedal2_Enable) { if (!_isPedal2_Pushed) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerDown_Offset, 0x00); } } else { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerDown_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerStatus_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerDown_Offset, 0x00); } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerUp_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerStatus_Offset, 0x00); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_TriggerUp_Offset, 0x00); } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _Pedal1_Key && _Pedal1_Enable) { _isPedal1_Pushed = true; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x21, new byte[] { 0x80, 0x02 }); //640 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x21, new byte[] { 0x80, 0x02 }); //640 } else if (s.scanCode == _Pedal2_Key && _Pedal2_Enable) { _isPedal2_Pushed = true; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x54, new byte[] { 0x80, 0x02 }); //640 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x54, new byte[] { 0x80, 0x02 }); //640 } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == _Pedal1_Key && _Pedal1_Enable) { _isPedal1_Pushed = false; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x21, new byte[] { 0x00, 0x00 }); //0 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x21, new byte[] { 0x00, 0x00 }); //0 } else if (s.scanCode == _Pedal2_Key && _Pedal2_Enable) { _isPedal2_Pushed = false; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x54, new byte[] { 0x00, 0x00 }); //0 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x54, new byte[] { 0x00, 0x00 }); //0 } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0026FA49) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0026FA4B) >> 7 & 0x01); //Motor byte value is 1 if enabled, 0 or 2 if disabled SetOutputValue(OutputId.P1_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_GunMotorOffset) & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_GunMotorOffset) & 0x01); //Gun motor is activated on shoot and dammage, and stays activated on dammaged untill the player hides //So Custom outputs will be based on game values (ammo, life) _P1_Life = 0; _P1_Ammo = 0; int P1_Clip = 0; _P2_Life = 0; _P2_Ammo = 0; int P2_Clip = 0; if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Playing_Offset) == 1) { if (ReadByte(_P1_RecoilEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilEnabled_CustomAddress, 0x00); } if (ReadByte(_P1_DamagedEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_DamagedEnabled_CustomAddress, 0x00); } _P1_Ammo = ReadByte(_P1_Ammo_CustomAddress); _P1_Life = ReadByte(_P1_Life_CustomAddress); } if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Playing_Offset) == 1) { if (ReadByte(_P2_RecoilEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilEnabled_CustomAddress, 0x00); } if (ReadByte(_P2_DamagedEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_DamagedEnabled_CustomAddress, 0x00); } _P2_Ammo = ReadByte(_P2_Ammo_CustomAddress); _P2_Life = ReadByte(_P2_Life_CustomAddress); } //@game.exe+a6051 -> decrease ammo (mov, ESI+2C) //Pointer address + 2c = ammo //pointer address + c0 = player ID //Need to separate betwen gameplay and attract mode :( SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0026FB88)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxGundam_V2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxGundam_V2 : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x0026F990; private UInt32 _P1_Y_Offset = 0x0026F992; private UInt32 _P2_X_Offset = 0x0026F9A0; private UInt32 _P2_Y_Offset = 0x0026F9A2; private UInt32 _JVS_Buttons_Offset = 0x0026FA2A; private UInt32 _JVS_Trigger_Offset = 0x0026FB0A; private UInt32 _ForceUseJvsData_Offset = 0x000D12C8; private UInt32 _SetKeyDownFunction_Offset = 0x000D0C60; private UInt32 _SetKeyUpFunction_Offset = 0x000D0E30; private UInt32 _SetMouseButtonDownFunction_Offset = 0x000D02F0; private UInt32 _SetMouseButtonUpFunction_Offset = 0x000D0320; private UInt32 _SetMouseMotionFunction_Offset = 0x000D0350; private UInt32 _JVS_AxisUpdate_Offset = 0x000D06E7; private UInt32 _Border_Check1_Injection_Offset = 0x000A7A50; private UInt32 _Border_Check1_Injection_Return_Offset = 0x000A7A7E; private UInt32 _Border_Check2_Injection_Offset = 0x000A7AEE; private UInt32 _Border_Check2_Injection_Return_Offset = 0x000A7B0C; private NopStruct _Nop_JvsCrashingFunction = new NopStruct(0x000D135B, 5); //PedalMode Codecave private Codecave _Cave_Check1, _Cave_Check2; private bool _isPedal1_Pushed = false; private bool _isPedal2_Pushed = false; //Hardcoded Keys private HardwareScanCode _Test_Key = HardwareScanCode.DIK_F2; private HardwareScanCode _Service_Key = HardwareScanCode.DIK_F7; private HardwareScanCode _P1_Start_Key = HardwareScanCode.DIK_1; private HardwareScanCode _P2_Start_Key = HardwareScanCode.DIK_2; private HardwareScanCode _Credits_Key = HardwareScanCode.DIK_5; /*** Outputs ***/ private UInt32 _P1_GunMotorOffset = 0x0026FB1C; private UInt32 _P2_GunMotorOffset = 0x0026FB20; //dword_66FB24 also set to 0 and 1 at the same time than motors sometimes ?? private UInt32 _P1_Playing_Offset = 0x0026FB9C; private UInt32 _P2_Playing_Offset = 0x0026FB9D; private UInt32 _Ammo_Injection_Offset = 0x000A7BA3; private UInt32 _Ammo_Injection_ReturnOffset = 0x000A7BA9; private UInt32 _Life_Injection_Offset = 0x00099964; private UInt32 _Life_Injection_ReturnOffset = 0x0009996A; //Sets value to 1 when active, 0 un-initialized, 2 otherwise private UInt32 _SetRumbleGunShot_Offset = 0x000A60FF; private UInt32 _SetRumbleCanonShot_Offset = 0x000A7649; private UInt32 _SetRumbleOnHit_Offset = 0x009B06B; private UInt32 _SetRumbleUnkownReason01_Offset = 0x000A70F3; private UInt32 _SetRumbleUnkownReason02_Offset = 0x000E19CA; private UInt32 _SetRumbleUnkownReason03_Offset = 0x000E1A31; private UInt32 _SetRumbleStateFuntionOffset = 0x000D0B40; private UInt32 _P1_RecoilEnabled_CustomAddress; private UInt32 _P2_RecoilEnabled_CustomAddress; private UInt32 _P1_DamagedEnabled_CustomAddress; private UInt32 _P2_DamagedEnabled_CustomAddress; private UInt32 _P1_Ammo_CustomAddress; private UInt32 _P2_Ammo_CustomAddress; private UInt32 _P1_Life_CustomAddress; private UInt32 _P2_Life_CustomAddress; private UInt32 _P1_Lamp_Offset = 0x0026FA49; private UInt32 _P2_Lamp_Offset = 0x0026FA4B; private UInt32 _Credits_Offset = 0x0026FB88; private UInt32 _BookKeeping_Credits_Offset = 0x0026FBC8; /// /// Constructor /// public Game_TtxGundam_V2(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Gundam : SoZ v1.01 - Dual Player, Unpatched I/O", "70af03a21a42d9042065fc65b7eb56f9"); _KnownMd5Prints.Add("Gundam : SoZ v1.01 - Single Player, Pathched I/O", "d8cd539967cc3c23f620139ab4669d30"); _KnownMd5Prints.Add("Gundam : SoZ v1.01 - Dual Player, Patched I/O", "c31187a57fd5ab6864b78eaa755ae3f0"); _tProcess.Start(); Logger.WriteLog("Waiting for Taito Type X " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals("game") || FindGameWindow_Equals("TeknoParrot - Gundam Spirits Of Zeon")) { Apply_MemoryHacks(); CheckExeMd5(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMinX = 0.0; double dMaxX = 640.0; double dMinY = 0.0; double dMaxY = 480.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; if (_RomName.Equals("gsoz2p")) { dMaxX = 1280.0; dMaxY = 480.0; //Side by Side dual screen, each one 640x480 max coordonates //So total X is 1280 PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); //For Y we need to change reference from Screen[X, Y] to real Game [X, Y] with black border; double Ratio = TotalResX / dMaxX; Logger.WriteLog("Ratio = " + Ratio.ToString()); double GameHeight = Ratio * dMaxY; Logger.WriteLog("Real game height (px) = " + GameHeight); double Border = (TotalResY - GameHeight) / 2.0; Logger.WriteLog("Black border top and bottom (px) = " + Border.ToString()); double y = PlayerData.RIController.Computed_Y - Border; double percent = y * 100.0 / GameHeight; PlayerData.RIController.Computed_Y = (int)(percent * dMaxY / 100.0); //Player One will have X value cut-off to [0-639] next //For player 2 we first shift value to the left if (PlayerData.ID == 2) PlayerData.RIController.Computed_X -= 640; } else if (_RomName.Equals("gsoz")) { dMaxX = 640.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); /*//Single screen, each one 640x480 max coordonates //So total Y is 480 PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / _screenHeight)); //For X we need to change reference from Screen[X, Y] to real Game [X, Y] with black border dMaxX = 640.0; double Ratio = (double)_screenHeight / dMaxY; Logger.WriteLog("Ratio = " + Ratio.ToString()); double GameWidth = Ratio * dMaxX; Logger.WriteLog("Real game width (px) = " + GameWidth); double Border = (_screenWidth - GameWidth) / 2.0; Logger.WriteLog("Black border left and right (px) = " + Border.ToString()); double x = PlayerData.RIController.Computed_X - Border; double percent = x * 100.0 / GameWidth; PlayerData.RIController.Computed_X = (int)(percent * dMaxX / 100.0);*/ } if (PlayerData.RIController.Computed_X < 1) PlayerData.RIController.Computed_X = 1; if (PlayerData.RIController.Computed_X > 639) PlayerData.RIController.Computed_X = 639; if (PlayerData.RIController.Computed_Y < 1) PlayerData.RIController.Computed_Y = 1; if (PlayerData.RIController.Computed_Y > (int)dMaxY - 1) PlayerData.RIController.Computed_Y = (int)dMaxY - 1; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { //Force to use JVS data for buttons and axis SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_JvsCrashingFunction); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _ForceUseJvsData_Offset, 0x85); //Disable all MouseEvents and Keyevents funtions (direct return without doing anything) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _SetKeyDownFunction_Offset, 0xC3); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _SetKeyUpFunction_Offset, 0xC3); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _SetMouseButtonDownFunction_Offset, 0xC3); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _SetMouseButtonUpFunction_Offset, 0xC3); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _SetMouseMotionFunction_Offset, 0xC3); //Disabling Axis update originally done by reading data WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_AxisUpdate_Offset, new byte[] { 0xE9, 0x39, 0x01, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90 }); SetHack_PedalMode(); //Initializing values byte[] initX = { 0x10, 0 }; byte[] initY = { 0x10, 0 }; //Write Axis WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, initX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, initY); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, initX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, initY); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_PedalMode() { /*** * If neither Pedal1 nor Pedal2 are enabled, no need for a codecave (default game). * Else, We need to make 2 codecave for the 2 checking procedure (X-Y min-max) * Each one will have to be split for P1 and P2 separatelly ***/ if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled || Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) { _Cave_Check1 = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); _Cave_Check1.Open(); _Cave_Check1.Alloc(0x800); //cmp [esi+1C], 0 _Cave_Check1.Write_StrBytes("83 7E 1C 00"); //je @player1 _Cave_Check1.Write_StrBytes("0F 84 0A 00 00 00"); //cmp [esi+1C], 1 _Cave_Check1.Write_StrBytes("83 7E 1C 01"); //je @player2 _Cave_Check1.Write_StrBytes("0F 84 33 00 00 00"); //player1: if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check1.Write_StrBytes("66 83 FF 10"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check1.Write_StrBytes("66 81 FF 70 02"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //mov eax, edi _Cave_Check1.Write_StrBytes("8B C7"); //shr eax, 10 _Cave_Check1.Write_StrBytes("C1 E8 10"); if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check1.Write_StrBytes("66 3D 10 00"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D E0 01"); else //cmp ax, 01D0 _Cave_Check1.Write_StrBytes("66 3D D0 01"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check1.Write_StrBytes("E9 2E 00 00 00"); //player2: if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check1.Write_StrBytes("66 83 FF 10"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp di, 00 _Cave_Check1.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check1.Write_StrBytes("66 81 FF 70 02"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //mov eax, edi _Cave_Check1.Write_StrBytes("8B C7"); //shr eax, 10 _Cave_Check1.Write_StrBytes("C1 E8 10"); if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check1.Write_StrBytes("66 3D 10 00"); //jng game.exe+A7B85 _Cave_Check1.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp ax, 00 _Cave_Check1.Write_StrBytes("66 3D E0 01"); else //cmp ax, 01D0 _Cave_Check1.Write_StrBytes("66 3D D0 01"); //jnl game.exe+A7B85 _Cave_Check1.Write_jnl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check1.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check1_Injection_Return_Offset); Logger.WriteLog("Adding check1 CodeCave at : 0x" + _Cave_Check1.CaveAddress.ToString("X8")); IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = _Cave_Check1.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check1_Injection_Offset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check1_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); _Cave_Check2 = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); _Cave_Check2.Open(); _Cave_Check2.Alloc(0x800); //cmp [esi+1C], 0 _Cave_Check2.Write_StrBytes("83 7E 1C 00"); //je @player1 _Cave_Check2.Write_StrBytes("0F 84 0A 00 00 00"); //cmp [esi+1C], 1 _Cave_Check2.Write_StrBytes("83 7E 1C 01"); //je @player2 _Cave_Check2.Write_StrBytes("0F 84 33 00 00 00"); //player1: if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check2.Write_StrBytes("66 83 FF 10"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check2.Write_StrBytes("66 81 FF 70 02"); //jg game.exe+A7B85 _Cave_Check2.Write_jg((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); //mov eax, [esp+26] _Cave_Check2.Write_StrBytes("66 8B 44 24 26"); if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check2.Write_StrBytes("66 3D 10 00"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D E0 01"); else //cmp ax, 01D0 _Cave_Check2.Write_StrBytes("66 3D D0 01"); //jle game.exe+A7B85 _Cave_Check2.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check2.Write_StrBytes("E9 2E 00 00 00"); //player2: if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 83 FF 00"); else //cmp di, 10 _Cave_Check2.Write_StrBytes("66 83 FF 10"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp di, 00 _Cave_Check2.Write_StrBytes("66 81 FF 00 00"); else //cmp di, 0270 _Cave_Check2.Write_StrBytes("66 81 FF 70 02"); //jg game.exe+A7B85 _Cave_Check2.Write_jg((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); //mov eax, [esp+26] _Cave_Check2.Write_StrBytes("66 8B 44 24 26"); if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D 00 00"); else //cmp ax, 10 _Cave_Check2.Write_StrBytes("66 3D 10 00"); //jl game.exe+A7B85 _Cave_Check2.Write_jl((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B0C); if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) //cmp ax, 00 _Cave_Check2.Write_StrBytes("66 3D E0 01"); else //cmp ax_Cave_Check2 01D0 _Cave_Check2.Write_StrBytes("66 3D D0 01"); //jle game.exe+A7B85 _Cave_Check2.Write_jng((UInt32)_TargetProcess_MemoryBaseAddress + 0xA7B85); //jmp EXIT _Cave_Check2.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check2_Injection_Return_Offset); Logger.WriteLog("Adding check2 CodeCave at : 0x" + _Cave_Check2.CaveAddress.ToString("X8")); bytesWritten = 0; jumpTo = _Cave_Check2.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check2_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Border_Check2_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilEnabled_CustomAddress = _OutputsDatabank_Address; _P2_RecoilEnabled_CustomAddress = _OutputsDatabank_Address + 0x04; _P1_DamagedEnabled_CustomAddress = _OutputsDatabank_Address + 0x08; _P2_DamagedEnabled_CustomAddress = _OutputsDatabank_Address + 0x0C; _P1_Ammo_CustomAddress = _OutputsDatabank_Address + 0x10; _P2_Ammo_CustomAddress = _OutputsDatabank_Address + 0x14; _P1_Life_CustomAddress = _OutputsDatabank_Address + 0x18; _P2_Life_CustomAddress = _OutputsDatabank_Address + 0x1C; GetRecoilEvent(_SetRumbleGunShot_Offset); GetRecoilEvent(_SetRumbleCanonShot_Offset); GetDamagedEvent(_SetRumbleOnHit_Offset); //Not knowing when these these part of code are called to vibrate, we will put it in the "Damage" output (less likely to be used than an unwanted recoil) GetDamagedEvent(_SetRumbleUnkownReason01_Offset); GetDamagedEvent(_SetRumbleUnkownReason02_Offset); GetDamagedEvent(_SetRumbleUnkownReason03_Offset); Hack_GetAmmo(); Hack_GetLife(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The function to set Rumble state Up is called at many places /// These one will catch when the Rumble is called because of gun fire, or cannon fire /// and put a flag in out data bank private void GetRecoilEvent(UInt32 Injection_Offset) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [esp+4] CaveMemory.Write_StrBytes("8B 44 24 04"); //shl eax, 2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_RecoilEnabled_customAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilEnabled_CustomAddress)); //mov [eax],00000001 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //call game.exe+D0B40 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _SetRumbleStateFuntionOffset); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset + 5); Logger.WriteLog("Adding Recoil Detetcion Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Same thing but injected at the moment the game is setting rumble on Damage Received private void GetDamagedEvent(UInt32 Injection_Offset) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [esp+4] CaveMemory.Write_StrBytes("8B 44 24 04"); //shl eax, 2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_RecoilEnabled_customAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_DamagedEnabled_CustomAddress)); //mov [eax],00000001 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //call game.exe+D0B40 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + _SetRumbleStateFuntionOffset); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset + 5); Logger.WriteLog("Adding Damage Detetcion Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //Weapon Struct: //+ 0x08 -> Weapon Type (0 = Gaitling, 1 = Canon, 2 = Mines) //+ 0x2C -> Gaitling Ammo //+ 0x30 -> Canon Ammo //+ 0x34 -> Mines Ammo //+ 0xC0 -> Player ID //We will read values at a time when the Struct Pointer created and is used by the game during playthrough private void Hack_GetAmmo() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx,[esi+54] CaveMemory.Write_StrBytes("8B 4E 54"); //mov [edx+0C],eax CaveMemory.Write_StrBytes("89 42 0C"); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //eax,[ecx+000000C0] CaveMemory.Write_StrBytes("8B 81 C0 00 00 00"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_Ammo_CustomAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Ammo_CustomAddress)); //mov ebx,[ecx+8] CaveMemory.Write_StrBytes("8B 59 08"); //shl ebx, 2 CaveMemory.Write_StrBytes("C1 E3 02"); //add ebx, 2C CaveMemory.Write_StrBytes("83 C3 2C"); //add ebx, ecx CaveMemory.Write_StrBytes("01 CB"); //mov ebx, [ebx] CaveMemory.Write_StrBytes("8B 1B"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Ammo_Injection_ReturnOffset); Logger.WriteLog("Adding Ammo Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Ammo_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Ammo_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //Struct : //+ 0x1C -> Player ID //+ 0x4C -> Life private void Hack_GetLife() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,[esi+4C] CaveMemory.Write_StrBytes("8B 46 4C"); //ecx,[esi+1C] CaveMemory.Write_StrBytes("8B 4E 1C"); //shl ecx,02 CaveMemory.Write_StrBytes("C1 E1 02"); //add ecx, _P1_Life_CustomAddress CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Life_CustomAddress)); //mov [ecx], eax CaveMemory.Write_StrBytes("89 01"); //mov ecx,[esi+50] CaveMemory.Write_StrBytes("8B 4E 50"); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Life_Injection_ReturnOffset); Logger.WriteLog("Adding Life Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Life_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Life_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0x20); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0xDF); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0x08); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0xF7); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //With "Pedal mode", enable right-click to shoot only when hiding if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) { if (!_isPedal1_Pushed) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0x20); } } //Without "Pedal Mode", enable right click only if the player is not pointing in the screen boundaries else { if (PlayerData.RIController.Computed_X < 17 || PlayerData.RIController.Computed_X > 624 || PlayerData.RIController.Computed_Y < 17 || PlayerData.RIController.Computed_Y > 464) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0x20); } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0xDF); } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0x10); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0xEF); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0x04); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0xFB); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //With "Pedal mode", enable right-click to shoot only when hiding if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) { if (!_isPedal2_Pushed) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0x10); } } //Without "Pedal Mode", enable right click only if the player is not pointing in the screen boundaries else { if (PlayerData.RIController.Computed_X < 17 || PlayerData.RIController.Computed_X > 624 || PlayerData.RIController.Computed_Y < 17 || PlayerData.RIController.Computed_Y > 464) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0x10); } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Offset, 0xEF); } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == Configurator.GetInstance().DIK_Gsoz_Pedal_P1 && Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) { _isPedal1_Pushed = true; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x21, new byte[] { 0x80, 0x02 }); //640 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x21, new byte[] { 0x80, 0x02 }); //640 } else if (s.scanCode == Configurator.GetInstance().DIK_Gsoz_Pedal_P2 && Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) { _isPedal2_Pushed = true; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x54, new byte[] { 0x80, 0x02 }); //640 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x54, new byte[] { 0x80, 0x02 }); //640 } else if (s.scanCode == _P1_Start_Key) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0x20); } else if (s.scanCode == _P2_Start_Key) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0x10); } else if (s.scanCode == _Test_Key) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset + 1, 0x80); } else if (s.scanCode == _Service_Key) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0x40); } else if (s.scanCode == _Credits_Key) { //Adding 1 Credits to the count byte iCredits = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset); iCredits++; WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, iCredits); //And saving it for BookKeepings iCredits = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _BookKeeping_Credits_Offset); iCredits++; WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _BookKeeping_Credits_Offset, iCredits); } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == Configurator.GetInstance().DIK_Gsoz_Pedal_P1 && Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) { _isPedal1_Pushed = false; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x21, new byte[] { 0x00, 0x00 }); //0 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x21, new byte[] { 0x00, 0x00 }); //0 } else if (s.scanCode == Configurator.GetInstance().DIK_Gsoz_Pedal_P2 && Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) { _isPedal2_Pushed = false; WriteBytes((UInt32)_Cave_Check1.CaveAddress + 0x54, new byte[] { 0x00, 0x00 }); //0 WriteBytes((UInt32)_Cave_Check2.CaveAddress + 0x54, new byte[] { 0x00, 0x00 }); //0 } else if (s.scanCode == _P1_Start_Key) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0xDF); } else if (s.scanCode == _P2_Start_Key) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0xEF); } else if (s.scanCode == _Test_Key) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset + 1, 0x7F); } else if (s.scanCode == _Service_Key) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _JVS_Buttons_Offset, 0xBF); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Lamp_Offset) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Lamp_Offset) >> 7 & 0x01); //Motor byte value is 1 if enabled, 0 or 2 if disabled SetOutputValue(OutputId.P1_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_GunMotorOffset) & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_GunMotorOffset) & 0x01); //Gun motor is activated on shoot and dammage, and stays activated on dammaged untill the player hides //So Custom outputs will be based on game values (ammo, life) _P1_Life = 0; _P1_Ammo = 0; int P1_Clip = 0; _P2_Life = 0; _P2_Ammo = 0; int P2_Clip = 0; if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Playing_Offset) == 1) { if (ReadByte(_P1_RecoilEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilEnabled_CustomAddress, 0x00); } if (ReadByte(_P1_DamagedEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_DamagedEnabled_CustomAddress, 0x00); } _P1_Ammo = ReadByte(_P1_Ammo_CustomAddress); _P1_Life = ReadByte(_P1_Life_CustomAddress); } if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Playing_Offset) == 1) { if (ReadByte(_P2_RecoilEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilEnabled_CustomAddress, 0x00); } if (ReadByte(_P2_DamagedEnabled_CustomAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_DamagedEnabled_CustomAddress, 0x00); } _P2_Ammo = ReadByte(_P2_Ammo_CustomAddress); _P2_Life = ReadByte(_P2_Life_CustomAddress); } //@game.exe+a6051 -> decrease ammo (mov, ESI+2C) //Pointer address + 2c = ammo //pointer address + c0 = player ID //Need to separate betwen gameplay and attract mode :( SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxGungun2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_TtxGungun2 : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x002B8104; private UInt32 _P1_Y_Offset = 0x002B8106; private UInt32 _P1_Trigger_Offset = 0x002B8108; private UInt32 _P1_Out_Offset = 0x002B8102; private UInt32 _P2_X_Offset = 0x002B810E; private UInt32 _P2_Y_Offset = 0x002B8110; private UInt32 _P2_Trigger_Offset = 0x002B8112; private UInt32 _P2_Out_Offset = 0x002B810C; private NopStruct _Nop_P1_X = new NopStruct(0x00106513, 7); private NopStruct _Nop_P1_Y = new NopStruct(0x00106538, 7); private NopStruct _Nop_P1_Trigger_1 = new NopStruct(0x001065E3, 7); private NopStruct _Nop_P1_Trigger_2 = new NopStruct(0x001065DA, 7); private NopStruct _Nop_P1_Out_1 = new NopStruct(0x0010658C, 7); private NopStruct _Nop_P1_Out_2 = new NopStruct(0x0010659C, 7); private NopStruct _Nop_P2_X = new NopStruct(0x00106555, 7); private NopStruct _Nop_P2_Y = new NopStruct(0x0010657A, 7); private NopStruct _Nop_P2_Trigger_1 = new NopStruct(0x001065FC, 7); private NopStruct _Nop_P2_Trigger_2 = new NopStruct(0x001065F3, 7); private NopStruct _Nop_P2_Out_1 = new NopStruct(0x001065C3, 7); private NopStruct _Nop_P2_Out_2 = new NopStruct(0x001065B3, 7); /// /// Constructor /// public Game_TtxGungun2(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Music Gun Gun 2 v1.01 JPN - Original", "590677da06b758728a1dd607cbf032de"); _KnownMd5Prints.Add("Music Gun Gun 2 v1.01 JPN - For JConfig", "57fb4970df6ef979d7ffc044e6161e84"); _tProcess.Start(); Logger.WriteLog("Waiting for Taito Type X " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0, 3FFO] = 16368 //Y => [0, 3FF0] = 16368 double dMaxX = 16368.0; double dMaxY = 16368.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them. /// protected override void Apply_InputsMemoryHack() { //NOPing proc SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Trigger_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Trigger_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Out_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Out_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Y); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Trigger_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Trigger_2); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Out_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Out_2); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, 0x00); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x00); } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, 0x01); System.Threading.Thread.Sleep(20); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, 0x00); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x00); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.LmpLBtn)); _Outputs.Add(new GameOutput(OutputId.LmpMBtn)); _Outputs.Add(new GameOutput(OutputId.LmpRBtn)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte Outputs1 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002BC311); byte Outputs2 = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002BC312); byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x002DBE08, 4); int iCredits = ReadByte(BitConverter.ToUInt32(buffer, 0) + 0x18); SetOutputValue(OutputId.P1_LmpStart, Outputs2 >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, Outputs2 >> 6 & 0x01); SetOutputValue(OutputId.P1_LmpGun, Outputs2 >> 5 & 0x01); SetOutputValue(OutputId.P2_LmpGun, Outputs2 >> 4 & 0x01); SetOutputValue(OutputId.LmpLBtn, Outputs1 >> 5 & 0x01); SetOutputValue(OutputId.LmpMBtn, Outputs1 >> 6 & 0x01); SetOutputValue(OutputId.LmpRBtn, Outputs1 >> 4 & 0x01); SetOutputValue(OutputId.Credits, iCredits); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxHauntedMuseum.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxHauntedMuseum : Game { /*** MEMORY ADDRESSES **/ private const UInt32 _Axis_Injection_Offset = 0x0009F0DA; private const UInt32 _Axis_Injection_Return_Offset = 0x0009F0E7; private const UInt32 _Buttons_Injection_Offset = 0x000012BE; private const UInt32 _Buttons_Injection_Return_Offset = 0x000012C3; private UInt32 _ScreenWidth_Offset = 0x00328520; private UInt32 _ScreenHeight_Offset = 0x00328524; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _P1_Trigger_CaveAddress; private UInt32 _P2_Trigger_CaveAddress; //Outputs private UInt32 _CustomRecoil_Injection_Offset = 0x0006FDCA; private UInt32 _CustomRecoil_InjectionReturn_Offset = 0x0006FDCF; private UInt32 _CustomDamage_Injection_Offset = 0x00072798; private UInt32 _CustomDamage_InjectionReturn_Offset = 0x0007279D; private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; private UInt32 _P1_DamageStatus_CaveAddress = 0; private UInt32 _P2_DamageStatus_CaveAddress = 0; /// /// Constructor /// public Game_TtxHauntedMuseum(String RomName) : base(RomName, "game") { _KnownMd5Prints.Add("Haunted Museum v1.00 - Original", "ea27e06f3f918697fe9f924e728f5e80"); _KnownMd5Prints.Add("Haunted Museum v1.00 - For JConfig", "792b34d2451c7a6c1fd347a29aaf0b35"); _tProcess.Start(); Logger.WriteLog("Waiting for TTX " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //This engine (common with other TTX shooter) is waiting for X and Y value in range [0 ; WindowSize] //BUT using the raw window size is troublesome when the game is combined with DxWnd as the //resulting real window is not the same size as the game engine parameters (SCREEN_WITH, RENDER_WIDTH, etc...) //That's why we're going to read the memory to find the INI parameter and scale the X,Y values accordingly byte[] bufferX = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenWidth_Offset, 4); double GameResX = (double)BitConverter.ToUInt32(bufferX, 0); byte[] bufferY = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenHeight_Offset, 4); double GameResY = (double)BitConverter.ToInt32(bufferY, 0); Logger.WriteLog("Game engine render resolution (Px) = [ " + GameResX + "x" + GameResY + " ]"); double dMinX = 0.0; double dMaxX = GameResX; double dMinY = 0.0; double dMaxY = GameResY; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; double RatioX = GameResX / TotalResX; double RatioY = GameResY / TotalResY; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(RatioX * PlayerData.RIController.Computed_X)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(RatioY * PlayerData.RIController.Computed_Y)); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; ; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x08; _P1_Trigger_CaveAddress = _InputsDatabank_Address + 0x10; _P2_X_CaveAddress = _InputsDatabank_Address + 0x20; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x28; _P2_Trigger_CaveAddress = _InputsDatabank_Address + 0x30; SetHack_Axis(); SetHack_Trigger(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// P1 and P2 share same memory values so we split them : /// Changing proc so that X and Y will be read on custom memomy values. /// We will feed it with device axis data. /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp ecx,O1 CaveMemory.Write_StrBytes("83 F9 01"); //je P2 CaveMemory.Write_StrBytes("0F 84 12 00 00 00"); //mov edx,[P1_X] byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_StrBytes("8B 15"); CaveMemory.Write_Bytes(b); //mov [ebx], edx CaveMemory.Write_StrBytes("89 13"); //mov eax,[P1_Y] b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //jmp exit CaveMemory.Write_StrBytes("E9 0D 00 00 00"); //P2 //mov edx,[P2_X] b = BitConverter.GetBytes(_P2_X_CaveAddress); CaveMemory.Write_StrBytes("8B 15"); CaveMemory.Write_Bytes(b); //mov [ebx],edx CaveMemory.Write_StrBytes("89 13"); //mov eax,[P2_Y] b = BitConverter.GetBytes(_P2_Y_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Return_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// For this hack we will wait the GetKeyboardState call, /// and immediately after we will read on our custom memory storage /// to replace lpKeystate bytes for mouse buttons (see WINUSER.H for virtualkey codes) /// private void SetHack_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //(lpKeystate+0x100) address is in ESI register //and [esi-100], 0xFF0000FF CaveMemory.Write_StrBytes("81 A6 00 FF FF FF FF 00 00 FF"); //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); byte[] b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Trigger CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [esi-FF], 80 CaveMemory.Write_StrBytes("81 8E 01 FF FF FF 80 00 00 00"); //P2_Trigger: //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P2_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne originalcode CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [esi-FF], 80 CaveMemory.Write_StrBytes("81 8E 02 FF FF FF 80 00 00 00"); //OriginalCode //call game.exe+AFCD0 CaveMemory.Write_call((UInt32)_TargetProcess.MainModule.BaseAddress + 0xAFCD0); //return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 0x00; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 0x04; _P1_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x10; _P2_DamageStatus_CaveAddress = _OutputsDatabank_Address + 0x14; SetHack_Damage(); SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Code injection where the game is calling for rumble because of Recoil /// That way we can knwo when a bullet is fired and create our own output /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); byte[] b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00975E54); //cmp dword ptr [ecx*8+game.exe+975E54], 00 CaveMemory.Write_StrBytes("83 3C CD"); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("00"); //jne originalCode CaveMemory.Write_StrBytes("0F 85 11 00 00 00"); //push ecx CaveMemory.Write_StrBytes("51"); //shl ecx, 2 CaveMemory.Write_StrBytes("C1 E1 02"); //add ecx, _P1_RecoilStatus_CaveAddress b = BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress); CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(b); //mov [ecx], 1 CaveMemory.Write_StrBytes("C7 01 01 00 00 00"); //pop ecx CaveMemory.Write_StrBytes("59"); //OriginalCode: //call game.exe+702C0 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + 0x000702C0); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _CustomRecoil_InjectionReturn_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _CustomRecoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _CustomRecoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Code injection where the game is calling for rumble because of damage. /// That way we can known when a player is damaged and make our own output. /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); byte[] b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00975E54); //cmp dword ptr [ecx*8+game.exe+975E54], 00 CaveMemory.Write_StrBytes("83 3C CD"); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("00"); //jne originalCode CaveMemory.Write_StrBytes("0F 85 11 00 00 00"); //push ecx CaveMemory.Write_StrBytes("51"); //shl ecx, 2 CaveMemory.Write_StrBytes("C1 E1 02"); //add ecx, _P1_DamageStatus_CaveAddress b = BitConverter.GetBytes(_P1_DamageStatus_CaveAddress); CaveMemory.Write_StrBytes("81 C1"); CaveMemory.Write_Bytes(b); //mov [ecx], 1 CaveMemory.Write_StrBytes("C7 01 01 00 00 00"); //pop ecx CaveMemory.Write_StrBytes("59"); //OriginalCode: //call game.exe+702C0 CaveMemory.Write_call((UInt32)_TargetProcess_MemoryBaseAddress + 0x000702C0); //jmp return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _CustomDamage_InjectionReturn_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _CustomDamage_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _CustomDamage_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x00); } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27022) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27022) >> 5 & 0x01); SetOutputValue(OutputId.P1_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27022) >> 7 & 0x01); SetOutputValue(OutputId.P1_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27022) >> 4 & 0x01); SetOutputValue(OutputId.P1_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27022) >> 3 & 0x01); SetOutputValue(OutputId.P2_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27021) >> 7 & 0x01); SetOutputValue(OutputId.P2_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27021) >> 6 & 0x01); SetOutputValue(OutputId.P2_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27021) >> 5 & 0x01); SetOutputValue(OutputId.P1_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27022) >> 2 & 0x01); SetOutputValue(OutputId.P2_GunMotor, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x01E27022) >> 1 & 0x01); //Custom Outputs: //[Damaged] custom Output if (ReadByte(_P1_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_DamageStatus_CaveAddress, 0x00); } if (ReadByte(_P2_DamageStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_DamageStatus_CaveAddress, 0x00); } //[Recoil] custom Output if (ReadByte(_P1_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilStatus_CaveAddress, 0x00); } if (ReadByte(_P2_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilStatus_CaveAddress, 0x00); } //Life if player is InGame //Player Status : //[0] : Inactive //[1] : In-Game //[2] : Continue Screen //[3] : Game Over int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00975EB0); if (P1_Status == 1) { int Life = (int)(100.0 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00975F6C, 4), 0)); if (Life < 0) Life = 0; SetOutputValue(OutputId.P1_Life, Life); } else { SetOutputValue(OutputId.P1_Life, 0); } //Life if player is InGame int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x00975EF4); if (P2_Status == 1) { int Life = (int)(100.0 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x00975F8C, 4), 0)); if (Life < 0) Life = 0; SetOutputValue(OutputId.P2_Life, Life); } else { SetOutputValue(OutputId.P2_Life, 0); } //Credits SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x0098B3F8)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxHauntedMuseum2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxHauntedMuseum2 : Game { private const String GAMEDATA_FOLDER = @"MemoryData\ttx\hmuseum2"; /*** MEMORY ADDRESSES **/ private UInt32 _Axis_Injection_Offset = 0x0009ECD0; private UInt32 _Axis_Injection_Return_Offset = 0x0009ECDD; private UInt32 _Buttons_Injection_Offset = 0x000B2A8A; private UInt32 _Buttons_Injection_Return_Offset = 0x000B2A90; private UInt32 _GetKeyboardState_Address = 0x00266348; private UInt32 _LpKeyState_Address = 0x00AEF498; private UInt32 _ScreenWidth_Offset = 0x00332EF0; private UInt32 _ScreenHeight_Offset = 0x00332EF4; private UInt32 _Outputs_Offset = 0x0029AE434; private UInt32 _Credits_Offset = 0x00AE7AF8; private UInt32 _P1_Status_Offset = 0x00AD1704; private UInt32 _P1_Ammo_Offset = 0x00AD40DC; private UInt32 _P1_Life_Offset = 0x00AD4028; private UInt32 _P2_Status_Offset = 0x00AD170C; private UInt32 _P2_Ammo_Offset = 0x00AD4168; private UInt32 _P2_Life_Offset = 0x00AD4034; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _P1_Trigger_CaveAddress; private UInt32 _P2_Trigger_CaveAddress; private UInt32 _P1_Action_CaveAddress; private UInt32 _P2_Action_CaveAddress; private bool _AlternativeGameplay = false; /// /// Constructor /// public Game_TtxHauntedMuseum2(String RomName, bool AlternativeGameplay) : base(RomName, "game") { _AlternativeGameplay = AlternativeGameplay; _KnownMd5Prints.Add("Haunted Museum 2 v1.01 JPN - First Release", "ede2a10fe37221d3994f31553b3a4ef5"); _KnownMd5Prints.Add("Haunted Museum 2 v1.01 JPN - First Release patched for NoCrosshair ", "e7d9f02fa52130707c16e9a83ba72dff"); _KnownMd5Prints.Add("Haunted Museum 2 v1.01 JPN - Second release", "fb493eda4cbc8a0866fa733fb784f0e5"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - For JConfig (v1.6) - game_v1.00_BGR.exe", "9b5567bda69941923feb3f2e05619c68"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - For JConfig (v1.6) - game_v1.00_BGR.exe patched for NoCrosshair", "f8c9d82705e04e4e03e010ce51ffc16b"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - For JConfig (v1.6) - game_v1.01_JPN.exe", "264d671b83282a09701b27f2249c5d0d"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - For JConfig (v1.6) - game_v1.01_JPN.exe patched for NoCrosshair", "6b6452843f2d07fd66814e7d0d6af765"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - For JConfig (v1.6) - game_v1.01_JPN_v2.exe", "0e8f49eb448ff7c9bd448940a53ac16a"); _KnownMd5Prints.Add("Haunted Museum 2 v1.00 - GameLoaderAllRH patched binary", "8372323ffceebfd064707064ac00c043"); _tProcess.Start(); Logger.WriteLog("Waiting for Taito type X " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //This engine (common with other TTX shooter) is waiting for X and Y value in range [0 ; WindowSize] //BUT using the raw window size is troublesome when the game is combined with DxWnd as the //resulting real window is not the same size as the game engine parameters (SCREEN_WITH, RENDER_WIDTH, etc...) //That's why we're going to read the memory to find the INI parameter and scale the X,Y values accordingly byte[] bufferX = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenWidth_Offset, 4); double GameResX = (double)BitConverter.ToUInt32(bufferX, 0); byte[] bufferY = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _ScreenHeight_Offset, 4); double GameResY = (double)BitConverter.ToUInt32(bufferY, 0); Logger.WriteLog("Game engine render resolution (Px) = [ " + GameResX + "x" + GameResY + " ]"); double dMinX = 0.0; double dMaxX = GameResX; double dMinY = 0.0; double dMaxY = GameResY; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; double RatioX = GameResX / TotalResX; double RatioY = GameResY / TotalResY; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(RatioX * PlayerData.RIController.Computed_X)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(RatioY * PlayerData.RIController.Computed_Y)); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address + 0x40; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x48; _P1_Trigger_CaveAddress = _InputsDatabank_Address + 0x50; _P1_Action_CaveAddress = _InputsDatabank_Address + 0x58; _P2_X_CaveAddress = _InputsDatabank_Address + 0x60; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x68; _P2_Trigger_CaveAddress = _InputsDatabank_Address + 0x70; _P2_Action_CaveAddress = _InputsDatabank_Address + 0x78; SetHack_Axis(); SetHack_Trigger(); //Initialize values if (_AlternativeGameplay) { WriteByte(_P1_Action_CaveAddress, 0x80); WriteByte(_P2_Action_CaveAddress, 0x80); } Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// P1 and P2 share same memory values so we split them : /// Changing proc so that X and Y will be read on custom memomy values. /// We will feed it with device axis data. /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp eax,O1 CaveMemory.Write_StrBytes("83 F8 01"); //je P2X CaveMemory.Write_StrBytes("0F 84 0B 00 00 00"); //mov edx,[P1_X] byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_StrBytes("8B 15"); CaveMemory.Write_Bytes(b); //jmp CaveMemory.Write_StrBytes("E9 06 00 00 00"); //P2X: mov edx,[P2_X] b = BitConverter.GetBytes(_P2_X_CaveAddress); CaveMemory.Write_StrBytes("8B 15"); CaveMemory.Write_Bytes(b); //mov [edi],edx CaveMemory.Write_StrBytes("89 17"); //cmp eax,O1 CaveMemory.Write_StrBytes("83 F8 01"); //je P2Y CaveMemory.Write_StrBytes("0F 84 0A 00 00 00"); //mov eax,[P1_Y] b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //jmp exit CaveMemory.Write_StrBytes("E9 05 00 00 00"); //P2Y: mov eax,[P2_Y] b = BitConverter.GetBytes(_P2_Y_CaveAddress); CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Return_Offset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Axis_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// For this hack we will wait the GetKeyboardState call, /// and immediately after we will read on our custom memory storage /// to replace lpKeystate bytes for mouse buttons (see WINUSER.H for virtualkey codes) /// private void SetHack_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //call USER32.GetKEyboardState CaveMemory.Write_StrBytes("FF 15"); byte[] b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GetKeyboardState_Address); CaveMemory.Write_Bytes(b); //and [lpkeystate + 1], 0xFF00FFFF CaveMemory.Write_StrBytes("81 25"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 1); /*0x00C55119*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("00 00 FF 00"); //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P1_Action CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + 1], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 1); /*0x00C55119*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //P1_Action: //cmp [_P1_Action], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P1_Action_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Trigger CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + X], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 51); /*0x00C5514B*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //P2_Trigger: //cmp [_P1_Trigger], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P2_Trigger_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne P2_Action CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + 2], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 2); /*0x00C5511A*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //P2_Action: //cmp [_P2_Action], 80 CaveMemory.Write_StrBytes("81 3D"); b = BitConverter.GetBytes(_P2_Action_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); //jne exit CaveMemory.Write_StrBytes("0F 85 0A 00 00 00"); //or [lpkeystate + X], 80 CaveMemory.Write_StrBytes("81 0D"); b = BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _LpKeyState_Address + 52); /*0x00C5514C*/ CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("80 00 00 00"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Trigger CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P1_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if (_AlternativeGameplay) WriteByte(_P1_Action_CaveAddress, 0x00); else WriteByte(_P1_Action_CaveAddress, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { if (_AlternativeGameplay) WriteByte(_P1_Action_CaveAddress, 0x80); else WriteByte(_P1_Action_CaveAddress, 0x00); } } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_P2_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if (_AlternativeGameplay) WriteByte(_P2_Action_CaveAddress, 0x00); else WriteByte(_P2_Action_CaveAddress, 0x80); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { if (_AlternativeGameplay) WriteByte(_P2_Action_CaveAddress, 0x80); else WriteByte(_P2_Action_CaveAddress, 0x00); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun recoil : is handled by the game like it should (On/Off with every bullets) //Gun motor : is activated when player gets hit _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { int P1_RecoilState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset) >> 5 & 0x01; int P1_RumbleState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2) >> 2 & 0x01; int P2_RecoilState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset) >> 4 & 0x01; int P2_RumbleState = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2) >> 1 & 0x01; //Orginal SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2) >> 5 & 0x01); SetOutputValue(OutputId.P1_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2) >> 7 & 0x01); SetOutputValue(OutputId.P1_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2) >> 4 & 0x01); SetOutputValue(OutputId.P1_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 2) >> 3 & 0x01); SetOutputValue(OutputId.P2_Lmp_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 1) >> 7 & 0x01); SetOutputValue(OutputId.P2_Lmp_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 1) >> 6 & 0x01); SetOutputValue(OutputId.P2_Lmp_B, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Outputs_Offset + 1) >> 5 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, P1_RecoilState); SetOutputValue(OutputId.P1_GunMotor, P1_RumbleState); SetOutputValue(OutputId.P2_GunRecoil, P2_RecoilState); SetOutputValue(OutputId.P2_GunMotor, P2_RumbleState); //Customs Outputs _P1_Ammo = 0; _P2_Ammo = 0; _P1_Life = 0; _P2_Life = 0; //Player Status : //[0] : Inactive //[1] : In-Game //[2] : Continue Screen //[3] : Game Over int P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset); if (P1_Status == 1) { _P1_Ammo = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset, 4), 0); _P1_Life = (int)(100.0 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset, 4), 0)); if (_P1_Life < 0) _P1_Life = 0; if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } int P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Status_Offset); if (P2_Status == 1) { _P2_Ammo = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset, 4), 0); _P2_Life = (int)(100.0 * BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset, 4), 0)); if (_P2_Life < 0) _P2_Life = 0; if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); //Custom Recoil will simply be activated just like original Recoil SetOutputValue(OutputId.P1_CtmRecoil, P1_RecoilState); SetOutputValue(OutputId.P2_CtmRecoil, P2_RecoilState); //Custom Damaged will simply be activated just like original rumble /* De-activated : Rumble also occurs with environmental actions ! SetOutputValue(OutputId.P1_Damaged, P1_RumbleState); SetOutputValue(OutputId.P2_Damaged, P2_RumbleState); */ SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_TtxSha.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_TtxSha : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x002E3964; private UInt32 _P1_Y_Offset = 0x002E3968; private UInt32 _P1_Out_Offset = 0x002E3960; private UInt32 _P2_X_Offset = 0x002E3970; private UInt32 _P2_Y_Offset = 0x002E3974; private UInt32 _P2_Out_Offset = 0x002E396C; private UInt32 _DisableAxisUpdate_Offset = 0x000D12E0; private InjectionStruct _Triggers_InjectionStruct = new InjectionStruct(0x000D1464, 6); private UInt32 _Triggers_CaveAddress; private UInt32 _Recoil_Injection_Offset = 0x000D0CD0; private UInt32 _P1_Recoil_CaveAddress; private UInt32 _P2_Recoil_CaveAddress; private UInt32 _GameResolutionWidth_Offset = 0x000083D4; private UInt32 _GameResolutionHeight_Offset = 0x000083CF; /// /// Constructor /// public Game_TtxSha(String RomName) : base (RomName, "KSHG") { _KnownMd5Prints.Add("Silent Hill Arcade - Original KSHG_no_cursor.exe", "0e58fd1c7bcb5e0cace0e4ee548ecd0c"); _KnownMd5Prints.Add("Silent Hill Arcade - Original KSHG.exe", "2778219dcaf3d8e09fb197417b17dc1b"); _tProcess.Start(); Logger.WriteLog("Waiting for Taito Type X " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcesses(); foreach (Process p in processes) { if (p.ProcessName.StartsWith("KSHG")) { _Target_Process_Name = p.ProcessName; _TargetProcess = p; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { //Win11 + AMD need this ? //Using DgVoodoo (or ReShade) results in bigger window and the base process resolution stays at 640x480. //Using original method to compute axis into the process resolution, shots get blocked in upper left 640x480 square. //Keeping the window coordinates solves this. return true; if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); byte[] buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameResolutionWidth_Offset, 4); double GameResX = (double)BitConverter.ToUInt32(buffer, 0); buffer = ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameResolutionHeight_Offset, 4); double GameResY = (double)BitConverter.ToUInt32(buffer, 0); Logger.WriteLog("Detected resolution (px) = [" + GameResX.ToString() + "x" + GameResY.ToString() + "]"); double dMinX = 0.0; double dMaxX = TotalResX; double dMinY = 0.0; double dMaxY = TotalResY; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / GameResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dRangeY * PlayerData.RIController.Computed_Y / GameResY)); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _Triggers_CaveAddress = _InputsDatabank_Address; SetHack_Triggers(); //Function is called whenever a trigger press is detected. //Function is computing Axis based on data + calibration values, and clamp it //Just RET at start to disable all of this WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _DisableAxisUpdate_Offset, 0xC3); //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X); //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y); //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P1_Out); //SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_P2_Out); //Set P2 IN_SCREEN WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, 0x01); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, 0x01); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Trigger status is handled by the IO dll and analysed after by the game /// This will just modify the values (and force OK status for P2) before the game read it /// private void SetHack_Triggers() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, esi CaveMemory.Write_StrBytes("8B C6"); //shl eax,2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _Triggers_CaveAddress byte[] b = BitConverter.GetBytes(_Triggers_CaveAddress); CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(b); //mov eax, [eax] CaveMemory.Write_StrBytes("8B 00"); //mov [edi-04, eax] CaveMemory.Write_StrBytes("89 47 FC"); //mov [edi],00000000 CaveMemory.Write_StrBytes("C7 07 00 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //cmp dword ptr [edi-04],00 CaveMemory.Write_StrBytes("83 7F FC 00"); //je KSHG_no_cursor.exe+D1497 CaveMemory.Write_je((UInt32)_TargetProcess_MemoryBaseAddress + 0xD1497); //Inject it CaveMemory.InjectToOffset(_Triggers_InjectionStruct, "Triggers"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_Recoil_CaveAddress = _OutputsDatabank_Address; _P2_Recoil_CaveAddress = _OutputsDatabank_Address + 0x04; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// To activate gun recoil, the game is calling the GUN_Reaction() function of shaiolib.dll. /// This function has been ripped from the cracked Dll, so we are injecting a piece of code /// to intercept the calls and handle the Recoil ourselves when the game wants to /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[esp+08] CaveMemory.Write_StrBytes("8B 44 24 08"); //shl eax,2 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_Recoil_CaveAddress byte[] b = BitConverter.GetBytes(_P1_Recoil_CaveAddress); CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(b); //mov [eax], 1 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //ret CaveMemory.Write_StrBytes("C3"); Logger.WriteLog("Adding Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Recoil_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Recoil_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteBytes(_Triggers_CaveAddress, new byte[]{ 0xFF, 0xFF, 0xFF, 0xFF }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteBytes(_Triggers_CaveAddress, new byte[] { 0x00, 0x00, 0x00, 0x00 }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //Set out of screen Byte WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, 0x00); //Trigger a shoot to reload !! WriteBytes(_Triggers_CaveAddress, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteBytes(_Triggers_CaveAddress, new byte[] { 0x00, 0x00, 0x00, 0x00 }); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Out_Offset, 0x01); } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteBytes(_Triggers_CaveAddress + 4, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteBytes(_Triggers_CaveAddress + 4, new byte[] { 0x00, 0x00, 0x00, 0x00 }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //Set out of screen Byte WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, 0x00); //Trigger a shoot to reload !! WriteBytes(_Triggers_CaveAddress + 4, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteBytes(_Triggers_CaveAddress + 4, new byte[] { 0x00, 0x00, 0x00, 0x00 }); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Out_Offset, 0x01); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpLeft)); _Outputs.Add(new GameOutput(OutputId.LmpRight)); _Outputs.Add(new GameOutput(OutputId.P1_LmpCard_R)); _Outputs.Add(new GameOutput(OutputId.P1_LmpCard_G)); _Outputs.Add(new GameOutput(OutputId.P2_LmpCard_R)); _Outputs.Add(new GameOutput(OutputId.P2_LmpCard_G)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Original Outputs byte bOutput = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38C0); SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38C4)); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38CC)); SetOutputValue(OutputId.P1_LmpCard_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38D4)); SetOutputValue(OutputId.P1_LmpCard_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38DC)); SetOutputValue(OutputId.P2_LmpCard_R, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38E4)); SetOutputValue(OutputId.P2_LmpCard_G, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38EC)); SetOutputValue(OutputId.LmpLeft, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38F4)); SetOutputValue(OutputId.LmpRight, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002E38FC)); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x002F9008)); //Custom Outputs //Original recoil Handling is stripped from the DLL so we are forced to handle the duration ourselve with an Async-reset output if (ReadByte(_P1_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_Recoil_CaveAddress, 0); } if (ReadByte(_P2_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_Recoil_CaveAddress, 0); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndAdCop95.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndAdCop95 : Game { //Memory values private UInt32 _Credits_Offset = 0x00060A45; private NopStruct _Nop_AxisX = new NopStruct(0x0000D970, 6); private NopStruct _Nop_AxisY = new NopStruct(0x0000D982, 6); private UInt32 _PlayersDataPtr_Offset = 0x00063607; private UInt32 _PlayersStatus_Offset = 0x00060A51; private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x0000D948, 9); private UInt32 _NoCrosshairPatch_P1_Offset = 0x000030F9; private UInt32 _NoCrosshairPatch_P2_Offset = 0x00003187; //custom values private UInt32 _P1_Buttons_CaveAddress = 0; private UInt32 _P2_Buttons_CaveAddress = 0; private HardwareScanCode _DIK_Credits = HardwareScanCode.DIK_5; /// /// Constructor /// public Game_WndAdCop95(String RomName) : base(RomName, "adcop95") { _KnownMd5Prints.Add("ADCOP v1.06 - Original exe", "e5ee4b73028672d5b30a5f0f38e0a05a"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-320] //Y => [0-240] double dMaxX = 320.0; double dMaxY = 240.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them. /// protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisX); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisY); Create_InputsDataBank(); _P1_Buttons_CaveAddress = _InputsDatabank_Address; _P2_Buttons_CaveAddress = _InputsDatabank_Address + 4; SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// At that moment the game is checking the button state to run functions based on the button value. /// - Player ID in EBP+8 (0 / 1) /// - Player Struct in EDI (+3BC and +3C0 are axis) /// - EDX+C has button info (2 = shoot, 1=reload, 4=grenade, 8= ??) /// Resetting the custom codecave value afterward is mandatory or all bullets will be fired at once with a button push /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov edi,[ebx+0000008A] CaveMemory.Write_StrBytes("8B BB 8A 00 00 00"); //mov edx,[ebp+0C] CaveMemory.Write_StrBytes("8B 55 0C"); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //mov eax,[ebp+08] CaveMemory.Write_StrBytes("8B 45 08"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_Buttons_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Buttons_CaveAddress)); //mov ebx, [eax] CaveMemory.Write_StrBytes("8B 18"); //mov [edx+c], eax CaveMemory.Write_StrBytes("89 5A 0C"); //mov byte ptr[eax], 0 CaveMemory.Write_StrBytes("C6 00 00"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Buttons_InjectionStruct, "Buttons"); } protected override void Apply_NoCrosshairMemoryHack() { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _NoCrosshairPatch_P1_Offset, new byte[] { 0x68, 0xFF, 0x07, 0x00, 0x00, 0x90 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _NoCrosshairPatch_P2_Offset, new byte[] { 0x68, 0xFF, 0x07, 0x00, 0x00, 0x90 }); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset, new uint[] { 0x8A }); if (PlayerAddress != 0) { WriteBytes(PlayerAddress + 0x3BC, bufferX); WriteBytes(PlayerAddress + 0x3C0, bufferY); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFD); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x04); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFB); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFE); } } else if (PlayerData.ID == 2) { UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset + 4, new uint[] { 0x8A }); if (PlayerAddress != 0) { WriteBytes(PlayerAddress + 0x3BC, bufferX); WriteBytes(PlayerAddress + 0x3C0, bufferY); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFD); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x04); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFB); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFE); } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _DIK_Credits) { int Coins = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, 4), 0); Coins++; WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, BitConverter.GetBytes(Coins)); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Custom Outputs //that byte uses bit 0 to set P1 status (playing/dead) and byte1 for P2 UInt32 PlayersStatus = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersStatus_Offset); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if ((PlayersStatus & 1) == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset, new uint[] { 0x8A }); if (PlayerAddress != 0) { _P1_Life = ReadByte(PlayerAddress); _P1_Ammo = ReadByte(PlayerAddress + 0x3D0); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } if ((PlayersStatus >> 1 & 1) == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P2_CtmLmpStart, 0); UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset + 4, new uint[] { 0x8A }); if (PlayerAddress != 0) { _P2_Life = ReadByte(PlayerAddress); _P2_Ammo = ReadByte(PlayerAddress + 0x3D0); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P2_CtmLmpStart, -1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndAdCopOverseas.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndAdCopOverseas : Game { //Memory values private UInt32 _Credits_Offset = 0x00064B29; private NopStruct _Nop_AxisX = new NopStruct(0x0000E1F1, 6); private NopStruct _Nop_AxisY = new NopStruct(0x0000E203, 6); private UInt32 _PlayersDataPtr_Offset = 0x00067817; private UInt32 _PlayersStatus_Offset = 0x00064B35; private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x0000E1C9, 9); private UInt32 _NoCrosshairPatch_P1_Offset = 0x000030FC; private UInt32 _NoCrosshairPatch_P2_Offset = 0x0000318A; //custom values private UInt32 _P1_Buttons_CaveAddress = 0; private UInt32 _P2_Buttons_CaveAddress = 0; private HardwareScanCode _DIK_Credits = HardwareScanCode.DIK_5; /// /// Constructor /// public Game_WndAdCopOverseas(String RomName) : base(RomName, "adcopsea") { _KnownMd5Prints.Add("ADCOP Overseas Mission - Original exe", "7e02a4e85cf1ed8c672e077b86cb370a"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-320] //Y => [0-240] double dMaxX = 320.0; double dMaxY = 240.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them. /// protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisX); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_AxisY); Create_InputsDataBank(); _P1_Buttons_CaveAddress = _InputsDatabank_Address; _P2_Buttons_CaveAddress = _InputsDatabank_Address + 4; SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// At that moment the game is checking the button state to run functions based on the button value. /// - Player ID in EBP+8 (0 / 1) /// - Player Struct in EDI (+3BC and +3C0 are axis) /// - EDX+C has button info (2 = shoot, 1=reload, 4=grenade, 8= ??) /// Resetting the custom codecave value afterward is mandatory or all bullets will be fired at once with a button push /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov edi,[ebx+0000008A] CaveMemory.Write_StrBytes("8B BB 8A 00 00 00"); //mov edx,[ebp+0C] CaveMemory.Write_StrBytes("8B 55 0C"); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //mov eax,[ebp+08] CaveMemory.Write_StrBytes("8B 45 08"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax, _P1_Buttons_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Buttons_CaveAddress)); //mov ebx, [eax] CaveMemory.Write_StrBytes("8B 18"); //mov [edx+c], eax CaveMemory.Write_StrBytes("89 5A 0C"); //mov byte ptr[eax], 0 CaveMemory.Write_StrBytes("C6 00 00"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Buttons_InjectionStruct, "Buttons"); } protected override void Apply_NoCrosshairMemoryHack() { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _NoCrosshairPatch_P1_Offset, new byte[] { 0x68, 0xFF, 0x07, 0x00, 0x00, 0x90 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _NoCrosshairPatch_P2_Offset, new byte[] { 0x68, 0xFF, 0x07, 0x00, 0x00, 0x90 }); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset, new uint[] { 0x8A }); if (PlayerAddress != 0) { WriteBytes(PlayerAddress + 0x3BC, bufferX); WriteBytes(PlayerAddress + 0x3C0, bufferY); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFD); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x04); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFB); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFE); } } else if (PlayerData.ID == 2) { UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset + 4, new uint[] { 0x8A }); if (PlayerAddress != 0) { WriteBytes(PlayerAddress + 0x3BC, bufferX); WriteBytes(PlayerAddress + 0x3C0, bufferY); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFD); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x04); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFB); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFE); } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _DIK_Credits) { int Coins = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, 4), 0); Coins++; WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, BitConverter.GetBytes(Coins)); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Custom Outputs //that byte uses bit 0 to set P1 status (playing/dead) and byte1 for P2 UInt32 PlayersStatus = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersStatus_Offset); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if ((PlayersStatus & 1) == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset, new uint[] { 0x8A }); if (PlayerAddress != 0) { _P1_Life = ReadByte(PlayerAddress); _P1_Ammo = ReadByte(PlayerAddress + 0x3D0); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } if ((PlayersStatus >> 1 & 1) == 1) { //Force Start Lamp to Off SetOutputValue(OutputId.P2_CtmLmpStart, 0); UInt32 PlayerAddress = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersDataPtr_Offset + 4, new uint[] { 0x8A }); if (PlayerAddress != 0) { _P2_Life = ReadByte(PlayerAddress); _P2_Ammo = ReadByte(PlayerAddress + 0x3D0); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P2_CtmLmpStart, -1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndAlienSafari.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_WndAlienSafari : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\ads"; //Memory locations private InjectionStruct _Axis_InjectionStruct = new InjectionStruct(0x0004C0BB, 6); //also 44C0FD ? private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x00017AE0A, 8); private InjectionStruct _IsPlaying_InjectionStruct = new InjectionStruct(0x000502BE, 6); private InjectionStruct _StartLevel_InjectionStruct = new InjectionStruct(0x00188E93, 5); private InjectionStruct _EndLevel_InjectionStruct = new InjectionStruct(0x00188EC8, 7); private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x000502FD, 6); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x00015F71, 6); //Custom values private UInt32 _P1_X_CaveAddress = 0; private UInt32 _P1_Y_CaveAddress = 0; private UInt32 _P1_Trigger_CaveAddress = 0; private UInt32 _P1_TriggerEventCountdown_CaveAddress = 0; private UInt32 _P1_Reload_CaveAddress = 0; private UInt32 _P1_ReloadEventCountdown_CaveAddress = 0; private UInt32 _FloatZero_CaveAddress = 0; private UInt32 _FloatOne_CaveAddress = 0; //Outputs custom values private UInt32 _PlayerIsPlaying_CaveAddress = 0; private UInt32 _WeaponMaxAmmo_CaveAddress = 0; private UInt32 _WeaponCurrentAmmo_CaveAddress = 0; private UInt32 _WeaponCurrentAmmo2_CaveAddress = 0; private int _P1_WeaponMaxAmmo_Last = 0; //Use to check if weapon has changed, to prevent triggering recoil when switching for a lower-munition weapon /// /// Constructor /// public Game_WndAlienSafari(String RomName) : base(RomName, "Alien") { _KnownMd5Prints.Add("Alien 1.0.0.1 - ToEng", "8b9db55dd8bf8af653f30fc0301cad6c"); _KnownMd5Prints.Add("Alien 1.0.0.1 - Original", "2618455f69182c9c47e2d8a959cd00e9"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => 800 //Y => 600 double dMaxX = 800.0; double dMaxY = 600.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x04; _P1_Trigger_CaveAddress = _InputsDatabank_Address + 0x08; _P1_TriggerEventCountdown_CaveAddress = _InputsDatabank_Address + 0x09; _P1_Reload_CaveAddress = _InputsDatabank_Address + 0x0C; _P1_ReloadEventCountdown_CaveAddress = _InputsDatabank_Address + 0x0D; _FloatZero_CaveAddress = _InputsDatabank_Address + 0x20; WriteBytes(_FloatZero_CaveAddress, BitConverter.GetBytes(0.0f)); _FloatOne_CaveAddress = _InputsDatabank_Address + 0x24; WriteBytes(_FloatOne_CaveAddress, BitConverter.GetBytes(1.0f)); SetHack_Axis(); SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //fstp dword ptr [eax+000000C0] CaveMemory.Write_StrBytes("D9 98 C0 00 00 00"); //push ebx CaveMemory.Write_StrBytes("53"); //mov ebx, [_P1_X_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_CaveAddress)); //mov [eax+BC], ebx CaveMemory.Write_StrBytes("89 98 BC 00 00 00"); //mov ebx, [_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Y_CaveAddress)); //mov [eax+C0], ebx CaveMemory.Write_StrBytes("89 98 C0 00 00 00"); //pop ebx CaveMemory.Write_StrBytes("5B"); //Inject it CaveMemory.InjectToOffset(_Axis_InjectionStruct, "AxisY"); } /// /// The game is using 1 procedure to call for button state, based on a parameter index /// Result is a float value, thresold is 0.5f /// So it's safe to reply 0 for not presses and 0x3F80 (1.0f) for pressed /// 15 = Axis X (relative movement) /// 16 = Axis Y (relative movement) Both called by the previously done hack IN-GAME (move a bit in menus ?) /// /// 5 = Trigger (EventDown) /// 8 = Trigger (Down) /// 7 = Reload (Eventdown) /// /// 6,9 = ? called for trigger check also. Maybe a second button ? /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov ecx,[esp+04] CaveMemory.Write_StrBytes("8B 4C 24 04"); //P1 Trigger Down event //cmp ecx, 05 CaveMemory.Write_StrBytes("83 F9 05"); //jne P1_TriggerDown CaveMemory.Write_StrBytes("75 22"); //cmp byte ptr [_P1_Trigger_CaveAddress],00 CaveMemory.Write_StrBytes("80 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Trigger_CaveAddress)); CaveMemory.Write_StrBytes("00"); //je Zero CaveMemory.Write_StrBytes("74 56"); //cmp byte ptr [_P1_TriggerEventCountdown_CaveAddress],00 CaveMemory.Write_StrBytes("80 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_TriggerEventCountdown_CaveAddress)); CaveMemory.Write_StrBytes("00"); //jg 00DE001D CaveMemory.Write_StrBytes("7F 02"); //jmp Zero CaveMemory.Write_StrBytes("EB 4B"); //fld dword ptr [_FloatOne_CaveAddress] CaveMemory.Write_StrBytes("D9 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_FloatOne_CaveAddress)); //shr [_P1_TriggerEventCountdown_CaveAddress],1 CaveMemory.Write_StrBytes("D1 2D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_TriggerEventCountdown_CaveAddress)); //jmp Exit CaveMemory.Write_StrBytes("EB 43"); //P1_TriggerDown: //cmp ecx,08 CaveMemory.Write_StrBytes("83 F9 08"); //jne Reload CaveMemory.Write_StrBytes("75 11"); //cmp byte ptr [_P1_Trigger_CaveAddress],00 CaveMemory.Write_StrBytes("80 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Trigger_CaveAddress)); CaveMemory.Write_StrBytes("00"); //je Zero CaveMemory.Write_StrBytes("74 2F"); //fld dword ptr [_P1_Trigger_CaveAddress] CaveMemory.Write_StrBytes("D9 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_FloatOne_CaveAddress)); //jmp Exit CaveMemory.Write_StrBytes("EB 2D"); //P1 Reload Down event //cmp ecx, 07 CaveMemory.Write_StrBytes("83 F9 07"); //jne Next CaveMemory.Write_StrBytes("75 22"); //cmp byte ptr [_P1_Reload_CaveAddress],00 CaveMemory.Write_StrBytes("80 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Reload_CaveAddress)); CaveMemory.Write_StrBytes("00"); //je Zero CaveMemory.Write_StrBytes("74 19"); //cmp byte ptr [_P1_ReloadEventCountdown_CaveAddress],00 CaveMemory.Write_StrBytes("80 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_ReloadEventCountdown_CaveAddress)); CaveMemory.Write_StrBytes("00"); //jg 00DE001D CaveMemory.Write_StrBytes("7F 02"); //jmp Zero CaveMemory.Write_StrBytes("EB 0E"); //fld dword ptr [_FloatOne_CaveAddress] CaveMemory.Write_StrBytes("D9 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_FloatOne_CaveAddress)); //shr [_P1_ReloadEventCountdown_CaveAddress],1 CaveMemory.Write_StrBytes("D1 2D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_ReloadEventCountdown_CaveAddress)); //jmp Exit CaveMemory.Write_StrBytes("EB 06"); //Next: //fld dword ptr [_FloatZero_CaveAddress] CaveMemory.Write_StrBytes("D9 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_FloatZero_CaveAddress)); //Inject it CaveMemory.InjectToOffset(_Buttons_InjectionStruct, "Buttons"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _PlayerIsPlaying_CaveAddress = _OutputsDatabank_Address; _WeaponCurrentAmmo_CaveAddress = _OutputsDatabank_Address + 0x04; _WeaponCurrentAmmo2_CaveAddress = _OutputsDatabank_Address + 0x08; _WeaponMaxAmmo_CaveAddress = _OutputsDatabank_Address + 0x0C; SetHack_StartLevel(); SetHack_UpdateOutputs(); SetHack_EndLevel(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercept Start of level event to start generating recoil/ammo outputs /// private void SetHack_StartLevel() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //lea edx,[esp+04] CaveMemory.Write_StrBytes("8D 54 24 04"); //mov [_PlayerIsPlaying_CaveAddress],1 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_PlayerIsPlaying_CaveAddress)); CaveMemory.Write_StrBytes("01"); //Inject it CaveMemory.InjectToOffset(_StartLevel_InjectionStruct, "EndLevel"); } /// /// This procedure is looped when a level is running to check for inputs. /// private void SetHack_UpdateOutputs() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push [ebp-000000A4] CaveMemory.Write_StrBytes("FF B5 5C FF FF FF"); //push ebx CaveMemory.Write_StrBytes("53"); //mov ebx,[eax+34] CaveMemory.Write_StrBytes("8B 58 34"); //mov [_WeaponCurrentAmmo_CaveAddress],ebx CaveMemory.Write_StrBytes("89 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_WeaponCurrentAmmo_CaveAddress)); //mov ebx,[eax+38] CaveMemory.Write_StrBytes("8B 58 38"); //mov [_WeaponMaxAmmo_CaveAddress],ebx CaveMemory.Write_StrBytes("89 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_WeaponMaxAmmo_CaveAddress)); //mov ebx,[eax+70] CaveMemory.Write_StrBytes("8B 58 70"); //mov [_WeaponCurrentAmmo_CaveAddress],ebx CaveMemory.Write_StrBytes("89 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_WeaponCurrentAmmo2_CaveAddress)); //pop ebx CaveMemory.Write_StrBytes("5B"); //Inject it CaveMemory.InjectToOffset(_IsPlaying_InjectionStruct, "IsPlaying"); } /// /// Intercept End of level event to stop generating recoil/ammo outputs /// private void SetHack_EndLevel() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov esi,[esp+6C] CaveMemory.Write_StrBytes("8B 74 24 6C"); //mov mov eax,[esi+28] CaveMemory.Write_StrBytes("8B 46 28"); //mov [_PlayerIsPlaying_CaveAddress],0 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_PlayerIsPlaying_CaveAddress)); CaveMemory.Write_StrBytes("00"); //Inject it CaveMemory.InjectToOffset(_EndLevel_InjectionStruct, "EndLevel"); } protected override void Apply_NoCrosshairMemoryHack() { //If level is already running, force crosshair to 0 UInt32 CursorAddress = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + 0x31FCC4); if (CursorAddress != 0) WriteByte(CursorAddress + 0xD4, 0); //Force the game to believe cursor is disabled, to not render the drawing Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov mov eax,[ebx+000000D4] CaveMemory.Write_StrBytes("8B 83 D4 00 00 00"); //cmp dword ptr [_PlayerIsPlaying_CaveAddress],00 CaveMemory.Write_StrBytes("83 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_PlayerIsPlaying_CaveAddress)); CaveMemory.Write_StrBytes("00"); //je OriginalCode CaveMemory.Write_StrBytes("74 02"); //xor ecx, ecx CaveMemory.Write_StrBytes("31 C0"); //Inject it CaveMemory.InjectToOffset(_NoCrosshair_InjectionStruct, "NoCrosshair"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((float)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte(_P1_Trigger_CaveAddress, 0x01); WriteByte(_P1_TriggerEventCountdown_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte(_P1_Trigger_CaveAddress, 0x00); WriteByte(_P1_TriggerEventCountdown_CaveAddress, 0x00); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteByte(_P1_Reload_CaveAddress, 0x01); WriteByte(_P1_ReloadEventCountdown_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte(_P1_Reload_CaveAddress, 0x00); WriteByte(_P1_ReloadEventCountdown_CaveAddress, 0x00); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { int MotorState = 0; int P1_WeaponMaxAmmo = 0; _P1_Ammo = 0; int P1_Clip = 0; int PlayerIsPlaying = ReadByte((_PlayerIsPlaying_CaveAddress)); if (PlayerIsPlaying == 1) { _P1_Ammo = BitConverter.ToInt32(ReadBytes(_WeaponCurrentAmmo_CaveAddress, 4), 0); if (_P1_Ammo < 0) _P1_Ammo = 0; if (_P1_Ammo > 0) P1_Clip = 1; //Max Ammo is 0 ==> Laser guns, activate rumble when trigger is pressed P1_WeaponMaxAmmo = BitConverter.ToInt32(ReadBytes(_WeaponMaxAmmo_CaveAddress, 4), 0); if (P1_WeaponMaxAmmo == 0) { MotorState = ReadByte(_P1_Trigger_CaveAddress); //Those weapons have different type of ammo counter float fAmmo = BitConverter.ToSingle(ReadBytes(_WeaponCurrentAmmo2_CaveAddress, 4), 0); _P1_Ammo = (int)fAmmo; } else { //Bullets-firing gun : activate recoil when a munition is fired if (P1_WeaponMaxAmmo == _P1_WeaponMaxAmmo_Last && _P1_Ammo < _P1_LastAmmo) { SetOutputValue(OutputId.P1_CtmRecoil, 1); } } } SetOutputValue(OutputId.P1_GunMotor, MotorState); SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); _P1_WeaponMaxAmmo_Last = P1_WeaponMaxAmmo; _P1_LastAmmo = _P1_Ammo; } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndArtIsDead.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { public class Game_WndArtIsDead : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _P1_Trigger_CaveAddress; private UInt32 _P2_Trigger_CaveAddress; /// /// Constructor /// public Game_WndArtIsDead(String RomName) : base(RomName, "gungallery") { _KnownMd5Prints.Add("Art Is Dead", "4cc4e814647a1d9f315fa1be4ac3d92a"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-640] = 640 //Y => [0-480] = 480 double dMaxX = 640; double dMaxY = 480; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { /*** * This game is made to work with ActLabs CRT guns, so no gun = no movement * The first part will be to allocate some memory to store P1 & P2 Aimtrak data (axis + trigger) * The second part is to patch the memory in 3 steps : * 1) Force the update of P1 and P2 values even if no act lab gun detected * 2) Read cursor data in newlly allocated memory instead of the original one * 3) Reset trigger state by writing the new allocated memory instead of the original one * * Theses patch must be done separatly for P1 (solo mode), P1 (2P mode) and P2 (2P mode) * as the procedures and memory location are not the same ***/ //First part = Allocating Memory to store P1 and P2 axis values and trigger Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P1_Y_CaveAddress = _InputsDatabank_Address + 4; _P1_Trigger_CaveAddress = _InputsDatabank_Address + 8; _P2_X_CaveAddress = _InputsDatabank_Address + 9; _P2_Y_CaveAddress = _InputsDatabank_Address + 13; _P2_Trigger_CaveAddress = _InputsDatabank_Address + 17; //Second Part //Step 1) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x20F38, 0x85); //P1 (2P mode) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x2106C, 0x85); //P2 (2P mode) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x21245, 0x85); //P1 (Solo) //Step 2) //P1 (2P mode) byte[] b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x20FDD, b); b = BitConverter.GetBytes(_P1_X_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x2100E, b); b = BitConverter.GetBytes(_P1_Y_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x21001, b); //P2 (2P mode) b = BitConverter.GetBytes(_P2_Trigger_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x211B2, b); b = BitConverter.GetBytes(_P2_X_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x211E3, b); b = BitConverter.GetBytes(_P2_Y_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x211D6, b); //P1 (Solo) b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x212E4, b); b = BitConverter.GetBytes(_P1_X_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x2130E, b); b = BitConverter.GetBytes(_P1_Y_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x21301, b); //Step 3) //P1 (2P mode) b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x2104D, b); //P2 (2P mode) b = BitConverter.GetBytes(_P2_Trigger_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x21222, b); //P1 (Solo) b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + 0x21353, b); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_P1_Trigger_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_P1_Trigger_CaveAddress, 0x00); } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((UInt32)_P2_Trigger_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((UInt32)_P2_Trigger_CaveAddress, 0x00); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndBE.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndBE : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\bestate"; /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _P1_X_Injection_Offset_1 = 0x008F09FE; private UInt32 _P1_X_Injection_Return_Offset_1 = 0x008F0A04; private UInt32 _P1_X_Injection_Offset_2 = 0x008F0CD2; private UInt32 _P1_X_Injection_Return_Offset_2 = 0x008F0CDA; private UInt32 _P1_Y_Injection_Offset_1 = 0x008F0A0B; //private UInt32 _P1_Y_Injection_Return_Offset_1 = 0x008F0A13; private UInt32 _P1_Y_Injection_Offset_2 = 0x008F0CFF; //private UInt32 _P1_Y_Injection_Return_Offset_2 = 0x008F0D07; private UInt32 _P2_Axis_Injection_Offset_1 = 0x008F0E20; //private UInt32 _P2_Axis_Injection_Return_Offset_1 = 0x008F0E29; private UInt32 _P2_Axis_Injection_Offset_2 = 0x008F0E49; //private UInt32 _P2_Axis_Injection_Return_Offset_2 = 0x008F0E52; //Keys to send //For player 2 if used, keys are choosed for x360kb.ini: //I usually prefer to send VirtualKeycodes (less troublesome when no physical Keyboard is plugged) //But with x360kb only DIK keycodes are working private HardwareScanCode _P2_Trigger_DIK = HardwareScanCode.DIK_T; private HardwareScanCode _P2_Reload_DIK = HardwareScanCode.DIK_Y; //Custom data to inject private float _P1_X_Value; private float _P1_Y_Value; private float _P2_X_Value; private float _P2_Y_Value; /// /// Constructor /// public Game_WndBE(String RomName) : base(RomName, "BEGame") { _KnownMd5Prints.Add("Blue Estate CODEX - Cracked", "188605d4083377e4ee3552b4c89f52fb"); // To play as Player2 the game needs a Joypad // By using x360kb.ini and xinput1_3.dll in the game's folder, we can add a virtual X360 Joypad to act as player 2 /*try { using (StreamWriter sw = new StreamWriter(GamePath + @"\x360kb.ini", false)) { if (_EnableP2) sw.Write(DemulShooter.Properties.Resources.x360kb_hfirea2p); else sw.Write(DemulShooter.Properties.Resources.x360kb_hfirea1p); } Logger.WriteLog("File \"" + GamePath + "\\x360kb.ini\" successfully written !"); } catch (Exception ex) { Logger.WriteLog("Error trying to write file " + GamePath + "\\x360kb.ini\" :" + ex.Message.ToString()); }*/ _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { /* Wait until Splash Screen is closed and real windows displayed */ /* Game Windows classname = "LaunchUnrealUWindowsClient" */ StringBuilder ClassName = new StringBuilder(256); int nRet = Win32API.GetClassName(_TargetProcess.MainWindowHandle, ClassName, ClassName.Capacity); if (nRet != 0 && ClassName.ToString() != "SplashScreenClass") { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); //Apply_MemoryHacks(); _ProcessHooked = true; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { Rect TotalRes = new Rect(); Win32API.GetClientRect(_TargetProcess.MainWindowHandle, ref TotalRes); int TotalResX = TotalRes.Right - TotalRes.Left; int TotalResY = TotalRes.Bottom - TotalRes.Top; Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [-1 ; 1] float //Y => [-1 ; 1] float float X_Value = (2.0f * PlayerData.RIController.Computed_X / TotalResX) - 1.0f; float Y_Value = (2.0f * PlayerData.RIController.Computed_Y / TotalResY) - 1.0f; if (X_Value < -1.0f) X_Value = -1.0f; if (Y_Value < -1.0f) Y_Value = -1.0f; if (X_Value > 1.0f) X_Value = 1.0f; if (Y_Value > 1.0f) Y_Value = 1.0f; if (PlayerData.ID == 1) { _P1_X_Value = X_Value; _P1_Y_Value = Y_Value; } else if (PlayerData.ID == 2) { _P2_X_Value = X_Value; _P2_Y_Value = Y_Value; } return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x04; _P2_X_CaveAddress = _InputsDatabank_Address + 0x20; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x24; SetHack_P1X(); SetHack_P1X_2(); SetHack_P1Y((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Y_Injection_Offset_1); SetHack_P1Y((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_Y_Injection_Offset_2); SetHack_P2Axis((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Axis_Injection_Offset_1); SetHack_P2Axis((UInt32)_TargetProcess.MainModule.BaseAddress + _P2_Axis_Injection_Offset_2); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The game use some fstp [XXX] instruction, but we can't just NOP it as graphical glitches may appear. /// So we just add another set of instructions instruction immediatelly after to change the register /// to our own desired value /// private void SetHack_P1X() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp dwordptr [esi+000002B0] CaveMemory.Write_StrBytes("D9 9E B0 02 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, [P1_X] CaveMemory.Write_StrBytes("A1"); byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_Bytes(b); //mov [esi+000002B0], eax CaveMemory.Write_StrBytes("89 86 B0 02 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_X_Injection_Return_Offset_1); Logger.WriteLog("Adding P1 X Axis Codecave_1 at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_X_Injection_Offset_1) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P1_X_Injection_Offset_1, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //This Codecave is modifying the xmm0 value with our own private void SetHack_P1X_2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_X_Address CaveMemory.Write_StrBytes("B8"); byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_Bytes(b); //movss xmm0, [eax] CaveMemory.Write_StrBytes("F3 0F 10 00"); //pop eax CaveMemory.Write_StrBytes("58"); //movss [esi+000002B0],xmm0 CaveMemory.Write_StrBytes("F3 0F 11 86 B0 02 00 00"); //return CaveMemory.Write_jmp((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_X_Injection_Return_Offset_2); Logger.WriteLog("Adding P1_X CodeCave_2 at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess.MainModule.BaseAddress + _P1_X_Injection_Offset_2) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess.MainModule.BaseAddress + _P1_X_Injection_Offset_2, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //This Codecave is modifying the xmm0 value with our own //This instruction is called 2 times so there will be 2 instance of this codecave at different places //The instruction lenght is fixed (8) so we won't use the Injection_Return_Offset, but Injection_Offset + 0x08 private void SetHack_P1Y(UInt32 OriginalProcAddress) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_Y_Address CaveMemory.Write_StrBytes("B8"); byte[] b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_Bytes(b); //movss xmm0, [eax] CaveMemory.Write_StrBytes("F3 0F 10 00"); //pop eax CaveMemory.Write_StrBytes("58"); //movss [esi+000002B4],xmm0 CaveMemory.Write_StrBytes("F3 0F 11 86 B4 02 00 00"); //return CaveMemory.Write_jmp(OriginalProcAddress + 0x08); Logger.WriteLog("Adding P1_Y CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - OriginalProcAddress - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, OriginalProcAddress, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } //This Codecave is modifying the xmm0 value with our own //This instruction is called 2 times so there will be 2 instance of this codecave at different places //The instruction lenght is fixed (9) so we won't use the Injection_Return_Offset, but Injection_Offset + 0x09 private void SetHack_P2Axis(UInt32 OriginalProcAddress) { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //cmp ecx, 01 CaveMemory.Write_StrBytes("83 F9 01"); //je AxisY CaveMemory.Write_StrBytes("0F 84 0A 00 00 00"); //mov eax, _P2_X_Address CaveMemory.Write_StrBytes("B8"); byte[] b = BitConverter.GetBytes(_P2_X_CaveAddress); CaveMemory.Write_Bytes(b); //jmp originalcode CaveMemory.Write_StrBytes("E9 05 00 00 00"); //AxisY: //mov eax, _P2_Y_Address CaveMemory.Write_StrBytes("B8"); b = BitConverter.GetBytes(_P2_Y_CaveAddress); CaveMemory.Write_Bytes(b); //originalcode: //movss xmm0, [eax] CaveMemory.Write_StrBytes("F3 0F 10 00"); //pop eax CaveMemory.Write_StrBytes("58"); //movss [esi+ecx*4+000002B0],xmm0 CaveMemory.Write_StrBytes("F3 0F 11 84 8E B0 02 00 00"); //return CaveMemory.Write_jmp(OriginalProcAddress + 0x08); Logger.WriteLog("Adding P2_Axis CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - OriginalProcAddress - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, OriginalProcAddress, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(_P1_X_Value); WriteBytes(_P1_X_CaveAddress, buffer); buffer = BitConverter.GetBytes(_P1_Y_Value); WriteBytes(_P1_Y_CaveAddress, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { //Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { //Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { //Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { //Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0xFD); } } else if (PlayerData.ID == 2) { //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(_P2_X_Value); WriteBytes(_P2_X_CaveAddress, buffer); buffer = BitConverter.GetBytes(_P2_Y_Value); WriteBytes(_P2_Y_CaveAddress, buffer); // Player 2 buttons are simulated by x360kb.ini so we just send needed Keyboard strokes if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) SendKeyDown(_P2_Trigger_DIK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) SendKeyUp(_P2_Trigger_DIK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) SendKeyDown(_P2_Reload_DIK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) SendKeyUp(_P2_Reload_DIK); } } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndBlueEstate.cs ================================================ using System; using System.Diagnostics; using System.Text; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; using System.Runtime.InteropServices; using System.Collections.Generic; using DsCore.MameOutput; namespace DemulShooter { class Game_WndBlueEstate : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\bestate"; //MEMORY ADDRESSES private InjectionStruct _XinputGetState_InjectionStruct = new InjectionStruct(0x00879457, 7); // private InjectionStruct _MouseButtons_InjectionStruct = new InjectionStruct(0x00870130, 5); // private InjectionStruct _P1_Axis_InjectionStruct = new InjectionStruct(0x008F09E8, 6); // private UInt32 _P2_Axis_Patch_Offset = 0x008F0E08; // private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x008AEA80, 6); // private InjectionStruct _Damaged_InjectionStruct = new InjectionStruct(0x008B105C, 8); // private InjectionStruct _PlayerInfo_InjectionStruct = new InjectionStruct(0x008B1075, 7); private UInt32 _ForceP2InControllerSelectScreen_Offset = 0x00A29114; private UInt32 _BlockP1InControllerSelectScreen_Offset = 0x00A29171; private NopStruct _Nop_P2Axis_1 = new NopStruct(0x00A2624C, 6); private NopStruct _Nop_P2Axis_2 = new NopStruct(0x00A26284, 2); private NopStruct _Nop_P2Axis_3 = new NopStruct(0x008F0E52, 2); // private NopStruct _Nop_P1BlockButton = new NopStruct(0x00A2921B, 5); private UInt32 _P1_Buttons_CaveAddress; private UInt32 _P1_X_CaveAddress; private UInt32 _P1_Y_CaveAddress; private UInt32 _P2_Buttons_CaveAddress; private UInt32 _P2_X_CaveAddress; private UInt32 _P2_Y_CaveAddress; private UInt32 _P1_Recoil_CaveAddress; private UInt32 _P2_Recoil_CaveAddress; private UInt32 _P1_Damaged_CaveAddress; private UInt32 _P2_Damaged_CaveAddress; private UInt32 _P1_Life_CaveAddress; private UInt32 _P2_Life_CaveAddress; private UInt32 _P1_Ammo_CaveAddress; private UInt32 _P2_Ammo_CaveAddress; /// /// Constructor /// public Game_WndBlueEstate(String RomName) : base(RomName, "BEGame") { _KnownMd5Prints.Add("Blue Estate CODEX x86", "188605d4083377e4ee3552b4c89f52fb"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; Logger.WriteLog(_TargetProcess_MemoryBaseAddress.ToString("X8")); if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { /* Wait until Splash Screen is closed and real windows displayed */ /* Game Windows classname = "LaunchUnrealUWindowsClient" */ StringBuilder ClassName = new StringBuilder(256); int nRet = Win32API.GetClassName(_TargetProcess.MainWindowHandle, ClassName, ClassName.Capacity); if (nRet != 0 && ClassName.ToString() != "SplashScreenClass") { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch (Exception Ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); Logger.WriteLog(Ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { Rect TotalRes = new Rect(); Win32API.GetClientRect(_TargetProcess.MainWindowHandle, ref TotalRes); int TotalResX = TotalRes.Right - TotalRes.Left; int TotalResY = TotalRes.Bottom - TotalRes.Top; Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [-1 ; 1] float //Y => [-1 ; 1] float float X_Value = (2.0f * PlayerData.RIController.Computed_X / TotalResX) - 1.0f; float Y_Value = (2.0f * PlayerData.RIController.Computed_Y / TotalResY) - 1.0f; if (X_Value < -1.0f) X_Value = -1.0f; if (Y_Value < -1.0f) Y_Value = -1.0f; if (X_Value > 1.0f) X_Value = 1.0f; if (Y_Value > 1.0f) Y_Value = 1.0f; PlayerData.RIController.Computed_X = (int)(X_Value * 1000.0f); PlayerData.RIController.Computed_Y = (int)(Y_Value * 1000.0f); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Buttons_CaveAddress = _InputsDatabank_Address; _P1_X_CaveAddress = _InputsDatabank_Address + 0x10; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x14; _P2_Buttons_CaveAddress = _InputsDatabank_Address + 0x20; _P2_X_CaveAddress = _InputsDatabank_Address + 0x30; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x34; //Block MouseButton down/up events to remove unwanted inputs from lightgun //Use custom WM_APP message to send our inputs instead of mouse WM SetHack_MouseButtons(); //Inject custom values and force Player2 (Gamepad) XInput read SetHack_XInputGetState(); SetHack_P1Axis(); SetHack_P2Axis(); /* //Block button disabling for P1 SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P1BlockButton); //block P2 coordinates changes SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P2Axis_1); SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P2Axis_2); SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P2Axis_3); //Force use P2 controller to START P2 in controller select screen //nop + mov al,1 + nop WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _ForceP2InControllerSelectScreen_Offset), new byte[] { 0x90, 0x90, 0xB0, 0x01, 0x90, 0x90, 0x90, 0x90, 0x90 }); //Block all P1 inputs in controller select screen WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _BlockP1InControllerSelectScreen_Offset), 0xEB); */ Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Replace Float values computed by the game, by our own [-1.0, +1.0] value /// private void SetHack_P1Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp dword ptr [esi+000002B4] CaveMemory.Write_StrBytes("D9 9E B4 02 00 00"); //fld dword ptr [_P1_X_CaveAddress] CaveMemory.Write_StrBytes("D9 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_CaveAddress)); //fstp dword ptr [esi+000002B0] CaveMemory.Write_StrBytes("D9 9E B0 02 00 00"); //fld dword ptr [_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("D9 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Y_CaveAddress)); //fstp dword ptr [esi+000002B4] CaveMemory.Write_StrBytes("D9 9E B4 02 00 00"); //Inject it CaveMemory.InjectToOffset(_P1_Axis_InjectionStruct, "P1 Axis"); } /// /// Load custom values intoi XInput buffer result and replace initial call by success return /// [rsp+60] has XINPUT STRUCTURE /// [rdx+04] has BUTTONS AND TRIGGERS /// [rdx+0C] has RIGHT PAD /// private void SetHack_XInputGetState() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov edx,eax CaveMemory.Write_StrBytes("8B D0"); //mov eax,[_P2_Buttons_CaveAddress] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Buttons_CaveAddress)); //mov [edx+04],eax CaveMemory.Write_StrBytes("89 42 04"); //mov [edx+0C],00000000 CaveMemory.Write_StrBytes("C7 42 0C 00 00 00 00"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //Inject it CaveMemory.InjectToOffset(_XinputGetState_InjectionStruct, "XInput"); } /// /// Loading X or Y CaveAddress instead of original value, based on ECX /// private void SetHack_P2Axis() { //push rcx //shl rcx, 02 //mov rax, _P2_X_CaveAddress //add rax, rcx //pop rcx //movss xmm0, [rax] //push ecx //shl ecx,02 //add ecx,12345678 //movss xmm0,[ecx] //pop ecx //jmp BEGame.exe+8F0E49 //nop //nop //nop //nop WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Axis_Patch_Offset, new byte[] { 0x51, 0xC1, 0xE1, 0x02, 0x81, 0xC1 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Axis_Patch_Offset + 6, BitConverter.GetBytes(_P2_X_CaveAddress)); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Axis_Patch_Offset + 10, new byte[] { 0xF3, 0x0F, 0x10, 0x01, 0x59, 0xEB, 0x30, 0x90, 0x90, 0x90, 0x90 }); } /// /// At the start of the wndProc procedure : /// Setting MSg to 0 if it's any of WM_LBUTTONDOWN/UP WM_RBUTTONDOWN/UP /// Using WM_APP+Index Msg for custom Inputs to replace original mouse Msg /// That way, any mouse lightgun would not be controlling buttons anymore /// private void SetHack_MouseButtons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push ebx CaveMemory.Write_StrBytes("53"); //mov ebx,[esp+0C] CaveMemory.Write_StrBytes("8B 5C 24 0C"); //cmp ebx,00008000 CaveMemory.Write_StrBytes("81 FB 00 80 00 00"); //jle Check_WmButton CaveMemory.Write_StrBytes("7E 08"); //sub ebx,00007E00 CaveMemory.Write_StrBytes("81 EB 00 7E 00 00"); //jmp Exit CaveMemory.Write_StrBytes("EB 25"); //cmp ebx,00000201 CaveMemory.Write_StrBytes("81 FB 01 02 00 00"); //je Block CaveMemory.Write_StrBytes("74 18"); //cmp ebx,00000202 CaveMemory.Write_StrBytes("81 FB 02 02 00 00"); //je Block CaveMemory.Write_StrBytes("74 10"); //cmp ebx,00000204 CaveMemory.Write_StrBytes("81 FB 04 02 00 00"); //je Block CaveMemory.Write_StrBytes("74 08"); //cmp ebx,00000205 CaveMemory.Write_StrBytes("81 FB 05 02 00 00"); //jne Exit CaveMemory.Write_StrBytes("75 05"); //mov ebx,00000000 CaveMemory.Write_StrBytes("BB 00 00 00 00"); //Inject it CaveMemory.InjectToOffset(_MouseButtons_InjectionStruct, "Mouse Buttons"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_Recoil_CaveAddress = _OutputsDatabank_Address; _P2_Recoil_CaveAddress = _OutputsDatabank_Address + 0x04; _P1_Damaged_CaveAddress = _OutputsDatabank_Address + 0x08; _P2_Damaged_CaveAddress = _OutputsDatabank_Address + 0x0C; _P1_Life_CaveAddress = _OutputsDatabank_Address + 0x10; _P1_Ammo_CaveAddress = _OutputsDatabank_Address + 0x14; _P2_Life_CaveAddress = _OutputsDatabank_Address + 0x18; _P2_Ammo_CaveAddress = _OutputsDatabank_Address + 0x1C; SetHack_Recoil(); SetHack_Damage(); SetHack_PlayerInfo(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// In ABEWeaponexecHasAmmo() called before a shot : /// EDI looks like a "Weapon Struct", and Ammo value is available in [EDI+2C0] /// EDI is changing when the gun is changing /// /// Trying to get info if it's for a player (also used for ennemies) and if it's P1 or P2 /// [EDI+0x9C] is pointing to what look like to be a "player" owner struct /// [EDI+0x9C] + 0x030 => Owner ID (1 or 2) /// [EDI+0x9C] + 0x2DC => Owner Life /// To differentiate Onwer type (player / Ennemy) we can check either : /// - [EDI+0x9C] + 0x50 => 0x16 / 0x2F /// - [EDI+0x9C] + 0x94 => 00 01 03 05 / 01 01 03 07 /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov ecx,edi CaveMemory.Write_StrBytes("8B CF"); //cmp ecx,00 CaveMemory.Write_StrBytes("83 F9 00"); //je Exit CaveMemory.Write_StrBytes("74 2D"); //mov ecx,[ecx+0000009C] CaveMemory.Write_StrBytes("8B 89 9C 00 00 00"); //cmp ecx,00 CaveMemory.Write_StrBytes("83 F9 00"); //je Exit CaveMemory.Write_StrBytes("74 22"); //cmp byte ptr [ecx+50],2F CaveMemory.Write_StrBytes("80 79 50 2F"); //jne Exit CaveMemory.Write_StrBytes("75 1C"); //cmp byte ptr [ecx+30],01 CaveMemory.Write_StrBytes("80 79 30 01"); //jne Player2 CaveMemory.Write_StrBytes("75 09"); //mov [_P1_Recoil_CaveAddress], 01 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Recoil_CaveAddress)); CaveMemory.Write_StrBytes("01"); //jmp Exit CaveMemory.Write_StrBytes("EB 0D"); //Player2: //cmp dword ptr [ecx+30],02 CaveMemory.Write_StrBytes("83 79 30 02"); //jne Exit CaveMemory.Write_StrBytes("75 07"); //mov [_P2_Recoil_CaveAddress],00000001 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Recoil_CaveAddress)); CaveMemory.Write_StrBytes("01"); //Exit: //mov ecx,[esp+10] CaveMemory.Write_StrBytes("8B 4C 24 10"); //mov eax,[edi] CaveMemory.Write_StrBytes("8B 07"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// /// In ABEPlayerPawnexecPlayerPadHitEffect() called before a shot : /// EDI has the value of ECX at the start of the function, same "Player" owner structure as previously used for recoil /// [EDI+0x030] => Player ID (1 or 2) /// [EDI+0x2DC] => Player Life /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp edi,00 CaveMemory.Write_StrBytes("83 FF 00"); //je Exit CaveMemory.Write_StrBytes("74 15"); //cmp dword ptr [edi+30],02 CaveMemory.Write_StrBytes("83 7F 30 02"); //je Player2 CaveMemory.Write_StrBytes("74 07"); //mov eax,_P1_Damaged_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Damaged_CaveAddress)); //jmp SetFlag CaveMemory.Write_StrBytes("EB 05"); //Player2: //mov eax,_P2_Damaged_CaveAddress CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Damaged_CaveAddress)); //SetFlag: //mov byte ptr [eax],01 CaveMemory.Write_StrBytes("C6 00 01"); //Exit: //mov eax,[edi] CaveMemory.Write_StrBytes("8B 07"); //mov edx,[eax+000004FC] CaveMemory.Write_StrBytes("8B 90 FC 04 00 00"); //Inject it CaveMemory.InjectToOffset(_Damaged_InjectionStruct, "Damaged"); } /// /// ABEPlayerPawnexecPlayerPadUpdate() is called in a loop /// In it, we can acces the "Player" owner struct in ECX and we can access : /// [ECX+0x030] => Player ID (1 or 2) /// [ECX+0x050] => Player playing ? (0x2F) - Not playing is 0x16 /// [ECX+0x2DC] => Player Life /// [ECX+0x3C4] => Player Weapon Struct pointer /// +[ECX+0x3C4]+0x2C0 => Weapon Ammo /// private void SetHack_PlayerInfo() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp byte ptr [ecx+30],01 CaveMemory.Write_StrBytes("80 79 30 01"); //jne Player2 CaveMemory.Write_StrBytes("75 52"); //cmp byte ptr [ecx+50],2F CaveMemory.Write_StrBytes("80 79 50 2F"); //je Player1Playing CaveMemory.Write_StrBytes("74 19"); //mov [_P1_Life_CaveAddress],00000000 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Life_CaveAddress)); CaveMemory.Write_StrBytes("00 00 00 00"); //mov [_P1_Ammo_CaveAddress],00000000 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Ammo_CaveAddress)); CaveMemory.Write_StrBytes("00 00 00 00"); //jmp Exit CaveMemory.Write_StrBytes("E9 86 00 00 00"); //Player1Playing: //mov eax,[ecx+000002DC] CaveMemory.Write_StrBytes("8B 81 DC 02 00 00"); //mov [_P1_Life_CaveAddress],eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Life_CaveAddress)); //cmp dword ptr [ecx+000003C4],00 CaveMemory.Write_StrBytes("83 B9 C4 03 00 00 00"); //jne HasWeapon CaveMemory.Write_StrBytes("75 0C"); //mov [_P1_Ammo_CaveAddress],00000000 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Ammo_CaveAddress)); CaveMemory.Write_StrBytes("00 00 00 00"); //jmp Exit CaveMemory.Write_StrBytes("EB 66"); //HasWeapon: //mov eax,[ecx+000003C4] CaveMemory.Write_StrBytes("8B 81 C4 03 00 00"); //mov eax,[eax+000002C0] CaveMemory.Write_StrBytes("8B 80 C0 02 00 00"); //mov [_P1_Ammo_CaveAddress],eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Ammo_CaveAddress)); //jmp Exit CaveMemory.Write_StrBytes("EB 53"); //Player2: //cmp byte ptr [ecx+30],02 CaveMemory.Write_StrBytes("80 79 30 02"); //jne Exit CaveMemory.Write_StrBytes("75 4D"); //cmp byte ptr [ecx+50],2F CaveMemory.Write_StrBytes("80 79 50 2F"); //je Player2Playing CaveMemory.Write_StrBytes("74 16"); //mov [_P2_Life_CaveAddress],00000000 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Life_CaveAddress)); CaveMemory.Write_StrBytes("00 00 00 00"); //mov [_P2_Ammo_CaveAddress],00000000 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Ammo_CaveAddress)); CaveMemory.Write_StrBytes("00 00 00 00"); //jmp Exit CaveMemory.Write_StrBytes("EB 31"); //Player2Playing: //mov eax,[ecx+000002DC] CaveMemory.Write_StrBytes("8B 81 DC 02 00 00"); //mov [_P2_Life_CaveAddress],eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Life_CaveAddress)); //cmp dword ptr [ecx+000003C4],00 CaveMemory.Write_StrBytes("83 B9 C4 03 00 00 00"); //jne HasWeapon CaveMemory.Write_StrBytes("75 0C"); //mov [_P2_Ammo_CaveAddress],00000000 CaveMemory.Write_StrBytes("C7 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Ammo_CaveAddress)); CaveMemory.Write_StrBytes("00 00 00 00"); //jmp Exit CaveMemory.Write_StrBytes("EB 11"); //HasWeapon: //mov eax,[ecx+000003C4] CaveMemory.Write_StrBytes("8B 81 C4 03 00 00"); //mov eax,[eax+000002C0] CaveMemory.Write_StrBytes("8B 80 C0 02 00 00"); //mov [_P2_Ammo_CaveAddress],eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Ammo_CaveAddress)); //Exit: //mov esi,[esp+0C] CaveMemory.Write_StrBytes("8B 74 24 0C"); //mov eax,[esi+18] CaveMemory.Write_StrBytes("8B 46 18"); //Inject it CaveMemory.InjectToOffset(_PlayerInfo_InjectionStruct, "PlayerInfo"); } #endregion #region Inputs public override void SendInput(PlayerSettings PlayerData) { float fX = (float)PlayerData.RIController.Computed_X / 1000.0f; float fY = (float)PlayerData.RIController.Computed_Y / 1000.0f; if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, BitConverter.GetBytes(fX)); WriteBytes(_P1_Y_CaveAddress, BitConverter.GetBytes(fY)); //Sending WM_APP+Index to simulate mouse click //As the message will be translated to WM_xBUTTONDOWN/UP, Wparam must be set to according pressed button too if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x001, new IntPtr(1), new IntPtr(0)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x002, new IntPtr(0), new IntPtr(0)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x004, new IntPtr(2), new IntPtr(0)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x005, new IntPtr(0), new IntPtr(0)); } if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, BitConverter.GetBytes(fX)); WriteBytes(_P2_Y_CaveAddress, BitConverter.GetBytes(fY)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 3, 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 3, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 2, 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 2, 0xBF); } } /// /// Writing Axis and Buttons data in memory /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_Z) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 2, 0x10); } if (s.scanCode == HardwareScanCode.DIK_X) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 2, 0x40); } if (s.scanCode == HardwareScanCode.DIK_C) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 2, 0x80); } if (s.scanCode == HardwareScanCode.DIK_SPACE) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress + 3, 0xFF); } if (s.scanCode == HardwareScanCode.DIK_0) { Win32API.PostMessage(_GameWindowHandle, 0x8001, new IntPtr(1), new IntPtr(0)); } if (s.scanCode == HardwareScanCode.DIK_9) { Win32API.PostMessage(_GameWindowHandle, 0x201, new IntPtr(1), new IntPtr(0)); } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == HardwareScanCode.DIK_Z) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 2, 0xEF); } if (s.scanCode == HardwareScanCode.DIK_X) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 2, 0xBF); } if (s.scanCode == HardwareScanCode.DIK_C) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 2, 0x7F); } if (s.scanCode == HardwareScanCode.DIK_SPACE) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress + 3, 0x00); } if (s.scanCode == HardwareScanCode.DIK_0) { Win32API.PostMessage(_GameWindowHandle, 0x8002, new IntPtr(0), new IntPtr(0)); } if (s.scanCode == HardwareScanCode.DIK_9) { Win32API.PostMessage(_GameWindowHandle, 0x202, new IntPtr(0), new IntPtr(0)); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (ReadByte(_P1_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_Recoil_CaveAddress, 0x00); } if (ReadByte(_P2_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_Recoil_CaveAddress, 0x00); } if (ReadByte(_P1_Damaged_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_P1_Damaged_CaveAddress, 0x00); } if (ReadByte(_P2_Damaged_CaveAddress) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_P2_Damaged_CaveAddress, 0x00); } SetOutputValue(OutputId.P1_Life, BitConverter.ToInt32(ReadBytes(_P1_Life_CaveAddress, 4), 0)); SetOutputValue(OutputId.P2_Life, BitConverter.ToInt32(ReadBytes(_P2_Life_CaveAddress, 4), 0)); SetOutputValue(OutputId.P1_Ammo, BitConverter.ToInt32(ReadBytes(_P1_Ammo_CaveAddress, 4), 0)); SetOutputValue(OutputId.P2_Ammo, BitConverter.ToInt32(ReadBytes(_P2_Ammo_CaveAddress, 4), 0)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndBonbon95.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndBonbon95 : Game { //Memory values private UInt32 _Credits_Offset = 0x00049FE9; private UInt32 _P1_ControlType_Offset = 0x00049DE1; private UInt32 _P2_ControlType_Offset = 0x00049DE5; private UInt32 _TextureDrawingCategoryIndex_Offset = 0x0004BFB4; private InjectionStruct _P1_MouseControls_InjectionStruct = new InjectionStruct(0x00004105, 5); private InjectionStruct _P2_MouseControls_InjectionStruct = new InjectionStruct(0x00004739, 5); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x2D63A, 11); private InjectionStruct _P1_Recoil_InjectionStruct = new InjectionStruct(0x00009393, 6); private InjectionStruct _P2_Recoil_InjectionStruct = new InjectionStruct(0x00009147, 6); //Custom Values private UInt32 _P1_Buttons_CaveAddress = 0; private UInt32 _P1_X_CaveAddress = 0; private UInt32 _P1_Y_CaveAddress = 0; private UInt32 _P2_Buttons_CaveAddress = 0; private UInt32 _P2_X_CaveAddress = 0; private UInt32 _P2_Y_CaveAddress = 0; private UInt32 _P1_RecoilStatus_CaveAddress = 0; private UInt32 _P2_RecoilStatus_CaveAddress = 0; private HardwareScanCode _DIK_Credits = HardwareScanCode.DIK_5; /// /// Constructor /// public Game_WndBonbon95(String RomName) : base(RomName, "Main95") { _KnownMd5Prints.Add("BONBON v1.04 - Original exe", "cd2d16ab00750d4c2ddf010aa5402407"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-640] //Y => [0-480] double dMaxX = 640.0; double dMaxY = 480.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them. /// protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Buttons_CaveAddress = _InputsDatabank_Address; _P1_X_CaveAddress = _InputsDatabank_Address + 4; _P1_Y_CaveAddress = _InputsDatabank_Address + 8; _P2_Buttons_CaveAddress = _InputsDatabank_Address + 0xC; _P2_X_CaveAddress = _InputsDatabank_Address + 0x10; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x14; //Force the game to act as if MOUSE was choosen in the Players Controls reading function // Keyboard = 0 // Mouse = 1 // Other ? To check... WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_ControlType_Offset, 1); WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_ControlType_Offset, 1); //Create custom function to handle MOUSE controls update and call them SetHack_P1Controls(); SetHack_P2Controls(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Creating a function that will replace the existing one to update buttons + axis values /// sub_4321EA(_DWORD *a1, _DWORD *a2, _DWORD *a3) /// a1 = Buttons /// a2 = X /// a3 = Y /// private void SetHack_P1Controls() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //mov ebx, [_P1_Buttons_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Buttons_CaveAddress)); //mov eax,[esp+08] CaveMemory.Write_StrBytes("8B 44 24 08"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //mov ebx, [_P1_X_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_CaveAddress)); //mov eax,[esp+0C] CaveMemory.Write_StrBytes("8B 44 24 0C"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //mov ebx, [_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Y_CaveAddress)); //mov eax,[esp+10] CaveMemory.Write_StrBytes("8B 44 24 10"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_P1_MouseControls_InjectionStruct, "P1 Controls"); } private void SetHack_P2Controls() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //mov ebx, [_P2_Buttons_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Buttons_CaveAddress)); //mov eax,[esp+08] CaveMemory.Write_StrBytes("8B 44 24 08"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //mov ebx, [_P2_X_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_X_CaveAddress)); //mov eax,[esp+0C] CaveMemory.Write_StrBytes("8B 44 24 0C"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //mov ebx, [_P2_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 1D"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Y_CaveAddress)); //mov eax,[esp+10] CaveMemory.Write_StrBytes("8B 44 24 10"); //mov [eax],ebx CaveMemory.Write_StrBytes("89 18"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_P2_MouseControls_InjectionStruct, "P2 Controls"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_RecoilStatus_CaveAddress = _OutputsDatabank_Address; _P2_RecoilStatus_CaveAddress = _OutputsDatabank_Address + 4; SetHack_Recoil_P1(); SetHack_Recoil_P2(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercepting some kind of call to draw impact texture when trigger is pressed /// private void SetHack_Recoil_P1() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov byte ptr[_P1_RecoilStatus_CaveAddress],00000001 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_RecoilStatus_CaveAddress)); CaveMemory.Write_StrBytes("01"); //mov eax,[ebp+14] CaveMemory.Write_StrBytes("8B 45 14"); //push [eax+58] CaveMemory.Write_StrBytes("FF 70 58"); //Inject it CaveMemory.InjectToOffset(_P1_Recoil_InjectionStruct, "P1 Recoil"); } /// /// Intercepting some kind of call to draw impact texture when trigger is pressed /// private void SetHack_Recoil_P2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov byte ptr[_P1_RecoilStatus_CaveAddress],00000001 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_RecoilStatus_CaveAddress)); CaveMemory.Write_StrBytes("01"); //mov eax,[ebp+14] CaveMemory.Write_StrBytes("8B 45 14"); //push [eax+58] CaveMemory.Write_StrBytes("FF 70 58"); //Inject it CaveMemory.InjectToOffset(_P2_Recoil_InjectionStruct, "P2 Recoil"); } /// /// To remove crosshair, we can force out-of -screen corrdinates when the game is drawing the needed texture /// To find the texture, it looks like the _TextureDrawingCategoryIndex_Offset is 0x11 when the function is called to draw crosshair-related resources /// And the ECX value (Texture ID ?) is 0x17 and 0x1C for Impact texture (untouched) /// Other Id (0x192, 0x193, etc..) will be changed to not be visible /// protected override void Apply_NoCrosshairMemoryHack() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //cmp dword ptr [Main95.exe+4BFB4],11 CaveMemory.Write_StrBytes("83 3D"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _TextureDrawingCategoryIndex_Offset)); CaveMemory.Write_StrBytes("11"); //jne Originalcode CaveMemory.Write_StrBytes("75 11"); //cmp ecx, 17 CaveMemory.Write_StrBytes("83 F9 1C"); //je OriginalCode CaveMemory.Write_StrBytes("74 0C"); //cmp ecx, 1C CaveMemory.Write_StrBytes("83 F9 17"); //je OriginalCode CaveMemory.Write_StrBytes("74 07"); //Patch: //mov eax, 3000 CaveMemory.Write_StrBytes("B8 00 03 00 00"); //jmp Next CaveMemory.Write_StrBytes("EB 03"); //OriginalCode: //mov eax,[ebx+54] CaveMemory.Write_StrBytes("8B 43 54"); //mov [edi],eax CaveMemory.Write_StrBytes("89 07"); //mov eax,[ebx+58] CaveMemory.Write_StrBytes("8B 43 58"); //mov [edi+04],eax CaveMemory.Write_StrBytes("89 47 04"); //Inject it CaveMemory.InjectToOffset(_NoCrosshair_InjectionStruct, "No Crosshair"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x04); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFB); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P1_Buttons_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P1_Buttons_CaveAddress, 0xFD); } } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFE); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x04); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFB); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask(_P2_Buttons_CaveAddress, 0x02); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { Apply_AND_ByteMask(_P2_Buttons_CaveAddress, 0xFD); } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _DIK_Credits) { int Coins = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, 4), 0); Coins++; WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, BitConverter.GetBytes(Coins)); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Custom Outputs //Original recoil Handling is stripped from the DLL so we are forced to handle the duration ourselve with an Async-reset output if (ReadByte(_P1_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_P1_RecoilStatus_CaveAddress, 0); } if (ReadByte(_P2_RecoilStatus_CaveAddress) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_P2_RecoilStatus_CaveAddress, 0); } SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndBugBusters.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndBugBusters : Game { //Memory values private UInt32 _TestMenu_BtnSelect_Offset = 0x00060C58; private UInt32 _TestMenu_BtnEnter_Offset = 0x00060C5C; private UInt32 _GameMode_Offset = 0x00060BF8; private UInt32 _TestMenu_CoinsCounter_Offset = 0x000C3488; private UInt32 _P1_X_Offset = 0x0005FBA8; private UInt32 _P1_Y_Offset = 0x0005FBAC; private UInt32 _P1_Trigger_Offset = 0x0005FBB0; private UInt32 _P2_X_Offset = 0x0005FBC4; private UInt32 _P2_Y_Offset = 0x0005FBC8; private UInt32 _P2_Trigger_Offset = 0x0005FBCC; private UInt32 _P1_Life_Offset = 0x00055094; private UInt32 _P2_Life_Offset = 0x00055098; private UInt32 _P1_Ammo_Offset = 0x0005509C; private UInt32 _P2_Ammo_Offset = 0x000550A0; private UInt32 _GameCost_Offset = 0x00055090; private UInt32 _TotalCoins_Offset = 0x00060C1C; private UInt32 _P1_Lamp_Offset = 0x0009553C; private UInt32 _P2_Lamp_Offset = 0x00095540; private UInt32 _P1_Air_Offset = 0x00095418; private UInt32 _P2_Air_Offset = 0x0009541C; private UInt32 _P1_Playing_Offset = 0x00060C20; private UInt32 _P2_Playing_Offset = 0x00060C24; private HardwareScanCode _DIK_Test = HardwareScanCode.DIK_F2; private HardwareScanCode _DIK_Service = HardwareScanCode.DIK_F3; private HardwareScanCode _DIK_Credits = HardwareScanCode.DIK_5; private NopStruct _Nop_EnterKeyPress = new NopStruct(0x0000A793, 10); private NopStruct _Nop_LbuttonDown = new NopStruct(0x0000A3FF, 2); private NopStruct _Nop_LbuttonUp = new NopStruct(0x0000A425, 2); private NopStruct _Nop_RbuttonDown = new NopStruct(0x0000A44B, 2); private NopStruct _Nop_RbuttonUp = new NopStruct(0x0000A572, 2); private NopStruct _Nop_MouseMove = new NopStruct(0x0000A598, 2); private NopStruct _Nop_GamePadLoop = new NopStruct(0x00010DD9, 2); private NopStruct _Nop_HidLoopTrigger_1 = new NopStruct(0x0001139C, 10); private NopStruct _Nop_HidLoopTrigger_2 = new NopStruct(0x000113AE, 10); private NopStruct _Nop_InitCoinsCounter = new NopStruct(0x0004091F, 10); private NopStruct _Nop_ShowCrosshairInGame = new NopStruct(0x000263C1, 2); /// /// Constructor /// public Game_WndBugBusters(String RomName) : base(RomName, "BBPC") { _KnownMd5Prints.Add("Bug Buster v1.0.0.1 - Original exe", "832f2fa9ef018d390b7b477f4240ca8e"); _KnownMd5Prints.Add("Bug Buster v1.0.0.1 - NO-CD patched", "7d4195bdfbfa843cac8af85616abbc21"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-640] //Y => [0-480] double dMaxX = 640.0; double dMaxY = 480.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Genuine Hack, just blocking Axis and Triggers input to replace them. /// protected override void Apply_InputsMemoryHack() { //Removing ENTER keypress event actions from the WndProc WM_KEYDOWN SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_EnterKeyPress); //Removing Mouse events SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_LbuttonDown); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_LbuttonUp); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_RbuttonDown); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_RbuttonUp); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_MouseMove); //If a gamepad is detected, looks like it's looping and overriding values //This removes the whole loop, maybe just noping +10F5E / +10F70 is enough and less risky ? SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_GamePadLoop); //Some other kind of devices reset buttons states too SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_HidLoopTrigger_1); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_HidLoopTrigger_2); //By default, this PC-version force the coins counter to 9 when displaying the start screen. Removing it.... SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_InitCoinsCounter); if (_HideCrosshair) SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_ShowCrosshairInGame); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset, 0x00); } //To reload, set back Gaz tank capacity to 1.0f (full) if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset, new byte[] {0x00, 0x00, 0x80, 0x3F}); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { // } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset, new byte[] {0x00, 0x00, 0x80, 0x3F}); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { // } } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset, 0x00); } //To reload, set back Gaz tank capacity to 1.0f (full) if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset, new byte[] { 0x00, 0x00, 0x80, 0x3F }); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { // } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset, new byte[] { 0x00, 0x00, 0x80, 0x3F }); //1.0f } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { // } } } /// /// Low-level Keyboard hook callback. /// This is used to detect Pedal action for "Pedal-Mode" hack of DemulShooter /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _DIK_Test) { if (GetGameMode() != 12) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameMode_Offset, 12); else WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TestMenu_BtnSelect_Offset, 0x01); } else if (s.scanCode == _DIK_Service) { if (GetGameMode() == 12) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TestMenu_BtnEnter_Offset, 0x01); } else if (s.scanCode == _DIK_Credits) { if (GetGameMode() != 12) { //Add a coins in the game int Coins = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _TotalCoins_Offset, 4), 0); Coins++; WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _TotalCoins_Offset, BitConverter.GetBytes(Coins)); } else { //Write the coins entry flag, to increment coins counter WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TestMenu_CoinsCounter_Offset, 0x01); } } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == _DIK_Test) { if (GetGameMode() == 12) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TestMenu_BtnSelect_Offset, 0x00); } else if (s.scanCode == _DIK_Service) { if (GetGameMode() == 12) WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _TestMenu_BtnEnter_Offset, 0x00); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_AirFront)); _Outputs.Add(new GameOutput(OutputId.P2_AirFront)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Lamp_Offset)); SetOutputValue(OutputId.P2_LmpStart, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Lamp_Offset)); SetOutputValue(OutputId.P1_AirFront, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Air_Offset)); SetOutputValue(OutputId.P2_AirFront, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Air_Offset)); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (GetGameMode() == 5) { if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Playing_Offset) == 1) { _P1_Ammo = (int)(BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset, 4), 0) * 100); _P1_Life = (int)(BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset, 4), 0) * 100); if (_P1_Life < 0) _P1_Life = 0; //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Playing_Offset) == 1) { _P2_Ammo = (int)(BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset, 4), 0) * 100); _P2_Life = (int)(BitConverter.ToSingle(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset, 4), 0) * 100); if (_P2_Life < 0) _P2_Life = 0; //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P2_LastAmmo = _P2_Ammo; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); //Rumble created based on Air activation //Fan value can be 0 or 2 (when fire spary is activated), so for rumble we will convert it to 0/1 if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Air_Offset) != 0) SetOutputValue(OutputId.P1_GunMotor, 1); else SetOutputValue(OutputId.P1_GunMotor, 0); if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Air_Offset) != 0) SetOutputValue(OutputId.P2_GunMotor, 1); else SetOutputValue(OutputId.P2_GunMotor, 0); //Credits int Cost = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _GameCost_Offset, 4), 0); if (Cost ==0) { SetOutputValue(OutputId.Credits, 0); // FREEPLAY } else { int Coins = BitConverter.ToInt32(ReadBytes((UInt32)_TargetProcess_MemoryBaseAddress + _TotalCoins_Offset, 4), 0); SetOutputValue(OutputId.Credits, Coins / Cost); } } #endregion /// /// Return Game mode /// 0 = ? /// 1 = Title Screen /// 2 = Attract Mode /// 3 = Level select /// 4 = Result /// 5 = In Game /// 6 = /// 7 = Ranking /// 8 = How To Play /// 9 = /// 10 = Credits /// 11 = Mode Select (Story / Free) /// 12 = Settings Menu /// 13 = Initial value on boot, then goes to 1 ? /// /// private int GetGameMode() { return ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _GameMode_Offset); } } } ================================================ FILE: DemulShooter/Games/Game_WndColtWildWestShootout.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; namespace DemulShooter { class Game_WndColtWildWestShootout : Game { /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x00022030; private UInt32 _P1_Y_Offset = 0x00022034; private UInt32 _P1_Buttons_Offset = 0x00093B68; private NopStruct _Nop_X = new NopStruct(0x00009AB4, 6); private NopStruct _Nop_Y = new NopStruct(0x00009ABA, 6); private UInt32 _MouseControlMessage_Patch_Offset = 0x0000C480; InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x000071D9, 5); private UInt32 _NoGun_InstructionOffset = 0x000079F0; //Outputs private UInt32 _P1_IsDead_Offset = 0x000924BC; private UInt32 _P1_Life_Offset = 0x000924AC; private UInt32 _P1_Weapon_Offset = 0x000924D8; private UInt32 _P1_Ammo_Offset = 0x000924E0; private UInt32 _P1_CrosshairId_Offset = 0x0006AF88; private int _P1_LastWeapon = 0; /// /// Constructor /// public Game_WndColtWildWestShootout(String RomName) : base(RomName, "Wild West Shootout") { _KnownMd5Prints.Add("Colt's Wild West Shootout", "97c9e516a287aab33a455a396dadaa45"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0 ; 319] => 320 //Y => [0; 239] => 240 double dMaxX = 320.0; double dMaxY = 240.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Simple Hack : NOPing Axis procedures /// protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y); //Jump over the switch case handling MOUSE BUTTONS event WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _MouseControlMessage_Patch_Offset, new byte[] { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 }); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } protected override void Apply_NoCrosshairMemoryHack() { Apply_CrosshairMod(); Apply_GunMod(); } /// /// Crosshair ID is used to change sprite: /// 1 = Star /// 2 = Exit /// 3 = Reticle /// Changing it to 0 displays nothing, instead of the "3" /// private void Apply_CrosshairMod() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax,["Wild West Shootout.exe" + _P1_CrosshairId_Offset] CaveMemory.Write_StrBytes("8B 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_CrosshairId_Offset)); //cmp eax,03 CaveMemory.Write_StrBytes("83 F8 03"); //jne exit CaveMemory.Write_StrBytes("75 02"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //Inject it CaveMemory.InjectToOffset(_NoCrosshair_InjectionStruct, "No Crosshair"); } private void Apply_GunMod() { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _NoGun_InstructionOffset, new byte[] { 0xE9, 0x8E, 0x00, 0x00, 0x00 }); } #endregion /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0xFE); /*if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Send_VK_KeyDown(_P1_Reload_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Send_VK_KeyUp(_P1_Reload_VK);*/ if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x02); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0xFD); } } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { int P1_Clip = 0; _P1_Life = 0; _P1_Ammo = 0; int P1_CurrentWeapon = 0; if (ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_IsDead_Offset) == 0) { _P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); _P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset); //Different weapons can change max ammo to have a lesser ammo count and cause recoil P1_CurrentWeapon = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Weapon_Offset); if (P1_CurrentWeapon == _P1_LastWeapon) { //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; } //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } _P1_LastAmmo = _P1_Ammo; _P1_LastLife = _P1_Life; _P1_LastWeapon = P1_CurrentWeapon; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndFriction.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndFriction : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\friction"; /*** Memory Addresses for VSIOBOARD V1.0 Hack***/ private UInt32 _P1_X_Injection_Offset = 0x00001F5B; private UInt32 _P1_Y_Injection_Offset = 0x00001F67; private UInt32 _P2_X_Injection_Offset = 0x00002314; private UInt32 _P2_Y_Injection_Offset = 0x00002320; private UInt32 _P1_Trigger_Injection_Offset = 0x00001F00; private UInt32 _P1_Reload_Injection_Offset = 0x00001F85; private UInt32 _P2_Trigger_Injection_Offset = 0x000022B9; private UInt32 _P2_Reload_Injection_Offset = 0x0000233E; /*** Memory Addresses for VSIOBOARD V3.0 Hack***/ private UInt32 _X_Injection_Offset = 0x00002228; private UInt32 _X_Injection_Return_Offset = 0x00002230; private UInt32 _Y_Injection_Offset = 0x00002252; private UInt32 _Y_Injection_Return_Offset = 0x00002259; private UInt32 _Trigger_Injection_Offset = 0x00002269; private UInt32 _Trigger_Injection_Return_Offset = 0x000022AA; private UInt32 _Reload_Injection_Offset = 0x00002307; private UInt32 _Reload_Injection_Return_Offset = 0x00002348; /*** Common Memory Addresses ***/ private UInt32 _P1_X_CaveAddress = 0; private UInt32 _P1_Y_CaveAddress = 0; private UInt32 _P1_Trigger_CaveAddress = 0; private UInt32 _P1_Reload_CaveAddress = 0; private UInt32 _P2_X_CaveAddress = 0; private UInt32 _P2_Y_CaveAddress = 0; private UInt32 _P2_Trigger_CaveAddress = 0; private UInt32 _P2_Reload_CaveAddress = 0; private IntPtr _VsIOBoard_Module_BaseAddress = IntPtr.Zero; //Custom Outputs private int[] _LastLife = new int[] { 0, 0 }; private int[] _LastAmmo = new int[] { 0, 0 }; private int[] _Life = new int[] { 0, 0 }; private int[] _Ammo = new int[] { 0, 0 }; /// /// Constructor /// public Game_WndFriction(String RomName) : base(RomName, "Friction") { _KnownMd5Prints.Add("Friction original dump", "931146ddcac8429634401d08158b8bec"); _KnownMd5Prints.Add("Friction hacked VSIOBOARD.dll - v1.0", "9bbe8c9bf916826d6ab22703f6d83f7b"); _KnownMd5Prints.Add("Friction hacked VSIOBOARD.dll - v2.0", "a86819412ce393fdb17e6ae6dbd5bd27"); _KnownMd5Prints.Add("Friction hacked VSIOBOARD.dll - v3.0", "03c872a3d141ee186412dfc0636510bc"); _KnownMd5Prints.Add("Friction original vsIOBoard.dll", "25a0495caecaf9bee66b09f13da5e8fa"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows " + _RomName + " game to hook....."); } /// /// Timer event when looking for Demul Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; if (!_DisableInputHack) { ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("vsioboard.dll")) { _VsIOBoard_Module_BaseAddress = m.BaseAddress; if (_VsIOBoard_Module_BaseAddress != IntPtr.Zero) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog("Friction.exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8") + ", vsIOBoard.dll = 0x" + _VsIOBoard_Module_BaseAddress.ToString("X8")); String VsIoBoardDll_Path = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", "vsioboard.dll"); CheckMd5(VsIoBoardDll_Path); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } else { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog("Friction.exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch (Exception ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); Logger.WriteLog(ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => 160 - 480 ==> 321 //Y => 120 - 360 ==> 241 double dMaxX = 321.0; double dMaxY = 241.0; /*double dMinX = 160.0; double dMaxX = 480.0; double dMinY = 120.0; double dMaxY = 360.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; //Test for forced ration 1.33 (4:3) double ForcedRatio = 1.33; double GameHeight = TotalResY; double GameWidth = TotalResY * ForcedRatio; double SideOffsetPercent = ((TotalResX - GameWidth) / 2) / TotalResX; Logger.WriteLog("Game Viewport size (Px) = [ " + GameWidth + "x" + GameHeight + " ]"); Logger.WriteLog("SideBars Width (%) = " + SideOffsetPercent.ToString()); double HorizontalRatio = TotalResX / GameWidth; dRangeX = 640.0 * HorizontalRatio; dMaxX = (dRangeX / 2); dMinX = -dMaxX; Logger.WriteLog("Horizontal Ratio = " + HorizontalRatio.ToString()); Logger.WriteLog("New dMaxX = " + dMaxX.ToString()); Logger.WriteLog("New dMinX = " + dMinX.ToString());*/ PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX) + 160); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY) + 120); if (PlayerData.RIController.Computed_X < 160) PlayerData.RIController.Computed_X = 160; if (PlayerData.RIController.Computed_Y < 120) PlayerData.RIController.Computed_Y = 120; if (PlayerData.RIController.Computed_X > 480) PlayerData.RIController.Computed_X = 480; if (PlayerData.RIController.Computed_Y > 360) PlayerData.RIController.Computed_Y = 360; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P2_X_CaveAddress = _InputsDatabank_Address + 0x08; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x10; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x18; _P1_Trigger_CaveAddress = _InputsDatabank_Address + 0x20; _P2_Trigger_CaveAddress = _InputsDatabank_Address + 0x28; _P1_Reload_CaveAddress = _InputsDatabank_Address + 0x30; _P2_Reload_CaveAddress = _InputsDatabank_Address + 0x38; //VSIOBOARD.dd v1.0 has a different Hack if (_TargetProcess_Md5Hash == _KnownMd5Prints["Friction hacked VSIOBOARD.dll - v1.0"]) { SetHack_P1_X(); SetHack_P1_Y(); SetHack_P1_Trigger(); SetHack_P1_Reload(); SetHack_P2_X(); SetHack_P2_Y(); SetHack_P2_Trigger(); SetHack_P2_Reload(); } //VSIOBOARD.dll v2.0 and v3.0 are sharing the same Hack else { SetHack_X(); SetHack_Y(); SetHack_Trigger(); SetHack_Reload(); } Logger.WriteLog("Inputs Arcade Hack complete !"); Logger.WriteLog("-"); } #region VSIOBOARD V1.0 Hack /// /// Not having found a secured way to access X and Y offsets, we can't NOP /// procedures to inject custom values... /// Instead we will modify each procedure so that it will read our values instad of original ones /// private void SetHack_P1_X() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); //mov eax, [_P1_X_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //mov [esp + 9A], ax CaveMemory.Write_StrBytes("66 89 84 24 9A 00 00 00"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P1_X_Injection_Offset + 8); Logger.WriteLog("Adding P1_X Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P1_X_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P1_X_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P2_X() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P2_X_CaveAddress); //mov eax, [_P2_X_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //mov [esp + AA], ax CaveMemory.Write_StrBytes("66 89 84 24 AA 00 00 00"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P2_X_Injection_Offset + 8); Logger.WriteLog("Adding P2_X Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P2_X_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P2_X_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1_Y() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P1_Y_CaveAddress); //mov eax, [_P1_Y_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //mov [esp + 9C], ax CaveMemory.Write_StrBytes("66 89 84 24 9C 00 00 00"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P1_Y_Injection_Offset + 8); Logger.WriteLog("Adding P1_Y Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P1_Y_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P1_Y_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P2_Y() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P2_Y_CaveAddress); //mov eax, [_P2_Y_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //mov [esp + AC], ax CaveMemory.Write_StrBytes("66 89 84 24 AC 00 00 00"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P2_Y_Injection_Offset + 8); Logger.WriteLog("Adding P2_Y Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P2_Y_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P2_Y_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// For buttons, the game is calling GetAsyncKeyState() for each Buttons. /// So for Trigger and Reload Buttons, we will modify the procedure to make it read our own values /// and overwrite GetAsyncKeyState return value. /// This way, the original behaviour of autofire for machinegun should be intact. /// private void SetHack_P1_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); //mov eax, [_P1_Trigger_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //shr eax,0F CaveMemory.Write_StrBytes("C1 E8 0F"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P1_Trigger_Injection_Offset + 6); Logger.WriteLog("Adding P1_Trigger Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P1_Trigger_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P1_Trigger_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1_Reload() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P1_Reload_CaveAddress); //mov eax, [_P1_Reload_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //shr eax,0F CaveMemory.Write_StrBytes("C1 E8 0F"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P1_Reload_Injection_Offset + 6); Logger.WriteLog("Adding P1_Reload Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P1_Reload_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P1_Reload_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P2_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P2_Trigger_CaveAddress); //mov eax, [_P2_Trigger_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //shr eax,0F CaveMemory.Write_StrBytes("C1 E8 0F"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P2_Trigger_Injection_Offset + 6); Logger.WriteLog("Adding P2_Trigger Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P2_Trigger_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P2_Trigger_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P2_Reload() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; byte[] b = BitConverter.GetBytes(_P2_Reload_CaveAddress); //mov eax, [_P2_Reload_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(b); //shr eax,0F CaveMemory.Write_StrBytes("C1 E8 0F"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _P2_Reload_Injection_Offset + 6); Logger.WriteLog("Adding P2_Reload Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _P2_Reload_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _P2_Reload_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region VSIOBOARD V3.0 Hack /// /// For this game, we are hokking inside the vsIOBoard.dll, in the loop where Players axis are calculated /// We are injecting our own data after the final calculated original values. /// The loop is the same for P1 and P2, so we will use the loop index (base 4) at ESP+20 to automatically /// access our own P1 or P2 axis X data without creating a codecave for each player /// P2_X_Address = P1_X_Address + (2*4) /// private void SetHack_X() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; //comisd xmm0,xmm4 CaveMemory.Write_StrBytes("66 0F 2F C4"); //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //mov eax, _P1_X_CaveAddress byte[] b = BitConverter.GetBytes(_P1_X_CaveAddress); CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(b); //mov ebx, [esp+20] CaveMemory.Write_StrBytes("8B 5C 24 20"); //shl ebx,1 CaveMemory.Write_StrBytes("D1 E3"); //add eax, ebx CaveMemory.Write_StrBytes("01 D8"); //movsd xmm2, [eax] CaveMemory.Write_StrBytes("F2 0F 10 10"); //movsd [ecx],xmm2 CaveMemory.Write_StrBytes("F2 0F 11 11"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _X_Injection_Return_Offset); Logger.WriteLog("Adding Axis_X Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _X_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _X_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_Y() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; //push eax CaveMemory.Write_StrBytes("50"); //push ebx CaveMemory.Write_StrBytes("53"); //mov eax, _P1_Y_CaveAddress byte[] b = BitConverter.GetBytes(_P1_Y_CaveAddress); CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(b); //mov ebx, [esp+20] CaveMemory.Write_StrBytes("8B 5C 24 20"); //shl ebx,1 CaveMemory.Write_StrBytes("D1 E3"); //add eax, ebx CaveMemory.Write_StrBytes("01 D8"); //movsd xmm1, [eax] CaveMemory.Write_StrBytes("F2 0F 10 08"); //movsd [ecx],xmm2 CaveMemory.Write_StrBytes("F2 0F 11 0F"); //pop ebx CaveMemory.Write_StrBytes("5B"); //pop eax CaveMemory.Write_StrBytes("58"); //movzx eax, ax CaveMemory.Write_StrBytes("0F B7 C0"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _Y_Injection_Return_Offset); Logger.WriteLog("Adding Axis_Y Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _Y_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _Y_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// For this game, we are hokking inside the vsIOBoard.dll, in the loop Gun data is generated. /// We are injecting our codecave to force the value of InputTrigger without letting the game test if it's keyboard or mouse based /// The vsIOBoard.dll is handling autofire in it's loop so we can't simple force "trigger press value" inside the main exe. /// The loop is the same for P1 and P2, so we will use the loop index (base 4) at ESP+10 to automatically /// access our own P1 or P2 Trigger data without creating a codecave for each player /// P2_Trigger_Address = P1_Trigger_Address + (2*4) /// If Down, injecting 0x7FFF, else injecting 0x0000 private void SetHack_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; //push ebx CaveMemory.Write_StrBytes("53"); //mov eax, _P1_Trigger_CaveAddress byte[] b = BitConverter.GetBytes(_P1_Trigger_CaveAddress); CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(b); //mov ebx, [esp+10] CaveMemory.Write_StrBytes("8B 5C 24 1C"); //shl ebx,1 CaveMemory.Write_StrBytes("D1 E3"); //add eax, ebx CaveMemory.Write_StrBytes("01 D8"); //mov esi,[eax] CaveMemory.Write_StrBytes("8B 30"); //pop ebx CaveMemory.Write_StrBytes("5B"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _Trigger_Injection_Return_Offset); Logger.WriteLog("Adding Trigger Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _Trigger_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _Trigger_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Same thing as Trigger /// private void SetHack_Reload() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; //push ebx CaveMemory.Write_StrBytes("53"); //mov eax, _P1_Trigger_CaveAddress byte[] b = BitConverter.GetBytes(_P1_Reload_CaveAddress); CaveMemory.Write_StrBytes("B8"); CaveMemory.Write_Bytes(b); //mov ebx, [esp+10] CaveMemory.Write_StrBytes("8B 5C 24 1C"); //shl ebx,1 CaveMemory.Write_StrBytes("D1 E3"); //add eax, ebx CaveMemory.Write_StrBytes("01 D8"); //mov edi,[eax] CaveMemory.Write_StrBytes("8B 38"); //pop ebx CaveMemory.Write_StrBytes("5B"); CaveMemory.Write_jmp((UInt32)_VsIOBoard_Module_BaseAddress + _Reload_Injection_Return_Offset); Logger.WriteLog("Adding Reload Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_VsIOBoard_Module_BaseAddress + _Reload_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_VsIOBoard_Module_BaseAddress + _Reload_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (_TargetProcess_Md5Hash == _KnownMd5Prints["Friction hacked VSIOBOARD.dll - v1.0"]) { byte[] bufferX = BitConverter.GetBytes((Int32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int32)PlayerData.RIController.Computed_Y); //VSIOBOARD.dll v1.0 has it's own different Input handling system if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteBytes(_P1_Trigger_CaveAddress, BitConverter.GetBytes(0x00008000)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteBytes(_P1_Trigger_CaveAddress, BitConverter.GetBytes(0x00000000)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteBytes(_P1_Reload_CaveAddress, BitConverter.GetBytes(0x00008000)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteBytes(_P1_Reload_CaveAddress, BitConverter.GetBytes(0x00000000)); } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteBytes(_P2_Trigger_CaveAddress, BitConverter.GetBytes(0x00008000)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteBytes(_P2_Trigger_CaveAddress, BitConverter.GetBytes(0x00000000)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteBytes(_P2_Reload_CaveAddress, BitConverter.GetBytes(0x00008000)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteBytes(_P2_Reload_CaveAddress, BitConverter.GetBytes(0x00000000)); } } //VSIOBOARD.dll v2.0 and v3.0 are sharing the same Input handling system else { byte[] bufferX = BitConverter.GetBytes(Convert.ToDouble(PlayerData.RIController.Computed_X)); byte[] bufferY = BitConverter.GetBytes(Convert.ToDouble(PlayerData.RIController.Computed_Y)); if (PlayerData.ID == 1) { WriteBytes(_P1_X_CaveAddress, bufferX); WriteBytes(_P1_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteBytes(_P1_Trigger_CaveAddress, new byte[] { 0xFF, 0x7F }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteBytes(_P1_Trigger_CaveAddress, new byte[] { 0x00, 0x00 }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteBytes(_P1_Reload_CaveAddress, new byte[] { 0xFF, 0x7F }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteBytes(_P1_Reload_CaveAddress, new byte[] { 0x00, 0x00 }); } else if (PlayerData.ID == 2) { WriteBytes(_P2_X_CaveAddress, bufferX); WriteBytes(_P2_Y_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteBytes(_P2_Trigger_CaveAddress, new byte[] { 0xFF, 0x7F }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteBytes(_P2_Trigger_CaveAddress, new byte[] { 0x00, 0x00 }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteBytes(_P2_Reload_CaveAddress, new byte[] { 0xFF, 0x7F }); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteBytes(_P2_Reload_CaveAddress, new byte[] { 0x00, 0x00 }); } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { UInt32 _GameDataPtr = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + 0x00145088); //In this game, the engine is swapping P1/P2 data according to the 1st Player to be started //To know which data is corresponding to which player, we need to process a little bit... //When players are uninitialized, both values are set to [-1] //When a player starts a game, PlayerA get a value of [0] if it's Player1, or [1] if it's player 2. This is used later by the game //to add an offset to determine which player is playing for accessing data. //When a second player is entering the game, PlayerB gets [0] if it's player1, [1] if it's player2 byte[] Players_Offset = new byte[] { 0x00, 0x00 }; Players_Offset[0] = ReadByte(_GameDataPtr + 0x28 + 0x45C); Players_Offset[1] = ReadByte(_GameDataPtr + 0x28 + 0x460); Array.Clear(_Life, 0, _Life.Length); Array.Clear(_Ammo, 0, _Ammo.Length); int[] Clip = new int[] {0, 0}; int[] StartLmp = new int[] {-1, -1}; for (uint i = 0; i < Players_Offset.Length; i++) { if (Players_Offset[i] != 0xFF) { //We can now look at the "InGame" flag for the player //1 = OnGame, 0 = Not Playing (GameOver or Continue) if (ReadByte(_GameDataPtr + 0x28 + 0x439 + i) == 1) { //Force Start Lamp to Off StartLmp[Players_Offset[i]] = 0; _Life[Players_Offset[i]] = ReadByte(_GameDataPtr + 0x28 + 0x44C + (i * 4)); _Ammo[Players_Offset[i]] = ReadByte(ReadPtr(_GameDataPtr + 0x28 + 0x560 + (i * 4)) + 8); //Custom Recoil if (_Ammo[Players_Offset[i]] < _LastAmmo[Players_Offset[i]]) { if (Players_Offset[i] == 0) SetOutputValue(OutputId.P1_CtmRecoil, 1); else if (Players_Offset[i] == 1) SetOutputValue(OutputId.P2_CtmRecoil, 1); } //[Clip Empty] custom Output if (_Ammo[Players_Offset[i]] > 0) Clip[Players_Offset[i]] = 1; //[Damaged] custom Output if (_Life[Players_Offset[i]] < _LastLife[Players_Offset[i]]) { if (Players_Offset[i] == 0) SetOutputValue(OutputId.P1_Damaged, 1); else if (Players_Offset[i] == 1) SetOutputValue(OutputId.P2_Damaged, 1); } } } } for (int i = 0; i < 2; i++) { _LastAmmo[i] = _Ammo[i]; _LastLife[i] = _Life[i]; } SetOutputValue(OutputId.P1_CtmLmpStart, StartLmp[0]); SetOutputValue(OutputId.P2_CtmLmpStart, StartLmp[1]); SetOutputValue(OutputId.P1_Ammo, _Ammo[0]); SetOutputValue(OutputId.P2_Ammo, _Ammo[1]); SetOutputValue(OutputId.P1_Clip, Clip[0]); SetOutputValue(OutputId.P2_Clip, Clip[1]); SetOutputValue(OutputId.P1_Life, _Life[0]); SetOutputValue(OutputId.P2_Life, _Life[1]); SetOutputValue(OutputId.Credits, ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + 0x001450C4))); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndHeavyFire3Pc.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { /*** Heavy Fire 4 => Heavy Fire Shattered Spear ***/ class Game_WndHeavyFire3Pc : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\hfa"; private const String HFA_STEAM_FILENAME = "heavyfire3_final"; private const String HFA_SKIDROW_FILENAME = "heavyfire3"; /*** MEMORY ADDRESSES **/ private UInt32 _Axis_X_CaveAddress = 0; private UInt32 _Axis_Y_CaveAddress = 0; private UInt32 _Trigger_CaveAddress = 0; private UInt32 _Reload_CaveAddress = 0; private UInt32 _Grenade_CaveAddress = 0; private UInt32 _PlayerOnOff_CaveAddress = 0; private UInt32 _Mouse_Buttons_PatchOffset = 0x00116390; private UInt32 _NoGun_Patch_Offset = 0x000D7592; private NopStruct _Nop_MouseCursorWhenGetFocus = new NopStruct(0x001156E0, 8); private NopStruct _Nop_MouseCursorWhenLooseFocus = new NopStruct(0x001157FA, 8); private InjectionStruct _PlayersOnOff_InjectionStruct = new InjectionStruct(0x0001E634, 6); private InjectionStruct _Mouse_Buttons_InjectionStruct = new InjectionStruct(0x0001E82A, 6); private InjectionStruct _Mouse_X_InjectionStruct = new InjectionStruct(0x0001E85F, 7); private InjectionStruct _Mouse_Y_InjectionStruct = new InjectionStruct(0x0001E87C, 7); private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x00E8045, 5); private InjectionStruct _Ammo_InjectionStruct = new InjectionStruct(0x000C5293, 8); private InjectionStruct _Damage_InjectionStruct = new InjectionStruct(0x000C1C7E, 7); private InjectionStruct _GamePlaying_InjectionStruct = new InjectionStruct(0x00108AE9, 7); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x000AF009, 8); private NopStruct _Nop_SteamForceMouseMainController = new NopStruct(0x0001E002, 6); //Outputs private UInt32 _GamePlaying_CaveAddress = 0; private UInt32 _Recoil_CaveAddress = 0; //4 bytes : P1~P4 private UInt32 _Damage_CaveAddress = 0; //4 bytes : P1~P4 private UInt32 _Ammo_CaveAddress = 0; //16 bytes : P1~P4 //custom Keys private HardwareScanCode _P1_OnOff_Key = HardwareScanCode.DIK_1; private HardwareScanCode _P2_OnOff_Key = HardwareScanCode.DIK_2; private HardwareScanCode _P3_OnOff_Key = HardwareScanCode.DIK_3; private HardwareScanCode _P4_OnOff_Key = HardwareScanCode.DIK_4; //Keys to send //For Player 1, hardcoded by the game : //Cover Left = A //Cover Bottom = S //Cover Right = D //QTE = Space private VirtualKeyCode _P1_QTE_W_VK = VirtualKeyCode.VK_W; private VirtualKeyCode _P1_CoverLeft_VK = VirtualKeyCode.VK_A; private VirtualKeyCode _P1_CoverRight_VK = VirtualKeyCode.VK_D; private VirtualKeyCode _P1_CoverBottom_VK = VirtualKeyCode.VK_S; private VirtualKeyCode _P1_QTE_Space_VK = VirtualKeyCode.VK_SPACE; protected float _Axis_X_Min; protected float _Axis_X_Max; protected bool _Reversecover = false; protected float _CoverDelta = 0.3f; protected bool _CoverLeftEnabled = false; protected bool _CoverBottomEnabled = false; protected bool _CoverRightEnabled = false; protected bool _QTE_Spacebar_Enabled = false; protected bool _QTE_W_Enabled = false; /// /// Constructor /// public Game_WndHeavyFire3Pc(String RomName) : base(RomName, "hf4") { _KnownMd5Prints.Add("Heavy Fire 3 - SKIDROW", "3f49951ae8232817a91ef5503374d6b3"); _KnownMd5Prints.Add("Heavy Fire 3 - STEAM", "1841e571286acb17a98546a439c08e57"); _KnownMd5Prints.Add("Heavy Fire 3 - Unknown #1", "4344f54b91e7a8ef20042d5f0b6e04aa"); _Reversecover = Configurator.GetInstance().HF_ReverseCover; _CoverDelta = (float)Configurator.GetInstance().HF_CoverSensibility / 10.0f; Logger.WriteLog("Setting Cover delta to screen border to " + _CoverDelta.ToString()); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); if (_HideGuns) Apply_NoGunsMemoryHack(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Y => [-1 ; 1] float //X => depend on ration Width/Height (ex : [-1.7777; 1.7777] with 1920x1080) float fRatio = (float)TotalResX / (float)TotalResY; _Axis_X_Min = -fRatio; _Axis_X_Max = fRatio; float Y_Value = (2.0f * PlayerData.RIController.Computed_Y / (float)TotalResY) - 1.0f; float X_Value = (fRatio * 2 * PlayerData.RIController.Computed_X / (float)TotalResX) - fRatio; if (X_Value < _Axis_X_Min) X_Value = _Axis_X_Min; if (Y_Value < -1.0f) Y_Value = -1.0f; if (X_Value > _Axis_X_Max) X_Value = _Axis_X_Max; if (Y_Value > 1.0f) Y_Value = 1.0f; Logger.WriteLog("Computed float values = [ " + X_Value + "x" + Y_Value + " ]"); //Store data in [0-1000] range to store as Int and divise later and get float value PlayerData.RIController.Computed_X = Convert.ToInt16(X_Value * 1000); PlayerData.RIController.Computed_Y = Convert.ToInt16(Y_Value * 1000); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _Axis_X_CaveAddress = _InputsDatabank_Address; _Axis_Y_CaveAddress = _InputsDatabank_Address + 0x10; _Trigger_CaveAddress = _InputsDatabank_Address + 0x20; _Reload_CaveAddress = _InputsDatabank_Address + 0x24; _Grenade_CaveAddress = _InputsDatabank_Address + 0x28; _PlayerOnOff_CaveAddress = _InputsDatabank_Address + 0x30; //Game is changing windows cursor position en enter/quit window focus, causing trouble to debug with a mouse as a lightgun SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_MouseCursorWhenGetFocus); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_MouseCursorWhenLooseFocus); //STEAM version and ISO version does not have the same controller procedure, so the patch will be different //to enable/disable players dynamically if (_Target_Process_Name.ToLower().Equals(HFA_SKIDROW_FILENAME)) { SetHack_EnablePlayers_Skidrow(); } else if (_Target_Process_Name.ToLower().Equals(HFA_STEAM_FILENAME)) { //Steam game has a setting to force mouse as main controller if a gamepad is plugged (or not) //Forcing check to act as if the setting is 1 SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_SteamForceMouseMainController); SetHack_EnablePlayers_Steam(); } SetHack_Mouse_Buttons(); SetHack_Mouse_X(); SetHack_Mouse_Y(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The game constantly sets Players controllers to either 0(OFF), 1(PAD) or 4(MOUSE) /// Forcing it to be our own value allows us to enable disable P2~P4 as we want /// private void SetHack_EnablePlayers_Skidrow() { //First we need to deactivate original set WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersOnOff_InjectionStruct.InjectionOffset + 0x0C, new byte[] { 0xEB, 0x12 }); List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,esi CaveMemory.Write_StrBytes("8B C6"); //add eax,_PlayerOnOff_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_PlayerOnOff_CaveAddress + 1)); //Only looping for P2~P4 (not P1) //mov al,[eax] CaveMemory.Write_StrBytes("8A 00"); //mov [ebx],al CaveMemory.Write_StrBytes("88 03"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_PlayersOnOff_InjectionStruct, "Players ON/OFF (Skidrow)"); } /// /// The game constantly sets Players controllers to either 0(OFF), 1(PAD) or 4(MOUSE) /// Forcing it to be our own value allows us to enable disable P2~P4 as we want /// private void SetHack_EnablePlayers_Steam() { //First we need to force some check WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersOnOff_InjectionStruct.InjectionOffset - 0x35, new byte[] { 0xEB, 0x2F }); //And force the byte to be stored //mov [edi], al WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersOnOff_InjectionStruct.InjectionOffset + 0x0A, new byte[] { 0x88, 0x07, 0x90, 0x90, 0x90, 0x90 }); List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,esi CaveMemory.Write_StrBytes("8B C6"); //add eax,_PlayerOnOff_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_PlayerOnOff_CaveAddress + 1)); //Only looping for P2~P4 (not P1) //mov al,[eax] CaveMemory.Write_StrBytes("8A 00"); //cmp [esi*4+hf4.exe+4234D4],ebp CaveMemory.Write_StrBytes("83 3C B5 D4 34 62 00 01"); //Inject it CaveMemory.InjectToOffset(_PlayersOnOff_InjectionStruct, "Players ON/OFF (Steam)"); } /// /// Overwriting results when the game is checking buttons one after the other /// also possible to change keys and/or override keyboard input if needed /// private void SetHack_Mouse_Buttons() { //First set to 0 any mouse input when the game is reading them WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Mouse_Buttons_PatchOffset, new byte[] { 0x31, 0xC0, 0xC3 }); //Then overriding with our own values List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov [esi+0000009B],al CaveMemory.Write_StrBytes("88 86 9B 00 00 00"); //push ebx CaveMemory.Write_StrBytes("53"); //mov ebx,[esp+04] //Player Id CaveMemory.Write_StrBytes("8B 5C 24 04"); //add ebx,_P1_Trigger_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Trigger_CaveAddress)); //mov al,[ebx] CaveMemory.Write_StrBytes("8A 03"); //mov [esi+00000080],al CaveMemory.Write_StrBytes("88 86 80 00 00 00"); //mov [esi+00000086],al CaveMemory.Write_StrBytes("88 86 86 00 00 00"); //add ebx,04 CaveMemory.Write_StrBytes("83 C3 04"); //Reload byte is +4 after Trigger cave address //mov al,[ebx] CaveMemory.Write_StrBytes("8A 03"); //mov [esi+00000083],al CaveMemory.Write_StrBytes("88 86 83 00 00 00"); //mov ebx,[esp+04] CaveMemory.Write_StrBytes("8B 5C 24 04"); //Overiding Grenade command only for P2~P4 if P1 needs middle click cover //Overwise, Overriding Grenade for all players if (!Configurator.GetInstance().HF_UseMiddleButtonAsGrenade) { //test ebx,ebx CaveMemory.Write_StrBytes("85 DB"); //je exit CaveMemory.Write_StrBytes("74 0E"); } else { //Do nothing CaveMemory.Write_StrBytes("90 90 90 90"); } //add ebx,_Grenade_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Grenade_CaveAddress)); //mov al,[ebx] CaveMemory.Write_StrBytes("8A 03"); //mov [esi+00000082],al CaveMemory.Write_StrBytes("88 86 82 00 00 00"); //Exit: //pop ebx CaveMemory.Write_StrBytes("5B"); //Inject it CaveMemory.InjectToOffset(_Mouse_Buttons_InjectionStruct, "P1 Buttons"); } /// /// All Axis codecave are the same : /// The game use some fstp [XXX] instruction, but we can't just NOP it as graphical glitches may appear. /// So we just add another set of instructions instruction immediatelly after to change the register /// to our own desired value /// private void SetHack_Mouse_X() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp [esi+edi*8+000000F0] CaveMemory.Write_StrBytes("D9 9C FE F0 00 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,edi CaveMemory.Write_StrBytes("8B C7"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax,_Axis_X_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_X_CaveAddress)); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //mov [esi+edi*8+000000F0], eax CaveMemory.Write_StrBytes("89 84 FE F0 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Mouse_X_InjectionStruct, "P1 X Axis"); } private void SetHack_Mouse_Y() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp [esi+edi*8+000000F4] CaveMemory.Write_StrBytes("D9 9C FE F4 00 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,edi CaveMemory.Write_StrBytes("8B C7"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax,_Axis_Y_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_Y_CaveAddress)); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //mov [esi+edi*8+000000F4], eax CaveMemory.Write_StrBytes("89 84 FE F4 00 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Mouse_Y_InjectionStruct, "P1 Y Axis"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _GamePlaying_CaveAddress = _OutputsDatabank_Address; _Recoil_CaveAddress = _OutputsDatabank_Address + 0x04; _Damage_CaveAddress = _OutputsDatabank_Address + 0x08; _Ammo_CaveAddress = _OutputsDatabank_Address + 0x10; SetHack_Recoil(); SetHack_Ammo(); SetHack_IsPlaying(); SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// This code is run constantly and sets some 0/1 flag we can intercept for our own use /// private void SetHack_IsPlaying() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov [_P1_Playing_CaveAddress],al CaveMemory.Write_StrBytes("A2"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_GamePlaying_CaveAddress)); //mov ecx,[esp+00000450] CaveMemory.Write_StrBytes("8B 8C 24 50 04 00 00"); //Inject it CaveMemory.InjectToOffset(_GamePlaying_InjectionStruct, "Player Playing"); } /// /// Set a flag when the game calls for a shooting procedure /// private void SetHack_Recoil() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov edi,esi CaveMemory.Write_StrBytes("8B FE"); //add edi,_Recoil_CaveAddress CaveMemory.Write_StrBytes("81 C7"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); //mov byte ptr[edi], 01 CaveMemory.Write_StrBytes("C6 07 01"); //mov edi,[esp+18] CaveMemory.Write_StrBytes("8B 7C 24 18"); //push esi CaveMemory.Write_StrBytes("56"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// This function is called in a loop while the player is playing /// We can get Ammo status in it /// private void SetHack_Ammo() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //push edx CaveMemory.Write_StrBytes("52"); //mov edx,[eax+esi+0000051C] CaveMemory.Write_StrBytes("8B 94 30 1C 05 00 00"); //mov eax,ebx CaveMemory.Write_StrBytes("8B C3"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax,_Ammo_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Ammo_CaveAddress)); //mov [eax],edx CaveMemory.Write_StrBytes("89 10"); //pop edx CaveMemory.Write_StrBytes("5A"); //pop eax CaveMemory.Write_StrBytes("58"); //cmp dword ptr [eax+esi+0000051C],00 CaveMemory.Write_StrBytes("83 BC 30 1C 05 00 00 00"); //Inject it CaveMemory.InjectToOffset(_Ammo_InjectionStruct, "Ammo"); } /// /// That code is called when the game needs to increment the hit counter of the player, and also run the "hit_00" soune effect ? /// We can get the player ID in ESP+24 after pushing EAX /// private void SetHack_Damage() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[esp+24] CaveMemory.Write_StrBytes("8B 44 24 24"); //add eax,_Damage_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Damage_CaveAddress)); //mov byte ptr[eax],01 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //add [ebx+eax+00000524],ecx CaveMemory.Write_StrBytes("01 8C 03 24 05 00 00"); //Inject it CaveMemory.InjectToOffset(_Damage_InjectionStruct, "Damage"); } /// /// Changing X/Y values to -10.0 for every elements in the HUD (crosshair, reload, etc...) /// protected override void Apply_NoCrosshairMemoryHack() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fst qword ptr [esp+44] CaveMemory.Write_StrBytes("DD 54 24 44"); //mov [esp+50],C1200000 CaveMemory.Write_StrBytes("C7 44 24 50 00 00 20 C1"); //mov [esp+4C],C1200000 CaveMemory.Write_StrBytes("C7 44 24 4C 00 00 20 C1"); //fsubr dword ptr [esp+50] CaveMemory.Write_StrBytes("D8 6C 24 50"); //Inject it CaveMemory.InjectToOffset(_NoCrosshair_InjectionStruct, "No Crosshair"); } /// /// Game is displaying gun on screen if less than 2 players, forcing it to hide /// private void Apply_NoGunsMemoryHack() { WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _NoGun_Patch_Offset, 0xEB); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { float fX = (float)PlayerData.RIController.Computed_X / 1000.0f; float fY = (float)PlayerData.RIController.Computed_Y / 1000.0f; int PlayerIndex = (PlayerData.ID - 1) * 4; //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(fX); WriteBytes(_Axis_X_CaveAddress + (uint)PlayerIndex, buffer); buffer = BitConverter.GetBytes(fY); WriteBytes(_Axis_Y_CaveAddress + (uint)PlayerIndex, buffer); if (PlayerData.ID == 1) { if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_Trigger_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_Reload_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_Reload_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if (Configurator.GetInstance().HF_UseMiddleButtonAsGrenade) { WriteByte(_Grenade_CaveAddress, 0x01); } else { //If the player is aiming on the left side of the screen before pressing the button //=> Cover Left if ((fX < _Axis_X_Min + _CoverDelta) && !_Reversecover) { Send_VK_KeyDown(_P1_CoverLeft_VK); _CoverLeftEnabled = true; } //If the player is aiming on the right side of the screen before pressing the button //=> Cover Left else if ((fX > _Axis_X_Max - _CoverDelta) && _Reversecover) { Send_VK_KeyDown(_P1_CoverLeft_VK); _CoverLeftEnabled = true; } //If the player is aiming on the right side of the screen before pressing the button //=> Cover Right else if ((fX > _Axis_X_Max - _CoverDelta) && !_Reversecover) { Send_VK_KeyDown(_P1_CoverRight_VK); _CoverRightEnabled = true; } //If the player is aiming on the left side of the screen before pressing the button //=> Cover Right else if ((fX < _Axis_X_Min + _CoverDelta) && _Reversecover) { Send_VK_KeyDown(_P1_CoverRight_VK); _CoverRightEnabled = true; } //If the player is aiming on the bottom side of the screen before pressing the button //=> Cover Down else if (fY > (1.0f - _CoverDelta)) { Send_VK_KeyDown(_P1_CoverBottom_VK); _CoverBottomEnabled = true; } //If the player is aiming on the top side of the screen before pressing the button //=> W [QTE] else if (fY < (-1.0f + _CoverDelta)) { Send_VK_KeyDown(_P1_QTE_W_VK); _QTE_W_Enabled = true; } //If nothing above //=> Spacebar [QTE] else { Send_VK_KeyDown(_P1_QTE_Space_VK); _QTE_Spacebar_Enabled = true; } } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { if (Configurator.GetInstance().HF_UseMiddleButtonAsGrenade) { WriteByte(_Grenade_CaveAddress, 0x00); } else { if (_CoverLeftEnabled) { Send_VK_KeyUp(_P1_CoverLeft_VK); _CoverLeftEnabled = false; } if (_CoverBottomEnabled) { Send_VK_KeyUp(_P1_CoverBottom_VK); _CoverBottomEnabled = false; } if (_CoverRightEnabled) { Send_VK_KeyUp(_P1_CoverRight_VK); _CoverRightEnabled = false; } if (_QTE_W_Enabled) { Send_VK_KeyUp(_P1_QTE_W_VK); _QTE_W_Enabled = false; } if (_QTE_Spacebar_Enabled) { Send_VK_KeyUp(_P1_QTE_Space_VK); _QTE_Spacebar_Enabled = false; } } } } else { if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_Trigger_CaveAddress + (uint)PlayerData.ID - 1, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_Trigger_CaveAddress + (uint)PlayerData.ID - 1, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(_Grenade_CaveAddress + (uint)PlayerData.ID - 1, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(_Grenade_CaveAddress + (uint)PlayerData.ID - 1, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_Reload_CaveAddress + (uint)PlayerData.ID - 1, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_Reload_CaveAddress + (uint)PlayerData.ID - 1, 0x00); } } } /// /// Low-level Keyboard hook callback. /// Using those custom keys, we can force players to be created (4=mouse controller) or not in the game /// Setting value to 1 ==> Auto validate popup to select profile, but need some patch to force the game to compute mouse procedure /// Setting value to 4 ==> Need validate popup to select profile (genuine way ??) /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _P1_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress) != 0) WriteByte(_PlayerOnOff_CaveAddress, 0x00); else WriteByte(_PlayerOnOff_CaveAddress, 0x04); } if (s.scanCode == _P2_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress + 0x01) != 0) WriteByte(_PlayerOnOff_CaveAddress + 0x01, 0x00); else WriteByte(_PlayerOnOff_CaveAddress + 0x01, 0x04); } if (s.scanCode == _P3_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress + 0x02) != 0) WriteByte(_PlayerOnOff_CaveAddress + 0x02, 0x00); else WriteByte(_PlayerOnOff_CaveAddress + 0x02, 0x04); } if (s.scanCode == _P4_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress + 0x03) != 0) WriteByte(_PlayerOnOff_CaveAddress + 0x03, 0x00); else WriteByte(_PlayerOnOff_CaveAddress + 0x03, 0x04); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P3_Ammo)); _Outputs.Add(new GameOutput(OutputId.P4_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new GameOutput(OutputId.P3_Clip)); _Outputs.Add(new GameOutput(OutputId.P4_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { _P1_Ammo = 0; _P2_Ammo = 0; _P3_Ammo = 0; _P4_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; int P3_Clip = 0; int P4_Clip = 0; if (ReadByte(_GamePlaying_CaveAddress) == 1) { //Recoil if (ReadByte(_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress, 0); } if (ReadByte(_Recoil_CaveAddress + 0x01) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 0x01, 0); } if (ReadByte(_Recoil_CaveAddress + 0x02) == 1) { SetOutputValue(OutputId.P3_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 0x02, 0); } if (ReadByte(_Recoil_CaveAddress + 0x03) == 1) { SetOutputValue(OutputId.P4_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 0x03, 0); } //Ammo + Clip _P1_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress, 4), 0); if (_P1_Ammo == -1) _P1_Ammo = 99; if (_P1_Ammo > 0) P1_Clip = 1; _P2_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress + 0x04, 4), 0); if (_P2_Ammo == -1) _P2_Ammo = 99; if (_P2_Ammo > 0) P2_Clip = 1; _P3_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress + 0x08, 4), 0); if (_P3_Ammo == -1) _P3_Ammo = 99; if (_P3_Ammo > 0) P3_Clip = 1; _P4_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress + 0x0C, 4), 0); if (_P4_Ammo == -1) _P4_Ammo = 99; if (_P4_Ammo > 0) P4_Clip = 1; //Damage if (ReadByte(_Damage_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_Damage_CaveAddress, 0); } if (ReadByte(_Damage_CaveAddress + 0x01) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_Damage_CaveAddress + 0x01, 0); } if (ReadByte(_Damage_CaveAddress + 0x02) == 1) { SetOutputValue(OutputId.P3_Damaged, 1); WriteByte(_Damage_CaveAddress + 0x02, 0); } if (ReadByte(_Damage_CaveAddress + 0x03) == 1) { SetOutputValue(OutputId.P4_Damaged, 1); WriteByte(_Damage_CaveAddress + 0x03, 0); } } SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P3_Ammo, _P3_Ammo); SetOutputValue(OutputId.P4_Ammo, _P4_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P3_Clip, P3_Clip); SetOutputValue(OutputId.P4_Clip, P4_Clip); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndHeavyFire4Pc.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { /*** Heavy Fire 4 => Heavy Fire Shattered Spear ***/ class Game_WndHeavyFire4Pc : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\hfss"; private const String HFA_STEAM_FILENAME = "hf4"; private const String HFA_SKIDROW_FILENAME = "heavyfire4"; /*** MEMORY ADDRESSES **/ private UInt32 _Axis_X_CaveAddress = 0; private UInt32 _Axis_Y_CaveAddress = 0; private UInt32 _Trigger_CaveAddress = 0; private UInt32 _Reload_CaveAddress = 0; private UInt32 _Grenade_CaveAddress = 0; private UInt32 _PlayerOnOff_CaveAddress = 0; private UInt32 _Mouse_Buttons_PatchOffset = 0x0013D7D0; private UInt32 _NoGun_Patch_Offset = 0x000A953F; private NopStruct _Nop_MouseCursorWhenGetFocus = new NopStruct(0x0013E44A, 8); private NopStruct _Nop_MouseCursorWhenLooseFocus = new NopStruct(0x0013E576, 8); private InjectionStruct _PlayersOnOff_InjectionStruct = new InjectionStruct(0x0002165D, 7); private InjectionStruct _Mouse_Buttons_InjectionStruct = new InjectionStruct(0x000218B0, 6); private InjectionStruct _Mouse_X_InjectionStruct = new InjectionStruct(0x000218DF, 7); private InjectionStruct _Mouse_Y_InjectionStruct = new InjectionStruct(0x000218FC, 7); private InjectionStruct _Gamepad_X_InjectionStruct = new InjectionStruct(0x000238B2, 7); private InjectionStruct _Gamepad_Y_InjectionStruct = new InjectionStruct(0x000238CB, 7); private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x0011AD4F, 6); private InjectionStruct _Ammo_InjectionStruct = new InjectionStruct(0x0011A8EE, 5); private InjectionStruct _Damage_InjectionStruct = new InjectionStruct(0x000C081A, 8); private InjectionStruct _GamePlaying_InjectionStruct = new InjectionStruct(0x0012A090, 7); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x000C3BB7, 7); private NopStruct _Nop_SteamForceMouseMainController = new NopStruct(0x000215F5, 6); //Outputs private UInt32 _GamePlaying_CaveAddress = 0; private UInt32 _Recoil_CaveAddress = 0; //4 bytes : P1~P4 private UInt32 _Damage_CaveAddress = 0; //4 bytes : P1~P4 private UInt32 _Ammo_CaveAddress = 0; //16 bytes : P1~P4 //custom Keys private HardwareScanCode _P1_OnOff_Key = HardwareScanCode.DIK_1; private HardwareScanCode _P2_OnOff_Key = HardwareScanCode.DIK_2; private HardwareScanCode _P3_OnOff_Key = HardwareScanCode.DIK_3; private HardwareScanCode _P4_OnOff_Key = HardwareScanCode.DIK_4; //Keys to send //For Player 1, hardcoded by the game : //Cover Left = A //Cover Bottom = S //Cover Right = D //QTE = Space private VirtualKeyCode _P1_QTE_W_VK = VirtualKeyCode.VK_W; private VirtualKeyCode _P1_CoverLeft_VK = VirtualKeyCode.VK_A; private VirtualKeyCode _P1_CoverRight_VK = VirtualKeyCode.VK_D; private VirtualKeyCode _P1_CoverBottom_VK = VirtualKeyCode.VK_S; private VirtualKeyCode _P1_QTE_Space_VK = VirtualKeyCode.VK_SPACE; protected float _Axis_X_Min; protected float _Axis_X_Max; protected bool _Reversecover = false; protected float _CoverDelta = 0.3f; protected bool _CoverLeftEnabled = false; protected bool _CoverBottomEnabled = false; protected bool _CoverRightEnabled = false; protected bool _QTE_Spacebar_Enabled = false; protected bool _QTE_W_Enabled = false; /// /// Constructor /// public Game_WndHeavyFire4Pc(String RomName) : base(RomName, "hf4") { _KnownMd5Prints.Add("Heavy Fire 4 - SKIDROW", "9476f9bba48aea6ca04d06158be07f1c"); _KnownMd5Prints.Add("Heavy Fire 4 - STEAM", "7f8bf20aaba80ac1239efc553d94a53f"); _Reversecover = Configurator.GetInstance().HF_ReverseCover; _CoverDelta = (float)Configurator.GetInstance().HF_CoverSensibility / 10.0f; Logger.WriteLog("Setting Cover delta to screen border to " + _CoverDelta.ToString()); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); if (_HideGuns) Apply_NoGunsMemoryHack(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Y => [-1 ; 1] float //X => depend on ration Width/Height (ex : [-1.7777; 1.7777] with 1920x1080) float fRatio = (float)TotalResX / (float)TotalResY; _Axis_X_Min = -fRatio; _Axis_X_Max = fRatio; float Y_Value = (2.0f * PlayerData.RIController.Computed_Y / (float)TotalResY) - 1.0f; float X_Value = (fRatio * 2 * PlayerData.RIController.Computed_X / (float)TotalResX) - fRatio; if (X_Value < _Axis_X_Min) X_Value = _Axis_X_Min; if (Y_Value < -1.0f) Y_Value = -1.0f; if (X_Value > _Axis_X_Max) X_Value = _Axis_X_Max; if (Y_Value > 1.0f) Y_Value = 1.0f; Logger.WriteLog("Computed float values = [ " + X_Value + "x" + Y_Value + " ]"); //Store data in [0-1000] range to store as Int and divise later and get float value PlayerData.RIController.Computed_X = Convert.ToInt16(X_Value * 1000); PlayerData.RIController.Computed_Y = Convert.ToInt16(Y_Value * 1000); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _Axis_X_CaveAddress = _InputsDatabank_Address; _Axis_Y_CaveAddress = _InputsDatabank_Address + 0x10; _Trigger_CaveAddress = _InputsDatabank_Address + 0x20; _Reload_CaveAddress = _InputsDatabank_Address + 0x24; _Grenade_CaveAddress = _InputsDatabank_Address + 0x28; _PlayerOnOff_CaveAddress = _InputsDatabank_Address + 0x30; //Game is changing windows cursor position en enter/quit window focus, causing trouble to debug with a mouse as a lightgun SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_MouseCursorWhenGetFocus); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_MouseCursorWhenLooseFocus); //STEAM version and ISO version does not have the same controller procedure, so the patch will be different //to enable/disable players dynamically if (_Target_Process_Name.ToLower().Equals(HFA_SKIDROW_FILENAME)) { SetHack_EnablePlayers_Skidrow(); } else if (_Target_Process_Name.ToLower().Equals(HFA_STEAM_FILENAME)) { //Steam game has a setting to force mouse as main controller if a gamepad is plugged (or not) //Forcing check to act as if the setting is 1 SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_SteamForceMouseMainController); SetHack_EnablePlayers_Steam(); } SetHack_Mouse_Buttons(); SetHack_Mouse_X(); SetHack_Mouse_Y(); //Not used anymore as all players will be redirected to the Mouse functions /*SetHack_Gamepad_X(); SetHack_Gamepad_Y();*/ Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The game constantly sets Players controllers to either 0(OFF), 1(PAD) or 4(MOUSE) /// Forcing it to be our own value allows us to enable disable P2~P4 as we want /// private void SetHack_EnablePlayers_Skidrow() { //First we need to deactivate original set WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersOnOff_InjectionStruct.InjectionOffset + 0x0C, new byte[] { 0xEB, 0x12 }); List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,esi CaveMemory.Write_StrBytes("8B C6"); //add eax,_PlayerOnOff_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_PlayerOnOff_CaveAddress + 1)); //Only looping for P2~P4 (not P1) //mov al,[eax] CaveMemory.Write_StrBytes("8A 00"); //mov [edi],al CaveMemory.Write_StrBytes("88 07"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_PlayersOnOff_InjectionStruct, "Players ON/OFF (Skidrow)"); } /// /// The game constantly sets Players controllers to either 0(OFF), 1(PAD) or 4(MOUSE) /// Forcing it to be our own value allows us to enable disable P2~P4 as we want /// private void SetHack_EnablePlayers_Steam() { //First we need to force some check WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersOnOff_InjectionStruct.InjectionOffset - 0x31, new byte[] { 0xEB, 0x2F }); //And force the byte to be stored //mov [edi], al WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _PlayersOnOff_InjectionStruct.InjectionOffset + 0x09, new byte[] { 0x88, 0x07 }); List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov eax,esi CaveMemory.Write_StrBytes("8B C6"); //add eax,_PlayerOnOff_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_PlayerOnOff_CaveAddress + 1)); //Only looping for P2~P4 (not P1) //mov al,[eax] CaveMemory.Write_StrBytes("8A 00"); //cmp [esi*4+hf4.exe+264660],ebp CaveMemory.Write_StrBytes("39 2C B5 60 46 66 00"); //Inject it CaveMemory.InjectToOffset(_PlayersOnOff_InjectionStruct, "Players ON/OFF (Steam)"); } /// /// Overwriting results when the game is checking buttons one after the other /// also possible to change keys and/or override keyboard input if needed /// private void SetHack_Mouse_Buttons() { //First set to 0 any mouse input when the game is reading them WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _Mouse_Buttons_PatchOffset, new byte[] { 0x31, 0xC0, 0xC3 }); //Then overriding with our own values List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov [esi+000000B3],al CaveMemory.Write_StrBytes("88 86 B3 00 00 00"); //push ebx CaveMemory.Write_StrBytes("53"); //mov ebx,[esp+14] //Player Id CaveMemory.Write_StrBytes("8B 5C 24 14"); //add ebx,_P1_Trigger_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Trigger_CaveAddress)); //mov al,[ebx] CaveMemory.Write_StrBytes("8A 03"); //mov [esi+00000094],al CaveMemory.Write_StrBytes("88 86 94 00 00 00"); //mov [esi+0000009A],al CaveMemory.Write_StrBytes("88 86 9A 00 00 00"); //add ebx,04 CaveMemory.Write_StrBytes("83 C3 04"); //Reload byte is +4 after Trigger cave address //mov al,[ebx] CaveMemory.Write_StrBytes("8A 03"); //mov [esi+00000097],al CaveMemory.Write_StrBytes("88 86 97 00 00 00"); //mov ebx,[esp+14] CaveMemory.Write_StrBytes("8B 5C 24 14"); //Overiding Grenade command only for P2~P4 if P1 needs middle click cover //Overwise, Overriding Grenade for all players if (!Configurator.GetInstance().HF_UseMiddleButtonAsGrenade) { //test ebx,ebx CaveMemory.Write_StrBytes("85 DB"); //je exit CaveMemory.Write_StrBytes("74 0E"); } else { //Do nothing CaveMemory.Write_StrBytes("90 90 90 90"); } //test ebx,ebx CaveMemory.Write_StrBytes("85 DB"); //je exit CaveMemory.Write_StrBytes("74 0E"); //add ebx,_Grenade_CaveAddress CaveMemory.Write_StrBytes("81 C3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Grenade_CaveAddress)); //mov al,[ebx] CaveMemory.Write_StrBytes("8A 03"); //mov [esi+00000096],al CaveMemory.Write_StrBytes("88 86 96 00 00 00"); //Exit: //pop ebx CaveMemory.Write_StrBytes("5B"); //Inject it CaveMemory.InjectToOffset(_Mouse_Buttons_InjectionStruct, "P1 Buttons"); } /// /// All Axis codecave are the same : /// The game use some fstp [XXX] instruction, but we can't just NOP it as graphical glitches may appear. /// So we just add another set of instructions instruction immediatelly after to change the register /// to our own desired value /// private void SetHack_Mouse_X() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp [esi+edi*8+00000114] CaveMemory.Write_StrBytes("D9 9C FE 14 01 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,edi CaveMemory.Write_StrBytes("8B C7"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax,_Axis_X_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_X_CaveAddress)); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //mov [esi+edi*8+00000114], eax CaveMemory.Write_StrBytes("89 84 FE 14 01 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Mouse_X_InjectionStruct, "P1 X Axis"); } private void SetHack_Mouse_Y() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp [esi+edi*8+00000118] CaveMemory.Write_StrBytes("D9 9C FE 18 01 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,edi CaveMemory.Write_StrBytes("8B C7"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax,_Axis_Y_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_Y_CaveAddress)); //mov eax,[eax] CaveMemory.Write_StrBytes("8B 00"); //mov [esi+edi*8+00000118], eax CaveMemory.Write_StrBytes("89 84 FE 18 01 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Mouse_Y_InjectionStruct, "P1 Y Axis"); } private void SetHack_Gamepad_X() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp [edi+esi*8+00000114] CaveMemory.Write_StrBytes("D9 9C F7 14 01 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[esi*4+_Axis_X_CaveAddress] CaveMemory.Write_StrBytes("8B 04 B5"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_X_CaveAddress)); //mov [edi+esi*8+00000114], eax CaveMemory.Write_StrBytes("89 84 F7 14 01 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Gamepad_X_InjectionStruct, "P2~P4 X Axis"); } private void SetHack_Gamepad_Y() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fstp [edi+esi*8+00000118] CaveMemory.Write_StrBytes("D9 9C F7 18 01 00 00"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[esi*4+_Axis_Y_CaveAddress] CaveMemory.Write_StrBytes("8B 04 B5"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Axis_Y_CaveAddress)); //mov [edi+esi*8+00000118], eax CaveMemory.Write_StrBytes("89 84 F7 18 01 00 00"); //pop eax CaveMemory.Write_StrBytes("58"); //Inject it CaveMemory.InjectToOffset(_Gamepad_Y_InjectionStruct, "P2~P4 Y Axis"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _GamePlaying_CaveAddress = _OutputsDatabank_Address; _Recoil_CaveAddress = _OutputsDatabank_Address + 0x04; _Damage_CaveAddress = _OutputsDatabank_Address + 0x08; _Ammo_CaveAddress = _OutputsDatabank_Address + 0x10; SetHack_Recoil(); SetHack_Ammo(); SetHack_IsPlaying(); SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// This code is run constantly and sets some 0/1 flag we can intercept for our own use /// private void SetHack_IsPlaying() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov [_P1_Playing_CaveAddress],al CaveMemory.Write_StrBytes("A2"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_GamePlaying_CaveAddress)); //mov ecx,[esp+0000024C] CaveMemory.Write_StrBytes("8B 8C 24 4C 02 00 00"); //Inject it CaveMemory.InjectToOffset(_GamePlaying_InjectionStruct, "Player Playing"); } /// /// Set a flag when the game calls for a shooting procedure /// private void SetHack_Recoil() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[esi+14] CaveMemory.Write_StrBytes("8B 46 14"); //add eax,_Recoil_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); //mov byte ptr[eax], 01 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //cmp [ecx+0000019C],ebx CaveMemory.Write_StrBytes("39 99 9C 01 00 00"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// This function is called in a loop while the player is playing /// We can get Ammo status in it /// private void SetHack_Ammo() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,ebp CaveMemory.Write_StrBytes("8B C5"); //shl eax,02 CaveMemory.Write_StrBytes("C1 E0 02"); //add eax,_Ammo_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Ammo_CaveAddress)); //mov esi,[ecx+18] CaveMemory.Write_StrBytes("8B 71 18"); //mov [eax],esi CaveMemory.Write_StrBytes("89 30"); //mov esi,ecx CaveMemory.Write_StrBytes("8B F1"); //pop eax CaveMemory.Write_StrBytes("58"); //or ebx, CaveMemory.Write_StrBytes("83 CB FF"); //test eax,eax CaveMemory.Write_StrBytes("85 C0"); //Inject it CaveMemory.InjectToOffset(_Ammo_InjectionStruct, "Ammo"); } /// /// That code is called when the game needs to increment the hit counter of the player, and also run the "hit_00" soune effect ? /// We can get the player ID in ESP+2C after pushing EAX /// private void SetHack_Damage() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push eax CaveMemory.Write_StrBytes("50"); //mov eax,[esp+2C] CaveMemory.Write_StrBytes("8B 44 24 2C"); //dec eax CaveMemory.Write_StrBytes("48"); //add eax,_Damage_CaveAddress CaveMemory.Write_StrBytes("05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Damage_CaveAddress)); //mov byte ptr[eax],01 CaveMemory.Write_StrBytes("C6 00 01"); //pop eax CaveMemory.Write_StrBytes("58"); //movsx edx,word ptr [esp+000000C8] CaveMemory.Write_StrBytes("0F BF 94 24 C8 00 00 00"); //Inject it CaveMemory.InjectToOffset(_Damage_InjectionStruct, "Damage"); } /// /// Changing X/Y values to -10.0 for every elements in the HUD (crosshair, reload, etc...) /// protected override void Apply_NoCrosshairMemoryHack() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //fst qword ptr [esp+00000184] CaveMemory.Write_StrBytes("DD 94 24 84 01 00 00"); //mov [esp+4C],C1200000 CaveMemory.Write_StrBytes("C7 44 24 4C 00 00 20 C1"); //mov [esp+50],C1200000 CaveMemory.Write_StrBytes("C7 44 24 50 00 00 20 C1"); //Inject it CaveMemory.InjectToOffset(_NoCrosshair_InjectionStruct, "No Crosshair"); } /// /// Game is displaying gun on screen if less than 2 players, forcing it to hide /// private void Apply_NoGunsMemoryHack() { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _NoGun_Patch_Offset, new byte[] { 0xE9, 0xB2, 0x00, 0x00, 0x00, 0x90}); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { float fX = (float)PlayerData.RIController.Computed_X / 1000.0f; float fY = (float)PlayerData.RIController.Computed_Y / 1000.0f; int PlayerIndex = (PlayerData.ID - 1) * 4; //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(fX); WriteBytes(_Axis_X_CaveAddress + (uint)PlayerIndex, buffer); buffer = BitConverter.GetBytes(fY); WriteBytes(_Axis_Y_CaveAddress + (uint)PlayerIndex, buffer); if (PlayerData.ID == 1) { if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_Trigger_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_Trigger_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_Reload_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_Reload_CaveAddress, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if (Configurator.GetInstance().HF_UseMiddleButtonAsGrenade) { WriteByte(_Grenade_CaveAddress, 0x01); } else { //If the player is aiming on the left side of the screen before pressing the button //=> Cover Left if ((fX < _Axis_X_Min + _CoverDelta) && !_Reversecover) { Send_VK_KeyDown(_P1_CoverLeft_VK); _CoverLeftEnabled = true; } //If the player is aiming on the right side of the screen before pressing the button //=> Cover Left else if ((fX > _Axis_X_Max - _CoverDelta) && _Reversecover) { Send_VK_KeyDown(_P1_CoverLeft_VK); _CoverLeftEnabled = true; } //If the player is aiming on the right side of the screen before pressing the button //=> Cover Right else if ((fX > _Axis_X_Max - _CoverDelta) && !_Reversecover) { Send_VK_KeyDown(_P1_CoverRight_VK); _CoverRightEnabled = true; } //If the player is aiming on the left side of the screen before pressing the button //=> Cover Right else if ((fX < _Axis_X_Min + _CoverDelta) && _Reversecover) { Send_VK_KeyDown(_P1_CoverRight_VK); _CoverRightEnabled = true; } //If the player is aiming on the bottom side of the screen before pressing the button //=> Cover Down else if (fY > (1.0f - _CoverDelta)) { Send_VK_KeyDown(_P1_CoverBottom_VK); _CoverBottomEnabled = true; } //If the player is aiming on the top side of the screen before pressing the button //=> W [QTE] else if (fY < (-1.0f + _CoverDelta)) { Send_VK_KeyDown(_P1_QTE_W_VK); _QTE_W_Enabled = true; } //If nothing above //=> Spacebar [QTE] else { Send_VK_KeyDown(_P1_QTE_Space_VK); _QTE_Spacebar_Enabled = true; } } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { if (Configurator.GetInstance().HF_UseMiddleButtonAsGrenade) { WriteByte(_Grenade_CaveAddress, 0x00); } else { if (_CoverLeftEnabled) { Send_VK_KeyUp(_P1_CoverLeft_VK); _CoverLeftEnabled = false; } if (_CoverBottomEnabled) { Send_VK_KeyUp(_P1_CoverBottom_VK); _CoverBottomEnabled = false; } if (_CoverRightEnabled) { Send_VK_KeyUp(_P1_CoverRight_VK); _CoverRightEnabled = false; } if (_QTE_W_Enabled) { Send_VK_KeyUp(_P1_QTE_W_VK); _QTE_W_Enabled = false; } if (_QTE_Spacebar_Enabled) { Send_VK_KeyUp(_P1_QTE_Space_VK); _QTE_Spacebar_Enabled = false; } } } } else { if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte(_Trigger_CaveAddress + (uint)PlayerData.ID - 1, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte(_Trigger_CaveAddress + (uint)PlayerData.ID - 1, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte(_Grenade_CaveAddress + (uint)PlayerData.ID - 1, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte(_Grenade_CaveAddress + (uint)PlayerData.ID - 1, 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) WriteByte(_Reload_CaveAddress + (uint)PlayerData.ID - 1, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) WriteByte(_Reload_CaveAddress + (uint)PlayerData.ID - 1, 0x00); } } } /// /// Low-level Keyboard hook callback. /// Using those custom keys, we can force players to be created (4=mouse controller) or not in the game /// Setting value to 1 ==> Auto validate popup to select profile, but need some patch to force the game to compute mouse procedure /// Setting value to 4 ==> Need validate popup to select profile (genuine way ??) /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _P1_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress) != 0) WriteByte(_PlayerOnOff_CaveAddress, 0x00); else WriteByte(_PlayerOnOff_CaveAddress, 0x04); } if (s.scanCode == _P2_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress + 0x01) != 0) WriteByte(_PlayerOnOff_CaveAddress + 0x01, 0x00); else WriteByte(_PlayerOnOff_CaveAddress + 0x01, 0x04); } if (s.scanCode == _P3_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress + 0x02) != 0) WriteByte(_PlayerOnOff_CaveAddress + 0x02, 0x00); else WriteByte(_PlayerOnOff_CaveAddress + 0x02, 0x04); } if (s.scanCode == _P4_OnOff_Key) { if (ReadByte(_PlayerOnOff_CaveAddress + 0x03) != 0) WriteByte(_PlayerOnOff_CaveAddress + 0x03, 0x00); else WriteByte(_PlayerOnOff_CaveAddress + 0x03, 0x04); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P3_Ammo)); _Outputs.Add(new GameOutput(OutputId.P4_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new GameOutput(OutputId.P3_Clip)); _Outputs.Add(new GameOutput(OutputId.P4_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { _P1_Ammo = 0; _P2_Ammo = 0; _P3_Ammo = 0; _P4_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; int P3_Clip = 0; int P4_Clip = 0; if (ReadByte(_GamePlaying_CaveAddress) == 1) { //Recoil if (ReadByte(_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress, 0); } if (ReadByte(_Recoil_CaveAddress + 0x01) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 0x01, 0); } if (ReadByte(_Recoil_CaveAddress + 0x02) == 1) { SetOutputValue(OutputId.P3_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 0x02, 0); } if (ReadByte(_Recoil_CaveAddress + 0x03) == 1) { SetOutputValue(OutputId.P4_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress + 0x03, 0); } //Ammo + Clip _P1_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress, 4), 0); if (_P1_Ammo == -1) _P1_Ammo = 99; if (_P1_Ammo > 0) P1_Clip = 1; _P2_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress + 0x04, 4), 0); if (_P2_Ammo == -1) _P2_Ammo = 99; if (_P2_Ammo > 0) P2_Clip = 1; _P3_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress + 0x08, 4), 0); if (_P3_Ammo == -1) _P3_Ammo = 99; if (_P3_Ammo > 0) P3_Clip = 1; _P4_Ammo = BitConverter.ToInt32(ReadBytes(_Ammo_CaveAddress + 0x0C, 4), 0); if (_P4_Ammo == -1) _P4_Ammo = 99; if (_P4_Ammo > 0) P4_Clip = 1; //Damage if (ReadByte(_Damage_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_Damage_CaveAddress, 0); } if (ReadByte(_Damage_CaveAddress + 0x01) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte(_Damage_CaveAddress + 0x01, 0); } if (ReadByte(_Damage_CaveAddress + 0x02) == 1) { SetOutputValue(OutputId.P3_Damaged, 1); WriteByte(_Damage_CaveAddress + 0x02, 0); } if (ReadByte(_Damage_CaveAddress + 0x03) == 1) { SetOutputValue(OutputId.P4_Damaged, 1); WriteByte(_Damage_CaveAddress + 0x03, 0); } } SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P3_Ammo, _P3_Ammo); SetOutputValue(OutputId.P4_Ammo, _P4_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P3_Clip, P3_Clip); SetOutputValue(OutputId.P4_Clip, P4_Clip); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndHod2pc.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Media; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndHod2pc : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\hod2pc"; /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x005C8FC4; private UInt32 _P1_Y_Offset = 0x005C8FC6; private UInt32 _P2_X_Offset = 0x005C8FEC ; private UInt32 _P2_Y_Offset = 0x005C8FEE; private UInt32 _Credits_Offset = 0x005C8E60; private NopStruct _Nop_X = new NopStruct(0x0000D1AD, 4); private NopStruct _Nop_Y = new NopStruct(0x0000D1B7, 4); //Outputs private UInt32 _P1_Status_Offset = 0x005A5C62; private UInt32 _P1_Life_Offset = 0x005A5C66; private UInt32 _P1_Ammo_Offset = 0x005A5C7C; private UInt32 _P2_Status_Offset = 0x005A5C92; private UInt32 _P2_Life_Offset = 0x005A5C96; private UInt32 _P2_Ammo_Offset = 0x005A5CAC; //Keys private VirtualKeyCode _P1_Trigger_VK = VirtualKeyCode.VK_RSHIFT; private VirtualKeyCode _P1_Reload_VK = VirtualKeyCode.VK_RCONTROL; private VirtualKeyCode _P2_Trigger_VK = VirtualKeyCode.VK_LSHIFT; private VirtualKeyCode _P2_Reload_VK = VirtualKeyCode.VK_LCONTROL; //Play the "Coins" sound when adding coin SoundPlayer _SndPlayer; /// /// Constructor /// public Game_WndHod2pc(String RomName) : base(RomName, "hod2") { _KnownMd5Prints.Add("hod2.exe PC-ISOZONE - uncracked", "97c9e516a287aab33a455a396dadaa45"); _KnownMd5Prints.Add("hod2.exe PC-ISOZONE - cracked", "eb51d3856997581ed3aa8ecb7d6d8d07"); _KnownMd5Prints.Add("hod2 Unknown Release #1", "fd53bc12b72958c819cf6931787df3cb"); _KnownMd5Prints.Add("hod2 Unknown Release #2", "a54ec23a78d07a78653bbd31919db0b5"); _KnownMd5Prints.Add("hod2 Unknown Release #3", "258f350774317f785b430be32d2abe9a"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); //Try to load the "coin" sound try { String strCoinSndPath = _TargetProcess.MainModule.FileName; strCoinSndPath = strCoinSndPath.Substring(0, strCoinSndPath.Length - 8); strCoinSndPath += @"sound\SE\START_COIN\coin1_16.wav"; _SndPlayer = new SoundPlayer(strCoinSndPath); } catch { Logger.WriteLog("Unable to find/open the coin1_16.wav file for Hotd2"); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [-320 ; +320] => 640 //Y => [-240; +240] => 480 double dMinX = -320.0; double dMaxX = 320.0; double dMinY = -240.0; double dMaxY = 240.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / TotalResX) - dRangeX / 2); PlayerData.RIController.Computed_Y = Convert.ToInt16((Math.Round(dRangeY * PlayerData.RIController.Computed_Y / TotalResY) - dRangeY / 2) * -1); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Simple Hack : NOPing Axis procedures /// protected override void Apply_InputsMemoryHack() { SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Send_VK_KeyDown(_P1_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Send_VK_KeyUp(_P1_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Send_VK_KeyDown(_P1_Reload_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Send_VK_KeyUp(_P1_Reload_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Send_VK_KeyDown(_P1_Reload_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Send_VK_KeyUp(_P1_Reload_VK); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Send_VK_KeyDown(_P2_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Send_VK_KeyUp(_P2_Trigger_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Send_VK_KeyDown(_P1_Reload_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Send_VK_KeyUp(_P2_Reload_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Send_VK_KeyDown(_P2_Reload_VK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Send_VK_KeyUp(_P2_Reload_VK); } } /// /// Low-level Keyboard hook callback. /// This is used to add coin to the game /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_5) { byte Credits = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset); Credits++; WriteByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset, Credits); if (_SndPlayer != null) _SndPlayer.Play(); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Player status : //[4] = Continue Screen //[5] = InGame //[6] = Game Over //[7] = Time attack Scoreboard //[9] = Menu or Attract Mode // We will use these values to compute ourselve Recoil and P1/P2 Start Button Lights UInt32 P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Status_Offset); UInt32 P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Status_Offset); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (P1_Status == 5) { //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); _P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); _P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } if (P2_Status == 5) { //Force Start Lamp to Off SetOutputValue(OutputId.P2_CtmLmpStart, 0); _P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset); _P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P2_CtmLmpStart, -1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndHod3pc.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndHod3pc : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\hod3pc"; /*** MEMORY ADDRESSES **/ private UInt32 _P1_X_Offset = 0x00367AA4; private UInt32 _P1_Y_Offset = 0x00367AA6; private UInt32 _P2_X_Offset = 0x00367AD8; private UInt32 _P2_Y_Offset = 0x00367ADA; private UInt32 _P1_Buttons_Offset = 0x0003ECF88; private UInt32 _P2_Buttons_Offset = 0x0003ED0F0; private UInt32 _Credits_Offset = 0x003B7DD0; private UInt32 _Buttons_Injection_Offset = 0x000B4BC1; private UInt32 _Buttons_Injection_Return_Offset = 0x000B4BC6; private NopStruct _Nop_X = new NopStruct(0x0006BE70, 4); private NopStruct _Nop_Y = new NopStruct(0x0006BE7B, 4); private NopStruct _Nop_Trigger_Button = new NopStruct(0x000B4C8C, 3); private NopStruct _Nop_Reload_Button = new NopStruct(0x000B4CB1, 4); private NopStruct _Nop_Left_Button = new NopStruct(0x000B4C54, 3); private NopStruct _Nop_Right_Button = new NopStruct(0x00B4C0E, 3); //Not yet in cfg files (always the same for all releases ?) private UInt32 _P1_Ammo_Offset = 0x003B8090; private UInt32 _P1_Life_Offset = 0x003B8044; private UInt32 _P2_Ammo_Offset = 0x003B82BC; private UInt32 _P2_Life_Offset = 0x003B8270; /// /// Constructor /// public Game_WndHod3pc(String RomName) : base (RomName, "hod3pc") { _KnownMd5Prints.Add("hod3pc SEGA Windows", "4bf19dcb7f0182596d93f038189f2301"); _KnownMd5Prints.Add("hod3pc RELOADED cracked", "3a4501d39bbb7271712421fb992ad37b"); _KnownMd5Prints.Add("hod3pc REVELATION No-CD", "b8af47f16d5e43cddad8df0a6fdb46f5"); _KnownMd5Prints.Add("hod3pc MYTH Release", "0228818e9412fc218fcd24bfd829a5a0"); _KnownMd5Prints.Add("hod3pc TEST", "733da4e3cfe24b015e5f795811d481e6"); _KnownMd5Prints.Add("hod3pc Unknown Release #1", "51dd72f83c0de5b27c0358ad11e2a036"); _KnownMd5Prints.Add("hod3pc Unknown Release #2", "e4819dcf2105b85a7e7bc9dc66159f5c"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [-320 ; +320] => 640 //Y => [-240; +240] => 480 double dMinX = -320.0; double dMaxX = 320.0; double dMinY = -240.0; double dMaxY = 240.0; double dRangeX = dMaxX - dMinX + 1; double dRangeY = dMaxY - dMinY + 1; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dRangeX * PlayerData.RIController.Computed_X / TotalResX) - dRangeX / 2); PlayerData.RIController.Computed_Y = Convert.ToInt16((Math.Round(dRangeY * PlayerData.RIController.Computed_Y / TotalResY) - dRangeY / 2) * -1); if (PlayerData.RIController.Computed_X < (int)dMinX) PlayerData.RIController.Computed_X = (int)dMinX; if (PlayerData.RIController.Computed_Y < (int)dMinY) PlayerData.RIController.Computed_Y = (int)dMinY; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { //Block Axis SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_X); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Y); //Block Buttons SetHack_Buttons(); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Trigger_Button); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Reload_Button); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Left_Button); SetNops((UInt32)_TargetProcess_MemoryBaseAddress, _Nop_Right_Button); //Gun data Init WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, new byte[] { 0x00, 0x00 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, new byte[] { 0x00, 0x00 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, new byte[] { 0x00, 0x00 }); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, new byte[] { 0x00, 0x00 }); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer; //mov ebp,ecx CaveMemory.Write_StrBytes("8B E9"); //and [ebp+00], 0101000C CaveMemory.Write_StrBytes("81 65 00 0C 00 01 01"); CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Return_Offset); Logger.WriteLog("Adding Buttons Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Buttons_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset + 2, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset + 2, 0x0E); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { //Check aiming position to set the action of MiddleButton according to wich half of the screen the user is pointing if (PlayerData.RIController.Computed_X < 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x04); else Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x08); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0x03); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset + 3, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset + 3, 0x0E); } else if (PlayerData.ID == 2) { WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_X_Offset, bufferX); WriteBytes((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset + 2, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset + 2, 0x0E); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { //Check aiming position to set the action of MiddleButton according to wich half of the screen the user is pointing if (PlayerData.RIController.Computed_X < 0) Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset, 0x04); else Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset, 0x08); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset, 0x03); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { Apply_OR_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset + 3, 0x01); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Buttons_Offset + 3, 0x0E); } } /// /// Low-level mouse hook callback. /// This is used to block middle and right click events in game /// public override IntPtr MouseHookCallback(IntPtr MouseHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0 && (UInt32)wParam == Win32Define.WM_MBUTTONDOWN) { //Just blocking middle clicks return new IntPtr(1); } else if (nCode >= 0 && (UInt32)wParam == Win32Define.WM_RBUTTONDOWN) { //Just blocking right clicks => if not P1 reload play animation but does not reload return new IntPtr(1); } } return Win32API.CallNextHookEx(MouseHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Player status : //[4] = Continue Screen //[5] = InGame //[6] = Game Over //[7] = Time attack Scoreboard //[9] = Menu or Attract Mode // We will use these values to compute ourselve Recoil and P1/P2 Start Button Lights UInt32 P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x003B8038); UInt32 P2_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + 0x003B8264); _P1_Life = 0; _P2_Life = 0; _P1_Ammo = 0; _P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (P1_Status == 5) { //Force Start Lamp to Off SetOutputValue(OutputId.P1_CtmLmpStart, 0); _P1_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Life_Offset); _P1_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P1_Ammo_Offset); //Custom Recoil if (_P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (_P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P1_CtmLmpStart, -1); } if (P2_Status == 5) { //Force Start Lamp to Off SetOutputValue(OutputId.P2_CtmLmpStart, 0); _P2_Life = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Life_Offset); _P2_Ammo = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _P2_Ammo_Offset); //Custom Recoil if (_P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (_P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } else { //Enable Start Lamp Blinking SetOutputValue(OutputId.P2_CtmLmpStart, -1); } _P1_LastAmmo = _P1_Ammo; _P2_LastAmmo = _P2_Ammo; _P1_LastLife = _P1_Life; _P2_LastLife = _P2_Life; SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P2_Ammo, _P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.P2_Life, _P2_Life); SetOutputValue(OutputId.Credits, ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _Credits_Offset)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndHotdoPc.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.Win32; namespace DemulShooter { class Game_WndHotdoPc : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\hodo"; /*** MEMORY ADDRESSES **/ private UInt32 _PlayerStatus_Offset = 0x0059C378; private UInt32 _CreditsPtr_Offset = 0x005996C8; private UInt32 _PlayerInfoPtr_Offset = 0x00599688; private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x00118C5F, 6); private InjectionStruct _Damage_InjectionStruct = new InjectionStruct(0x0018B399, 6); private UInt32 _Recoil_CaveAddress = 0; private UInt32 _Damage_CaveAddress = 0; /// /// Constructor /// public Game_WndHotdoPc(String RomName) : base (RomName, "HOTD_NG") { _KnownMd5Prints.Add("Typing Of The Dead SEGA Windows", "da39156a426e3f3faca25d3c8cb2b401"); _KnownMd5Prints.Add("Typing Of The Dead SEGA Windows STEAM", "9dcb7083e3e3ede186c9a809498a0d3b"); _KnownMd5Prints.Add("Typing of The Dead STEAM", "ac70ca4b6d310fe1d5f18965575ece68"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _Recoil_CaveAddress = _OutputsDatabank_Address; _Damage_CaveAddress = _OutputsDatabank_Address + 0x04; SetHack_Recoil(); SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Using Ammo count to compute recoil does not work : /// - Game set ammo to zero before reloading (and trigger recoil) /// - Gaitling gun has no ammo count -> no recoil /// Using this codecave, we can intercept call in function when the vullet is fired /// private void SetHack_Recoil() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp [esi+000007A0],bl CaveMemory.Write_StrBytes("38 9E A0 07 00 00"); //mov byte ptr [_Recoil_CaveAddress],01 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); CaveMemory.Write_StrBytes("01"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// To prevent false positive damage flag based on life number (set to 0 at the end of a level) /// Intercepting call in function where player health is decreased when hit /// private void SetHack_Damage() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov [esi+0000020C],edi CaveMemory.Write_StrBytes("89 BE 0C 02 00 00"); //mov byte ptr [_Damage_CaveAddress],01 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Damage_CaveAddress)); CaveMemory.Write_StrBytes("01"); //Inject it CaveMemory.InjectToOffset(_Damage_InjectionStruct, "Damage"); } #endregion #region Inputs /// /// Low-level Keyboard hook callback. /// This is used to add coin to the game /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_5) { UInt32 Credits_Address = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsPtr_Offset, new UInt32[] { 0x228 }) + 0x17C;// ReadByte(_Credits_Address); byte Credits = ReadByte(Credits_Address); Credits++; WriteByte(Credits_Address, Credits); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Player status : //[0] = Not Playing //[1] = Playing UInt32 P1_Status = ReadByte((UInt32)_TargetProcess_MemoryBaseAddress + _PlayerStatus_Offset); UInt32 iTemp = ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayerInfoPtr_Offset); UInt32 PlayerInfo = 0; //Player nfo struct is created and allocated on game, point to NULL in menus if (iTemp != 0) { PlayerInfo = ReadPtr(iTemp + 0x24); _P1_Life = ReadByte(PlayerInfo + 0x20C); _P1_Ammo = ReadByte(PlayerInfo + 0x28C); } if (P1_Status != 0) { //Custom Recoil if (ReadByte(_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress, 0); } //[Clip Empty] custom Output if (_P1_Ammo <= 0) SetOutputValue(OutputId.P1_Clip, 0); else SetOutputValue(OutputId.P1_Clip, 1); //[Damaged] custom Output if (ReadByte(_Damage_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_Damage_CaveAddress, 0); } } else { SetOutputValue(OutputId.P1_Clip, 0); } SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P1_Life, _P1_Life); SetOutputValue(OutputId.Credits, ReadByte(ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + _CreditsPtr_Offset, new UInt32[] { 0x228 }) + 0x17C)); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndMadBullets.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; namespace DemulShooter { class Game_WndMadBullets : Game { /*** MEMORY ADDRESSES **/ private UInt32 _PlayerPlayingPtr_Offset = 0x001D55E8; private UInt32 _LifePtr_Offset = 0x001D28C0; private UInt32 _AmmoPtr_Offset = 0x001D2890; private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x0004FFA2, 7); private InjectionStruct _Damage_InjectionStruct = new InjectionStruct(0x000B1E92, 6); private UInt32 _Recoil_CaveAddress = 0; private UInt32 _Damage_CaveAddress = 0; /// /// Constructor /// public Game_WndMadBullets(String RomName) : base (RomName, "game") { _KnownMd5Prints.Add("Mad Bullets v1.0.2", "69807fe94a14ada9c792f20017d9a2a8"); //Not sure about the version, mine is from IGG-Games _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); //ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Memory Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _Recoil_CaveAddress = _OutputsDatabank_Address; _Damage_CaveAddress = _OutputsDatabank_Address + 0x04; SetHack_Recoil(); SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Using Ammo count to compute recoil does not work : /// - Game has unlimited ammo bonus, and the number does not change during it /// Using this codecave, we can intercept call in function when the bullet is fired /// private void SetHack_Recoil() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp dword ptr[edi+0F8h], 0 CaveMemory.Write_StrBytes("83 BF F8 00 00 00 00"); //mov byte ptr [_Recoil_CaveAddress],01 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); CaveMemory.Write_StrBytes("01"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// Intercepting call in function where player health should be decreased (or not) according to the game mode /// private void SetHack_Damage() { List Buffer = new List(); Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov al, [ecx+189h] CaveMemory.Write_StrBytes("8A 81 89 01 00 00"); //mov byte ptr [_Damage_CaveAddress],01 CaveMemory.Write_StrBytes("C6 05"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Damage_CaveAddress)); CaveMemory.Write_StrBytes("01"); //Inject it CaveMemory.InjectToOffset(_Damage_InjectionStruct, "Damage"); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Player status : //[0] = Not Playing //[1] = Playing UInt32 P1_Status = ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _PlayerPlayingPtr_Offset) + 0x1AC); _P1_Ammo = 0; _P1_Life = 0; if (P1_Status != 0) { _P1_Ammo = ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _AmmoPtr_Offset) + 0xD7); _P1_Life = ReadByte(ReadPtr((UInt32)_TargetProcess_MemoryBaseAddress + _LifePtr_Offset) + 0x24); //Custom Recoil if (ReadByte(_Recoil_CaveAddress) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte(_Recoil_CaveAddress, 0); } //[Clip Empty] custom Output if (_P1_Ammo <= 0) SetOutputValue(OutputId.P1_Clip, 0); else SetOutputValue(OutputId.P1_Clip, 1); //[Damaged] custom Output if (ReadByte(_Damage_CaveAddress) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte(_Damage_CaveAddress, 0); } } else { SetOutputValue(OutputId.P1_Clip, 0); } SetOutputValue(OutputId.P1_Ammo, _P1_Ammo); SetOutputValue(OutputId.P1_Life, _P1_Life); } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndProjectGreenBeat.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.Win32; namespace DemulShooter { public class Game_WndProjectGreenBeat : Game { //Memory values private UInt32 _AxisX_Address = 0; private UInt32 _AxisY_Address = 0; private UInt32 _Axis_Injection_Offset = 0x00163CB1; private UInt32 _Axis_Injection_Return_Offset = 0x00163CB8; private UInt32 _AxisX_BankAddress = 0; private UInt32 _AxisY_BankAddress = 0; //Custom data to inject private float _P1_X_Value; private float _P1_Y_Value; //Custom Outputs private UInt32 _ShotCounts_Address = 0; private int _LastShotsCount = 0; private int _ShotsCount = 0; /// /// Constructor /// public Game_WndProjectGreenBeat(String RomName) : base(RomName, "ProjectGreenBeat") { _KnownMd5Prints.Add("Project Green Beat - PROPHET", "7962b8b40d71464a988a7c6db96c88c0"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); FetchAxisAddress(); _ShotCounts_Address = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + 0x0168E728, new UInt32[] { 0xEC }) + 0xD4; Logger.WriteLog("AxisX_Address = 0x" + _AxisX_Address.ToString("X8")); Logger.WriteLog("AxisY_Address = 0x" + _AxisY_Address.ToString("X8")); if (_AxisX_Address != 0 && _AxisY_Address != 0) { Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { //Axis address is changing during the game, need to update it FetchAxisAddress(); Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } private void FetchAxisAddress() { _AxisX_Address = ReadPtrChain((UInt32)_TargetProcess_MemoryBaseAddress + 0x022D0BF0, new UInt32[] { 0x04, 0xD8, 0x450, 0x3C4 }) + 0x360; _AxisY_Address = _AxisX_Address + 0x04; if (_AxisX_BankAddress != 0) WriteBytes(_AxisX_BankAddress, BitConverter.GetBytes(_AxisX_Address)); if (_AxisY_BankAddress != 0) WriteBytes(_AxisY_BankAddress, BitConverter.GetBytes(_AxisY_Address)); } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0 ; 1] float //Y => [0 ; 1] float float X_Value = PlayerData.RIController.Computed_X / (float)TotalResX; float Y_Value = PlayerData.RIController.Computed_Y / (float)TotalResY; if (X_Value < 0) X_Value = 0; if (Y_Value < 0) Y_Value = 0; if (X_Value > 1.0f) X_Value = 1.0f; if (Y_Value > 1.0f) Y_Value = 1.0f; _P1_X_Value = X_Value; _P1_Y_Value = Y_Value; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// UE3 engine game, input in menu is fine with mouse+lightgun but does only work with mouse in-game /// Float [-1;1] computed values are updated with a generic function so we need to filter with the Axis pointer to stop it and write ourself later /// Axis address must be updated often as the game is changign it with each level /// protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _AxisX_BankAddress = _InputsDatabank_Address; _AxisY_BankAddress = _InputsDatabank_Address + 0x08; SetHack_Axis(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //movaps xmm0,xmm1 CaveMemory.Write_StrBytes("0F 28 C1"); //mov ecx, [_AxisX_BankAddress] CaveMemory.Write_StrBytes("8B 0D"); byte[] b = BitConverter.GetBytes(_AxisX_BankAddress); CaveMemory.Write_Bytes(b); //cmp eax, ecx CaveMemory.Write_StrBytes("39 C8"); //je exit CaveMemory.Write_StrBytes("74 0B"); //add ecx, 04 CaveMemory.Write_StrBytes("83 C1 04"); //cmp eax, ecx CaveMemory.Write_StrBytes("39 C8"); //je exit CaveMemory.Write_StrBytes("74 04"); //movss [eax],xmm0 CaveMemory.Write_StrBytes("F3 0F 11 00"); //return CaveMemory.Write_jmp((UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Injection_Return_Offset); Logger.WriteLog("Adding Axis CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_TargetProcess_MemoryBaseAddress + _Axis_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(_P1_X_Value); WriteBytes(_AxisX_Address, buffer); buffer = BitConverter.GetBytes(_P1_Y_Value); WriteBytes(_AxisY_Address, buffer); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (_ShotCounts_Address != 0) _ShotsCount = BitConverter.ToInt32(ReadBytes(_ShotCounts_Address, 4), 0); else _ShotsCount = 0; if (_ShotsCount > _LastShotsCount) SetOutputValue(OutputId.P1_CtmRecoil, 1); _LastShotsCount = _ShotsCount; } #endregion } } ================================================ FILE: DemulShooter/Games/Game_WndReload.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.Memory; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooter { class Game_WndReload : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\reload"; /*** MEMORY ADDRESSES **/ /// The game needs 3 different values to be overwritten : /// - Menu axis (float between 0 and windows size) /// - In Game crosshair (between -0.5 and +0.5) /// - In Game shooting axis (between -1.0 and +1.0 ) private UInt32 _P1_X_Menu_CaveAddress; private UInt32 _P1_Y_Menu_CaveAddress; private UInt32 _P1_X_Shoot_CaveAddress; private UInt32 _P1_Y_Shoot_CaveAddress; private UInt32 _P1_X_Crosshair_CaveAddress; private UInt32 _P1_Y_Crosshair_CaveAddress; //For Outputs private UInt32 _P1_Recoil_CaveAddress; private UInt32 _P1_Ammo_CaveAddress; private UInt32 _P1_X_Menu_Injection_Offset = 0x001BD16C; private UInt32 _P1_X_Menu_Injection_Return_Offset = 0x001BD174; private UInt32 _P1_Y_Menu_Injection_Offset = 0x001BD1D3; private UInt32 _P1_Y_Menu_Injection_Return_Offset = 0x001BD1DB; private UInt32 _P1_Menu_Fix_Offset = 0x001BD185; private UInt32 _P1_InGame_Crosshair_Injection_Offset = 0x00218756; private UInt32 _P1_InGame_Crosshair_Injection_Return_Offset = 0x00218761; private UInt32 _P1_InGame_X_Injection_Offset = 0x0027CFD1; private UInt32 _P1_InGame_X_Injection_Return_Offset = 0x0027CFD7; private UInt32 _P1_InGame_Y_Injection_Offset = 0x0027D027; private UInt32 _P1_InGame_Y_Injection_Return_Offset = 0x0027D02E; private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x0021874F, 5); private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x00263C0C, 5); private InjectionStruct _Ammo_InjectionStruct = new InjectionStruct(0x0021E3AC, 10); protected IntPtr _RldGameDll_ModuleBaseAddress = IntPtr.Zero; //Custom data to inject protected float _P1_X_Menu_Value; protected float _P1_Y_Menu_Value; protected float _P1_X_Shoot_Value; protected float _P1_Y_Shoot_Value; protected float _P1_Crosshair_X_Value; protected float _P1_Crosshair_Y_Value; //Keyboard KEYS protected HardwareScanCode _P1_HoldBreath_DIK = HardwareScanCode.DIK_SPACE; /// /// Constructor /// public Game_WndReload(String RomName) : base(RomName, "Reload") { _KnownMd5Prints.Add("Reload v1.0.0.1 - IGG", "aaaf22c6671c12176d8317d4cc4b478d"); _KnownMd5Prints.Add("Reload v1.0.0.1 - Unknown release 1", "f3c4068a49f07aa99d2a92544d5c5748"); _KnownMd5Prints.Add("Reload v1.0.0.1 - Unknown release 2", "2e1e22229c90b53153d2c015371e643a"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("rld_game.dll")) { _RldGameDll_ModuleBaseAddress = m.BaseAddress; if (_RldGameDll_ModuleBaseAddress != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Logger.WriteLog("rld_game.dll Module Base Address = 0x " + _RldGameDll_ModuleBaseAddress.ToString("X8")); CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //During Menus : //Y => [0; RezY] en float //X => [0; RezX] en float _P1_X_Menu_Value = (float)PlayerData.RIController.Computed_X; _P1_Y_Menu_Value = (float)PlayerData.RIController.Computed_Y; if (_P1_X_Menu_Value < 0.0f) _P1_X_Menu_Value = 0.0f; if (_P1_Y_Menu_Value < 0.0f) _P1_Y_Menu_Value = 0.0f; if (_P1_X_Menu_Value > (float)TotalResX) _P1_X_Menu_Value = (float)TotalResX; if (_P1_Y_Menu_Value > (float)TotalResY) _P1_Y_Menu_Value = (float)TotalResY; //In Game, Shoot : //Y => [-1;1] en float //X => [-1;1] en float _P1_X_Shoot_Value = (2.0f * PlayerData.RIController.Computed_X / (float)TotalResX) - 1.0f; _P1_Y_Shoot_Value = (2.0f * PlayerData.RIController.Computed_Y / (float)TotalResY) - 1.0f; if (_P1_X_Shoot_Value < -1.0f) _P1_X_Shoot_Value = -1.0f; if (_P1_Y_Shoot_Value < -1.0f) _P1_Y_Shoot_Value = -1.0f; if (_P1_X_Shoot_Value > 1.0f) _P1_X_Shoot_Value = 1.0f; if (_P1_Y_Shoot_Value > 1.0f) _P1_Y_Shoot_Value = 1.0f; //In Game, Crosshair : //Y => [-0.5;0.5] en float //X => [-0.5;0.5] en float _P1_Crosshair_X_Value = _P1_X_Shoot_Value / 2.0f; _P1_Crosshair_Y_Value = _P1_Y_Shoot_Value / 2.0f; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// So far I ran into 2 versions of the game, both with different .exe and .dll files /// Although the hack is the same, .dll files have slighty different asm codes so for some of them we need to rewrite /// it all differently, because only changing offsets won't be enough /// protected override void Apply_InputsMemoryHack() { //Common procedure Create_InputsDataBank(); _P1_X_Menu_CaveAddress = _InputsDatabank_Address; _P1_Y_Menu_CaveAddress = _InputsDatabank_Address + 0x04; _P1_X_Shoot_CaveAddress = _InputsDatabank_Address + 0x10; _P1_Y_Shoot_CaveAddress = _InputsDatabank_Address + 0x14; _P1_X_Crosshair_CaveAddress = _InputsDatabank_Address + 0x20; _P1_Y_Crosshair_CaveAddress = _InputsDatabank_Address + 0x24; if (_TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - IGG"]) { SetHack_P1X_Menu(); SetHack_P1Y_Menu(); //If HideCrossair option is activated, the P1_Crosshair codecave will overwrite the NoCrosshair injection //That why we need to check before installing it if (!_HideCrosshair) SetHack_P1_Crosshair(); SetHack_P1_X_Shoot(); SetHack_P1_Y_Shoot(); } else if (_TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - Unknown release 1"] || _TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - Unknown release 2"]) { SetHack_P1X_Menu_V2(); SetHack_P1Y_Menu_V2(); //If HideCrossair option is activated, the P1_Crosshair codecave will overwrite the NoCrosshair injection //That why we need to check before installing it if (!_HideCrosshair) SetHack_P1_Crosshair_V2(); SetHack_P1_X_Shoot_V2(); SetHack_P1_Y_Shoot_V2(); } Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// For outputs, we need to create a custom DataBank and inject code to intercept the gun firing event to create recoil /// protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_Recoil_CaveAddress = _OutputsDatabank_Address; _P1_Ammo_CaveAddress = _OutputsDatabank_Address + 0x04; if (_TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - IGG"]) { SetHack_Recoil(); SetHack_Ammo(); } else if (_TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - Unknown release 1"] || _TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - Unknown release 2"]) { SetHack_Recoil_V2(); SetHack_Ammo_V2(); } Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } protected override void Apply_NoCrosshairMemoryHack() { if (_TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - IGG"]) SetHack_NoCrosshair(); else if (_TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - Unknown release 1"] || _TargetProcess_Md5Hash == _KnownMd5Prints["Reload v1.0.0.1 - Unknown release 2"]) SetHack_NoCrosshair_V2(); } #endregion #region Memory Hack_ExeV1 /// /// All Axis codecave are the same : /// The game use some fstp [XXX] instruction, but we can't just NOP it as graphical glitches may appear. /// So we just add another set of instructions immediatelly after to change the register /// to our own desired value /// private void SetHack_P1X_Menu() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_X_Address CaveMemory.Write_StrBytes("B8"); byte[] b = BitConverter.GetBytes(_P1_X_Menu_CaveAddress); CaveMemory.Write_Bytes(b); //movss xmm1, [eax] CaveMemory.Write_StrBytes("F3 0F 10 08"); //pop eax CaveMemory.Write_StrBytes("58"); //movss [ecx+0000013C],xmm1 CaveMemory.Write_StrBytes("F3 0F 11 89 3C 01 00 00"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_X_Menu_Injection_Return_Offset); Logger.WriteLog("Adding P1_X CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_X_Menu_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_X_Menu_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1Y_Menu() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_Y_Address CaveMemory.Write_StrBytes("B8"); byte[] b = BitConverter.GetBytes(_P1_Y_Menu_CaveAddress); CaveMemory.Write_Bytes(b); //movss xmm0, [eax] CaveMemory.Write_StrBytes("F3 0F 10 00"); //pop eax CaveMemory.Write_StrBytes("58"); //movss [ecx+0000014C],xmm0 CaveMemory.Write_StrBytes("F3 0F 11 81 40 01 00 00"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_Y_Menu_Injection_Return_Offset); Logger.WriteLog("Adding P1_X CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_Y_Menu_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_Y_Menu_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Cursor is not stable, so a little mod: Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_Menu_Fix_Offset, new byte[] { 0xD9 }, 1, ref bytesWritten); } /// /// The game is using different procedures to handle mouse position in menu and in-game /// Only one codecave for Menus handling as X and Y are 4 instructions next to each other /// private void SetHack_P1_Crosshair() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //fld dword ptr[_P1_X_Address] CaveMemory.Write_StrBytes("D9 05"); byte[] b = BitConverter.GetBytes(_P1_X_Crosshair_CaveAddress); CaveMemory.Write_Bytes(b); //fstp dword ptr[edx+28] CaveMemory.Write_StrBytes("D9 5A 28"); //fld dword ptr[_P1_Y_Address] CaveMemory.Write_StrBytes("D9 05"); b = BitConverter.GetBytes(_P1_Y_Crosshair_CaveAddress); CaveMemory.Write_Bytes(b); //fstp dword ptr[edx+2C] CaveMemory.Write_StrBytes("D9 5A 2C"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Crosshair_Injection_Return_Offset); Logger.WriteLog("Adding P1_InGame_Crosshair CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Crosshair_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Crosshair_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1_X_Shoot() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax [ebp + 10] CaveMemory.Write_StrBytes("8B 45 10"); //fstp dword ptr[esi+48] CaveMemory.Write_StrBytes("D9 5E 48"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_X_Shoot_Address CaveMemory.Write_StrBytes("A1"); byte[] b = BitConverter.GetBytes(_P1_X_Shoot_CaveAddress); CaveMemory.Write_Bytes(b); //push eax CaveMemory.Write_StrBytes("50"); //fld dword ptr[esp] CaveMemory.Write_StrBytes("D9 04 24"); //pop eax CaveMemory.Write_StrBytes("58"); //fstp dword ptr[esi+48] CaveMemory.Write_StrBytes("D9 5E 48"); //pop eax CaveMemory.Write_StrBytes("58"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_X_Injection_Return_Offset); Logger.WriteLog("Adding P1_InGame_X CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_X_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_X_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1_Y_Shoot() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_Y_Shoot_Address CaveMemory.Write_StrBytes("A1"); byte[] b = BitConverter.GetBytes(_P1_Y_Shoot_CaveAddress); CaveMemory.Write_Bytes(b); //push eax CaveMemory.Write_StrBytes("50"); //fld dword ptr[esp] CaveMemory.Write_StrBytes("D9 04 24"); //pop eax CaveMemory.Write_StrBytes("58"); //fstp dword ptr[esi+4C] CaveMemory.Write_StrBytes("D9 5E 4C"); //pop eax CaveMemory.Write_StrBytes("58"); //cmp byte ptr [esi+000000CC],00 CaveMemory.Write_StrBytes("80 BE CC 00 00 00 00"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Y_Injection_Return_Offset); Logger.WriteLog("Adding P1_InGame_Y CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Y_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Y_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// csControllerActor::Shoot() calls csControllerActorPlayer::Shoot() /// Intercepting the call will make us create a recoil signal+ /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov byte ptr[_P1_Recoil_CaveAddress], 1 CaveMemory.Write_StrBytes("C6 05"); byte[] b = BitConverter.GetBytes(_P1_Recoil_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("01"); //mov ecx, esi CaveMemory.Write_StrBytes("8B CE"); //fstp dword ptr [esp] CaveMemory.Write_StrBytes("D9 1C 24"); //Inject it CaveMemory.InjectToOffset(_RldGameDll_ModuleBaseAddress, _Recoil_InjectionStruct, "Recoil"); } /// /// Reading the result of GetRemainingBullets() call in the ActorHud::StatusBar_AdvanceTime() loop /// private void SetHack_Ammo() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [_P1_Ammo_CaveAddress], eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Ammo_CaveAddress)); //mov esi,eax CaveMemory.Write_StrBytes("8B F0"); //mov edx,[ecx] CaveMemory.Write_StrBytes("8B 11"); //mov [ebp-00000148],esi CaveMemory.Write_StrBytes("89 B5 B8 FE FF FF"); //Inject it CaveMemory.InjectToOffset(_RldGameDll_ModuleBaseAddress, _Ammo_InjectionStruct, "Ammo"); } /// /// Set values to [-1.0; -1.0] to display crosshair in ActorHud::Crosshair_SetPosition() function /// private void SetHack_NoCrosshair() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov edi,[ebp+0C] CaveMemory.Write_StrBytes("8B 7D 0C"); //mov [edi],BF800000 { -1.00 } CaveMemory.Write_StrBytes("C7 07 00 00 80 BF"); //mov [edi+04],BF800000 { -1.00 } CaveMemory.Write_StrBytes("C7 47 04 00 00 80 BF"); //test edx,edx CaveMemory.Write_StrBytes("85 D2"); //Inject it CaveMemory.InjectToOffset(_RldGameDll_ModuleBaseAddress, _NoCrosshair_InjectionStruct, "No Crosshair"); } #endregion #region Memory Hack_ExeV2 /// /// All Axis codecave are the same : /// The game use some fstp [XXX] instruction, but we can't just NOP it as graphical glitches may appear. /// So we just add another set of instructions immediatelly after to change the register /// to our own desired value /// private void SetHack_P1X_Menu_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_X_Address CaveMemory.Write_StrBytes("B8"); byte[] b = BitConverter.GetBytes(_P1_X_Menu_CaveAddress); CaveMemory.Write_Bytes(b); //movss xmm1, [eax] CaveMemory.Write_StrBytes("F3 0F 10 08"); //pop eax CaveMemory.Write_StrBytes("58"); //movss [esp+4+arg0],xmm1 CaveMemory.Write_StrBytes("F3 0F 11 4C 24 08"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_X_Menu_Injection_Return_Offset); Logger.WriteLog("Adding P1_X CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_X_Menu_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_X_Menu_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1Y_Menu_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_Y_Address CaveMemory.Write_StrBytes("B8"); byte[] b = BitConverter.GetBytes(_P1_Y_Menu_CaveAddress); CaveMemory.Write_Bytes(b); //movss xmm0, [eax] CaveMemory.Write_StrBytes("F3 0F 10 00"); //pop eax CaveMemory.Write_StrBytes("58"); //movss [ecx+00000140],xmm0 CaveMemory.Write_StrBytes("F3 0F 11 81 40 01 00 00"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_Y_Menu_Injection_Return_Offset); Logger.WriteLog("Adding P1_X CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_Y_Menu_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_Y_Menu_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); //Cursor is not stable, so a little mod: Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_Menu_Fix_Offset, new byte[] { 0xD9 }, 1, ref bytesWritten); } /// /// The game is using different procedures to handle mouse position in menu and in-game /// Only one codecave for Menus handling as X and Y are 4 instructions next to each other /// private void SetHack_P1_Crosshair_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //fld dword ptr[_P1_X_Address] CaveMemory.Write_StrBytes("D9 05"); byte[] b = BitConverter.GetBytes(_P1_X_Crosshair_CaveAddress); CaveMemory.Write_Bytes(b); //fstp dword ptr[edx+24] CaveMemory.Write_StrBytes("D9 5A 24"); //fld dword ptr[_P1_Y_Address] CaveMemory.Write_StrBytes("D9 05"); b = BitConverter.GetBytes(_P1_Y_Crosshair_CaveAddress); CaveMemory.Write_Bytes(b); //fstp dword ptr[edx+28] CaveMemory.Write_StrBytes("D9 5A 28"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Crosshair_Injection_Return_Offset); Logger.WriteLog("Adding P1_InGame_Crosshair CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Crosshair_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Crosshair_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1_X_Shoot_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov eax [esp + 54h + arg_8] CaveMemory.Write_StrBytes("8B 4C 24 60"); //fstp dword ptr[esi+48] CaveMemory.Write_StrBytes("D9 5E 48"); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_X_Shoot_Address CaveMemory.Write_StrBytes("A1"); byte[] b = BitConverter.GetBytes(_P1_X_Shoot_CaveAddress); CaveMemory.Write_Bytes(b); //push eax CaveMemory.Write_StrBytes("50"); //fld dword ptr[esp] CaveMemory.Write_StrBytes("D9 04 24"); //pop eax CaveMemory.Write_StrBytes("58"); //fstp dword ptr[esi+48] CaveMemory.Write_StrBytes("D9 5E 48"); //pop eax CaveMemory.Write_StrBytes("58"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_X_Injection_Return_Offset); Logger.WriteLog("Adding P1_InGame_X CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_X_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_X_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } private void SetHack_P1_Y_Shoot_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push eax CaveMemory.Write_StrBytes("50"); //mov eax, _P1_Y_Shoot_Address CaveMemory.Write_StrBytes("A1"); byte[] b = BitConverter.GetBytes(_P1_Y_Shoot_CaveAddress); CaveMemory.Write_Bytes(b); //push eax CaveMemory.Write_StrBytes("50"); //fld dword ptr[esp] CaveMemory.Write_StrBytes("D9 04 24"); //pop eax CaveMemory.Write_StrBytes("58"); //fstp dword ptr[esi+4C] CaveMemory.Write_StrBytes("D9 5E 4C"); //pop eax CaveMemory.Write_StrBytes("58"); //cmp byte ptr [esi+000000CC],00 CaveMemory.Write_StrBytes("80 BE CC 00 00 00 00"); //return CaveMemory.Write_jmp((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Y_Injection_Return_Offset); Logger.WriteLog("Adding P1_InGame_Y CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = CaveMemory.CaveAddress - ((UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Y_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemory(ProcessHandle, (UInt32)_RldGameDll_ModuleBaseAddress + _P1_InGame_Y_Injection_Offset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// csControllerActor::Shoot() calls csControllerActorPlayer::Shoot() /// Intercepting the call will make us create a recoil signal+ /// private void SetHack_Recoil_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov byte ptr[_P1_Recoil_CaveAddress], 1 CaveMemory.Write_StrBytes("C6 05"); byte[] b = BitConverter.GetBytes(_P1_Recoil_CaveAddress); CaveMemory.Write_Bytes(b); CaveMemory.Write_StrBytes("01"); //mov ecx, edi CaveMemory.Write_StrBytes("8B CF"); //fstp dword ptr [esp] CaveMemory.Write_StrBytes("D9 1C 24"); //Inject it CaveMemory.InjectToOffset(_RldGameDll_ModuleBaseAddress, _Recoil_InjectionStruct, "Recoil"); } /// /// Reading the result of GetRemainingBullets() call in the ActorHud::StatusBar_AdvanceTime() loop /// private void SetHack_Ammo_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //mov [_P1_Ammo_CaveAddress], eax CaveMemory.Write_StrBytes("A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Ammo_CaveAddress)); //mov edx,[ecx] CaveMemory.Write_StrBytes("8B 11"); //mov ebp,eax CaveMemory.Write_StrBytes("8B E8"); //mov eax,[edx+60] CaveMemory.Write_StrBytes("8B 42 60"); //Inject it CaveMemory.InjectToOffset(_RldGameDll_ModuleBaseAddress, _Ammo_InjectionStruct, "Ammo"); } /// /// Set values to [-1.0; -1.0] to display crosshair in ActorHud::Crosshair_SetPosition() function /// private void SetHack_NoCrosshair_V2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); List Buffer = new List(); //push edi CaveMemory.Write_StrBytes("57"); //mov edi,[esp+10] CaveMemory.Write_StrBytes("8B 7C 24 10"); //mov [edi],BF800000 { -1.00 } CaveMemory.Write_StrBytes("C7 07 00 00 80 BF"); //mov [edi+04],BF800000 { -1.00 } CaveMemory.Write_StrBytes("C7 47 04 00 00 80 BF"); //Inject it CaveMemory.InjectToOffset(_RldGameDll_ModuleBaseAddress, _NoCrosshair_InjectionStruct, "No Crosshair"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(_P1_X_Menu_Value); WriteBytes(_P1_X_Menu_CaveAddress, buffer); buffer = BitConverter.GetBytes(_P1_Y_Menu_Value); WriteBytes(_P1_Y_Menu_CaveAddress, buffer); buffer = BitConverter.GetBytes(_P1_Crosshair_X_Value); WriteBytes(_P1_X_Crosshair_CaveAddress, buffer); buffer = BitConverter.GetBytes(_P1_Crosshair_Y_Value); WriteBytes(_P1_Y_Crosshair_CaveAddress, buffer); buffer = BitConverter.GetBytes(_P1_X_Shoot_Value); WriteBytes(_P1_X_Shoot_CaveAddress, buffer); buffer = BitConverter.GetBytes(_P1_Y_Shoot_Value); WriteBytes(_P1_Y_Shoot_CaveAddress, buffer); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) SendKeyDown(_P1_HoldBreath_DIK); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) SendKeyUp(_P1_HoldBreath_DIK); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Custom Recoil will simply be read on memory and reset //the codecave injected will update it for the "ON" state byte P1_RecoilState = ReadByte(_P1_Recoil_CaveAddress); SetOutputValue(OutputId.P1_CtmRecoil, P1_RecoilState); if (P1_RecoilState == 1) WriteByte(_P1_Recoil_CaveAddress, 0x00); int P1_ammo = BitConverter.ToInt32(ReadBytes(_P1_Ammo_CaveAddress, 4), 0); SetOutputValue(OutputId.P1_Ammo, P1_ammo); } #endregion } } ================================================ FILE: DemulShooter/Games/Game__Unity.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.IPC; namespace DemulShooter.Games { public class Game__Unity : Game { protected class Base_InputData : DsTcpData { public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public Base_InputData(int PlayerNumber) : base(PlayerNumber) { } } protected class Base_OutputData : DsTcpData { public Base_OutputData(int PlayerNumber) : base(PlayerNumber) { } } protected string _MainWindowTitle = string.Empty; protected DsTcp_Client _Tcpclient; protected Base_OutputData _OutputData; protected Base_InputData _InputData; /// /// Constructor /// public Game__Unity(String RomName, String TargetProcessName, String MainWindowTitle) : base(RomName, TargetProcessName) { _MainWindowTitle = MainWindowTitle; } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals(_MainWindowTitle)) { String AssemblyDllPath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", _Target_Process_Name + @"_Data\Managed\Assembly-CSharp.dll"); CheckMd5(AssemblyDllPath); //Start TcpClient to dial with Unity Game _Tcpclient = new DsTcp_Client("127.0.0.1", DsTcp_Client.DS_TCP_CLIENT_PORT); _Tcpclient.PacketReceived += DsTcp_Client_PacketReceived; _Tcpclient.TcpConnected += DsTcp_client_TcpConnected; _Tcpclient.Connect(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } ~Game__Unity() { if (_Tcpclient != null) _Tcpclient.Disconnect(); } #region Screen /// /// Default GameScale functio, just sending coordinate in a similar way Unity Input.MousePosition() would work /// /// /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Coordinates are Window size range, but inverted Y int X_Value = PlayerData.RIController.Computed_X; int Y_Value = (int)TotalResY - PlayerData.RIController.Computed_Y; if (X_Value < 0) X_Value = 0; if (Y_Value < 0) Y_Value = 0; if (X_Value > (int)TotalResX) X_Value = (int)TotalResX; if (Y_Value > (int)TotalResY) Y_Value = (int)TotalResY; PlayerData.RIController.Computed_X = X_Value; PlayerData.RIController.Computed_Y = Y_Value; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion /// /// Sending a TCP message on connection /// protected void DsTcp_client_TcpConnected(Object Sender, EventArgs e) { if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; if (_DisableInputHack) _InputData.EnableInputsHack = 0; else _InputData.EnableInputsHack = 1; _Tcpclient.SendMessage(_InputData.ToByteArray()); } protected virtual void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { } } } ================================================ FILE: DemulShooter/Program.cs ================================================ using System; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Globalization; using System.Collections.Generic; namespace DemulShooter { class Program { [DllImport("kernel32.dll")] static extern bool AttachConsole(int dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] static extern bool FreeConsole(); [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); const uint WM_CHAR = 0x0102; const int VK_ENTER = 0x0D; const int ATTACH_PARENT_PROCESS = -1; static IntPtr ConsoleHwnd = IntPtr.Zero; static String strTarget = string.Empty; static String strRom = string.Empty; static void Main(string[] args) { // Attach to the parent process via AttachConsole SDK call AttachConsole(ATTACH_PARENT_PROCESS); ConsoleHwnd = GetConsoleWindow(); bool isVerbose = false; bool isTrace = false; Dictionary _SystemTargets = new Dictionary (){ {"arcadepc","Various modern PC Arcade Games without dedicated system"}, {"chihiro","Cxbx-Reloaded"}, {"coastal","Coastal Arcade"}, {"demul057","Demul v0.57"}, {"demul058","Demul v0.582"}, {"demul07a","Demul v0.7a 180428"}, {"dolphin5","Dolphin v5.0"}, {"es4","Namco ES4 games"}, {"gamewax","Gamewax Arcade"}, {"globalvr","Global VR"}, {"konami","Konami Arcade"}, {"lindbergh","TeknoParrot Loader"}, {"model2","Nebula Model2Emulator v1.1a"}, {"ppmarket","P&P Marketing Arcade"}, {"rawthrill","TeknoParrot Loader"}, {"ringedge2", "RingEdge2 Games"}, {"ringwide","TeknoParrot Loader / JConfig"}, {"ttx","Taito Type X"}, {"windows","Windows games"} }; Dictionary _ArcadepcRoms = new Dictionary(){ {"gbusters","Ghostbusters"}, {"pvz","Plants VS Zombies : The Last stand"}, {"rhood", "Robin Hood"}, {"wws","Wild West Shootout"} }; Dictionary _ChihiroRoms = new Dictionary(){ {"gsquad","Ghost Squad"}, {"hod3","House Of The Dead 3"}, {"vcop3","Virtua Cop 3"}, {"vcop3_old","Virtua Cop 3 (main Cxbx branch)"} }; Dictionary _DemulRoms = new Dictionary(){ {"braveff","Brave Fire Fighters"}, {"claychal","SEGA Clay Challenge"}, {"confmiss","Confidential Mission"}, {"deathcox","Death Crimson OX"}, {"hotd2","House of the Dead II (USA)"}, {"hotd2o","House of the Dead II"}, {"hotd2p","House of the Dead II (Prototype)"}, {"lupinsho","Lupin the Third"}, {"manicpnc","Manic Panic Ghosts"}, {"mok","The Maze of the Kings"}, {"ninjaslt","Ninja Assault (World)"}, {"ninjaslta","Ninja Assault (Asia)"}, {"ninjasltj","Ninja Assault (Japan)"}, {"ninjasltu","Ninja Assault (US)"}, {"pokasuka","Pokasuka Ghost"}, {"rangrmsn","Ranger Mission"}, {"sprtshot","Sports Shooting USA"}, {"xtrmhunt","Extreme Hunting"}, {"xtrmhnt2","Extreme Hunting 2"} }; Dictionary _Es4Roms = new Dictionary(){ {"pblankx","Point Blank X"} }; Dictionary _GamewaxRoms = new Dictionary(){ {"akuma","Akuma Mortis Immortal"} }; Dictionary _GlobalVrRoms = new Dictionary(){ {"aliens","Aliens Extermination Dehasped (2nd dump, x86 and x64, no need for VM)"}, {"farcry","Far Cry : Paradise Lost"}, {"fearland","Fright Fear Land"} }; Dictionary _KonamiRoms = new Dictionary(){ {"coop9","Cooper's 9"}, {"gashn2","Gashaaaan Refills"}, {"hcv","Castlevania Arcade"}, {"le3","Lethal Enforcers 3"}, {"wartran","Wartran Troopers"} }; Dictionary _LindberghRoms = new Dictionary(){ {"2spicy","Too Spicy"}, {"gsevo","Ghost Squad Evolution"}, {"hotd4","House of The Dead 4"}, {"hotd4sp","House of The Dead 4 : Special"}, {"hotdex","House of The Dead : EX"}, {"lgj","Let's Go Jungle"}, {"lgjsp","Let's Go Jungle Special"}, {"rambo","Rambo Arcade"} }; Dictionary _Model2Roms = new Dictionary(){ {"bel","Behind Ennemy Lines"}, {"gunblade","Gunblade NY"}, {"hotd","House of the Dead"}, {"rchase2","Rail Chase 2"}, {"vcop","Virtua Cop"}, {"vcop2","Virtua Cop 2"} }; Dictionary _PpMarketRoms = new Dictionary(){ {"policetr2","Police Trainer 2"}, }; Dictionary _RawThrillRoms = new Dictionary(){ {"aa","Aliens Armageddon"}, {"jp","Jurassic Park"}, {"ts","Terminator Salvation"}, {"ttg","Target : Terror - GOLD"}, {"wd","Walking Dead"} }; Dictionary _RingEdge2Roms = new Dictionary(){ {"tsr","Transformers : Shadow Rising"} }; Dictionary _RingSystemRoms = new Dictionary(){ {"sgg","SEGA golden guns"}, {"lgi","Let's Go Island: Lost on the Island of Tropics"}, {"lgi3d","Let's go Island 3D"}, {"mng","Medaru no Gunman"}, {"og","Operation G.H.O.S.T"}, {"sdr","SEGA Dream Raiders"}, {"tb","Target Bravo" }, {"tha","Transformers : Human Alliance"} }; Dictionary _TtxRoms = new Dictionary(){ {"bkbs","Block King Ball Shooter"}, {"sha","Silent Hill Arcade"}, {"eadp", "Elevator Action : Death Parade"}, {"gattack4","Gaia Attack 4"}, {"gsoz","Gundam : Spirit of Zeon"}, {"gsoz2p", "Gundam : Spirit of Zeon (Dual Player)"}, {"hmuseum","Haunted Museum"}, {"hmuseum2","Haunted Museum 2"}, {"mgungun2","Music Gun Gun! 2"} }; Dictionary _WindowsRoms = new Dictionary(){ {"ads","Alien Disco Safari"}, {"artdead","Art Is Dead"}, {"bugbust","Bug Busters"}, {"coltwws","Colt's Wild West Shootout"}, {"friction","Friction"}, {"hfa", "Heavy Fire Afghanistan"}, {"hfa2p","Heavy Fire Afghanistan (dual Player)"}, {"hfss","Heavy Fire Shaterred Spear"}, {"hfss2p", "Heavy Fire Shaterred Spear (dual player)"}, {"hod2pc","House of the Dead II"}, {"hod3pc","House of the Dead III"}, {"hodo","House of the Dead Overkill"}, {"madbul", "Mad Bullets"}, {"pgbeat", "Project Green Beat"}, {"reload","Reload"} //{"spray", "Spray Gun"} }; if (args.Length > 0) { for (int i = 0; i < args.Length; i++) { if (args[i].ToLower().Equals("-h") || args[i].ToLower().Equals("--help") || args[i].ToLower().Equals("-?")) { Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("DemulShooter v" + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString()); DateTime CompileTime = new DateTime(Builtin.CompileTime, DateTimeKind.Utc); String CompileDate = CompileTime.ToString("MMMM d", new CultureInfo("en-US")); CompileDate = String.Format("{0}{1}, {2}", CompileDate, GetDaySuffix(CompileTime.Day), CompileTime.ToString("yyyy")); Console.WriteLine("Build date : " + CompileDate); Console.WriteLine(""); Console.WriteLine("usage : DemulShooter.exe -target=[target] -rom=[rom] [options]"); Console.WriteLine(""); Console.WriteLine("Supported [target] :"); DisplayDictionnaryList(_SystemTargets); Console.WriteLine(""); Console.WriteLine("Supported [rom] :"); Console.WriteLine("Arcadepc roms :"); DisplayDictionnaryList(_ArcadepcRoms); Console.WriteLine(""); Console.WriteLine("Chihiro roms :"); DisplayDictionnaryList(_ChihiroRoms); Console.WriteLine(""); Console.WriteLine("Demul roms :"); DisplayDictionnaryList(_DemulRoms); Console.WriteLine(""); Console.WriteLine("Dolphin roms :"); Console.WriteLine(" - Parameter not used - "); Console.WriteLine(""); Console.WriteLine("ES4 roms :"); DisplayDictionnaryList(_Es4Roms); Console.WriteLine(""); Console.WriteLine("Global VR Games :"); DisplayDictionnaryList(_GlobalVrRoms); Console.WriteLine(""); Console.WriteLine("Gamewax Games :"); DisplayDictionnaryList(_GamewaxRoms); Console.WriteLine(""); Console.WriteLine("Konami Games :"); DisplayDictionnaryList(_KonamiRoms); Console.WriteLine(""); Console.WriteLine("Lindbergh roms :"); DisplayDictionnaryList(_LindberghRoms); Console.WriteLine(""); Console.WriteLine("Model2 roms :"); DisplayDictionnaryList(_Model2Roms); Console.WriteLine(""); Console.WriteLine("P&P Marketing roms :"); DisplayDictionnaryList(_PpMarketRoms); Console.WriteLine(""); Console.WriteLine("Raw Thrill roms :"); DisplayDictionnaryList(_RawThrillRoms); Console.WriteLine(""); Console.WriteLine("Ringwide roms :"); DisplayDictionnaryList(_RingSystemRoms); Console.WriteLine(""); Console.WriteLine("Ringedge2 roms :"); DisplayDictionnaryList(_RingEdge2Roms); Console.WriteLine(""); Console.WriteLine("Taito Type X Games :"); DisplayDictionnaryList(_TtxRoms); Console.WriteLine(""); Console.WriteLine("Windows Games :"); DisplayDictionnaryList(_WindowsRoms); Console.WriteLine(""); Console.WriteLine("Supported [options] :"); Console.WriteLine(" -ddinumber \t\tDolphin's DirectInput number for P2 device"); Console.WriteLine(" -hardffl \t\tAlternative gameplay for Fright Fear Land / Haunted Museum 2"); Console.WriteLine(" -noautofire \t\tDisable in-game autofire for SEGA Golden Gun"); Console.WriteLine(" -nocrosshair \t\tHide in-game crosshair (game dependant)"); Console.WriteLine(" -nogun \t\tHide in-game weapon model (game dependant)"); Console.WriteLine(" -noinput \t\tDisable any input hack"); Console.WriteLine(" -noresize \t\tFix Demul exit fullscreen bug, when shooting upper left corner"); Console.WriteLine(" -pname=[ProcessName]\tchange the name of the expected executable instead of expecting specific name like game.exe"); Console.WriteLine(" -profile=[ConfigFile]\tspecify a config file name for DemulShooter to load"); Console.WriteLine(" -widescreen \t\tDemul Widescreen hack"); Console.WriteLine(" -? -h --help\t\tShow this help"); Console.WriteLine(" -v --verbose\t\tEnable output to log file"); ExitConsole(); } else if (args[i].ToLower().Equals("-v") || args[i].ToLower().Equals("--verbose")) { isVerbose = true; } else if (args[i].ToLower().Equals("-t") || args[i].ToLower().Equals("--trace")) { isTrace = true; } else if (args[i].ToLower().StartsWith("-target")) { strTarget = (args[i].ToLower().Split('='))[1].Trim(); if (strTarget != "wip") { if (!CheckParameter(strTarget, _SystemTargets)) { Console.WriteLine("\n\n\tUnsupported [target] parameter : \"" + strTarget + "\". See help for supported targets"); ExitConsole(); } } } } if (strTarget == String.Empty) { Console.WriteLine("\n\n\tNo [target] parameter specified. See help for supported targets"); ExitConsole(); } for (int i = 0; i < args.Length; i++) { if (args[i].ToLower().StartsWith("-rom")) { strRom = (args[i].ToLower().Split('='))[1].Trim(); if (strTarget.StartsWith("chihiro")) { if (!CheckParameter(strRom, _ChihiroRoms)) { Console.WriteLine("\n\n\tUnsupported Chihiro rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.StartsWith("arcadepc")) { if (!CheckParameter(strRom, _ArcadepcRoms)) { Console.WriteLine("Unsupported Arcadepc rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.StartsWith("demul")) { if (!CheckParameter(strRom, _DemulRoms)) { Console.WriteLine("Unsupported Demul rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.StartsWith("es4")) { if (!CheckParameter(strRom, _Es4Roms)) { Console.WriteLine("Unsupported ES4 rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("gamewax")) { if (!CheckParameter(strRom, _GamewaxRoms)) { Console.WriteLine("Unsupported Gamewax rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("globalvr")) { if (!CheckParameter(strRom, _GlobalVrRoms)) { Console.WriteLine("Unsupported GlobalVR rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("konami")) { if (!CheckParameter(strRom, _KonamiRoms)) { Console.WriteLine("Unsupported Konami rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.StartsWith("lindbergh")) { if (!CheckParameter(strRom, _LindberghRoms)) { Console.WriteLine("Unsupported Lindbergh rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("model2")) { if (!CheckParameter(strRom, _Model2Roms)) { Console.WriteLine("Unsupported Model2 rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("ppmarket")) { if (!CheckParameter(strRom, _PpMarketRoms)) { Console.WriteLine("Unsupported P&P Marketing rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("rawthrill")) { if (!CheckParameter(strRom, _RawThrillRoms)) { Console.WriteLine("Unsupported Raw Thrill rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("ringedge2")) { if (!CheckParameter(strRom, _RingEdge2Roms)) { Console.WriteLine("Unsupported RingEdge2 rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("ringwide")) { if (!CheckParameter(strRom, _RingSystemRoms)) { Console.WriteLine("Unsupported RingWide rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("ttx")) { if (!CheckParameter(strRom, _TtxRoms)) { Console.WriteLine("Unsupported Taito Type X rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("windows")) { if (!CheckParameter(strRom, _WindowsRoms)) { Console.WriteLine("Unsupported Windows rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } } } if (strRom == String.Empty && strTarget != "dolphin5") { Console.WriteLine("\n\n\tNo [Rom] parameter specified. See help for supported targets"); ExitConsole(); } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new DemulShooterWindow(args, isVerbose, isTrace)); } else { Console.WriteLine("\n\n\tMissing parameter!\n\tSee help with DemulShooter.exe -h for correct usage."); ExitConsole(); } } static string GetDaySuffix(int day) { switch (day) { case 1: case 21: case 31: return "st"; case 2: case 22: return "nd"; case 3: case 23: return "rd"; default: return "th"; } } static void DisplayDictionnaryList(Dictionary list) { foreach (KeyValuePair item in list) { Console.Write(" " + item.Key); for (int t = 0; t < (11 - item.Key.Length); t++) Console.Write(" "); Console.WriteLine(item.Value); } } static bool CheckParameter(String param, Dictionary list) { foreach (KeyValuePair Item in list) { if (param.Equals(Item.Key)) return true; } return false; } static void ExitConsole() { FreeConsole(); //Send a {ENTER} key message to the attached console to show prompt SendMessage(ConsoleHwnd, WM_CHAR, (IntPtr)VK_ENTER, IntPtr.Zero); Environment.Exit(0); } } } ================================================ FILE: DemulShooter/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("DemulShooter")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DemulShooter")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("5ca8460d-6a53-46ca-8f46-937bb728416c")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.5.0")] ================================================ FILE: DemulShooter/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. // //------------------------------------------------------------------------------ namespace DemulShooter.Properties { using System; /// /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. /// // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder // à l'aide d'un outil, tel que ResGen ou Visual Studio. // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen // avec l'option /str ou régénérez votre projet VS. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DemulShooter.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Remplace la propriété CurrentUICulture du thread actuel pour toutes /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Recherche une ressource localisée de type System.Drawing.Icon semblable à (Icône). /// internal static System.Drawing.Icon DemulShooter_Hooked_Icon { get { object obj = ResourceManager.GetObject("DemulShooter_Hooked_Icon", resourceCulture); return ((System.Drawing.Icon)(obj)); } } /// /// Recherche une ressource localisée de type System.Drawing.Icon semblable à (Icône). /// internal static System.Drawing.Icon DemulShooter_Icon { get { object obj = ResourceManager.GetObject("DemulShooter_Icon", resourceCulture); return ((System.Drawing.Icon)(obj)); } } /// /// Recherche une ressource localisée de type System.Drawing.Icon semblable à (Icône). /// internal static System.Drawing.Icon DemulShooter_UnHooked_Icon { get { object obj = ResourceManager.GetObject("DemulShooter_UnHooked_Icon", resourceCulture); return ((System.Drawing.Icon)(obj)); } } /// /// Recherche une chaîne localisée semblable à [Options] ///UseInitBeep=0 ///Log=0 ///BackgroundMode=0 ///PadCount=0 /// ///[PAD1] ///A=T ///B=U ///Left Trigger=Y ///Right Trigger=T ///D-Pad Up=Up ///D-Pad Down=Down ///D-Pad Left=Left ///D-Pad Right=Right ///Right Analog Left=I ///Right Analog Down=O ///Right Analog Right=P ///. /// internal static string x360kb_hfirea1p { get { return ResourceManager.GetString("x360kb_hfirea1p", resourceCulture); } } /// /// Recherche une chaîne localisée semblable à [Options] ///UseInitBeep=0 ///Log=0 ///BackgroundMode=0 ///PadCount=1 /// ///[PAD1] ///A=T ///B=U ///Left Trigger=Y ///Right Trigger=T ///D-Pad Up=Up ///D-Pad Down=Down ///D-Pad Left=Left ///D-Pad Right=Right ///Right Analog Left=I ///Right Analog Down=O ///Right Analog Right=P ///. /// internal static string x360kb_hfirea2p { get { return ResourceManager.GetString("x360kb_hfirea2p", resourceCulture); } } } } ================================================ FILE: DemulShooter/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\DemulShooter_Hooked_Icon.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\DemulShooter_Icon.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\DemulShooter_UnHooked_Icon.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\x360kb_hfirea1p.ini;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 ..\Resources\x360kb_hfirea2p.ini;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 ================================================ FILE: DemulShooter/Properties/Settings.Designer.cs ================================================ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. // //------------------------------------------------------------------------------ namespace DemulShooter.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("ououiouo")] public string Setting { get { return ((string)(this["Setting"])); } } [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("65")] public byte Setting1 { get { return ((byte)(this["Setting1"])); } } } } ================================================ FILE: DemulShooter/Properties/Settings.settings ================================================  ououiouo 65 ================================================ FILE: DemulShooter/Resources/x360kb_hfirea1p.ini ================================================ [Options] UseInitBeep=0 Log=0 BackgroundMode=0 PadCount=0 [PAD1] A=T B=U Left Trigger=Y Right Trigger=T D-Pad Up=Up D-Pad Down=Down D-Pad Left=Left D-Pad Right=Right Right Analog Left=I Right Analog Down=O Right Analog Right=P ================================================ FILE: DemulShooter/Resources/x360kb_hfirea2p.ini ================================================ [Options] UseInitBeep=0 Log=0 BackgroundMode=0 PadCount=1 [PAD1] A=T B=U Left Trigger=Y Right Trigger=T D-Pad Up=Up D-Pad Down=Down D-Pad Left=Left D-Pad Right=Right Right Analog Left=I Right Analog Down=O Right Analog Right=P ================================================ FILE: DemulShooter/app.config ================================================ ================================================ FILE: DemulShooter.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.10.35122.118 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsDiag", "DsDiag\DsDiag.csproj", "{A42BEED3-14C0-4034-ABD9-75C3D70B02A2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemulShooter_GUI", "DemulShooter_GUI\DemulShooter_GUI.csproj", "{20DE55D0-833D-45CA-96C8-DF3BEDB394A8}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemulShooter", "DemulShooter\DemulShooter.csproj", "{D99B7B7F-8B86-4AA0-A6F8-62A81F603599}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemulShooterX64", "DemulShooterX64\DemulShooterX64.csproj", "{E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsCore", "DsCore\DsCore.csproj", "{BD88CC4D-2944-43F8-85C3-A274A45487AF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|Mixed Platforms = Release|Mixed Platforms Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Debug|Any CPU.ActiveCfg = Debug|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Debug|Mixed Platforms.Build.0 = Debug|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Debug|x64.ActiveCfg = Debug|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Debug|x86.ActiveCfg = Debug|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Debug|x86.Build.0 = Debug|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Release|Any CPU.ActiveCfg = Release|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Release|Mixed Platforms.ActiveCfg = Release|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Release|Mixed Platforms.Build.0 = Release|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Release|x64.ActiveCfg = Release|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Release|x86.ActiveCfg = Release|x86 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2}.Release|x86.Build.0 = Release|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|Mixed Platforms.Build.0 = Debug|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|x64.ActiveCfg = Debug|x64 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|x64.Build.0 = Debug|x64 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|x86.ActiveCfg = Debug|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Debug|x86.Build.0 = Debug|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|Any CPU.Build.0 = Release|Any CPU {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|Mixed Platforms.ActiveCfg = Release|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|Mixed Platforms.Build.0 = Release|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|x64.ActiveCfg = Release|x64 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|x64.Build.0 = Release|x64 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|x86.ActiveCfg = Release|x86 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8}.Release|x86.Build.0 = Release|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Debug|Any CPU.ActiveCfg = Debug|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Debug|Mixed Platforms.Build.0 = Debug|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Debug|x64.ActiveCfg = Debug|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Debug|x86.ActiveCfg = Debug|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Debug|x86.Build.0 = Debug|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Release|Any CPU.ActiveCfg = Release|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Release|Mixed Platforms.ActiveCfg = Release|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Release|Mixed Platforms.Build.0 = Release|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Release|x64.ActiveCfg = Release|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Release|x86.ActiveCfg = Release|x86 {D99B7B7F-8B86-4AA0-A6F8-62A81F603599}.Release|x86.Build.0 = Release|x86 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Debug|Any CPU.ActiveCfg = Debug|x86 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Debug|Mixed Platforms.Build.0 = Debug|x64 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Debug|x64.ActiveCfg = Debug|x86 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Debug|x86.ActiveCfg = Debug|x86 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Debug|x86.Build.0 = Debug|x86 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Release|Any CPU.ActiveCfg = Release|x86 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Release|Mixed Platforms.ActiveCfg = Release|x64 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Release|Mixed Platforms.Build.0 = Release|x64 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Release|x64.ActiveCfg = Release|x86 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Release|x86.ActiveCfg = Release|x64 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4}.Release|x86.Build.0 = Release|x64 {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Debug|Any CPU.ActiveCfg = Debug|x86 {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Debug|x64.ActiveCfg = Debug|x86 {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Debug|x86.ActiveCfg = Debug|x86 {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Debug|x86.Build.0 = Debug|x86 {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Release|Any CPU.ActiveCfg = Release|x86 {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Release|Mixed Platforms.Build.0 = Release|Any CPU {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Release|x64.ActiveCfg = Release|x86 {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Release|x86.ActiveCfg = Release|Any CPU {BD88CC4D-2944-43F8-85C3-A274A45487AF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: DemulShooterX64/DemulShooterWindowX64.cs ================================================ using System; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Security.Principal; using System.Threading; using System.Windows.Forms; using DemulShooterX64.Games; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); public class DemulShooterWindowX64 : ApplicationContext { IntPtr _RawMessageWnd_hWnd = IntPtr.Zero; public IntPtr hWnd { get { return _RawMessageWnd_hWnd; } } private WndProc delegWndProc; private static DemulShooterWindowX64 _This; private const string DEMULSHOOTER_CONF_FILENAME = "config.ini"; private string _UserDefinedIniFile = string.Empty; //Timer for Hooking TimeOut private System.Timers.Timer _TimerHookTimeout; //Tray Icon private ContextMenu _TrayIconMenu; private System.Windows.Forms.NotifyIcon _TrayIcon; //Available RawInput devices (filters by thir Type) private RawInputController[] _AvailableControllers; //Low-Level Hooks protected Win32API.HookProc _MouseHookProc; protected IntPtr _MouseHookID = IntPtr.Zero; private IntPtr _KeyboardHookID; private Win32API.HookProc _KeyboardHookProc; //Output (MameHooker) private Wm_OutputHelper _Wm_OutputHelper; //Output (Network) private Net_OutputHelper _Net_OutputHelper; private Thread _OutputUpdateLoop; //Game options private Game _Game; private string _Rom = String.Empty; private string _Target = String.Empty; private bool _NoInput = false; private double _ForceScalingX = 1.0; //InterProcessCommunication (Memory Mapped Files) private const String DEMULSHOOTER_INPUTS_MMF_NAME = "DemulShooter_MMF_Inputs"; private const String DEMULSHOOTER_OUTPUTS_MMF_NAME = "DemulShooter_MMF_Outputs"; private const String DEMULSHOOTER_INPUTS_MUTEX_NAME = "DemulShooter_Inputs_Mutex"; private const String DEMULSHOOTER_OUTPUTS_MUTEX_NAME = "DemulShooter_Outputs_Mutex"; private bool _EnableInputsIpc = false; private bool _EnableOutputsIpc = false; private DsCore.IPC.MemoryMappedFileHelper_Old _MMF_Inputs; private DsCore.IPC.MemoryMappedFileHelper_Old _MMF_Outputs; public DemulShooterWindowX64(string[] Args, bool isVerbose, bool isTrace) { _This = this; //Stop program if Demulshooter already running Process[] pDemulShooter = Process.GetProcessesByName("DemulShooterX64"); if (pDemulShooter.Length > 1) { MessageBox.Show("Another instance of DemulShooterX64 is already running.\nPlease terminate it before launching a new one", "DemulShooterX64", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(0); } //Creating TrayIcon and TrayIcon "Exit" menu Application.ApplicationExit += new EventHandler(OnApplicationExit); InitializeComponent(); //Creating the timeout Timer _TimerHookTimeout = new System.Timers.Timer(); _TimerHookTimeout.Enabled = false; _TimerHookTimeout.Elapsed += tHookTimeOut_Elapsed; Logger.IsEnabled = isVerbose; Logger.IsTraceEnabled = isTrace; Logger.InitLogFileName(); Logger.WriteLog(""); Logger.WriteLog("---------------- Program Start -- DemulShooterX64 v" + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString() + " ----------------"); // Parsing commandline arguments for (int i = 0; i < Args.Length; i++) { Logger.WriteLog("Cmdline arg " + i + " : " + Args[i]); if (Args[i].ToLower().StartsWith("-forcescalingx")) { String sX = (Args[i].Split('='))[1].Trim(); try { if (sX.Contains("/") && sX.Split('/').Length > 1) { double d1 = Double.Parse(sX.Split('/')[0]); double d2 = Double.Parse(sX.Split('/')[1]); _ForceScalingX = d1 / d2; } else _ForceScalingX = Double.Parse(sX, CultureInfo.InvariantCulture); Logger.WriteLog("-ForceScalingX parameter set to " + _ForceScalingX.ToString()); } catch { Logger.WriteLog("Can't set -ForceScalingX option : " + sX + " is not a valid value"); } } else if (Args[i].ToLower().Equals("-ipcinputs")) { _EnableInputsIpc = true; } else if (Args[i].ToLower().Equals("-ipcoutputs")) { _EnableOutputsIpc = true; } else if (Args[i].ToLower().Equals("-noinput")) { _NoInput = true; } else if (Args[i].ToLower().StartsWith("-profile")) { _UserDefinedIniFile = (Args[i].Split('='))[1].Trim(); } else if (Args[i].ToLower().StartsWith("-rom")) { _Rom = (Args[i].Split('='))[1].Trim(); } else if (Args[i].ToLower().StartsWith("-target")) { _Target = (Args[i].Split('='))[1].Trim(); } } if (_TrayIcon != null) _TrayIcon.Text += "[" + _Target + "] [" + _Rom + "]"; Logger.WriteLog("Running as Administrator : " + IsRunningAsAdmin().ToString()); //Finding plugged devices _AvailableControllers = RawInputHelper.GetRawInputDevices(new RawInputDeviceType[] { RawInputDeviceType.RIM_TYPEHID, RawInputDeviceType.RIM_TYPEMOUSE }); Logger.WriteLog("Found " + _AvailableControllers.Length + " available RawInput devices :"); foreach (RawInputController c in _AvailableControllers) { try { Logger.WriteLog("[Handle=0x" + c.DeviceHandle.ToString("X16") + "] [" + c.DeviceType.ToString() + "] " + c.DeviceName); } catch (Exception ex) { Logger.WriteLog("ERROR : " + ex.Message.ToString()); } } //Reading config file to get parameters if (_UserDefinedIniFile != string.Empty) Configurator.GetInstance().ReadDsConfig(_UserDefinedIniFile); else Configurator.GetInstance().ReadDsConfig(AppDomain.CurrentDomain.BaseDirectory + @"\" + DEMULSHOOTER_CONF_FILENAME); foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { Logger.WriteLog("P" + Player.ID + " mode = " + Player.Mode); if (Player.Mode == PlayerSettings.PLAYER_MODE_RAWINPUT) { Logger.WriteLog("P" + Player.ID + " device = " + Player.DeviceName); foreach (RawInputController Controller in _AvailableControllers) { if (Controller.DeviceName == Player.DeviceName) { Player.RIController = Controller; Player.RIController.Selected_AxisX = Player.HidAxisX; Player.RIController.Selected_AxisY = Player.HidAxisY; Player.RIController.Selected_OnScreenTriggerButton = Player.HidButton_OnScreenTrigger; Player.RIController.Selected_ActionButton = Player.HidButton_Action; Player.RIController.Selected_OffScreenTriggerButton = Player.HidButton_OffScreenTrigger; Logger.WriteLog("P" + Player.ID + " device plugged and found, Handle = 0x" + Controller.DeviceHandle); break; } } } else Logger.WriteLog("P" + Player.ID + " Gamepad ID = " + Player.GamepadID); } //Setting up IPC for inputs/outputs if (_EnableInputsIpc) { _MMF_Inputs = new DsCore.IPC.MemoryMappedFileHelper_Old(DEMULSHOOTER_INPUTS_MUTEX_NAME); _MMF_Inputs.MMFInit(DEMULSHOOTER_INPUTS_MMF_NAME, 2048); } if (_EnableOutputsIpc) { _MMF_Outputs = new DsCore.IPC.MemoryMappedFileHelper_Old(DEMULSHOOTER_OUTPUTS_MUTEX_NAME); _MMF_Outputs.MMFInit(DEMULSHOOTER_OUTPUTS_MMF_NAME, 2048); } CreateRawMessageWindow(); //Register to RawInput thanks to the previously created window Handle RawInputDevice[] rid = new RawInputDevice[3]; rid[0].UsagePage = HidUsagePage.GENERIC; rid[0].Usage = HidUsage.Joystick; rid[0].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[0].hwndTarget = _RawMessageWnd_hWnd; rid[1].UsagePage = HidUsagePage.GENERIC; rid[1].Usage = HidUsage.Mouse; rid[1].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[1].hwndTarget = _RawMessageWnd_hWnd; rid[2].UsagePage = HidUsagePage.GENERIC; rid[2].Usage = HidUsage.Gamepad; rid[2].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[2].hwndTarget = _RawMessageWnd_hWnd; if (!Win32API.RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) { MessageBox.Show("Failed to register raw input device(s).", "DemulShooter Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Application.Exit(); } //Starting Mame-style output daemon if (Configurator.GetInstance().OutputEnabled) { Logger.WriteLog("Starting Output daemon..."); if (Configurator.GetInstance().Wm_OutputEnabled) { Logger.WriteLog("Creating Window Message Output Helper..."); _Wm_OutputHelper = new Wm_OutputHelper(_RawMessageWnd_hWnd); _Wm_OutputHelper.Start(); } if (Configurator.GetInstance().Net_OutputEnabled) { Logger.WriteLog("Creating Network Output Helper..."); _Net_OutputHelper = new Net_OutputHelper(_Rom); _Net_OutputHelper.Start(); } _OutputUpdateLoop = new Thread(new ThreadStart(ReadAndSendOutput_Thread)); _OutputUpdateLoop.Start(); } //Starting the fun... if (_Target.Length > 0 && (_Rom.Length > 0)) { //Install Low-Level mouse hook ApplyMouseHook(); //Install Low Level keyboard hook ApplyKeyboardHook(); /* //Running Xinput daemon if needed bool EnableXInputProc = false; foreach (ControllerDevice Device in _ControllerDevices) { if (Device.GamepadID != -1) { EnableXInputProc = true; break; } } if (EnableXInputProc) Bgw_XInput.RunWorkerAsync(); */ //Adrenaline Amusements Games if (_Target.Equals("arcadepc")) { switch (_Rom.ToLower()) { case "drk": { _Game = new Game_ArcadepcDrakon(_Rom.ToLower()); }break; case "eai": { _Game = new Game_ArcadepcElevatorActionInvasion(_Rom.ToLower()); }break; case "marss": { _Game = new Game_ArcadepcMarsSortie(_Rom.ToLower()); }; break; case "mib": { _Game = new Game_ArcadepcMIB(_Rom.ToLower()); }; break; case "misimp": { _Game = new Game_ArcadepcMissionImpossible(_Rom.ToLower()); }; break; case "nha": { _Game = new Game_ArcadepcNightHunterArcade(_Rom.ToLower()); } break; case "racramp": { _Game = new Game_ArcadepcRaccoonRampage(_Rom.ToLower()); } break; case "rha": { _Game = new Game_ArcadepcRha(_Rom.ToLower()); }; break; case "tra": { _Game = new Game_ArcadepcTra(_Rom.ToLower()); }; break; } } //SEGA ALLS Games else if (_Target.Equals("alls")) { switch (_Rom.ToLower()) { case "hodsd": { _Game = new Game_AllsHodSd(_Rom.ToLower()); }; break; } } //NAMCO ES3 Games else if (_Target.Equals("es3")) { switch (_Rom.ToLower()) { case "tc5": { _Game = new Game_Es3Tc5(_Rom.ToLower()); }; break; } } //Flycast games else if (_Target.Equals("flycast")) { if (_Rom.ToLower().Equals("confmiss") || _Rom.ToLower().StartsWith("deathcox") || _Rom.ToLower().StartsWith("hotd2") || _Rom.ToLower().Equals("lupinsho") || _Rom.ToLower().Equals("mok")) { _Game = new Game_FlycastNaomi(_Rom.ToLower()); } else if (_Rom.ToLower().StartsWith("ninjaslt")) { _Game = new Game_FlycastNinjaslt(_Rom.ToLower()); } /*else if (_Rom.ToLower().Equals("braveff")) { _Game = new Game_DemulHikaru(_Rom.ToLower(), _DemulVersion, _ForceXratio, _NoInput, isVerbose, _DisableWindow, _WidescreenHack); }*/ /*else if (_Rom.ToLower().Equals("manicpnc") || _Rom.ToLower().Equals("pokasuka")) { _Game = new Game_DemulManicpnc(_Rom.ToLower(), _ForceXratio, _NoInput, isVerbose, _DisableWindow, _WidescreenHack); }*/ else { _Game = new Game_FlycastAtomiswave(_Rom.ToLower()); } } else if (_Target.Equals("rpcs3")) { switch (_Rom.ToLower()) { case "deadstorm": { _Game = new Game_S357DeadStormPirates(_Rom.ToLower()); }; break; case "de4d": { _Game = new Game_S357DarkEscape(_Rom.ToLower()); }; break; case "razstorm": { _Game = new Game_S357RazingStorm(_Rom.ToLower()); } ; break; case "sailorz": { _Game = new Game_S357SailorZombie(_Rom.ToLower()); }; break; } } else if (_Target.Equals("rawthrill")) { switch (_Rom.ToLower()) { case "nerfa": { _Game = new Game_RtNerfArcade(_Rom.ToLower()); }; break; } } //SEGA NU Games else if (_Target.Equals("seganu")) { switch (_Rom.ToLower()) { case "lma": { _Game = new Game_NuLuigiMansion_v2(_Rom.ToLower()); }; break; } } //Windows games else if (_Target.Equals("windows")) { switch (_Rom.ToLower()) { case "bbhut": { _Game = new Game_WndBigBuckHunterUltimate(_Rom.ToLower()); } break; case "bhapc": { _Game = new Game_WndBhapc(_Rom.ToLower()); } break; case "dcop": { _Game = new Game_WndDcop(_Rom.ToLower()); } break; case "hotdra": { _Game = new Game_WndHotdremakeArcade(_Rom.ToLower()); } break; case "opwolfr": { _Game = new Game_WndOpWolfReturn(_Rom.ToLower()); }; break; } } //Wip Games else if (_Target.Equals("wip")) { switch (_Rom.ToLower()) { case "bullseye": { _Game = new Game_ArcadepcBullseye(_Rom.ToLower()); } ; break; case "drakon": { _Game = new Game_ArcadepcDrakon_NoPlugin(_Rom.ToLower()); }; break; case "dino": { _Game = new Game_ArcadepcDinoInvasion(_Rom.ToLower()); }; break; case "mechd": { _Game = new Game_ArcadepcMechaDino(_Rom.ToLower()); }; break; case "onept": { _Game = new Game_ArcadepcOnePoint(_Rom.ToLower()); }; break; case "skullos": { _Game = new Game_ArcadepcSkullOfShadow(_Rom.ToLower()); } ; break; case "topgun2": { _Game = new Game_ArcadepcTopGun2(_Rom.ToLower()); } ; break; case "wzombies": { _Game = new Game_ArcadepcWisdomZombies(_Rom.ToLower()); }; break; case "be": { _Game = new Game_WndBlueEstate(_Rom.ToLower()); }; break; default: break; } } _Game.OnGameHooked += new Game.GameHookedHandler(OnGameHooked); //starting the TimeOut Timer if (Configurator.GetInstance().HookTimeout != 0) { _TimerHookTimeout.Interval = (Configurator.GetInstance().HookTimeout * 1000); _TimerHookTimeout.Start(); } } } /// /// Check if user has Elevated rights (Admin) /// /// private bool IsRunningAsAdmin() { bool isElevated = false; try { using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) { WindowsPrincipal principal = new WindowsPrincipal(identity); isElevated = principal.IsInRole(WindowsBuiltInRole.Administrator); } return isElevated; } catch (Exception Ex) { Logger.WriteLog("Error checking Admin rights for current user : " + Ex.Message.ToString()); return false; } } public bool CreateRawMessageWindow() { delegWndProc = myWndProc; WNDCLASSEX wind_class = new WNDCLASSEX(); wind_class.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX)); wind_class.style = 0; wind_class.hbrBackground = IntPtr.Zero; wind_class.cbClsExtra = 0; wind_class.cbWndExtra = 0; wind_class.hInstance = Marshal.GetHINSTANCE(this.GetType().Module); ;// alternative: Process.GetCurrentProcess().Handle; wind_class.hIcon = IntPtr.Zero; wind_class.hCursor = IntPtr.Zero; wind_class.lpszMenuName = null; wind_class.lpszClassName = "RI_MsgLoop"; wind_class.lpfnWndProc = Marshal.GetFunctionPointerForDelegate(delegWndProc); wind_class.hIconSm = IntPtr.Zero; ushort regResult = Win32API.RegisterClassEx(ref wind_class); if (regResult == 0) { uint error = Win32API.GetLastError(); return false; } string wndClass = wind_class.lpszClassName; //This version worked and resulted in a non-zero hWnd //_hWnd = CreateWindowEx(0, regResult, "Hello Win32", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 300, 400, IntPtr.Zero, IntPtr.Zero, wind_class.hInstance, IntPtr.Zero); IntPtr HWND_MESSAGE = new IntPtr(-3); _RawMessageWnd_hWnd = Win32API.CreateWindowEx(0, regResult, "DemulShooter_RawInputWnd", 0, 0, 0, 0, 0, HWND_MESSAGE, IntPtr.Zero, wind_class.hInstance, IntPtr.Zero); if (hWnd == ((IntPtr)0)) { uint error = Win32API.GetLastError(); return false; } return true; //The explicit message pump is not necessary, messages are obviously dispatched by the framework. //However, if the while loop is implemented, the functions are called... Windows mysteries... //MSG msg; //while (GetMessage(out msg, IntPtr.Zero, 0, 0) != 0) //{ // TranslateMessage(ref msg); // DispatchMessage(ref msg); //} } /// /// Window Message-Loop /// private IntPtr myWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { if (Configurator.GetInstance().OutputEnabled && _Wm_OutputHelper != null) { if (msg == _Wm_OutputHelper.MameOutput_RegisterClient) { _Wm_OutputHelper.RegisterClient(wParam, (UInt32)lParam); } else if (msg == _Wm_OutputHelper.MameOutput_UnregisterClient) { _Wm_OutputHelper.UnregisterClient(wParam, (UInt32)lParam); } else if (msg == _Wm_OutputHelper.MameOutput_GetIdString) { uint Id = (uint)lParam; if (Id == 0) { _Wm_OutputHelper.SendIdString(wParam, _Rom, 0); _Wm_OutputHelper.RomNameSent = true; } else { if (_Game != null && _Game.Outputs.Count > 0) { String s = _Game.GetOutputDescriptionFromId(Id); _Wm_OutputHelper.SendIdString(wParam, s, Id); } } } } switch (msg) { case Win32Define.WM_INPUT: { ProcessRawInputMessage(lParam); } break; case Win32Define.WM_QUIT : { Logger.WriteLog("myWndProc() => WM_QUIT message received !"); }break; default: break; } return Win32API.DefWindowProc(hWnd, msg, wParam, lParam); } /// /// Processing of the RawInput message : /// - Detection of the device creating the event /// - Retrieve data /// - Scale Raw axis values to Screen -> ClientWindow -> Game values /// - Send axis values and buttons to the Game /// /// private void ProcessRawInputMessage(IntPtr RawInputHandle) { foreach (RawInputController Controller in _AvailableControllers) { if (Controller.isSourceOfRawInputMessage(RawInputHandle)) { foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { if (Player.DeviceName == Controller.DeviceName) { if (_Game != null && _Game.ProcessHooked) { Controller.ProcessRawInputData(RawInputHandle); Logger.WriteLog("RawData event for Player #" + Player.ID.ToString() + ":"); Logger.WriteLog("Device rawinput data (Hex) = [ " + Player.RIController.Computed_X.ToString("X8") + ", " + Player.RIController.Computed_Y.ToString("X8") + " ]"); //Overrriding RAWINPUT data (relative movement) for single mouse by a call to GetCursorPos WIN32 API //That way we can get mouse position as if it's Absolute position if (Controller.DeviceType == RawInputDeviceType.RIM_TYPEMOUSE && Controller.IsRelativeCoordinates) { Logger.WriteLog("Relative positionning detected, switching to cursor position :"); POINT p = new POINT(); if (Win32API.GetCursorPos(out p)) { Logger.WriteLog("WM_MOUSEMOVE Cursor position = [ " + p.X + "," + p.Y + " ]"); Player.RIController.Computed_X = p.X; Player.RIController.Computed_Y = p.Y; Logger.WriteLog("OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); } else { Logger.WriteLog("WM_MOUSEMOVE : GetCursorPos() returned error"); return; } } if (_EnableInputsIpc) _MMF_Inputs.UpdateRawPlayerData(Player.ID, (UInt32)Player.RIController.Computed_X, (UInt32)Player.RIController.Computed_Y); _Game.GetScreenResolution(); Logger.WriteLog("PrimaryScreen Size (Px) = [ " + _Game.ScreenWidth + "x" + _Game.ScreenHeight + " ]"); if (!Controller.IsRelativeCoordinates) { //If manual calibration override for analog guns if (Player.RIController.DeviceType == RawInputDeviceType.RIM_TYPEHID && Player.AnalogAxisRangeOverride) { Logger.WriteLog("Overriding player axis range values : X => [ " + Player.AnalogManual_Xmin.ToString() + ", " + Player.AnalogManual_Xmax.ToString() + " ], Y => [ " + Player.AnalogManual_Ymin.ToString() + ", " + Player.AnalogManual_Ymax.ToString() + " ]"); Player.RIController.Computed_X = _Game.ScreenScale(Player.RIController.Computed_X, Player.AnalogManual_Xmin, Player.AnalogManual_Xmax, 0, _Game.ScreenWidth); Player.RIController.Computed_Y = _Game.ScreenScale(Player.RIController.Computed_Y, Player.AnalogManual_Ymin, Player.AnalogManual_Ymax, 0, _Game.ScreenHeight); } else { Player.RIController.Computed_X = _Game.ScreenScale(Player.RIController.Computed_X, Player.RIController.Axis_X_Min, Player.RIController.Axis_X_Max, 0, _Game.ScreenWidth); Player.RIController.Computed_Y = _Game.ScreenScale(Player.RIController.Computed_Y, Player.RIController.Axis_Y_Min, Player.RIController.Axis_Y_Max, 0, _Game.ScreenHeight); } //Optionnal invert axis if (Player.InvertAxis_X) Player.RIController.Computed_X = _Game.ScreenWidth - Player.RIController.Computed_X; if (Player.InvertAxis_Y) Player.RIController.Computed_Y = _Game.ScreenHeight - Player.RIController.Computed_Y; Logger.WriteLog("OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); if (Configurator.GetInstance().Act_Labs_Offset_Enable) { Player.RIController.Computed_X += Player.Act_Labs_Offset_X; Player.RIController.Computed_Y += Player.Act_Labs_Offset_Y; Logger.WriteLog("ActLabs adaptated OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); } } //Change X asxis scaling based on user requirements if (_ForceScalingX != 1.0) { Logger.WriteLog("Forcing X Scaling = " + _ForceScalingX.ToString()); double HalfScreenSize = (double)_Game.ScreenWidth / 2.0; double NewX = (((double)Player.RIController.Computed_X - HalfScreenSize) * _ForceScalingX) + HalfScreenSize; Player.RIController.Computed_X = Convert.ToInt32(NewX); Logger.WriteLog("Forced scaled OnScreen Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); } _Game.IsFullscreen = _Game.GetFullscreenStatus(); if (!_Game.IsFullscreen) { Logger.WriteLog("ClientWindow Style = Windowed"); //Retrieve info for debug/replay purpose _Game.GetClientwindowInfo(); if (!_Game.ClientScale(Player)) { Logger.WriteLog("Error converting screen location to client location"); return; } Logger.WriteLog("ClientWindow Location (px) = [ " + _Game.clientWindowLocation.X.ToString() + ", " + _Game.clientWindowLocation.Y.ToString() + " ]"); Logger.WriteLog("ClientWindow Size (px) = [ " + (_Game.WindowRect.Right - _Game.WindowRect.Left).ToString() + "x" + (_Game.WindowRect.Bottom - _Game.WindowRect.Top).ToString() + " ]"); Logger.WriteLog("OnClient Cursor Position (Px) = [ " + Player.RIController.Computed_X + ", " + Player.RIController.Computed_Y + " ]"); if (!_Game.GetClientRect()) { Logger.WriteLog("Error getting client Rect"); return; } } else { Logger.WriteLog("ClientWindow Style = FullScreen"); //No need to translate coordinates from screen -> client and risk error. //As fuulscreen, we will consider window size = screen size Rect r = new Rect(); r.Top = 0; r.Left = 0; r.Bottom = _Game.ScreenHeight; r.Right = _Game.ScreenWidth; _Game.ClientRect = r; } if (!_Game.GameScale(Player)) { Logger.WriteLog("Error converting client location to game location"); return; } Logger.WriteLog("Game Position (Hex) = [ " + Player.RIController.Computed_X.ToString("X4") + ", " + Player.RIController.Computed_Y.ToString("X4") + " ]"); Logger.WriteLog("Game Position (Dec) = [ " + Player.RIController.Computed_X.ToString() + ", " + Player.RIController.Computed_Y.ToString() + " ]"); if (Controller.Computed_Buttons != 0) Logger.WriteLog("Controller Buttons Events : " + Player.RIController.Computed_Buttons.ToString()); Logger.WriteLog("-"); if (!_NoInput) _Game.SendInput(Player); if (_EnableInputsIpc) { _MMF_Inputs.UpdateComputedPlayerData(Player.ID, Player.RIController.Computed_X, Player.RIController.Computed_Y, Player.RIController.Hid_Buttons); if (_MMF_Inputs.WriteData() != 0) Logger.WriteLog("Succesfully copied P" + Player.ID.ToString() + " data to MMF " + _MMF_Inputs.MemoryFileName); } } } } } } } /// /// Output handling thread : /// This infinite loop will check the targeted game values and send them to registerd Output clients /// private void ReadAndSendOutput_Thread() { while (true) { if (_Game != null && _Game.ProcessHooked) { _Game.UpdateOutputValues(); if (Configurator.GetInstance().Wm_OutputEnabled && _Wm_OutputHelper != null && _Wm_OutputHelper.RomNameSent) _Wm_OutputHelper.SendValues(_Game.Outputs); if (Configurator.GetInstance().Net_OutputEnabled && _Net_OutputHelper != null) _Net_OutputHelper.BroadcastValues(_Game.Outputs); } DsCore.Win32.Win32API.MM_BeginPeriod(1); Thread.Sleep(Configurator.GetInstance().OutputPollingDelay); DsCore.Win32.Win32API.MM_EndPeriod(1); } } /// /// GUI Init : mostly TrayIcon + Menu /// private void InitializeComponent() { //Tray Icon //Looking for explorer.exe process, if not skip TrayIcon to make the program work Process[] pExplorer = Process.GetProcessesByName("explorer"); if (pExplorer.Length > 0) { _TrayIcon = new NotifyIcon(); _TrayIcon.Text = "DemulShooter"; _TrayIcon.Icon = DemulShooterX64.Properties.Resources.DemulShooter_UnHooked_Icon; _TrayIconMenu = new ContextMenu(); _TrayIconMenu.MenuItems.Add("Exit", OnTrayExitSelected); _TrayIcon.ContextMenu = _TrayIconMenu; _TrayIcon.Visible = true; } } /// /// Exit from TrayIcon menu entry /// private void OnTrayExitSelected(object sender, EventArgs e) { Application.Exit(); } private void OnGameHooked(object sender, EventArgs e) { if (_TrayIcon != null) { _TrayIcon.Icon = DemulShooterX64.Properties.Resources.DemulShooter_Hooked_Icon; _TrayIcon.Text += "[Hooked]"; } //Stopping the Timeout timer _TimerHookTimeout.Stop(); if (_Wm_OutputHelper != null) { _Game.SetMamePauseState(false); _Wm_OutputHelper.SendValues(_Game.Outputs); } //Sending info to the Net_OutputHelper if existing if (_Net_OutputHelper != null) { _Net_OutputHelper.SetGameHookedState(true); _Net_OutputHelper.BroadcatStartMessage(); } } private void OnApplicationExit(object sender, EventArgs e) { Logger.WriteLog("Cleaning things before exiting application..."); CleanAppBeforeExit(); } /// /// Application Exit cleanup /// private void CleanAppBeforeExit() { if (_OutputUpdateLoop != null) _OutputUpdateLoop.Abort(); if (_Wm_OutputHelper != null) { //Simply sending MameStop may cause MameHooker to not release dll properly //MAME goes from MameStop to MameStart with PAUSE enabled and "__empty" rom //Which is not possible here due to the WndProc quitting before getting MameHooker request ?? // Solution would be to Send a new MameStart with no values before MAmeStopping again _Game.SetMamePauseState(true); _Wm_OutputHelper.SendValues(_Game.Outputs); _Wm_OutputHelper.Stop(); _Wm_OutputHelper.Start(); _Wm_OutputHelper.Stop(); } if (_Net_OutputHelper != null) { _Net_OutputHelper.Stop(); } //Cleanup so that the icon will be removed when the application is closed if (_TrayIcon != null) { _TrayIcon.Visible = false; _TrayIcon.Dispose(); } } /// /// Low-level mouse hook. /// Some game will need this to block mouse inputs so that we can inject our own values. /// protected void ApplyMouseHook() { _MouseHookProc = new Win32API.HookProc(MouseHookCallback); using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) _MouseHookID = Win32API.SetWindowsHookEx(Win32Define.WH_MOUSE_LL, _MouseHookProc, Win32API.GetModuleHandle(curModule.ModuleName), 0); if (_MouseHookID == IntPtr.Zero) { Logger.WriteLog("MouseHook Error : " + Marshal.GetLastWin32Error()); } else { Logger.WriteLog("LowLevelMouseHook installed !"); } } private IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (_Game != null && _Game.ProcessHooked) return _Game.MouseHookCallback(_MouseHookID, nCode, wParam, lParam); else return Win32API.CallNextHookEx(_MouseHookID, nCode, wParam, lParam); } protected void RemoveMouseHook() { Win32API.UnhookWindowsHookEx(_MouseHookID); } /// /// Low-level Keyboard hook. /// protected void ApplyKeyboardHook() { _KeyboardHookProc = new Win32API.HookProc(KeyboardHookCallback); using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) _KeyboardHookID = Win32API.SetWindowsHookEx(Win32Define.WH_KEYBOARD_LL, _KeyboardHookProc, Win32API.GetModuleHandle(curModule.ModuleName), 0); if (_KeyboardHookID == IntPtr.Zero) { Logger.WriteLog("KeyboardHook Error : " + Marshal.GetLastWin32Error()); } else { Logger.WriteLog("LowLevel-KeyboardHook installed !"); } } protected virtual IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (_Game != null && _Game.ProcessHooked && !_NoInput) { try { Logger.WriteLog("KeyboardHook Event : wParam = 0x " + wParam.ToString("X8") + ", lParam = 0x" + lParam.ToString("X8")); //First step : use the Hook to determine if a virtual Middle/Right button has been pushed if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { Logger.WriteLog("KeyboardHook Event : WM_KEYDOWN event detected"); KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); Logger.WriteLog("KBDLLHOOKSTRUCT : " + s.ToString()); foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { if (Player.isVirtualMouseButtonsEnabled && Player.RIController != null) { if (s.scanCode == Player.DIK_VirtualMouseButton_Left) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Left detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OnScreenTriggerDown; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Middle) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Middle detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.ActionDown; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Right) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Right detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OffScreenTriggerDown; _Game.SendInput(Player); } } } Logger.WriteLog("-"); } if ((UInt32)wParam == Win32Define.WM_KEYUP) { Logger.WriteLog("KeyboardHook Event : WM_KEYUP event detected"); KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); Logger.WriteLog("KBDLLHOOKSTRUCT : " + s.ToString()); foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { if (Player.isVirtualMouseButtonsEnabled && Player.RIController != null) { if (s.scanCode == Player.DIK_VirtualMouseButton_Left) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Left detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OnScreenTriggerUp; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Middle) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Middle detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.ActionUp; _Game.SendInput(Player); } else if (s.scanCode == Player.DIK_VirtualMouseButton_Right) { Logger.WriteLog("Player " + Player.ID + "VirtualMouseButton_Right detected"); Player.RIController.Computed_Buttons = RawInputcontrollerButtonEvent.OffScreenTriggerUp; _Game.SendInput(Player); } } } Logger.WriteLog("-"); } //Second step : forward the event to the Game return _Game.KeyboardHookCallback(_MouseHookID, nCode, wParam, lParam); } catch (Exception Ex) { Logger.WriteLog("Error handling KeyboardHookCallback : " + Ex.Message.ToString()); return _Game.KeyboardHookCallback(_MouseHookID, nCode, wParam, lParam); } } else return Win32API.CallNextHookEx(_KeyboardHookID, nCode, wParam, lParam); } protected void RemoveKeyboardHook() { Win32API.UnhookWindowsHookEx(_KeyboardHookID); } //quit application if TimeOut enabled for game hooking private void tHookTimeOut_Elapsed(Object Sender, EventArgs e) { Logger.WriteLog("Hook timeout expired, exiting application."); CleanAppBeforeExit(); Environment.Exit(0); } } } ================================================ FILE: DemulShooterX64/DemulShooterX64.csproj ================================================  Debug x86 8.0.30703 2.0 {E853BB0E-66BA-45AB-A34B-90CAC33BCDC4} WinExe Properties DemulShooterX64 DemulShooterX64 v4.8 512 x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 false x86 pdbonly true bin\Release\ TRACE prompt 4 false true bin\x64\Debug\ DEBUG;TRACE full x64 prompt false false false bin\x64\Release\ TRACE true pdbonly x64 prompt true true false DemulShooter_Icon.ico True True Resources.resx {BD88CC4D-2944-43F8-85C3-A274A45487AF} DsCore ResXFileCodeGenerator Resources.Designer.cs ================================================ FILE: DemulShooterX64/Games/Game.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Timers; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.Win32; namespace DemulShooterX64 { public class Game { public delegate void GameHookedHandler(object sender, EventArgs e); public event GameHookedHandler OnGameHooked; //Games options protected bool _HideCrosshair = false; protected bool _HideGuns = false; protected bool _DisableInputHack = false; protected string _CustomTargetProcessName = string.Empty; protected bool _VerboseEnable; #region Process variables protected System.Timers.Timer _tProcess; protected String _RomName = string.Empty; protected Process _TargetProcess; protected String _Target_Process_Name = String.Empty; protected IntPtr _TargetProcess_MemoryBaseAddress = IntPtr.Zero; protected IntPtr _ProcessHandle = IntPtr.Zero; protected IntPtr _GameWindowHandle = IntPtr.Zero; //MD5 check of target binaries, may help to know if it's the wrong version or not compatible protected Dictionary _KnownMd5Prints; protected String _TargetProcess_Md5Hash = string.Empty; //Output values handling protected List _Outputs; public List Outputs { get { return _Outputs; } } protected bool _ProcessHooked; public bool ProcessHooked { get { return _ProcessHooked; } } #endregion #region Custom Databanks protected UInt64 _InputsDatabank_Address = 0; protected UInt64 _OutputsDatabank_Address = 0; #endregion #region Custom Outputs protected int _P1_LastLife = 0; protected int _P2_LastLife = 0; protected int _P1_LastAmmo = 0; protected int _P2_LastAmmo = 0; #endregion /// /// Common constructor for every single games /// /// DemumShooter [-rom] Parameter /// Executable name to hook in process list /// Create a debug.txt file if TRUE public Game(String RomName, string TargetProcessName) { GetGameOptions(); _KnownMd5Prints = new Dictionary(); GetScreenResolution(); Logger.WriteLog("Windows screen scaling : " + GetScreenScaling()); _RomName = RomName; _ProcessHooked = false; _Target_Process_Name = _CustomTargetProcessName == string.Empty ? TargetProcessName : _CustomTargetProcessName; Logger.WriteLog("Target process name : " + _Target_Process_Name + ".exe"); CreateOutputList(); _tProcess = new System.Timers.Timer(); _tProcess.Interval = 500; _tProcess.Elapsed += new ElapsedEventHandler(tProcess_Elapsed); _tProcess.Enabled = true; } ~Game() { if (_XOutputManager != null) { if (_Player1_X360_Gamepad_Port != 0) UninstallX360Gamepad(1); if (_Player2_X360_Gamepad_Port != 0) UninstallX360Gamepad(2); } } protected virtual void tProcess_Elapsed(Object sender, EventArgs e) {} /// /// Raise custom event to main window (to change TrayIcon status) /// protected void RaiseGameHookedEvent() { // Make sure someone is listening to event if (OnGameHooked == null) return; OnGameHooked(this, new EventArgs()); } private void GetGameOptions() { string[] sArgs = Environment.GetCommandLineArgs(); foreach (string sOption in sArgs) { if (sOption.ToLower().Equals("-nocrosshair")) { _HideCrosshair = true; } else if (sOption.ToLower().Equals("-nogun")) { _HideGuns = true; } else if (sOption.ToLower().Equals("-noinput")) { _DisableInputHack = true; } else if (sOption.ToLower().StartsWith("-pname=")) { _CustomTargetProcessName = (sOption.ToLower().Split('='))[1].Trim(); if (_CustomTargetProcessName.EndsWith(".exe")) _CustomTargetProcessName = _CustomTargetProcessName.Substring(0, _CustomTargetProcessName.Length - 4); } else if (sOption.ToLower().Equals("-v")) { _VerboseEnable = true; } } } #region MD5 Verification /// /// Compute the MD5 hash of the target executable and compare it to the known list of MD5 Hashes /// This can be usefull if people are using some unknown dump with different memory, /// or a wrong version of emulator /// This is absolutely not blocking, just for debuging with output log /// protected void CheckExeMd5() { CheckMd5(_TargetProcess.MainModule.FileName); } protected void CheckMd5(String TargetFileName) { GetMd5HashAsString(TargetFileName); Logger.WriteLog("MD5 hash of " + TargetFileName + " = " + _TargetProcess_Md5Hash); String FoundMd5 = String.Empty; foreach (KeyValuePair pair in _KnownMd5Prints) { if (pair.Value.ToLower() == _TargetProcess_Md5Hash) { FoundMd5 = pair.Key; break; } } if (FoundMd5 == String.Empty) { Logger.WriteLog(@"/!\ MD5 Hash unknown, DemulShooter may not work correctly with this target /!\"); } else { Logger.WriteLog("MD5 Hash is corresponding to a known target = " + FoundMd5); } } /// /// Compute the MD5 hash from the target file. /// /// Full filepath of the targeted executable. private void GetMd5HashAsString(String FileName) { if (File.Exists(FileName)) { using (var md5 = MD5.Create()) { using (var stream = File.OpenRead(FileName)) { var hash = md5.ComputeHash(stream); _TargetProcess_Md5Hash = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } } } } #endregion #region Screen protected int _screenWidth; public int ScreenWidth { get { return _screenWidth; } } protected int _screenHeight; public int ScreenHeight { get { return _screenHeight; } } protected int _screenCursorPosX; public int screenCursorPosX { get { return _screenCursorPosX; } set { _screenCursorPosX = value; } } protected int _screenCursorPosY; public int screenCursorPosY { get { return _screenCursorPosY; } set { _screenCursorPosY = value; } } protected Rect _WindowRect; public Rect WindowRect { get { return _WindowRect; } set { _WindowRect = value; } } protected POINT _clientWindowLocation; public POINT clientWindowLocation { get { return _clientWindowLocation; } set { _clientWindowLocation = value; } } protected Rect _ClientRect; public Rect ClientRect { get { return _ClientRect; } set { _ClientRect = value; } } protected bool _IsFullscreen; public bool IsFullscreen { get { return _IsFullscreen; } set { _IsFullscreen = value; } } /// /// HKEY_CURRENT_USER\Control Panel\Desktop\ ///Key: LogPixels ///Values: ///96 – Smaller 100% ///120 – Medium 125% ///144 - Larger 150% ///192 – Extra Large 200% ///240 – Custom 250% ///288 – Custom 300% ///384 – Custom 400% ///480 – Custom 500%*/ /// private string GetScreenScaling() { IntPtr desktopDc = Win32API.GetDC(IntPtr.Zero); // Get native resolution //#DEFINE LOGPIXELSX 88 //#DEFINE LOGPIXELSY 90 int horizontalDPI = Win32API.GetDeviceCaps(desktopDc, 88); int verticalDPI = Win32API.GetDeviceCaps(desktopDc, 90); return (horizontalDPI * 100 / 96).ToString() + "% (HorizontalDPI=" + horizontalDPI + ", VerticalDPI=" + verticalDPI + ")"; /*switch (horizontalDPI.ToString()) { case "96": return "100%"; case "120": return "125%"; case "144": return "150%"; case "192": return "200%"; case "240": return "250%"; case "288": return "300%"; case "384": return "400%"; case "480": return "500%"; default: return @"Unknown value : " + horizontalDPI.ToString(); }*/ } public virtual bool GetFullscreenStatus() { QUERY_USER_NOTIFICATION_STATE state; int r = Win32API.SHQueryUserNotificationState(out state); if (r == 0) { Logger.WriteLog("NotificationState: " + state.ToString()); switch (state) { // A screen saver is displayed, the machine is locked, or a nonactive Fast User Switching session is in progress. case QUERY_USER_NOTIFICATION_STATE.QUNS_NOT_PRESENT: return false; // A full-screen application is running or Presentation Settings are applied. Presentation Settings allow a user to put their machine into a state fit for an uninterrupted presentation, such as a set of PowerPoint slides, with a single click. case QUERY_USER_NOTIFICATION_STATE.QUNS_BUSY: return true; // A full-screen (exclusive mode) Direct3D application is running. case QUERY_USER_NOTIFICATION_STATE.QUNS_RUNNING_D3D_FULL_SCREEN: return true; // The user has activated Windows presentation settings to block notifications and pop-up messages. case QUERY_USER_NOTIFICATION_STATE.QUNS_PRESENTATION_MODE: return false; // None of the other states are found, notifications can be freely sent. case QUERY_USER_NOTIFICATION_STATE.QUNS_ACCEPTS_NOTIFICATIONS: return false; // We are in OOBE quiet period case QUERY_USER_NOTIFICATION_STATE.QUNS_QUIET_TIME: return false; // A Windows Store app is running. case QUERY_USER_NOTIFICATION_STATE.QUNS_APP: return false; default: return false; } } else { Logger.WriteLog("Error running SHQueryUserNotificationState: " + r.ToString()); return false; } } public virtual void GetScreenResolution() { _screenWidth = Win32API.GetSystemMetrics(SystemMetricsIndex.SM_CXSCREEN); _screenHeight = Win32API.GetSystemMetrics(SystemMetricsIndex.SM_CYSCREEN); /*_screenWidth = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; _screenHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;*/ } public void GetScreenresolution2() { IntPtr hDesktop = Win32API.GetDesktopWindow(); Rect DesktopRect = new Rect(); Win32API.GetWindowRect(hDesktop, ref DesktopRect); _screenWidth = DesktopRect.Right; _screenHeight = DesktopRect.Bottom; } /// /// Contains value inside min-max range /// protected int Clamp(int val, int minVal, int maxVal) { if (val > maxVal) return maxVal; else if (val < minVal) return minVal; else return val; } /// /// Transforming 0x0000-0xFFFF absolute rawdata to absolute x,y position on Desktop resolution /// public int ScreenScale(int val, int fromMinVal, int fromMaxVal, int toMinVal, int toMaxVal) { return ScreenScale(val, fromMinVal, fromMinVal, fromMaxVal, toMinVal, toMinVal, toMaxVal); } protected int ScreenScale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal) { double fromRange; double frac; if (fromMaxVal > fromMinVal) { val = Clamp(val, fromMinVal, fromMaxVal); if (val > fromOffVal) { fromRange = (double)(fromMaxVal - fromOffVal); frac = (double)(val - fromOffVal) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMinVal); frac = (double)(val - fromOffVal) / fromRange; } else return toOffVal; } else if (fromMinVal > fromMaxVal) { val = Clamp(val, fromMaxVal, fromMinVal); if (val > fromOffVal) { fromRange = (double)(fromMinVal - fromOffVal); frac = (double)(fromOffVal - val) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMaxVal); frac = (double)(fromOffVal - val) / fromRange; } else return toOffVal; } else return toOffVal; double toRange; if (toMaxVal > toMinVal) { if (frac >= 0) toRange = (double)(toMaxVal - toOffVal); else toRange = (double)(toOffVal - toMinVal); int val1 = (int)(toRange * frac); return toOffVal + (int)(toRange * frac); } else { if (frac >= 0) toRange = (double)(toOffVal - toMaxVal); else toRange = (double)(toMinVal - toOffVal); return toOffVal - (int)(toRange * frac); } } /// /// Convert screen location of pointer to Client area location /// public virtual bool ClientScale(PlayerSettings PlayerData) { //Convert Screen location to Client location if (_TargetProcess != null) { POINT p = new POINT(PlayerData.RIController.Computed_X, PlayerData.RIController.Computed_Y); if (Win32API.ScreenToClient(_GameWindowHandle, ref p)) { PlayerData.RIController.Computed_X = (p.X); PlayerData.RIController.Computed_Y = (p.Y); return true; } else return false; } else return false; } public void GetClientwindowInfo() { if (_TargetProcess != null) { if (Win32API.GetWindowRect(_GameWindowHandle, ref _WindowRect)) { _clientWindowLocation.X = _WindowRect.Left; _clientWindowLocation.Y = _WindowRect.Top; } else { _clientWindowLocation.X = 0; _clientWindowLocation.Y = 0; } } } /// /// Get the Client rect to translate axis coordinate to game coordinates /// Only used in windowed Mode /// public virtual bool GetClientRect() { //Window size _ClientRect = new Rect(); if (Win32API.GetClientRect(_GameWindowHandle, ref _ClientRect)) return true; else return false; } /// /// Convert client area pointer location to Game speciffic data for memory injection /// public virtual bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)TotalResX) PlayerData.RIController.Computed_X = (int)TotalResX; if (PlayerData.RIController.Computed_Y > (int)TotalResX) PlayerData.RIController.Computed_X = (int)TotalResY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region I/O FILE /// /// Read memory values in .cfg file /// protected virtual void ReadGameData() {} /// /// Read memory values in .cfg file, whose name depends on the MD5 hash of the targeted exe. /// Mostly used for PC games /// /// protected virtual void ReadGameDataFromMd5Hash(String GameData_Folder) { String ConfigFile = AppDomain.CurrentDomain.BaseDirectory + GameData_Folder + @"\" + _TargetProcess_Md5Hash + ".cfg"; if (File.Exists(ConfigFile)) { Logger.WriteLog("Reading game memory setting from " + ConfigFile); using (StreamReader sr = new StreamReader(ConfigFile)) { String line; String FieldName = String.Empty; line = sr.ReadLine(); while (line != null) { if (!line.StartsWith(";")) { String[] buffer = line.Split('='); if (buffer.Length > 1) { try { FieldName = "_" + buffer[0].Trim(); if (buffer[0].Contains("Nop")) { NopStruct n = new NopStruct(buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, n); Logger.WriteLog(FieldName + " successfully set to following value : 0x" + n.MemoryOffset.ToString("X8") + "|" + n.Length.ToString()); } else if (buffer[0].Contains("InjectionStruct")) { InjectionStruct n = new InjectionStruct(buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, n); Logger.WriteLog(FieldName + " successfully set to following value : 0x" + n.InjectionOffset.ToString("X8") + "|" + n.Length.ToString()); } else if (buffer[0].Contains("DIK")) { HardwareScanCode sc = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, sc); Logger.WriteLog(FieldName + " successfully set to following value :" + sc.ToString()); } else if (buffer[0].Contains("VK")) { VirtualKeyCode vk = (VirtualKeyCode)Enum.Parse(typeof(VirtualKeyCode), buffer[1].Trim()); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, vk); Logger.WriteLog(FieldName + " successfully set to following value :" + vk.ToString()); } else { UInt64 v = UInt64.Parse(buffer[1].Substring(3).Trim(), NumberStyles.HexNumber); this.GetType().GetField(FieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase).SetValue(this, v); Logger.WriteLog(FieldName + " successfully set to following value : 0x" + v.ToString("X8")); } } catch (Exception ex) { Logger.WriteLog("Error reading game data for " + FieldName + " : " + ex.Message.ToString()); } } } line = sr.ReadLine(); } sr.Close(); } } else { Logger.WriteLog("File not found : " + ConfigFile); } } #endregion #region MemoryHack x64 protected virtual void Apply_MemoryHacks() { if (!_DisableInputHack) Apply_InputsMemoryHack(); else Logger.WriteLog("Input Hack disabled"); Apply_OutputsMemoryHack(); if (_HideCrosshair) { Logger.WriteLog("Applying No-Crosshair hack..."); Apply_NoCrosshairMemoryHack(); } } protected virtual void Apply_InputsMemoryHack() { } protected virtual void Create_InputsDataBank() { try { Codecave CaveMemoryInputs = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemoryInputs.Open(); CaveMemoryInputs.Alloc(0x800); _InputsDatabank_Address = CaveMemoryInputs.CaveAddress; Logger.WriteLog("Custom output data will be stored at : 0x" + CaveMemoryInputs.CaveAddress.ToString("X16")); } catch (Exception Ex) { Logger.WriteLog("Impossible to create Inputs Databank : " + Ex.Message); } } protected virtual void Apply_OutputsMemoryHack() { } protected virtual void Create_OutputsDataBank() { try { Codecave CaveMemoryOutputs = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemoryOutputs.Open(); CaveMemoryOutputs.Alloc(0x800); _OutputsDatabank_Address = CaveMemoryOutputs.CaveAddress; Logger.WriteLog("Custom output data will be stored at : 0x" + CaveMemoryOutputs.CaveAddress.ToString("X16")); } catch (Exception Ex) { Logger.WriteLog("Impossible to create Outputs Databank : " + Ex.Message); } } protected virtual void Apply_NoCrosshairMemoryHack() { } public virtual void SendInput(PlayerSettings PlayerData) {} protected Byte ReadByte(IntPtr Address) { byte[] Buffer = { 0 }; UIntPtr bytesRead = UIntPtr.Zero; if (!Win32API.ReadProcessMemoryX64((IntPtr)_ProcessHandle, Address, Buffer, (UIntPtr)1, out bytesRead)) { Logger.WriteLog("Cannot read memory at address 0x" + Address.ToString("X16")); } return Buffer[0]; } protected Byte[] ReadBytes(IntPtr Address, UInt32 Bytes) { byte[] Buffer = new byte[Bytes]; UIntPtr bytesRead = UIntPtr.Zero; if (!Win32API.ReadProcessMemoryX64((IntPtr)_ProcessHandle, Address, Buffer, (UIntPtr)Buffer.Length, out bytesRead)) { Logger.WriteLog("Cannot read memory at address 0x" + Address.ToString("X16")); } return Buffer; } protected UInt64 ReadPtr(IntPtr PtrAddress) { if (PtrAddress != IntPtr.Zero) { byte[] Buffer = ReadBytes(PtrAddress, 8); return BitConverter.ToUInt64(Buffer, 0); } else { return 0; } } protected UInt64 ReadPtrChain(IntPtr BaseAddress, UInt64[] Offsets) { byte[] Buffer = ReadBytes(BaseAddress, 8); UInt64 Ptr = BitConverter.ToUInt64(Buffer, 0); if (Ptr == 0) { return 0; } else { for (int i = 0; i < Offsets.Length; i++) { Buffer = ReadBytes((IntPtr)(Ptr + Offsets[i]), 8); Ptr = BitConverter.ToUInt64(Buffer, 0); if (Ptr == 0) return 0; } } return Ptr; } protected bool WriteByte(IntPtr Address, byte Value) { UIntPtr bytesWritten = UIntPtr.Zero; Byte[] Buffer = { Value }; if (Win32API.WriteProcessMemoryX64((IntPtr)_ProcessHandle, Address, Buffer, (UIntPtr)1, out bytesWritten)) { if ((int)bytesWritten == 1) return true; else return false; } else return false; } protected bool WriteBytes(IntPtr Address, byte[] Buffer) { UIntPtr bytesWritten = UIntPtr.Zero; if (Win32API.WriteProcessMemoryX64((IntPtr)_ProcessHandle, Address, Buffer, (UIntPtr)Buffer.Length, out bytesWritten)) { if ((int)bytesWritten == Buffer.Length) return true; else return false; } else return false; } protected void SetNops(IntPtr BaseAddress, NopStruct Nop) { for (UInt64 i = 0; i < Nop.Length; i++) { UInt64 Address = (UInt64)BaseAddress + Nop.MemoryOffset + i; if (!WriteByte((IntPtr)Address, 0x90)) { Logger.WriteLog("Impossible to NOP address 0x" + Address.ToString("X16")); break; } } } protected void Apply_OR_ByteMask(IntPtr MemoryAddress, byte Mask) { byte b = ReadByte(MemoryAddress); b |= Mask; WriteByte(MemoryAddress, b); } protected void Apply_AND_ByteMask(IntPtr MemoryAddress, byte Mask) { byte b = ReadByte(MemoryAddress); b &= Mask; WriteByte(MemoryAddress, b); } #endregion #region MAME-Like Outputs protected virtual void CreateOutputList() { _Outputs = new List(); } public virtual void UpdateOutputValues() { } public void SetMamePauseState(bool PauseState) { if (PauseState) SetOutputValue(OutputId.MamePause, 1); else SetOutputValue(OutputId.MamePause, 0); } /// /// Return a GameOutput object corresponding to a desired GameOutputId /// /// Desired GameOutputId /// Desired GameOutput object protected GameOutput GetOutputById(OutputId Id) { foreach (GameOutput CurrentOutput in _Outputs) { if (CurrentOutput.Id == (uint)Id) return CurrentOutput; } return null; } /// /// Update a value for the desired GameOutput /// /// GameOutput Id to update /// Value to update the GameOutput object protected void SetOutputValue(OutputId Id, int Value) { foreach (GameOutput CurrentOutput in _Outputs) { if (CurrentOutput.Id == (uint)Id) { CurrentOutput.OutputValue = Value; break; } } } /// /// Return the Text description for a desired Id /// /// Desired GameOutput Id /// GameOutput string description public String GetOutputDescriptionFromId(uint Id) { foreach (GameOutput o in _Outputs) { if (o.Id == Id) return o.Name; } return String.Empty; } #endregion #region XOutput protected XOutput _XOutputManager = null; protected int _Player1_X360_Gamepad_Port = 0; protected int _Player2_X360_Gamepad_Port = 0; /// /// Create and plug a virtual XInput device /// /// XInput ID of the device to create and plug (From 1 To 4) protected virtual void InstallX360Gamepad(int Player) { if (_XOutputManager != null) { if (_XOutputManager.isVBusExists()) { for (int i = 1; i < 5; i++) { if (_XOutputManager.PlugIn(i)) { if (Player == 1) { Logger.WriteLog("Plugged P1 virtual Gamepad to port " + i.ToString()); _Player1_X360_Gamepad_Port = i; } else if (Player == 2) { Logger.WriteLog("Plugged P2 virtual Gamepad to port " + i.ToString()); _Player2_X360_Gamepad_Port = i; } break; } else Logger.WriteLog("Failed to plug virtual GamePad to port " + i.ToString() + ". (Port already used ?)"); } } else { Logger.WriteLog("ScpBus driver not found or not installed"); } } else { Logger.WriteLog("XOutputManager Creation Failed !"); } } protected bool UninstallX360Gamepad(int Player) { if (_XOutputManager != null) { if (Player == 1 && _Player1_X360_Gamepad_Port != 0) { if (_XOutputManager.Unplug(_Player1_X360_Gamepad_Port, true)) { Logger.WriteLog("Succesfully unplug P1 virtual Gamepad on port " + _Player1_X360_Gamepad_Port.ToString()); return true; } else { Logger.WriteLog("Failed to unplug P1 virtual Gamepad on port " + _Player1_X360_Gamepad_Port.ToString()); return false; } } else if (Player == 2 && _Player2_X360_Gamepad_Port != 0) { if (_XOutputManager.Unplug(_Player2_X360_Gamepad_Port, true)) { Logger.WriteLog("Succesfully unplug P2 virtual Gamepad on port " + _Player2_X360_Gamepad_Port.ToString()); return true; } else { Logger.WriteLog("Failed to unplug P2 virtual Gamepad on port " + _Player2_X360_Gamepad_Port.ToString()); return false; } } } return true; } #endregion #region Keyboard SendKeys /// /// Simulate a keyboard key action (up or down) /// /// Hardware ScanCode of the key to simulate /// State of the key. (0=Down, 1 = Up) private void SendKey(HardwareScanCode Keycode, KeybdInputFlags KeybdInputFlags) { INPUT[] InputData = new INPUT[1]; InputData[0].type = InputType.INPUT_KEYBOARD; InputData[0].ki.wScan = Keycode; InputData[0].ki.dwFlags = KeybdInputFlags; InputData[0].ki.time = 0; InputData[0].ki.dwExtraInfo = IntPtr.Zero; if ( Win32API.SendInput(1, InputData, Marshal.SizeOf(typeof(INPUT))) == 0) { Logger.WriteLog("SendInput API failed : wScan=" + Keycode.ToString() + ", dwFlags=" + KeybdInputFlags.ToString()); Logger.WriteLog("GetLastError returned : " + Marshal.GetLastWin32Error().ToString()); } } /// /// Send KeyUp and KeyDown separated by a desired delay /// /// DirectInput Keycode (hardware scan code) /// Delay in milliseconds protected void SendKeyStroke(HardwareScanCode Keycode, int DelayPressed) { SendKeyDown(Keycode); System.Threading.Thread.Sleep(DelayPressed); SendKeyUp(Keycode); } protected void SendKeyDown(HardwareScanCode Keycode) { SendKey(Keycode, KeybdInputFlags.KEYEVENTF_SCANCODE); } protected void SendKeyUp(HardwareScanCode Keycode) { SendKey(Keycode, KeybdInputFlags.KEYEVENTF_KEYUP | KeybdInputFlags.KEYEVENTF_SCANCODE); } /// /// VirtualKeyCode inputs to send /// /// protected void Send_VK_KeyDown(VirtualKeyCode Keycode) { Win32API.keybd_event(Keycode, 0, KeybdInputFlags.KEYEVENTF_EXTENDEDKEY | 0, 0); } protected void Send_VK_KeyUp(VirtualKeyCode Keycode) { Win32API.keybd_event(Keycode, 0, KeybdInputFlags.KEYEVENTF_EXTENDEDKEY | KeybdInputFlags.KEYEVENTF_KEYUP, 0); } /// /// Convert a HardwareScanCode to a corresponding VirtualKeyCode /// /// Hardware Scancode to convert /// public VirtualKeyCode MapScanCodeToVirtualKeyCode(HardwareScanCode ScanCode) { UInt32 Vk = Win32API.MapVirtualKey((UInt32)ScanCode, VirtualKeyMapType.MAPVK_VSC_TO_VK); return (VirtualKeyCode)Vk; } /// /// Convert a VirtualScanCode to a corresponding HardwareScanCode /// /// Hardware Scancode to convert /// public HardwareScanCode MapScanCodeToVirtualKeyCode(VirtualKeyCode ScanCode) { UInt32 Vk = Win32API.MapVirtualKey((UInt32)ScanCode, VirtualKeyMapType.MAPVK_VK_TO_VSC); return (HardwareScanCode)Vk; } #endregion #region LowLevel Hooks /// /// Low-Level mouse hook callback to process data. /// This procedure will be override by each Game according to it's needs /// public virtual IntPtr MouseHookCallback(IntPtr MouseHookID, int nCode, IntPtr wParam, IntPtr lParam) { return Win32API.CallNextHookEx(MouseHookID, nCode, wParam, lParam); } /// /// Low-Level keyboard hook callback to process data. /// This procedure will be override by each Game according to it's needs /// public virtual IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion /// /// Select a specific window from the target process where the title contains a wanted string /// /// /// protected bool FindGameWindow_Contains(string TargetWindowTitle) { foreach (IntPtr handle in EnumerateProcessWindowHandles(_TargetProcess.Id)) { int length = Win32API.GetWindowTextLength(handle); if (length >= 0) { StringBuilder builder = new StringBuilder(length); Win32API.GetWindowText(handle, builder, length + 1); string WindowTitle = builder.ToString(); Logger.WriteLog("Found a window : Handle = 0x" + handle.ToString("X8") + ", Title = " + WindowTitle); if (WindowTitle.StartsWith("FPS:") || WindowTitle.Contains(TargetWindowTitle)) { _GameWindowHandle = handle; Logger.WriteLog("=> Selecting 0x" + handle.ToString("X8") + " as game Window Handle"); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); return true; } } } return false; } /// /// Select a specific window from the target process where the title is excatly the specific string /// /// /// protected bool FindGameWindow_Equals(string TargetWindowTitle) { foreach (IntPtr handle in EnumerateProcessWindowHandles(_TargetProcess.Id)) { int length = Win32API.GetWindowTextLength(handle); if (length >= 0) { StringBuilder builder = new StringBuilder(length); Win32API.GetWindowText(handle, builder, length + 1); string WindowTitle = builder.ToString(); Logger.WriteLog("Found a window : Handle = 0x" + handle.ToString("X8") + ", Title = " + WindowTitle); if (WindowTitle.StartsWith("FPS:") || WindowTitle.Equals(TargetWindowTitle)) { _GameWindowHandle = handle; Logger.WriteLog("=> Selecting 0x" + handle.ToString("X8") + " as game Window Handle"); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); return true; } } } return false; } /// /// Get the list of Windows for a given process /// protected static IEnumerable EnumerateProcessWindowHandles(int processId) { List handles = new List(); foreach (ProcessThread thread in Process.GetProcessById(processId).Threads) { Win32API.EnumThreadWindows(thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero); } return handles; } /// /// Get the Window Title /// /// protected string Get_GameWindowTitle() { string sTitle = string.Empty; int length = Win32API.GetWindowTextLength(_GameWindowHandle); if (length >= 0) { StringBuilder builder = new StringBuilder(length); Win32API.GetWindowText(_GameWindowHandle, builder, length + 1); sTitle = builder.ToString(); } return sTitle; } /// /// Check a series of bytes agains some awaited values /// protected bool CheckBytes(IntPtr AddressToCheck, byte[] BytesToFind) { Logger.WriteLog("Checking Bytes at 0x" + AddressToCheck.ToString("X8") + "..."); byte[] ReadBuffer = ReadBytes(AddressToCheck, (uint)BytesToFind.Length); for (int i = 0; i < BytesToFind.Length; i++) { Logger.WriteLog("Read: 0x" + ReadBuffer[i].ToString("X2") + ", Awaited: 0x" + BytesToFind[i].ToString("X2")); if (ReadBuffer[i] != BytesToFind[i]) return false; } return true; } } } ================================================ FILE: DemulShooterX64/Games/Game_AllsHodSd.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.Win32; namespace DemulShooterX64 { public class Game_AllsHodSd : Game { //Memory values private InjectionStruct _Jvs_P1X_Injection = new InjectionStruct(0x001E28D2, 15); private InjectionStruct _Jvs_P1Y_Injection = new InjectionStruct(0x001E28F5, 15); private InjectionStruct _Jvs_P2X_Injection = new InjectionStruct(0x001E291C, 15); private InjectionStruct _Jvs_P2Y_Injection = new InjectionStruct(0x001E293F, 15); private InjectionStruct _MenuFlag_Injection = new InjectionStruct(0x00DA3050, 17); private InjectionStruct _AxisCorrection_Injection = new InjectionStruct(0x001DED89, 17); //private UInt32 _RemoveMenuCorrection_Offset = 0x00DA3041; private UInt64 _PlayerStatusPointer_Offset = 0x03087750; //Custom Values private UInt64 _Databank_WindowWidth_Address = 0; private UInt64 _Databank_WindowHeight_Address = 0; private UInt64 _Databank_JVS_P1X_Address = 0; private UInt64 _Databank_JVS_P1Y_Address = 0; private UInt64 _Databank_JVS_P2X_Address = 0; private UInt64 _Databank_JVS_P2Y_Address = 0; private UInt64 _Databank_InMenu_Address = 0; /// /// Constructor /// public Game_AllsHodSd(String RomName) : base(RomName, "Hodzero-Win64-Shipping") { _KnownMd5Prints.Add("Hodzero-Win64-Shipping.exe - Original Dump", "cde48c217d04caa64ee24a72f73dcce4"); //Add amdaemon check ? (difference between original/Jconfig) //amdaemon.exe - original : 2ccb852ba8f98d6adf42ed62d1d1b759 //amdaemon.exe - Jconfig : 2cd0d9d4771f84724b6ea9a008f53ea4 _tProcess.Start(); Logger.WriteLog("Waiting for SEGA ALLS " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (because of AMDaemon app console) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals("Hodzero ") || FindGameWindow_Equals("TeknoParrot - House of the Dead: Scarlet Dawn")) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); CheckExeMd5(); /*if (!_DisableInputHack) SetHack();*/ _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } else { Logger.WriteLog("ROM not Loaded..."); } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0x000 - 0x3FF] //Y => [0x000 - 0x3FF] double dMaxX = 1024.0; double dMaxY = 1024.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(dMaxY - Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; //Need to store data for InGame dynamic correction WriteBytes((IntPtr)_Databank_WindowWidth_Address, BitConverter.GetBytes((UInt32)TotalResX)); WriteBytes((IntPtr)_Databank_WindowHeight_Address, BitConverter.GetBytes((UInt32)TotalResY)); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _Databank_WindowWidth_Address = _InputsDatabank_Address; _Databank_WindowHeight_Address = _InputsDatabank_Address + 0x08; _Databank_InMenu_Address = _InputsDatabank_Address + 0x10; //First part is to set our own JVS base values when the game is reading them in the JVS data array SetHack_JVS_P1_X(); SetHack_JVS_P1_Y(); SetHack_JVS_P2_X(); SetHack_JVS_P2_Y(); //Then to correct offsets between the cursors on menu screens and real data, we need to change the cursor position so that it's correct //whatever the window size is (otherwise, only aligned in 1920x1080) //Based on [0-1024] JVS data, the game calculates in-menu coordinates based on 1920x1080 range, whereas in-game it is based on window size //This also removes the 1.5x multiplier offset between cursor and aim in menu SetHack_AxisCorrection(); //LAstly, forcing jump to not use the in-game part screwing the menus aim with screen multiplier according to resolution Height //WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _RemoveMenuCorrection_Offset), 0xEB); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// At this point the game uses that code only when in-menu /// We can : /// 1 - Remove the 1.5x multiplier applied to the cursor hand in menu /// 2 - Set a flag to tell we are in menu for later use (settle the offset) /// private void SetHack_InMenuFlag() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //xorps xmm0,xmm0 CaveMemory.Write_StrBytes("0F 57 C0"); //movaps xmm1,xmm2 CaveMemory.Write_StrBytes("0F 28 CA"); //mulss xmm2,[rsp+24] CaveMemory.Write_StrBytes("F3 0F 59 54 24 24"); //push rax CaveMemory.Write_StrBytes("50"); //movabs rax, [_Databank_InMenu_Address] CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Databank_InMenu_Address)); //mov byte ptr[rax], 1 CaveMemory.Write_StrBytes("C6 00 01"); //pop rax CaveMemory.Write_StrBytes("58"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _MenuFlag_Injection.InjectionReturnOffset); Logger.WriteLog("Adding InMenuFlag Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _MenuFlag_Injection.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// At this point the game uses the Raw JVS values (0-1024) to compute real position of the gun in game /// We can : /// 1 - Remove the gun calibration values that are screwing our aim /// 2 - Look at the InMenu flag to change the calculation accordingly : in-menu range is 1920x1080 whereas in-game is WindowWidth x WindowHeight /// private void SetHack_AxisCorrection() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //mov rax, _PlayerstatusPointer_Offset CaveMemory.Write_StrBytes("48 B8"); UInt64 uAddress = (UInt64)_TargetProcess_MemoryBaseAddress + _PlayerStatusPointer_Offset; CaveMemory.Write_Bytes(BitConverter.GetBytes(uAddress)); //mov rax, [rax] CaveMemory.Write_StrBytes("48 8B 00"); //add rax, 0x231 CaveMemory.Write_StrBytes("48 05 31 02 00 00"); //cmp dword ptr [rax],01 CaveMemory.Write_StrBytes("83 38 01"); //je InGame CaveMemory.Write_StrBytes("0F 84 26 00 00 00"); //add rax, 1 CaveMemory.Write_StrBytes("48 83 C0 01"); //cmp dword ptr [rax],01 CaveMemory.Write_StrBytes("83 38 01"); //je InGame CaveMemory.Write_StrBytes("0F 84 19 00 00 00"); //InMenu: //mov [rdi+00000D4C],00000780 //1920 CaveMemory.Write_StrBytes("C7 87 4C 0D 00 00 80 07 00 00"); //mov [rdi+00000D50],00000438 //1080 CaveMemory.Write_StrBytes("C7 87 50 0D 00 00 38 04 00 00"); //jmp Next CaveMemory.Write_StrBytes("E9 26 00 00 00"); //InGame: //mov rax, _Databank_windowWidth CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Databank_WindowWidth_Address)); //mov rax,[rax] CaveMemory.Write_StrBytes("48 8B 00"); //mov [rdi+00000D4C],rax CaveMemory.Write_StrBytes("89 87 4C 0D 00 00"); //mov rax, _Databank_windowHeight CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Databank_WindowHeight_Address)); //mov rax,[rax] CaveMemory.Write_StrBytes("48 8B 00"); //mov [rdi+00000D50],rax CaveMemory.Write_StrBytes("89 87 50 0D 00 00"); //Next: //pop rax CaveMemory.Write_StrBytes("58"); //mov [rdi+00000D04],00000000 CaveMemory.Write_StrBytes("C7 87 04 0D 00 00 00 00 00 00"); //mov [rdi+00000D0C],00000400 CaveMemory.Write_StrBytes("C7 87 0C 0D 00 00 00 04 00 00"); //mov [rdi+00000D14],00000200 CaveMemory.Write_StrBytes("C7 87 14 0D 00 00 00 02 00 00"); //mov [rdi+00000D44],00000000 CaveMemory.Write_StrBytes("C7 87 44 0D 00 00 00 00 00 00"); //movd xmm8,[rdi+00000D4C] CaveMemory.Write_StrBytes("66 44 0F 6E 87 4C 0D 00 00"); //movd xmm0,[rdi+00000D0C] CaveMemory.Write_StrBytes("66 0F 6E 87 0C 0D 00 00"); //mov [rdi+00000CF8],00000000 CaveMemory.Write_StrBytes("C7 87 F8 0C 00 00 00 00 00 00"); //mov [rdi+00000D00],00000400 CaveMemory.Write_StrBytes("C7 87 00 0D 00 00 00 04 00 00"); //mov [rdi+00000D18],00000200 CaveMemory.Write_StrBytes("C7 87 18 0D 00 00 00 02 00 00"); //mov [rdi+00000D48],00000000 CaveMemory.Write_StrBytes("C7 87 48 0D 00 00 00 00 00 00"); //movd xmm8,[rdi+00000D4C] CaveMemory.Write_StrBytes("66 44 0F 6E 87 4C 0D 00 00"); //movd xmm0,[rdi+00000D0C] CaveMemory.Write_StrBytes("66 0F 6E 87 0C 0D 00 00"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _AxisCorrection_Injection.InjectionReturnOffset); Logger.WriteLog("Adding AxisCorrection Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _AxisCorrection_Injection.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /**/ /* Overwrite raw JVS values with our own /**/ private void SetHack_JVS_P1_X() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _Databank_JVS_P1X_Address = CaveMemory.CaveAddress + 0x20; Logger.WriteLog("_Databank_JVS_P1X_Address = 0x" + _Databank_JVS_P1X_Address.ToString("X16")); //movzx eax,byte ptr [rbx+52] CaveMemory.Write_StrBytes("0F B6 43 52"); //movzx ecx,byte ptr [rbx+53] CaveMemory.Write_StrBytes("0F B6 4B 53"); //mov eax, [RIP+0x12] (==> _Databank_JVS_P1X_Address) CaveMemory.Write_StrBytes("8B 05 12 00 00 00"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P1X_Injection.InjectionReturnOffset); Logger.WriteLog("Adding JVS_P1X Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P1X_Injection.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } private void SetHack_JVS_P1_Y() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _Databank_JVS_P1Y_Address = CaveMemory.CaveAddress + 0x20; Logger.WriteLog("_Databank_JVS_P1Y_Address = 0x" + _Databank_JVS_P1Y_Address.ToString("X16")); //movzx eax,byte ptr [rbx+55] CaveMemory.Write_StrBytes("0F B6 43 55"); //movzx ecx,byte ptr [rbx+54] CaveMemory.Write_StrBytes("0F B6 4B 54"); //mov ecx, [RIP+0x12] (==> _Databank_JVS_P1Y_Address) CaveMemory.Write_StrBytes("8B 0D 12 00 00 00"); //mov eax, edx CaveMemory.Write_StrBytes("8B C2"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P1Y_Injection.InjectionReturnOffset); Logger.WriteLog("Adding JVS_P1Y Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P1Y_Injection.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } private void SetHack_JVS_P2_X() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _Databank_JVS_P2X_Address = CaveMemory.CaveAddress + 0x20; Logger.WriteLog("_Databank_JVS_P2X_Address = 0x" + _Databank_JVS_P2X_Address.ToString("X16")); //movzx eax,byte ptr [rbx+57] CaveMemory.Write_StrBytes("0F B6 43 57"); //movzx ecx,byte ptr [rbx+56] CaveMemory.Write_StrBytes("0F B6 4B 56"); //mov eax, [RIP+0x12] (==> _Databank_JVS_P2X_Address) CaveMemory.Write_StrBytes("8B 05 12 00 00 00"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P2X_Injection.InjectionReturnOffset); Logger.WriteLog("Adding JVS_P2X Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P2X_Injection.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } private void SetHack_JVS_P2_Y() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _Databank_JVS_P2Y_Address = CaveMemory.CaveAddress + 0x20; Logger.WriteLog("_Databank_JVS_P2Y_Address = 0x" + _Databank_JVS_P2Y_Address.ToString("X16")); //movzx eax,byte ptr [rbx+55] CaveMemory.Write_StrBytes("0F B6 43 55"); //movzx ecx,byte ptr [rbx+54] CaveMemory.Write_StrBytes("0F B6 4B 54"); //mov ecx, [RIP+0x12] (==> _Databank_JVS_P2Y_Address) CaveMemory.Write_StrBytes("8B 0D 12 00 00 00"); //mov eax, edx CaveMemory.Write_StrBytes("8B C2"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P2Y_Injection.InjectionReturnOffset); Logger.WriteLog("Adding JVS_P2Y Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Jvs_P2Y_Injection.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { /*byte[] bufferX = BitConverter.GetBytes(PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes(PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((IntPtr)_Databank_JVS_P1X_Address, bufferX); WriteBytes((IntPtr)_Databank_JVS_P1Y_Address, bufferY); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)_Databank_JVS_P2X_Address, bufferX); WriteBytes((IntPtr)_Databank_JVS_P2Y_Address, bufferY); }*/ } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { /*UInt64 PtrOutput1 = ReadPtr((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032FC700)); PtrOutput1 = ReadPtr((IntPtr)(PtrOutput1 + 0x40)); PtrOutput1 = ReadPtr((IntPtr)(PtrOutput1 + 0xA0)); PtrOutput1 = ReadPtr((IntPtr)PtrOutput1);*/ UInt64 PtrOutput1 = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032FC700), new UInt64[] { 0x40, 0xA0, 0x00 }); if (PtrOutput1 != 0) { SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(PtrOutput1 + 0x4A0)) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(PtrOutput1 + 0x4A0)) >> 3 & 0x01); //SetOutputValue(OutputId.P1_CoinBlocker, ReadByte((IntPtr)(PtrOutput1 + 0x478)) >> 5 & 0x01); //SetOutputValue(OutputId.P2_CoinBlocker, ReadByte((IntPtr)(PtrOutput1 + 0x478)) >> 2 & 0x01); } int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; UInt64 Ptr1 = ReadPtr((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _PlayerStatusPointer_Offset)); if (Ptr1 != 0) { //Customs Outputs //Player Status : //[0] : Inactive //[1] : In-Game int P1_Status = ReadByte((IntPtr)(Ptr1 + 0x231)); int P2_Status = ReadByte((IntPtr)(Ptr1 + 0x232)); if (P1_Status != 0) { /*UInt64 PtrAmmo = ReadPtr((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032B5B88)); PtrAmmo = ReadPtr((IntPtr)(PtrAmmo + 0x30)); PtrAmmo = ReadPtr((IntPtr)(PtrAmmo + 0xB0)); PtrAmmo = ReadPtr((IntPtr)(PtrAmmo + 0x90));*/ UInt64 PtrAmmo = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032B5B88), new UInt64[] { 0x30, 0xB0, 0x90 }); P1_Ammo = ReadByte((IntPtr)(PtrAmmo + 0x378)); P1_Life = ReadByte((IntPtr)(Ptr1 + 0x364)); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status != 0) { /*UInt64 PtrAmmo = ReadPtr((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032B5B88)); PtrAmmo = ReadPtr((IntPtr)(PtrAmmo + 0x30)); PtrAmmo = ReadPtr((IntPtr)(PtrAmmo + 0xB0)); PtrAmmo = ReadPtr((IntPtr)(PtrAmmo + 0x98));*/ UInt64 PtrAmmo = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032B5B88), new UInt64[] { 0x30, 0xB0, 0x98 }); P2_Ammo = ReadByte((IntPtr)(PtrAmmo + 0x378)); P2_Life = ReadByte((IntPtr)(Ptr1 + 0x43C)); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } _P1_LastAmmo = P1_Ammo; _P1_LastLife = P1_Life; _P2_LastAmmo = P2_Ammo; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); /*UInt64 PtrCredits = ReadPtr((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032FC700)); PtrCredits = ReadPtr((IntPtr)(PtrCredits + 0x88)); PtrCredits = ReadPtr((IntPtr)(PtrCredits + 0x80)); PtrCredits = ReadPtr((IntPtr)(PtrCredits + 0x50));*/ UInt64 PtrCredits = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x032FC700), new UInt64[] { 0x88, 0x90, 0x50 }); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)(PtrCredits + 0x21C))); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadePcMechaDino.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { internal class Game_ArcadepcMechaDino : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] BallMotor = null; public byte[] StartLamp = null; public byte[] PlayerBonusWeaponLamp = null; public byte[] SpineMotor = null; public byte BonusWeaponLamp = 0; public byte SeatVibrationLamp = 0; public byte WaterFallLamp = 0; public byte WaterLevelLamp = 0; public float[] Life = null; public byte[] Damaged = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcMechaDino(String RomName) : base(RomName, "game", "RobotDragon") { _KnownMd5Prints.Add("Mechanical Dinosaur v6.5 - Original", "25dd74d2eeef2f61ed32ce439bcd264a"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P3_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P4_LmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P3_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P4_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P3_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P4_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_LmpBonusWeapon)); _Outputs.Add(new GameOutput(OutputId.P2_LmpBonusWeapon)); _Outputs.Add(new GameOutput(OutputId.P3_LmpBonusWeapon)); _Outputs.Add(new GameOutput(OutputId.P4_LmpBonusWeapon)); /*_Outputs.Add(new GameOutput(OutputId.P1_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P2_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P3_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P4_TicketFeeder));*/ _Outputs.Add(new GameOutput(OutputId.BonusWeaponLamp)); _Outputs.Add(new GameOutput(OutputId.SeatVibrationLamp)); _Outputs.Add(new GameOutput(OutputId.WaterFallLamp)); _Outputs.Add(new GameOutput(OutputId.WaterLevelLamp)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); //Handling Start Lamps based on player status if (((OutputData)_OutputData).StartLamp[0] == 1) SetOutputValue(OutputId.P1_LmpStart, -1); else SetOutputValue(OutputId.P1_LmpStart, 0); if (((OutputData)_OutputData).StartLamp[1] == 1) SetOutputValue(OutputId.P2_LmpStart, -1); else SetOutputValue(OutputId.P2_LmpStart, 0); if (((OutputData)_OutputData).StartLamp[2] == 1) SetOutputValue(OutputId.P3_LmpStart, -1); else SetOutputValue(OutputId.P3_LmpStart, 0); if (((OutputData)_OutputData).StartLamp[3] == 1) SetOutputValue(OutputId.P4_LmpStart, -1); else SetOutputValue(OutputId.P4_LmpStart, 0); SetOutputValue(OutputId.P1_LmpGun, ((OutputData)_OutputData).BallMotor[0]); SetOutputValue(OutputId.P2_LmpGun, ((OutputData)_OutputData).BallMotor[1]); SetOutputValue(OutputId.P3_LmpGun, ((OutputData)_OutputData).BallMotor[2]); SetOutputValue(OutputId.P4_LmpGun, ((OutputData)_OutputData).BallMotor[3]); SetOutputValue(OutputId.P1_GunMotor, (int)(sbyte)((OutputData)_OutputData).SpineMotor[0]); SetOutputValue(OutputId.P2_GunMotor, (int)(sbyte)((OutputData)_OutputData).SpineMotor[1]); SetOutputValue(OutputId.P3_GunMotor, (int)(sbyte)((OutputData)_OutputData).SpineMotor[2]); SetOutputValue(OutputId.P4_GunMotor, (int)(sbyte)((OutputData)_OutputData).SpineMotor[3]); SetOutputValue(OutputId.P1_LmpBonusWeapon, ((OutputData)_OutputData).PlayerBonusWeaponLamp[0]); SetOutputValue(OutputId.P2_LmpBonusWeapon, ((OutputData)_OutputData).PlayerBonusWeaponLamp[1]); SetOutputValue(OutputId.P3_LmpBonusWeapon, ((OutputData)_OutputData).PlayerBonusWeaponLamp[2]); SetOutputValue(OutputId.P4_LmpBonusWeapon, ((OutputData)_OutputData).PlayerBonusWeaponLamp[3]); SetOutputValue(OutputId.BonusWeaponLamp, ((OutputData)_OutputData).BonusWeaponLamp); SetOutputValue(OutputId.SeatVibrationLamp, ((OutputData)_OutputData).SeatVibrationLamp); SetOutputValue(OutputId.WaterFallLamp, ((OutputData)_OutputData).WaterLevelLamp); SetOutputValue(OutputId.WaterLevelLamp, ((OutputData)_OutputData).WaterLevelLamp); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P3_Damaged, ((OutputData)_OutputData).Damaged[2]); SetOutputValue(OutputId.P4_Damaged, ((OutputData)_OutputData).Damaged[3]); if (((OutputData)_OutputData).IsPlaying[0] == 1) SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); else SetOutputValue(OutputId.P1_Life, 0); if (((OutputData)_OutputData).IsPlaying[1] == 1) SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); else SetOutputValue(OutputId.P2_Life, 0); if (((OutputData)_OutputData).IsPlaying[2] == 1) SetOutputValue(OutputId.P3_Life, (int)((OutputData)_OutputData).Life[2]); else SetOutputValue(OutputId.P4_Life, 0); if (((OutputData)_OutputData).IsPlaying[3] == 1) SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[3]); else SetOutputValue(OutputId.P4_Life, 0); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, (int)((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, (int)((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadePcMissionImpossible.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcMissionImpossible : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] TriggerL = null; public byte[] TriggerR = null; public byte[] Reload = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Life = null; public int[] AmmoGunL = null; public int[] AmmoGunR = null; public int Credits = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcMissionImpossible(String RomName) : base(RomName, "MissionImpossible", "MissionImpossible") { _KnownMd5Prints.Add("Mission Impossible Arcade v201123 - Original", "aa47159c7b394366da6e7b8f402c52ef"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { if (Configurator.GetInstance().MissionImpossible_MergeTriggers) { ((InputData)_InputData).TriggerL[PlayerData.ID - 1] = 1; ((InputData)_InputData).TriggerR[PlayerData.ID - 1] = 1; } else { ((InputData)_InputData).TriggerL[PlayerData.ID - 1] = 1; } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { if (Configurator.GetInstance().MissionImpossible_MergeTriggers) { ((InputData)_InputData).TriggerL[PlayerData.ID - 1] = 0; ((InputData)_InputData).TriggerR[PlayerData.ID - 1] = 0; } else { ((InputData)_InputData).TriggerL[PlayerData.ID - 1] = 0; } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { if (!Configurator.GetInstance().MissionImpossible_MergeTriggers) { ((InputData)_InputData).TriggerR[PlayerData.ID - 1] = 1; } } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { if (!Configurator.GetInstance().MissionImpossible_MergeTriggers) { ((InputData)_InputData).TriggerR[PlayerData.ID - 1] = 0; } } if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); if (((OutputData)_OutputData).IsPlaying[0] == 1) { SetOutputValue(OutputId.P1_CtmLmpStart, 0); SetOutputValue(OutputId.P1_Ammo, (int)(((OutputData)_OutputData).AmmoGunL[0] + ((OutputData)_OutputData).AmmoGunR[0])); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); } else { SetOutputValue(OutputId.P1_CtmLmpStart, -1); SetOutputValue(OutputId.P1_Ammo, 0); SetOutputValue(OutputId.P1_Life, 0); } if (((OutputData)_OutputData).IsPlaying[1] == 1) { SetOutputValue(OutputId.P2_CtmLmpStart, 0); SetOutputValue(OutputId.P2_Ammo, (int)(((OutputData)_OutputData).AmmoGunL[1] + ((OutputData)_OutputData).AmmoGunR[1])); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); } else { SetOutputValue(OutputId.P2_CtmLmpStart, -1); SetOutputValue(OutputId.P2_Ammo, 0); SetOutputValue(OutputId.P2_Life, 0); } SetOutputValue(OutputId.Credits, ((OutputData)_OutputData).Credits); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcBullseye.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcBullseye : Game__Unity { private class InputData : Base_InputData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte IsPlaying = 0; public byte Recoil = 0; public byte LED_LeftButton = 0; public byte LED_RightButton = 0; public byte LED_StartButton = 0; public byte LED_RedLight = 0; public byte LED_Screen = 0; public byte Damaged = 0; public int Ammo = 0; public int Credits = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 1; /// /// Constructor /// public Game_ArcadepcBullseye(String RomName) : base(RomName, "WW2021", "WW2021") { _KnownMd5Prints.Add("Bullseye Crackshot v4.14.40417 - Original", "F5DC92EB3412CBE3D93E66D840D14B3F"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpLeft)); _Outputs.Add(new GameOutput(OutputId.LmpRight)); _Outputs.Add(new GameOutput(OutputId.Lmp_RedLight)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).LED_StartButton); SetOutputValue(OutputId.LmpLeft, ((OutputData)_OutputData).LED_LeftButton); SetOutputValue(OutputId.LmpRight, ((OutputData)_OutputData).LED_RightButton); SetOutputValue(OutputId.Lmp_RedLight, ((OutputData)_OutputData).LED_RedLight); SetOutputValue(OutputId.LmpBillboard, ((OutputData)_OutputData).LED_Screen); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil); SetOutputValue(OutputId.P1_Ammo, (int)((OutputData)_OutputData).Ammo); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcDinoInvasion.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcDinoInvasion : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Ammo = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcDinoInvasion(String RomName) : base(RomName, "konglong", "shoulie_konglong") { _KnownMd5Prints.Add("DinoInvasion EN v1.2.8 - Original", "30aacfb5fb65d409e7bd6baee679ba2d"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); /*_Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P3_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P4_LmpStart, 500));*/ _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P3_Ammo)); _Outputs.Add(new GameOutput(OutputId.P4_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); //Handling Start Lamps based on player status /*if (((OutputData)_OutputData).IsPlaying[0] == 0) SetOutputValue(OutputId.P1_CtmLmpStart, -1); else SetOutputValue(OutputId.P1_CtmLmpStart, 0); if (((OutputData)_OutputData).IsPlaying[1] == 0) SetOutputValue(OutputId.P2_CtmLmpStart, -1); else SetOutputValue(OutputId.P2_CtmLmpStart, 0); if (((OutputData)_OutputData).IsPlaying[2] == 0) SetOutputValue(OutputId.P3_CtmLmpStart, -1); else SetOutputValue(OutputId.P3_CtmLmpStart, 0); if (((OutputData)_OutputData).IsPlaying[3] == 0) SetOutputValue(OutputId.P4_CtmLmpStart, -1); else SetOutputValue(OutputId.P4_CtmLmpStart, 0);*/ SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P3_CtmRecoil, ((OutputData)_OutputData).Recoil[2]); SetOutputValue(OutputId.P4_CtmRecoil, ((OutputData)_OutputData).Recoil[3]); SetOutputValue(OutputId.P1_Ammo, ((OutputData)_OutputData).Ammo[0]); SetOutputValue(OutputId.P2_Ammo, ((OutputData)_OutputData).Ammo[1]); SetOutputValue(OutputId.P3_Ammo, ((OutputData)_OutputData).Ammo[2]); SetOutputValue(OutputId.P4_Ammo, ((OutputData)_OutputData).Ammo[3]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P3_Damaged, ((OutputData)_OutputData).Damaged[2]); SetOutputValue(OutputId.P4_Damaged, ((OutputData)_OutputData).Damaged[3]); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, (int)((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, (int)((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcDrakon.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { internal class Game_ArcadepcDrakon : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Start_Led = null; public byte[] Recoil = null; public byte[] Damaged = null; public byte[] Rumble = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcDrakon(String RomName) : base(RomName, "Game", "09038_Adrenaline_Skyride_Turret") { _KnownMd5Prints.Add("Drakon Realm Keepers - Development Build v227996", "783a592917167b3a3a3e42f9f0717a06"); _KnownMd5Prints.Add("Drakon Realm Keepers - Release Build v223011", "b9eaa606548f04d684876c17f48deaa3"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); /*_Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits));*/ } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P1_GunMotor, ((OutputData)_OutputData).Rumble[0]); SetOutputValue(OutputId.P2_GunMotor, ((OutputData)_OutputData).Rumble[1]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).Start_Led[0]); SetOutputValue(OutputId.P2_LmpStart, ((OutputData)_OutputData).Start_Led[1]); /*SetOutputValue(OutputId.P1_Credits, _OutputData.P1_Credits); SetOutputValue(OutputId.P2_Credits, _OutputData.P2_Credits);*/ } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcDrakon_NoPlugin.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { class Game_ArcadepcDrakon_NoPlugin : Game { private string _GameAssemblyDll_Name = "gameassembly.dll"; private IntPtr _GameAssemblyDll_BaseAddress = IntPtr.Zero; private float _P1_X_Value; private float _P1_Y_Value; private float _P2_X_Value; private float _P2_Y_Value; /*** Game Data for Memory Hack ***/ /*** MEMORY ADDRESSES **/ private InjectionStruct _Axis_InjectionStruct = new InjectionStruct(0x03EFCC0, 15); private InjectionStruct _Trigger_InjectionStruct = new InjectionStruct(0x03EFBE0, 15); private UInt64 _FireBreathStatus_Ptr_Offset = 0x01EB5108; private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x00389664, 15); private InjectionStruct _Damage_InjectionStruct = new InjectionStruct(0x0041B8B0, 15); private InjectionStruct _PlayerPlaying_InjectionStruct = new InjectionStruct(0x00EB6310, 15); private InjectionStruct _IsLevelSelected_InjectionStruct = new InjectionStruct(0x0417004, 15); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x040E4E4, 16); //Custom Input Data private UInt64 _P1_X_CaveAddress = 0; private UInt64 _P1_Y_CaveAddress = 0; private UInt64 _P2_X_CaveAddress = 0; private UInt64 _P2_Y_CaveAddress = 0; private UInt64 _P1_TriggerState_CaveAddress = 0; private UInt64 _P2_TriggerState_CaveAddress = 0; //Custom Output Data private UInt64 _P1_Recoil_CaveAddress = 0; private UInt64 _P2_Recoil_CaveAddress = 0; private UInt64 _P1_Damage_CaveAddress = 0; private UInt64 _P2_Damage_CaveAddress = 0; private UInt64 _P1_Playing_CaveAddress = 0; private UInt64 _P2_Playing_CaveAddress = 0; private UInt64 _IsLevelChoosen_Caveaddress = 0; /// /// Constructor /// public Game_ArcadepcDrakon_NoPlugin(String RomName) : base(RomName, "Game") { _KnownMd5Prints.Add("Drakon Realm Keepers - Development Build v227996 [Original Dump]", "783a592917167b3a3a3e42f9f0717a06"); _KnownMd5Prints.Add("Drakon Realm Keepers - Release Build v223011 [Original Dump]", "b9eaa606548f04d684876c17f48deaa3"); _tProcess.Start(); Logger.WriteLog("Waiting for Adrenaline Amusements game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals(_GameAssemblyDll_Name)) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); _GameAssemblyDll_BaseAddress = m.BaseAddress; Logger.WriteLog(_GameAssemblyDll_Name + " = 0x" + _GameAssemblyDll_BaseAddress); String GameAssemblyDll_Path = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", _GameAssemblyDll_Name); CheckMd5(GameAssemblyDll_Path); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Coordinates goes from [-1.0, -1.0] in bottom left corner to [1.0, 1.0] in upper right, no matter the display ratio float X_Value = (2.0f * (float)PlayerData.RIController.Computed_X / (float)TotalResX) - 1.0f; float Y_Value = (1.0f - (2.0f * (float)PlayerData.RIController.Computed_Y / (float)TotalResY)); if (X_Value < -1.0f) X_Value = -1.0f; if (Y_Value < -1.0f) Y_Value = 1.0f; if (X_Value > (float)1.0f) X_Value = (float)1.0f; if (Y_Value > (float)1.0f) Y_Value = (float)1.0f; Logger.WriteLog("Computed float values = [ " + X_Value + "x" + Y_Value + " ]"); if (PlayerData.ID == 1) { _P1_X_Value = X_Value; _P1_Y_Value = Y_Value; } else if (PlayerData.ID == 2) { _P2_X_Value = X_Value; _P2_Y_Value = Y_Value; } PlayerData.RIController.Computed_X = Convert.ToInt16(X_Value * 100); PlayerData.RIController.Computed_Y = Convert.ToInt16(Y_Value * 100); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region MemoryHack #region Inputs Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x04; _P2_X_CaveAddress = _InputsDatabank_Address + 0x08; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x0C; _P1_TriggerState_CaveAddress = _InputsDatabank_Address + 0x10; _P2_TriggerState_CaveAddress = _InputsDatabank_Address + 0x11; SetHack_Axis(); SetHack_Trigger(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Replace incomming parameter from InputsManager.SendPositionEvent() function /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //push rdx CaveMemory.Write_StrBytes("52"); //mov rax, _P1_X_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_CaveAddress)); //shl rdx,03 CaveMemory.Write_StrBytes("48 C1 E2 03"); //add rax,rdx CaveMemory.Write_StrBytes("48 01 D0"); //mov rdx,[rax] CaveMemory.Write_StrBytes("48 8B 10"); //mov [r8],rdx CaveMemory.Write_StrBytes("49 89 10"); //pop rdx CaveMemory.Write_StrBytes("5A"); //pop rax CaveMemory.Write_StrBytes("58"); //mov [rsp+08],rbx CaveMemory.Write_StrBytes("48 89 5C 24 08"); //mov [rsp+10],rbp CaveMemory.Write_StrBytes("48 89 6C 24 10"); //mov [rsp+18],rsi CaveMemory.Write_StrBytes("48 89 74 24 18"); //jmp back CaveMemory.Write_jmp((UInt64)_GameAssemblyDll_BaseAddress + _Axis_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _Axis_InjectionStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Replace incomming parameter from InputsManager.SendButtonEvent() function /// private void SetHack_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //mov rax, _P1_TriggerState_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_TriggerState_CaveAddress)); //add rax,rdx CaveMemory.Write_StrBytes("48 01 D0"); //mov rax,[rax] CaveMemory.Write_StrBytes("48 8B 00"); //and rax,000000FF CaveMemory.Write_StrBytes("48 25 FF 00 00 00"); //mov r8,rax CaveMemory.Write_StrBytes("4C 8B C0"); //pop rax CaveMemory.Write_StrBytes("58"); //mov [rsp+08],rbx CaveMemory.Write_StrBytes("48 89 5C 24 08"); //mov [rsp+10],rsi CaveMemory.Write_StrBytes("48 89 74 24 10"); //push rdi CaveMemory.Write_StrBytes("57"); //sub rsp,20 CaveMemory.Write_StrBytes("48 83 EC 20"); //jmp back CaveMemory.Write_jmp((UInt64)_GameAssemblyDll_BaseAddress + _Trigger_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding Trigger Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _Trigger_InjectionStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Outputs Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_Recoil_CaveAddress = _OutputsDatabank_Address; _P2_Recoil_CaveAddress = _OutputsDatabank_Address + 1; _P1_Damage_CaveAddress = _OutputsDatabank_Address + 0x08; _P2_Damage_CaveAddress = _OutputsDatabank_Address + 0x09; _P1_Playing_CaveAddress = _OutputsDatabank_Address + 0x10; _P2_Playing_CaveAddress = _OutputsDatabank_Address + 0x11; _IsLevelChoosen_Caveaddress = _OutputsDatabank_Address + 0x20; SetHack_Recoil(); SetHack_Damage(); SetHack_IsPlayerPlaying(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercepting calls to SBK.Skyride.Turret.Turret.FireBullet() /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //push rbx CaveMemory.Write_StrBytes("53"); //mov rax, _P1_Recoil_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Recoil_CaveAddress)); //xor rbx,rbx CaveMemory.Write_StrBytes("48 31 DB"); //mov bl,[rcx+18] CaveMemory.Write_StrBytes("8A 59 18"); //add rax,rbx CaveMemory.Write_StrBytes("48 01 D8"); //mov byte ptr [rax],01 CaveMemory.Write_StrBytes("C6 00 01"); //pop rbx CaveMemory.Write_StrBytes("5B"); //pop rax CaveMemory.Write_StrBytes("58"); //lea rbp,[rsp CaveMemory.Write_StrBytes("48 8D AC 24 60 FF FF FF"); //sub rsp,000001A0 CaveMemory.Write_StrBytes("48 81 EC A0 01 00 00"); //jmp back CaveMemory.Write_jmp((UInt64)_GameAssemblyDll_BaseAddress + _Recoil_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _Recoil_InjectionStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Intercepting calls to HudWindow.OnPlayerHitByEnemy() /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov [rsp+08],rbx CaveMemory.Write_StrBytes("48 89 5C 24 08"); //mov rbx, _P1_Damage_CaveAddress CaveMemory.Write_StrBytes("48 BB"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Damage_CaveAddress)); //add rbx,r8 CaveMemory.Write_StrBytes("4C 01 C3"); //mov byte ptr [rbx],01 CaveMemory.Write_StrBytes("C6 03 01"); //mov [rsp+10],rsi CaveMemory.Write_StrBytes("48 89 74 24 10"); //push rdi CaveMemory.Write_StrBytes("57"); //sub rsp,20 CaveMemory.Write_StrBytes("48 83 EC 20"); //jmp back CaveMemory.Write_jmp((UInt64)_GameAssemblyDll_BaseAddress + _Damage_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding Damage Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _Damage_InjectionStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Intercepting return value of Player.IsPlayerPlaying() function, which is called in loop all long /// private void SetHack_IsPlayerPlaying() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rbx CaveMemory.Write_StrBytes("53"); //push rdx CaveMemory.Write_StrBytes("52"); //xor rbx,rbx CaveMemory.Write_StrBytes("48 31 DB"); //mov bl,[rdi+28] CaveMemory.Write_StrBytes("8A 5F 28"); //mov rdx, _P1_Playing_CaveAddress CaveMemory.Write_StrBytes("48 BA"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Playing_CaveAddress)); //add rdx,rbx CaveMemory.Write_StrBytes("48 01 DA"); //mov [rdx],al CaveMemory.Write_StrBytes("88 02"); //pop rbx CaveMemory.Write_StrBytes("5B"); //pop rbx CaveMemory.Write_StrBytes("5B"); //mov rsi,[rsp+38] CaveMemory.Write_StrBytes("48 8B 74 24 38"); //mov rbp,[rsp+30] CaveMemory.Write_StrBytes("48 8B 6C 24 30"); //mov rbx,[rsp+40] CaveMemory.Write_StrBytes("48 8B 5C 24 40"); //jmp back CaveMemory.Write_jmp((UInt64)_GameAssemblyDll_BaseAddress + _PlayerPlaying_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding PlayerStatus Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _PlayerPlaying_InjectionStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion /// /// Changind axis value in Target.OnTurretPosition() to [-2.0, -2.0] will put crosshair AND lasers out of sight /// protected override void Apply_NoCrosshairMemoryHack() { SetHack_GetLevelSelectedStatus(); SetHack_RemoveCrosshairAndLasers(); } /// /// Getting the IsLevelSelected flag of the class during ChooseLevelWindow.Update() loop /// private void SetHack_GetLevelSelectedStatus() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //push rdx CaveMemory.Write_StrBytes("52"); //movzx eax,byte ptr [rbx+000000CA] CaveMemory.Write_StrBytes("0F B6 83 CA 00 00 00"); //mov rdx, _IsLevelChoosen_Caveaddress CaveMemory.Write_StrBytes("48 BA"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_IsLevelChoosen_Caveaddress)); //mov [rdx],rax CaveMemory.Write_StrBytes("48 89 02"); //pop rdx CaveMemory.Write_StrBytes("5A"); //pop rax CaveMemory.Write_StrBytes("58"); //mov [rsp+58],rbp CaveMemory.Write_StrBytes("48 89 6C 24 58"); //mov [rsp+60],rdi CaveMemory.Write_StrBytes("48 89 7C 24 60"); //mov [rsp+30],r14 CaveMemory.Write_StrBytes("4C 89 74 24 30"); //jmp back CaveMemory.Write_jmp((UInt64)_GameAssemblyDll_BaseAddress + _IsLevelSelected_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding IsLevelSelected Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _IsLevelSelected_InjectionStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Changind axis value in Target.OnTurretPosition() to [-2.0, -2.0] will put crosshair AND lasers out of sight /// Unfortunately, on the level selection window, this makes impossible to choose a level /// Sowe can filter by checking the IsLevelChoosen flag o disable the mod /// private void SetHack_RemoveCrosshairAndLasers() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //mov rax, [_IsLevelChoosen_Caveaddress] CaveMemory.Write_StrBytes("48 A1"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_IsLevelChoosen_Caveaddress)); //test rax,rax CaveMemory.Write_StrBytes("48 85 C0"); //jne next CaveMemory.Write_StrBytes("74 0D"); //mov [rdi],C0000000 CaveMemory.Write_StrBytes("C7 07 00 00 00 C0"); //mov [rdi+04],C0000000 CaveMemory.Write_StrBytes("C7 47 04 00 00 00 C0"); //pop rax CaveMemory.Write_StrBytes("58"); //movss xmm6,[rsp+50] CaveMemory.Write_StrBytes("F3 0F 10 74 24 50"); //movss xmm7,[rsp+54] CaveMemory.Write_StrBytes("F3 0F 10 7C 24 54"); //mulss xmm6,[rdi] CaveMemory.Write_StrBytes("F3 0F 59 37"); //jmp back CaveMemory.Write_jmp((UInt64)_GameAssemblyDll_BaseAddress + _NoCrosshair_InjectionStruct.InjectionReturnOffset); Logger.WriteLog("Adding No Crosshair Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _NoCrosshair_InjectionStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteBytes((IntPtr)(_P1_X_CaveAddress), BitConverter.GetBytes(_P1_X_Value)); WriteBytes((IntPtr)(_P1_Y_CaveAddress), BitConverter.GetBytes(_P1_Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((IntPtr)_P1_TriggerState_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((IntPtr)_P1_TriggerState_CaveAddress, 0x00); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)(_P2_X_CaveAddress), BitConverter.GetBytes(_P2_X_Value)); WriteBytes((IntPtr)(_P2_Y_CaveAddress), BitConverter.GetBytes(_P2_Y_Value)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((IntPtr)_P2_TriggerState_CaveAddress, 0x01); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((IntPtr)_P2_TriggerState_CaveAddress, 0x00); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte _P1Playing = ReadByte((IntPtr)_P1_Playing_CaveAddress); byte _P2Playing = ReadByte((IntPtr)_P2_Playing_CaveAddress); if (_P1Playing == 1) { SetOutputValue(OutputId.P1_CtmLmpStart, 0); } else { SetOutputValue(OutputId.P1_CtmLmpStart, -1); } if (_P2Playing == 1) { SetOutputValue(OutputId.P2_CtmLmpStart, 0); } else { SetOutputValue(OutputId.P2_CtmLmpStart, -1); } //Custom Recoil if (ReadByte((IntPtr)_P1_Recoil_CaveAddress) != 0) { WriteByte((IntPtr)_P1_Recoil_CaveAddress, 0x00); SetOutputValue(OutputId.P1_CtmRecoil, 1); } if (ReadByte((IntPtr)_P2_Recoil_CaveAddress) != 0) { WriteByte((IntPtr)_P2_Recoil_CaveAddress, 0x00); SetOutputValue(OutputId.P2_CtmRecoil, 1); } //Custom Damage if (ReadByte((IntPtr)_P1_Damage_CaveAddress) != 0) { WriteByte((IntPtr)_P1_Damage_CaveAddress, 0x00); SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte((IntPtr)_P2_Damage_CaveAddress) != 0) { WriteByte((IntPtr)_P2_Damage_CaveAddress, 0x00); SetOutputValue(OutputId.P2_Damaged, 1); } //Rumble will be activated while FireBreathing UInt64 P1_FireBreathStatus_Address = ReadPtrChain((IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _FireBreathStatus_Ptr_Offset), new UInt64[]{ 0xB8 }); SetOutputValue(OutputId.P1_GunMotor, ReadByte((IntPtr)P1_FireBreathStatus_Address)); SetOutputValue(OutputId.P2_GunMotor, ReadByte((IntPtr)(P1_FireBreathStatus_Address + 1))); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcElevatorActionInvasion.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { class Game_ArcadepcElevatorActionInvasion : Game { private string _UsbPluginsDllName = "usbpluginsdll.dll"; private IntPtr _UsbPluginsDll_BaseAddress = IntPtr.Zero; /*** Game Data for Memory Hack ***/ /*** MEMORY ADDRESSES **/ private static UInt32 _P1_X_Offset = 0x3A91850; private static UInt32 _P1_Y_Offset = 0x3A91854; private static UInt32 _P1_Trigger_Offset = 0x3A91858; private static UInt32 _P1_Credits_Offset = 0x3A9185C; private static UInt32 _P2_X_Offset = 0x3A91860; private static UInt32 _P2_Y_Offset = 0x3A91864; private static UInt32 _P2_Trigger_Offset = 0x3A91868; private static UInt32 _P2_Credits_Offset = 0x3A9186C; private static UInt32 _IoBoard_Payload_Offset = 0x3A91870 + 0x2CC; private NopStruct _Nop_KeyboardAndMouse = new NopStruct(0xB85E4A, 5); private UInt64 _Recoil_Injection_Offset = 0x794D54; private UInt64 _Recoil_Injection_Return_Offset = 0x794D63; private UInt64 _Damage_Injection_Offset = 0x796360; private UInt64 _Damage_Injection_Return_Offset = 0x796370; //Outputs private UInt64 _P1_Recoil_CaveAddress = 0; private UInt64 _P2_Recoil_CaveAddress = 0; private UInt64 _P1_Damage_CaveAddress = 0; private UInt64 _P2_Damage_CaveAddress = 0; /// /// Constructor /// public Game_ArcadepcElevatorActionInvasion(String RomName) : base(RomName, "ESGame-Win64-Shipping") { _KnownMd5Prints.Add("Elevator Action Invasion v1.6.1[C] clean dump", "bc34cc1f04de2977eeb4e2fd5895ebf4"); _KnownMd5Prints.Add("Elevator Action Invasion v1.6.1[C] patched by Argonlefou", "d2c4c99a86648b34316ca4f61a23c1ea"); _tProcess.Start(); Logger.WriteLog("Waiting for Unis " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals(_UsbPluginsDllName)) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); _UsbPluginsDll_BaseAddress = m.BaseAddress; Logger.WriteLog(_UsbPluginsDllName + " = 0x" + _UsbPluginsDll_BaseAddress); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); _ProcessHooked = true; break; } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region MemoryHack protected override void Apply_InputsMemoryHack() { //+B85E4A --> NOP | 5 --> Nop mousebuttons and keyboard keys SetNops(_TargetProcess_MemoryBaseAddress, _Nop_KeyboardAndMouse); } protected override void Apply_OutputsMemoryHack() { SetHack_Recoil(); SetHack_Damage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } //To sync recoil pulse with fired bullets, we will retrieve the information when the game call the "PlayFireMV" //Player Index is in edx private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _P1_Recoil_CaveAddress = CaveMemory.CaveAddress + 0x40; _P2_Recoil_CaveAddress = CaveMemory.CaveAddress + 0x44; Logger.WriteLog("_Recoil_CaveAddress = 0x" + _P1_Recoil_CaveAddress.ToString("X16")); //push rax CaveMemory.Write_StrBytes("50"); //push rdx CaveMemory.Write_StrBytes("52"); //lea rax, [RIP+ 0x37] = P1_Recoil_CaveAddress CaveMemory.Write_StrBytes("48 8D 05 37 00 00 00"); //shl rdx, 2 CaveMemory.Write_StrBytes("48 C1 E2 02"); //add rax, rdx CaveMemory.Write_StrBytes("48 01 D0"); //mov [rax], 1 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //pop rdx CaveMemory.Write_StrBytes("5A"); //pop rax CaveMemory.Write_StrBytes("58"); //OriginalCode CaveMemory.Write_StrBytes("53 48 83 EC 20 48 63 DA 48 8B 91 38 02 00 00"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Recoil_Injection_Return_Offset); Logger.WriteLog("Adding Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Recoil_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } //Intercept a call to PlayTakeDamageSound() function to get dammage event //Player Index is in edx private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _P1_Damage_CaveAddress = CaveMemory.CaveAddress + 0x40; _P2_Damage_CaveAddress = CaveMemory.CaveAddress + 0x44; Logger.WriteLog("_Damage_CaveAddress = 0x" + _P1_Recoil_CaveAddress.ToString("X16")); //push rax CaveMemory.Write_StrBytes("50"); //push rdx CaveMemory.Write_StrBytes("52"); //lea rax, [RIP+ 0x37] = P1_Damage_CaveAddress CaveMemory.Write_StrBytes("48 8D 05 37 00 00 00"); //shl rdx, 2 CaveMemory.Write_StrBytes("48 C1 E2 02"); //add rax, rdx CaveMemory.Write_StrBytes("48 01 D0"); //mov [rax], 1 CaveMemory.Write_StrBytes("C7 00 01 00 00 00"); //pop rdx CaveMemory.Write_StrBytes("5A"); //pop rax CaveMemory.Write_StrBytes("58"); //OriginalCode CaveMemory.Write_StrBytes("40 57 41 56 41 57 48 81 EC 80 00 00 00 48 8B F9"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Damage_Injection_Return_Offset); Logger.WriteLog("Adding Recoil Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Damage_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 1080.0; double dMaxY = 1920.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_X_Offset), BitConverter.GetBytes(PlayerData.RIController.Computed_X)); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Y_Offset), BitConverter.GetBytes(PlayerData.RIController.Computed_Y)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset), 1); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Trigger_Offset), 0); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_X_Offset), BitConverter.GetBytes(PlayerData.RIController.Computed_X)); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Y_Offset), BitConverter.GetBytes(PlayerData.RIController.Computed_Y)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset), 1); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Trigger_Offset), 0); } } public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == Configurator.GetInstance().DIK_Eai_P1_Start) Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0x20); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_P2_Start) Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0x10); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_Settings) Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0x04); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_MenuUp) Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0x04); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_MenuDown) Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0x02); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_MenuEnter) Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0x01); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_P1_Credits) { UInt32 c = BitConverter.ToUInt32(ReadBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Credits_Offset), 4), 0); c++; WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Credits_Offset), BitConverter.GetBytes(c)); } else if (s.scanCode == Configurator.GetInstance().DIK_Eai_P2_Credits) { UInt32 c = BitConverter.ToUInt32(ReadBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Credits_Offset), 4), 0); c++; WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Credits_Offset), BitConverter.GetBytes(c)); } /*else if (s.scanCode == HardwareScanCode.DIK_F) SetDoorSensor(SensorDoor.A, DoorState.ClosedOn); else if (s.scanCode == HardwareScanCode.DIK_G) SetDoorSensor(SensorDoor.B, DoorState.ClosedOn); else if (s.scanCode == HardwareScanCode.DIK_V) SetDoorSensor(SensorDoor.A, DoorState.InfraredSwitchOn); else if (s.scanCode == HardwareScanCode.DIK_B) SetDoorSensor(SensorDoor.B, DoorState.InfraredSwitchOn);*/ } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == Configurator.GetInstance().DIK_Eai_P1_Start) Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0xDF); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_P2_Start) Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0xEF); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_Settings) Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0xFB); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_MenuUp) Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0xFB); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_MenuDown) Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0xFD); else if (s.scanCode == Configurator.GetInstance().DIK_Eai_MenuEnter) Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 2), 0xFE); /*else if (s.scanCode == HardwareScanCode.DIK_F) SetDoorSensor(SensorDoor.A, DoorState.ClosedOff); else if (s.scanCode == HardwareScanCode.DIK_G) SetDoorSensor(SensorDoor.B, DoorState.ClosedOff); else if (s.scanCode == HardwareScanCode.DIK_V) SetDoorSensor(SensorDoor.A, DoorState.InfraredSwitchOff); else if (s.scanCode == HardwareScanCode.DIK_B) SetDoorSensor(SensorDoor.B, DoorState.InfraredSwitchOff);*/ } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } //Door A (Left) = 1 / Door B (Right = 0) //State public enum SensorDoor { B=0, A } public enum DoorState { InfraredSwitchOn, InfraredSwitchOff, ErrorOn, ErrorOff, ClosedOn, ClosedOff, } private void SetDoorSensor(SensorDoor DoorNumber, DoorState State) { if (State == DoorState.InfraredSwitchOn) { Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 3 + (uint)DoorNumber), 0x80); } else if (State == DoorState.ErrorOn) { Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 3 + (uint)DoorNumber), 0x40); } else if (State == DoorState.ClosedOn) { Apply_OR_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 3 + (uint)DoorNumber), 0x0F); } else if (State == DoorState.InfraredSwitchOff) { Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 3 + (uint)DoorNumber), 0x7F); } else if (State == DoorState.ErrorOff) { Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 3 + (uint)DoorNumber), 0xBF); } else if (State == DoorState.ClosedOff) { Apply_AND_ByteMask((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IoBoard_Payload_Offset + 3 + (uint)DoorNumber), 0xF0); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.DoorA)); _Outputs.Add(new GameOutput(OutputId.DoorB)); _Outputs.Add(new GameOutput(OutputId.ElevatorLedsStatus)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); /*_Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life));*/ _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Gun motor data goes from 0 to 10. Power of rumble ? SetOutputValue(OutputId.P1_GunMotor, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + 0x2a290 + 0x45C))); SetOutputValue(OutputId.P2_GunMotor, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + 0x2a290 + 0x460))); //Start Led have 3 original values : //0 = ON //1 = BLINK //2 = OFF byte b = ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + 0x2a290 + 0x450)); SetOutputValue(OutputId.P1_LmpStart, 2 - b); b = ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + 0x2a290 + 0x454)); SetOutputValue(OutputId.P2_LmpStart, 2 - b); //Door can be 0x09 or 0x11...don't know how it works SetOutputValue(OutputId.DoorB, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + 0x2a290 + 0x448))); SetOutputValue(OutputId.DoorA, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + 0x2a290 + 0x44C))); //Game LED status can have a lot of states : // 0 = Standby // 1 = OFF // 2 = NormalFight // 3 = Intense Fight // 4 = climax Fight // 5 = Damage // 6 = Continue // 7 = Level Passed // 8 = Elevator CLosed //I suppose this is handling the whole set of lights on the cabinet SetOutputValue(OutputId.ElevatorLedsStatus, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + 0x2a290 + 0x458))); //Custom recoil will be generated by a call to one of the game Gun function int P1_Recoil = ReadByte((IntPtr)_P1_Recoil_CaveAddress); if (P1_Recoil != 0) { WriteByte((IntPtr)_P1_Recoil_CaveAddress, 0x00); SetOutputValue(OutputId.P1_CtmRecoil, 1); } int P2_Recoil = ReadByte((IntPtr)_P2_Recoil_CaveAddress); if (P2_Recoil != 0) { WriteByte((IntPtr)_P2_Recoil_CaveAddress, 0x00); SetOutputValue(OutputId.P2_CtmRecoil, 1); } //Same Thing for custom dammage int P1_Damage = ReadByte((IntPtr)_P1_Damage_CaveAddress); if (P1_Damage != 0) { WriteByte((IntPtr)_P1_Damage_CaveAddress, 0x00); SetOutputValue(OutputId.P1_Damaged, 1); } int P2_Damage = ReadByte((IntPtr)_P2_Damage_CaveAddress); if (P2_Damage != 0) { WriteByte((IntPtr)_P2_Damage_CaveAddress, 0x00); SetOutputValue(OutputId.P2_Damaged, 1); } //Credits SetOutputValue(OutputId.P1_Credits, BitConverter.ToInt32(ReadBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Credits_Offset), 4), 0)); SetOutputValue(OutputId.P2_Credits, BitConverter.ToInt32(ReadBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Credits_Offset), 4), 0)); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcMIB.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { internal class Game_ArcadepcMIB : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte HideGuns = 0; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Motor = null; public byte[] Damaged = null; public byte NeuralyzerLamp = 0; public byte[] GunLight = null; public int Credits = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcMIB(String RomName) : base(RomName, "MIB", "MIB") { _KnownMd5Prints.Add("Men In Blacks - Original", "2ded47c90d49c4afd1bf5d510a7b6bbd"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } protected override void DsTcp_client_TcpConnected(Object Sender, EventArgs e) { if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; if (_DisableInputHack) _InputData.EnableInputsHack = 0; else _InputData.EnableInputsHack = 1; if (_HideGuns) ((InputData)_InputData).HideGuns = 1; else ((InputData)_InputData).HideGuns = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; if (_HideGuns) ((InputData)_InputData).HideGuns = 1; else ((InputData)_InputData).HideGuns = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.Neuralizer, 200, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); if (((OutputData)_OutputData).IsPlaying[0] == 1) { SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P1_LmpGun, ((OutputData)_OutputData).GunLight[0]); SetOutputValue(OutputId.P1_GunMotor, ((OutputData)_OutputData).Motor[0]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); } else { SetOutputValue(OutputId.P1_GunMotor, 0); } if (((OutputData)_OutputData).IsPlaying[1] == 1) { SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P2_LmpGun, ((OutputData)_OutputData).GunLight[1]); SetOutputValue(OutputId.P2_GunMotor, ((OutputData)_OutputData).Motor[1]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); } else { SetOutputValue(OutputId.P2_GunMotor, 0); } SetOutputValue(OutputId.Neuralizer, ((OutputData)_OutputData).NeuralyzerLamp); SetOutputValue(OutputId.Credits, (int)((OutputData)_OutputData).Credits); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcMarsSortie.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { internal class Game_ArcadepcMarsSortie : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] ChangeWeapon = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] WeaponId = null; public int[] Ammo = null; public int[] Credits = null; public byte Flashlight = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcMarsSortie(String RomName) : base(RomName, "Shooter", "Shooter") { _KnownMd5Prints.Add("Mars Sortie v1.46.9 - Original", "01a643a3f615a22338c2505bcb1b9609"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } protected override void DsTcp_client_TcpConnected(Object Sender, EventArgs e) { if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; if (_DisableInputHack) _InputData.EnableInputsHack = 0; else _InputData.EnableInputsHack = 1; _Tcpclient.SendMessage(_InputData.ToByteArray()); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.Lmp_WhiteStrobe)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P3_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P4_CtmLmpStart, 500)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.Lmp_WhiteStrobe, ((OutputData)_OutputData).Flashlight); //Handling Start Lamps based on player status if (((OutputData)_OutputData).IsPlaying[0] == 0) SetOutputValue(OutputId.P1_CtmLmpStart, -1); else SetOutputValue(OutputId.P1_CtmLmpStart, 0); if (((OutputData)_OutputData).IsPlaying[1] == 0) SetOutputValue(OutputId.P2_CtmLmpStart, -1); else SetOutputValue(OutputId.P2_CtmLmpStart, 0); if (((OutputData)_OutputData).IsPlaying[2] == 0) SetOutputValue(OutputId.P3_CtmLmpStart, -1); else SetOutputValue(OutputId.P3_CtmLmpStart, 0); if (((OutputData)_OutputData).IsPlaying[3] == 0) SetOutputValue(OutputId.P4_CtmLmpStart, -1); else SetOutputValue(OutputId.P4_CtmLmpStart, 0); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P3_CtmRecoil, ((OutputData)_OutputData).Recoil[2]); SetOutputValue(OutputId.P4_CtmRecoil, ((OutputData)_OutputData).Recoil[3]); SetOutputValue(OutputId.P1_Credits, ((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, ((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, ((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, ((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcNightHunterArcade.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcNightHunterArcade : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Action = null; public byte[] ChangeWeapon = null; public byte HideGuns = 0; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Life = null; public int[] GunType = null; public int Credits = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcNightHunterArcade(String RomName) : base(RomName, "qumo2_en", "test_gun") { _KnownMd5Prints.Add("Night Hunter v2.0.6 - Clean Dump", "1d6cbc2b0ebaf0bacbcbea84aa0f4a27"); _KnownMd5Prints.Add("Night Hunter v2.0.6 - Dongle patched", "d223a04447e84073a178a14d50349da8"); _KnownMd5Prints.Add("Night Hunter v3.0.2 - Clean Dump", "a7ddc5d05f9081b2fb5086cdd807deae"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } protected override void DsTcp_client_TcpConnected(Object Sender, EventArgs e) { if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; if (_DisableInputHack) _InputData.EnableInputsHack = 0; else _InputData.EnableInputsHack = 1; if (_HideGuns) ((InputData)_InputData).HideGuns = 1; else ((InputData)_InputData).HideGuns = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) ((InputData)_InputData).Action[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) ((InputData)_InputData).Action[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_LmpStart, 500)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_GunType)); _Outputs.Add(new GameOutput(OutputId.P2_GunType)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); //Will be triggered when a super weapon is active _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); //GunType: //0 = No //1 = Blue //2 = Red //3 = Big Blue //4 = Big Red int SuperGunAvailable = 0; if (((OutputData)_OutputData).IsPlaying[0] == 1) { SetOutputValue(OutputId.P1_LmpStart, 0); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P1_GunType, ((OutputData)_OutputData).GunType[0]); if (((OutputData)_OutputData).GunType[0] > 2) SuperGunAvailable = 1; } else { SetOutputValue(OutputId.P1_LmpStart, -1); SetOutputValue(OutputId.P1_Ammo, 0); SetOutputValue(OutputId.P1_Life, 0); SetOutputValue(OutputId.P1_GunType, 0); } if (((OutputData)_OutputData).IsPlaying[1] == 1) { SetOutputValue(OutputId.P2_LmpStart, 0); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P2_GunType, ((OutputData)_OutputData).GunType[1]); if (((OutputData)_OutputData).GunType[1] > 2) SuperGunAvailable = 1; } else { SetOutputValue(OutputId.P2_LmpStart, -1); SetOutputValue(OutputId.P2_Ammo, 0); SetOutputValue(OutputId.P2_Life, 0); SetOutputValue(OutputId.P2_GunType, 0); } SetOutputValue(OutputId.LmpBillboard, SuperGunAvailable); SetOutputValue(OutputId.Credits, ((OutputData)_OutputData).Credits); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcOnePoint.cs ================================================ using System; using System.Collections.Generic; using DemulShooterX64.Games; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64 { class Game_ArcadepcOnePoint : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte IsPlaying = 0; public byte Recoil = 0; public byte LED_LeftButton = 0; public byte LED_RightButton = 0; public byte LED_StartButton = 0; public byte LED_Safety = 0; public byte LED_TapeLed = 0; public int Ammo = 0; public int Credits = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 1; /// /// Constructor /// public Game_ArcadepcOnePoint(String RomName) : base(RomName, "game", "Bullet") { _KnownMd5Prints.Add("Gun Arena v1.14 Original", "bc0505ae1aa930797c59cbd6cdff7443"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpLeft)); _Outputs.Add(new GameOutput(OutputId.LmpRight)); _Outputs.Add(new GameOutput(OutputId.LmpSafety)); _Outputs.Add(new GameOutput(OutputId.LmpPanel)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).LED_StartButton); SetOutputValue(OutputId.LmpLeft, ((OutputData)_OutputData).LED_LeftButton); SetOutputValue(OutputId.LmpRight, ((OutputData)_OutputData).LED_RightButton); SetOutputValue(OutputId.LmpSafety, ((OutputData)_OutputData).LED_Safety); SetOutputValue(OutputId.LmpPanel, ((OutputData)_OutputData).LED_TapeLed); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil); SetOutputValue(OutputId.P1_Ammo, (int)((OutputData)_OutputData).Ammo); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcRaccoonRampage.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { class Game_ArcadepcRaccoonRampage : Game { private string _UsbPluginsDllName = "usbpluginsdll.dll"; private IntPtr _UsbPluginsDll_BaseAddress = IntPtr.Zero; /*** Game Data for Memory Hack ***/ /*** MEMORY ADDRESSES **/ private UInt64 _P1_X_CaveAddress = 0; private UInt64 _P1_Y_CaveAddress = 0; private UInt64 _SystemButtons_CaveAddress = 0; private UInt64 _StartButtonsAndWater_CaveAddress = 0; private UInt64 _P1_Coins_CaveAddress = 0; private UInt64 _P2_Coins_CaveAddress = 0; private UInt64 _P3_Coins_CaveAddress = 0; private UInt64 _P4_Coins_CaveAddress = 0; //Outputs private UInt64 _Outputs_BaseOffset = 0x2E740; public enum OutputIndex { P1_NewGunLightState = 0x19A, // P2_NewGunLightState, //Changed at the same time than GunLightState P3_NewGunLightState, //So....Useless ? P4_NewGunLightState, // P1_GunLightState = 0x5AC, P2_GunLightState = 0x5B4, P3_GunLightState = 0x5BC, P4_GunLightState = 0x5C4, LackTicketLight_12P = 0x5E8, LackTicketLight_34P = 0x5EC, DisinfectionLight = 0x5F0, P1_InnerWater = 0x5F4, P2_InnerWater, P3_InnerWater, P4_InnerWater, P1_OuterWater, P2_OuterWater, P3_OuterWater, P4_OuterWater, P1_GunShock = 0x62C, P2_GunShock = 0x630, P3_GunShock = 0x634, P4_GunShock = 0x638, Marquee_Character1 = 0x688, Marquee_Character2 = 0x68C } private UInt64 _P1_InnerWater_CaveAddress = 0; private UInt64 _P1_OuterWater_CaveAddress = 0; private UInt64 _P1_Damage_CaveAddress = 0; private HardwareScanCode _P1_Start_Key = HardwareScanCode.DIK_1; private HardwareScanCode _P2_Start_Key = HardwareScanCode.DIK_2; private HardwareScanCode _P3_Start_Key = HardwareScanCode.DIK_3; private HardwareScanCode _P4_Start_Key = HardwareScanCode.DIK_4; private HardwareScanCode _Settings_Key = HardwareScanCode.DIK_0; private HardwareScanCode _P1_Credits_Key = HardwareScanCode.DIK_5; private HardwareScanCode _P2_Credits_Key = HardwareScanCode.DIK_6; private HardwareScanCode _P3_Credits_Key = HardwareScanCode.DIK_7; private HardwareScanCode _P4_Credits_Key = HardwareScanCode.DIK_8; private HardwareScanCode _MenuUp_Key = HardwareScanCode.DIK_NUMPAD8; private HardwareScanCode _MenuDown_Key = HardwareScanCode.DIK_NUMPAD2; private HardwareScanCode _MenuEnter_Key = HardwareScanCode.DIK_RETURN; private InjectionStruct _PlayerDamage_Injection = new InjectionStruct(0xA3C480, 15); private UInt64 _IsGunCalibrate_Offset = 0x9B7290; private UInt64 _CustomCoins_Offset = 0x2BF5D50; /// /// Constructor /// public Game_ArcadepcRaccoonRampage(String RomName) : base(RomName, "RSGame-Win64-Shipping") { _KnownMd5Prints.Add("Raccon Rampage v1.4 clean dump", "d3d3a8b3c7a8f7254d3c7641bb91bd03"); _KnownMd5Prints.Add("Racoon Rampage v1.4 patched by Argonlefou", "8e3a7b0e5c1065c5669c3f169e5a5c5f"); _tProcess.Start(); Logger.WriteLog("Waiting for Unis " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals(_UsbPluginsDllName)) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); _UsbPluginsDll_BaseAddress = m.BaseAddress; Logger.WriteLog(_UsbPluginsDllName + " = 0x" + _UsbPluginsDll_BaseAddress); String UsbPluginPath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", @"..\..\Plugins\" + _UsbPluginsDllName); CheckMd5(UsbPluginPath); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); break; } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region MemoryHack #region Inputs Hack protected override void Apply_InputsMemoryHack() { //+B85E4A --> NOP | 5 --> Nop mousebuttons and keyboard keys //SetNops(_TargetProcess_MemoryBaseAddress, "0x0E38CA7|5"); //Force return OK at the end of IsGunCalibrate() function WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _IsGunCalibrate_Offset), new byte[]{ 0xB0, 0x01}); //mov al, 01 //Using unused place at the end of Data segment to store custom Credits value (so that it can live between different DemulShooter session) _P1_Coins_CaveAddress = (UInt64)_TargetProcess_MemoryBaseAddress + _CustomCoins_Offset; _P2_Coins_CaveAddress = (UInt64)_TargetProcess_MemoryBaseAddress + _CustomCoins_Offset +1; _P3_Coins_CaveAddress = (UInt64)_TargetProcess_MemoryBaseAddress + _CustomCoins_Offset +2; _P4_Coins_CaveAddress = (UInt64)_TargetProcess_MemoryBaseAddress + _CustomCoins_Offset +3; Create_InputsDataBank(); _P1_X_CaveAddress = _InputsDatabank_Address; // _P1_Y_CaveAddress = _InputsDatabank_Address + 0x04; // P2, P3 and P4 will be accessed from P1 address, with added offset _SystemButtons_CaveAddress = _InputsDatabank_Address + 0x20; _StartButtonsAndWater_CaveAddress = _InputsDatabank_Address + 0x21; //Set Water Level to "normal" Apply_OR_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0x04); SetHack_Axis(); SetHack_StartButtons(); SetHack_GetCoinsNum(); SetHack_DecCoins(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Replace UBPGameInstance::SetMouseLocation() floatX and floatY parameter with already screen-accurate data for each player /// private void SetHack_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //push rdx CaveMemory.Write_StrBytes("52"); //movabs rax, [_P1_X_Offset] CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_X_CaveAddress)); //shl rdx, 3 CaveMemory.Write_StrBytes("48 C1 E2 03"); //add rax, rdx CaveMemory.Write_StrBytes("48 01 D0"); //movss xmm2,[rax] CaveMemory.Write_StrBytes("F3 0F 10 10"); //movss xmm3,[rax+04] CaveMemory.Write_StrBytes("F3 0F 10 58 04"); //pop rdx CaveMemory.Write_StrBytes("5A"); //pop rax CaveMemory.Write_StrBytes("58"); //mov eax,[rcx+00000624] CaveMemory.Write_StrBytes("8B 81 24 06 00 00"); //mov r14,rcx CaveMemory.Write_StrBytes("4C 8B F1"); //movaps [rsp+30],xmm7 CaveMemory.Write_StrBytes("0F 29 7C 24 30"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + 0x9CFF96); Logger.WriteLog("Adding Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x9CFF88), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } //Replace GetIoData() call by reading custom buttons bytes private void SetHack_StartButtons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea rcx,[r14+000002C0] CaveMemory.Write_StrBytes("49 8D 8E C0 02 00 00"); ////movabs rax, [_SystemButtons_CaveAddress] //CaveMemory.Write_StrBytes("48 B8"); //CaveMemory.Write_Bytes(BitConverter.GetBytes(_SystemButtons_CaveAddress)); ////mov rax, [rax] //CaveMemory.Write_StrBytes("48 8B 00"); ////mov [rcx+02],al //CaveMemory.Write_StrBytes("88 41 02"); //movabs rax, [_StartButtonsAndWater_CaveAddress] CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_StartButtonsAndWater_CaveAddress)); //mov rax, [rax] CaveMemory.Write_StrBytes("48 8B 00"); //mov [rcx+02],al CaveMemory.Write_StrBytes("88 41 05"); //xor rax,rax CaveMemory.Write_StrBytes("48 31 C0"); //mov al,01 CaveMemory.Write_StrBytes("B0 01"); //movzx esi,al CaveMemory.Write_StrBytes("0F B6 F0"); //test rbx,rbx CaveMemory.Write_StrBytes("48 85 DB"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + 0x9B10D7); Logger.WriteLog("Adding Buttons Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x9B10C8), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Force the game to read our own coin counters /// private void SetHack_GetCoinsNum() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rcx CaveMemory.Write_StrBytes("51"); //movabs rax, [_P1_Coins_CaveAddress] CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Coins_CaveAddress)); //add rcx,rax CaveMemory.Write_StrBytes("48 01 C1"); //xor rax,rax CaveMemory.Write_StrBytes("48 31 C0"); //mov al,[rcx] CaveMemory.Write_StrBytes("8A 01"); //pop rcx CaveMemory.Write_StrBytes("59"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + 0x9DC345); Logger.WriteLog("Adding GetCoinsNum Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x9DC2AF), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Force the game to decrement our own coin counters /// private void SetHack_DecCoins() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //push rbx CaveMemory.Write_StrBytes("53"); //movabs rax, [_P1_Coins_CaveAddress] CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Coins_CaveAddress)); //add rax,rbp CaveMemory.Write_StrBytes("48 01 E8"); //mov rbx,rax CaveMemory.Write_StrBytes("48 8B D8"); //xor rax,rax CaveMemory.Write_StrBytes("48 31 C0"); //mov al,[rbx] CaveMemory.Write_StrBytes("8A 03"); //sub rax,rdx CaveMemory.Write_StrBytes("48 29 D0"); //mov [rbx],al CaveMemory.Write_StrBytes("88 03"); //pop rbx CaveMemory.Write_StrBytes("5B"); //pop rax CaveMemory.Write_StrBytes("58"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + 0x9DC224); Logger.WriteLog("Adding DecCoins Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x9DC17E), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Outputs Hack protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_InnerWater_CaveAddress = _OutputsDatabank_Address; _P1_OuterWater_CaveAddress = _OutputsDatabank_Address + 0x01; _P1_Damage_CaveAddress = _OutputsDatabank_Address + 0x08; SetHack_GetDamage(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } private void SetHack_GetDamage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rbx CaveMemory.Write_StrBytes("53"); //movabs rax, [_P1_Damage_CaveAddress] CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Damage_CaveAddress)); //cmp rbx, 4 CaveMemory.Write_StrBytes("48 83 FB 04"); //je All Damaged CaveMemory.Write_StrBytes("74 11"); //Single damage: //add rax, rbx CaveMemory.Write_StrBytes("48 01 D8"); //mov rbx, 1 CaveMemory.Write_StrBytes("48 BB 01 00 00 00 00 00 00 00"); //mov [rax], bl CaveMemory.Write_StrBytes("88 18"); //jmp exit CaveMemory.Write_StrBytes("EB 0C"); //All Damaged : //mov rbx, 01 01 01 01 CaveMemory.Write_StrBytes("48 BB 01 01 01 01 00 00 00 00"); //mov [rax], bx CaveMemory.Write_StrBytes("89 18"); //Exit: //sub rsp,000000C0 CaveMemory.Write_StrBytes("48 81 EC C0 00 00 00"); //mov rax,[rcx] CaveMemory.Write_StrBytes("48 8B 01"); //mov rbx,rcx CaveMemory.Write_StrBytes("48 8B D9"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _PlayerDamage_Injection.InjectionReturnOffset); Logger.WriteLog("Adding Damage Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _PlayerDamage_Injection.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #endregion #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 1920.0; double dMaxY = 1080.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { //Writing Axis WriteBytes((IntPtr)(_P1_X_CaveAddress + 8 * (UInt64)(PlayerData.ID - 1)), BitConverter.GetBytes((float)PlayerData.RIController.Computed_X)); WriteBytes((IntPtr)(_P1_Y_CaveAddress + 8 * (UInt64)(PlayerData.ID - 1)), BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y)); //Using Trigger And Middle Button as Start Button if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { int bData = 0x10 << (4 - PlayerData.ID); Apply_OR_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), (byte)bData); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { int bData = 0x10 << (4 - PlayerData.ID); Apply_AND_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), (byte)~bData); } } } public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == _P1_Start_Key) Apply_OR_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0x80); else if (s.scanCode == _P2_Start_Key) Apply_OR_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0x40); if (s.scanCode == _P3_Start_Key) Apply_OR_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0x20); else if (s.scanCode == _P4_Start_Key) Apply_OR_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0x10); else if (s.scanCode == _Settings_Key) Apply_OR_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0x04); else if (s.scanCode == _MenuUp_Key) Apply_OR_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0x04); else if (s.scanCode == _MenuDown_Key) Apply_OR_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0x02); else if (s.scanCode == _MenuEnter_Key) Apply_OR_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0x01); else if (s.scanCode == _P1_Credits_Key) { byte c = ReadByte((IntPtr)_P1_Coins_CaveAddress); if (c < 99) { c++; WriteByte((IntPtr)_P1_Coins_CaveAddress, c); } } else if (s.scanCode == _P2_Credits_Key) { byte c = ReadByte((IntPtr)_P2_Coins_CaveAddress); if (c < 99) { c++; WriteByte((IntPtr)_P2_Coins_CaveAddress, c); } } else if (s.scanCode == _P3_Credits_Key) { byte c = ReadByte((IntPtr)_P3_Coins_CaveAddress); if (c < 99) { c++; WriteByte((IntPtr)_P3_Coins_CaveAddress, c); } } else if (s.scanCode == _P4_Credits_Key) { byte c = ReadByte((IntPtr)_P4_Coins_CaveAddress); if (c < 99) { c++; WriteByte((IntPtr)_P4_Coins_CaveAddress, c); } } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == _P1_Start_Key) Apply_AND_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0x7F); else if (s.scanCode == _P2_Start_Key) Apply_AND_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0xBF); if (s.scanCode == _P3_Start_Key) Apply_AND_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0xDF); else if (s.scanCode == _P4_Start_Key) Apply_AND_ByteMask((IntPtr)(_StartButtonsAndWater_CaveAddress), 0xEF); if (s.scanCode == _Settings_Key) Apply_AND_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0xFB); else if (s.scanCode == _MenuUp_Key) Apply_AND_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0xFB); else if (s.scanCode == _MenuDown_Key) Apply_AND_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0xFD); else if (s.scanCode == _MenuEnter_Key) Apply_AND_ByteMask((IntPtr)(_SystemButtons_CaveAddress), 0xFE); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P3_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P4_LmpGun)); _Outputs.Add(new GameOutput(OutputId.LmpLeft)); _Outputs.Add(new GameOutput(OutputId.LmpRight)); _Outputs.Add(new GameOutput(OutputId.Lmp_Disinfection)); _Outputs.Add(new GameOutput(OutputId.Lmp_P1_P2_OutOfTickets)); _Outputs.Add(new GameOutput(OutputId.Lmp_P3_P4_OutOfTickets)); _Outputs.Add(new GameOutput(OutputId.Lmp_Marquee_1)); _Outputs.Add(new GameOutput(OutputId.Lmp_Marquee_2)); _Outputs.Add(new GameOutput(OutputId.P1_InnerWater)); _Outputs.Add(new GameOutput(OutputId.P2_InnerWater)); _Outputs.Add(new GameOutput(OutputId.P3_InnerWater)); _Outputs.Add(new GameOutput(OutputId.P4_InnerWater)); _Outputs.Add(new GameOutput(OutputId.P1_OuterWater)); _Outputs.Add(new GameOutput(OutputId.P2_OuterWater)); _Outputs.Add(new GameOutput(OutputId.P3_OuterWater)); _Outputs.Add(new GameOutput(OutputId.P4_OuterWater)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P3_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P4_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Gun LED status can have different states (BIND_SetGunLight_State()) // 0 = OFF // 1 = Continuous // 2 = Flash SetOutputValue(OutputId.P1_LmpGun, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P1_GunLightState))); SetOutputValue(OutputId.P2_LmpGun, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P2_GunLightState))); SetOutputValue(OutputId.P3_LmpGun, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P3_GunLightState))); SetOutputValue(OutputId.P4_LmpGun, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P4_GunLightState))); //Side LED status can have different states (BIND_SetLRSideLight_State()) // 0 = OFF // 1 = Continuous // 2 = Flash //-- TODO : Only one value as the state is changed with 2 calls : one to set the desired LED and one to set the State. Impossible to read everytime both sides status //Disinfection LED SetOutputValue(OutputId.Lmp_Disinfection, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.DisinfectionLight))); //Tickets LED status can have different states (BIND_SetLackTicketLight()) // 0 = OFF // 1 = Continuous // 2 = Flash SetOutputValue(OutputId.Lmp_P1_P2_OutOfTickets, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.LackTicketLight_12P))); SetOutputValue(OutputId.Lmp_P3_P4_OutOfTickets, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.LackTicketLight_34P))); //MarqueeLEDs (BIND_SetICO_1() and BIND_SetICO_2()) SetOutputValue(OutputId.Lmp_Marquee_1, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.Marquee_Character1))); SetOutputValue(OutputId.Lmp_Marquee_2, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.Marquee_Character2))); //Water mechanics SetOutputValue(OutputId.P1_InnerWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P1_InnerWater))); SetOutputValue(OutputId.P2_InnerWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P2_InnerWater))); SetOutputValue(OutputId.P3_InnerWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P3_InnerWater))); SetOutputValue(OutputId.P4_InnerWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P4_InnerWater))); SetOutputValue(OutputId.P1_OuterWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P1_OuterWater))); SetOutputValue(OutputId.P2_OuterWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P2_OuterWater))); SetOutputValue(OutputId.P3_OuterWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P3_OuterWater))); SetOutputValue(OutputId.P4_OuterWater, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P4_OuterWater))); //Gun motor data goes from 0 to 2. Power of rumble ? SetOutputValue(OutputId.P1_GunMotor, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P1_GunShock))); SetOutputValue(OutputId.P2_GunMotor, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P2_GunShock))); SetOutputValue(OutputId.P3_GunMotor, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P3_GunShock))); SetOutputValue(OutputId.P4_GunMotor, ReadByte((IntPtr)((UInt64)_UsbPluginsDll_BaseAddress + _Outputs_BaseOffset + (uint)OutputIndex.P4_GunShock))); ////Custom Damage will be generated by a call to one of the game Gun function int P1_Damage = ReadByte((IntPtr)_P1_Damage_CaveAddress); if (P1_Damage != 0) { WriteByte((IntPtr)_P1_Damage_CaveAddress, 0x00); SetOutputValue(OutputId.P1_Damaged, 1); } int P2_Damage = ReadByte((IntPtr)(_P1_Damage_CaveAddress + 1)); if (P2_Damage != 0) { WriteByte((IntPtr)(_P1_Damage_CaveAddress + 1), 0x00); SetOutputValue(OutputId.P2_Damaged, 1); } int P3_Damage = ReadByte((IntPtr)(_P1_Damage_CaveAddress + 2)); if (P3_Damage != 0) { WriteByte((IntPtr)(_P1_Damage_CaveAddress + 2), 0x00); SetOutputValue(OutputId.P3_Damaged, 1); } int P4_Damage = ReadByte((IntPtr)(_P1_Damage_CaveAddress + 3)); if (P4_Damage != 0) { WriteByte((IntPtr)(_P1_Damage_CaveAddress + 3), 0x00); SetOutputValue(OutputId.P4_Damaged, 1); } //Credits SetOutputValue(OutputId.P1_Credits, ReadByte((IntPtr)(_P1_Coins_CaveAddress))); SetOutputValue(OutputId.P2_Credits, ReadByte((IntPtr)(_P2_Coins_CaveAddress))); SetOutputValue(OutputId.P3_Credits, ReadByte((IntPtr)(_P3_Coins_CaveAddress))); SetOutputValue(OutputId.P4_Credits, ReadByte((IntPtr)(_P4_Coins_CaveAddress))); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcRha.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcRha : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] StartLamp = null; public byte[] Recoil = null; public byte[] Damaged = null; public float[] Life = null; public int[] Ammo = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcRha(String RomName) : base(RomName, "Game", "RabbidsShooter") { _KnownMd5Prints.Add("Rabbids Hollywood Arcade - Clean Dump", "2dac74521cd3bb08b61f93830bf2660d"); _KnownMd5Prints.Add("Rabbids Hollywood Arcade - Patch by Ducon v2 (dongle+operator)", "72b58266f2d1311b2ba2e7c96ca774fa"); _KnownMd5Prints.Add("Rabbids Hollywood Arcade - Patch by Ducon v3 (dongle+operator+attract mode)", "7edf14803ae7d43d14e7459b2baa651e"); _KnownMd5Prints.Add("Rabbids Hollywood Arcade - Patch by Argon (dongle)", "1e74554181161f8a83084e02beeec5fc"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P3_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P4_LmpStart)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_LmpStart, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P3_LmpStart, ((OutputData)_OutputData).Recoil[2]); SetOutputValue(OutputId.P4_LmpStart, ((OutputData)_OutputData).Recoil[3]); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P3_CtmRecoil, ((OutputData)_OutputData).Recoil[2]); SetOutputValue(OutputId.P4_CtmRecoil, ((OutputData)_OutputData).Recoil[3]); SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P3_Life, (int)((OutputData)_OutputData).Life[2]); SetOutputValue(OutputId.P4_Life, (int)((OutputData)_OutputData).Life[3]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P3_Damaged, ((OutputData)_OutputData).Damaged[2]); SetOutputValue(OutputId.P4_Damaged, ((OutputData)_OutputData).Damaged[3]); SetOutputValue(OutputId.P1_Credits, ((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, ((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, ((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, ((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcSkullOfShadow.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcSkullOfShadow : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] ChangeWeapon = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Light = null; public byte[] SelectedWeapon = null; public byte[] GunMotor = null; public byte[] Recoil = null; public byte[] Damaged = null; public byte[] Life = null; public int[] Ammo = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcSkullOfShadow(String RomName) : base(RomName, "PirateWar", "PirateWar") { _KnownMd5Prints.Add("Skull Of Shadow v2.54", "5089d486f1ea28642f2d746f915a6caa"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P3_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P4_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P3_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P4_GunMotor)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P3_Ammo)); _Outputs.Add(new GameOutput(OutputId.P4_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_LmpGun, ((OutputData)_OutputData).Light[0]); SetOutputValue(OutputId.P2_LmpGun, ((OutputData)_OutputData).Light[1]); SetOutputValue(OutputId.P3_LmpGun, ((OutputData)_OutputData).Light[2]); SetOutputValue(OutputId.P4_LmpGun, ((OutputData)_OutputData).Light[3]); //Gun motor is activated permanently while the gun is firing //Value is vibration power SetOutputValue(OutputId.P1_GunMotor, (int)((OutputData)_OutputData).GunMotor[0]); SetOutputValue(OutputId.P2_GunMotor, (int)((OutputData)_OutputData).GunMotor[1]); SetOutputValue(OutputId.P3_GunMotor, (int)((OutputData)_OutputData).GunMotor[2]); SetOutputValue(OutputId.P4_GunMotor, (int)((OutputData)_OutputData).GunMotor[3]); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P3_CtmRecoil, ((OutputData)_OutputData).Recoil[2]); SetOutputValue(OutputId.P4_CtmRecoil, ((OutputData)_OutputData).Recoil[3]); SetOutputValue(OutputId.P1_Ammo, ((OutputData)_OutputData).Ammo[0]); SetOutputValue(OutputId.P2_Ammo, ((OutputData)_OutputData).Ammo[1]); SetOutputValue(OutputId.P3_Ammo, ((OutputData)_OutputData).Ammo[2]); SetOutputValue(OutputId.P4_Ammo, ((OutputData)_OutputData).Ammo[3]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P3_Damaged, ((OutputData)_OutputData).Damaged[2]); SetOutputValue(OutputId.P4_Damaged, ((OutputData)_OutputData).Damaged[3]); SetOutputValue(OutputId.P1_Life, ((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P2_Life, ((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P3_Life, ((OutputData)_OutputData).Life[2]); SetOutputValue(OutputId.P4_Life, ((OutputData)_OutputData).Life[3]); //Gun motor is activated permanently while the gun is firing //Value is vibration power SetOutputValue(OutputId.P1_GunMotor, (int)((OutputData)_OutputData).GunMotor[0]); SetOutputValue(OutputId.P2_GunMotor, (int)((OutputData)_OutputData).GunMotor[1]); SetOutputValue(OutputId.P3_GunMotor, (int)((OutputData)_OutputData).GunMotor[2]); SetOutputValue(OutputId.P4_GunMotor, (int)((OutputData)_OutputData).GunMotor[3]); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, (int)((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, (int)((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcTopGun2.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { internal class Game_ArcadepcTopGun2 : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Reload = null; public byte[] ChangeWeapon = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] StartLamp = null; public byte[] GunLamp = null; public byte Lamp = 0; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_ArcadepcTopGun2(String RomName) : base(RomName, "ShotRedLight", "ShotRedLight") { _KnownMd5Prints.Add("Top Gun 2 (War of gun) v2.0.5_20250325 - Original", "e57da49af7557076e77d5e3f89786f61"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.LmpPanel)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); //Handling Start Lamps based on player status if (((OutputData)_OutputData).StartLamp[0] == 1) SetOutputValue(OutputId.P1_CtmLmpStart, -1); else SetOutputValue(OutputId.P1_CtmLmpStart, 0); if (((OutputData)_OutputData).StartLamp[1] == 1) SetOutputValue(OutputId.P2_CtmLmpStart, -1); else SetOutputValue(OutputId.P2_CtmLmpStart, 0); SetOutputValue(OutputId.P1_LmpGun, ((OutputData)_OutputData).GunLamp[0]); SetOutputValue(OutputId.P2_LmpGun, ((OutputData)_OutputData).GunLamp[1]); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcTra.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcTra : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Reload = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public float[] Life = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Ammo = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcTra(String RomName) : base(RomName, "Game", "SquareEnix_TombRaider") { _KnownMd5Prints.Add("Tomb Raider Arcade - Clean Dump (dongle patched)", "7fccfcaa13ebe72043a32601de6d1a6b"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) ((InputData)_InputData).Reload[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) ((InputData)_InputData).Reload[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P3_CtmLmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P4_CtmLmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P3_Ammo)); _Outputs.Add(new GameOutput(OutputId.P4_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); if (((OutputData)_OutputData).IsPlaying[0] == 1) { SetOutputValue(OutputId.P1_CtmLmpStart, 0); SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P1_Ammo, ((OutputData)_OutputData).Ammo[0]); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); } else { SetOutputValue(OutputId.P1_CtmLmpStart, -1); SetOutputValue(OutputId.P1_Life, 0); SetOutputValue(OutputId.P1_Ammo, 0); SetOutputValue(OutputId.P1_Damaged, 0); } if (((OutputData)_OutputData).IsPlaying[1] == 1) { SetOutputValue(OutputId.P2_CtmLmpStart, 0); SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P2_Ammo, ((OutputData)_OutputData).Ammo[1]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); } else { SetOutputValue(OutputId.P2_CtmLmpStart, -1); SetOutputValue(OutputId.P2_Life, 0); SetOutputValue(OutputId.P2_Ammo, 0); SetOutputValue(OutputId.P2_Damaged, 0); } if (((OutputData)_OutputData).IsPlaying[2] == 1) { SetOutputValue(OutputId.P3_CtmLmpStart, 0); SetOutputValue(OutputId.P3_Life, (int)((OutputData)_OutputData).Life[2]); SetOutputValue(OutputId.P3_Ammo, ((OutputData)_OutputData).Ammo[2]); SetOutputValue(OutputId.P3_CtmRecoil, ((OutputData)_OutputData).Recoil[2]); SetOutputValue(OutputId.P3_Damaged, ((OutputData)_OutputData).Damaged[2]); } else { SetOutputValue(OutputId.P3_CtmLmpStart, -1); SetOutputValue(OutputId.P3_Life, 0); SetOutputValue(OutputId.P3_Ammo, 0); SetOutputValue(OutputId.P3_Damaged, 0); } if (((OutputData)_OutputData).IsPlaying[3] == 1) { SetOutputValue(OutputId.P4_CtmLmpStart, 0); SetOutputValue(OutputId.P4_Life, (int)((OutputData)_OutputData).Life[3]); SetOutputValue(OutputId.P4_Ammo, ((OutputData)_OutputData).Ammo[3]); SetOutputValue(OutputId.P4_CtmRecoil, ((OutputData)_OutputData).Recoil[3]); SetOutputValue(OutputId.P4_Damaged, ((OutputData)_OutputData).Damaged[3]); } else { SetOutputValue(OutputId.P4_CtmLmpStart, -1); SetOutputValue(OutputId.P4_Life, 0); SetOutputValue(OutputId.P4_Ammo, 0); SetOutputValue(OutputId.P4_Damaged, 0); } SetOutputValue(OutputId.P1_Credits, ((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, ((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, ((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, ((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_ArcadepcWisdomZombies.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { public class Game_ArcadepcWisdomZombies : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] StartLamp = null; public byte[] SmallWater = null; public byte[] BigWater = null; public byte[] GunMotor = null; public byte[] TicketFeeder = null; public byte BonusWeaponLamp = 0; public byte SeatVibrationMotor = 0; public byte WaterLevelLamp = 0; public byte[] Life = null; public byte[] Damaged = null; public int[] Credits = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 4; /// /// Constructor /// public Game_ArcadepcWisdomZombies(String RomName) : base(RomName, "ShootZombies", "ShotZembies") { _KnownMd5Prints.Add("ShootZombies EN v1.2.8 - Original", "f0288515dd04d49c85dedacaaf922edd"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P1_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P2_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P3_LmpStart, 500)); _Outputs.Add(new SyncBlinkingGameOutput(OutputId.P4_LmpStart, 500)); _Outputs.Add(new GameOutput(OutputId.P1_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P2_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P3_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P4_WaterFire)); _Outputs.Add(new GameOutput(OutputId.P1_BigGun)); _Outputs.Add(new GameOutput(OutputId.P2_BigGun)); _Outputs.Add(new GameOutput(OutputId.P3_BigGun)); _Outputs.Add(new GameOutput(OutputId.P4_BigGun)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P3_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P4_GunMotor)); /*_Outputs.Add(new GameOutput(OutputId.P1_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P2_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P3_TicketFeeder)); _Outputs.Add(new GameOutput(OutputId.P4_TicketFeeder));*/ _Outputs.Add(new GameOutput(OutputId.BonusWeaponLamp)); _Outputs.Add(new GameOutput(OutputId.SeatVibrationLamp)); _Outputs.Add(new GameOutput(OutputId.WaterLevelLamp)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new GameOutput(OutputId.P3_Life)); _Outputs.Add(new GameOutput(OutputId.P4_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P3_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P4_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); _Outputs.Add(new GameOutput(OutputId.P3_Credits)); _Outputs.Add(new GameOutput(OutputId.P4_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); if (((OutputData)_OutputData).StartLamp[0] == 1) SetOutputValue(OutputId.P1_LmpStart, -1); else SetOutputValue(OutputId.P1_LmpStart, 0); if (((OutputData)_OutputData).StartLamp[1] == 1) SetOutputValue(OutputId.P2_LmpStart, -1); else SetOutputValue(OutputId.P2_LmpStart, 0); if (((OutputData)_OutputData).StartLamp[2] == 1) SetOutputValue(OutputId.P3_LmpStart, -1); else SetOutputValue(OutputId.P3_LmpStart, 0); if (((OutputData)_OutputData).StartLamp[3] == 1) SetOutputValue(OutputId.P4_LmpStart, -1); else SetOutputValue(OutputId.P4_LmpStart, 0); SetOutputValue(OutputId.P1_WaterFire, ((OutputData)_OutputData).SmallWater[0]); SetOutputValue(OutputId.P2_WaterFire, ((OutputData)_OutputData).SmallWater[1]); SetOutputValue(OutputId.P3_WaterFire, ((OutputData)_OutputData).SmallWater[2]); SetOutputValue(OutputId.P4_WaterFire, ((OutputData)_OutputData).SmallWater[3]); SetOutputValue(OutputId.P1_BigGun, ((OutputData)_OutputData).BigWater[0]); SetOutputValue(OutputId.P2_BigGun, ((OutputData)_OutputData).BigWater[1]); SetOutputValue(OutputId.P3_BigGun, ((OutputData)_OutputData).BigWater[2]); SetOutputValue(OutputId.P4_BigGun, ((OutputData)_OutputData).BigWater[3]); SetOutputValue(OutputId.P1_GunMotor, ((OutputData)_OutputData).GunMotor[0]); SetOutputValue(OutputId.P2_GunMotor, ((OutputData)_OutputData).GunMotor[1]); SetOutputValue(OutputId.P3_GunMotor, ((OutputData)_OutputData).GunMotor[2]); SetOutputValue(OutputId.P4_GunMotor, ((OutputData)_OutputData).GunMotor[3]); /*SetOutputValue(OutputId.P1_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[0]); SetOutputValue(OutputId.P2_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[1]); SetOutputValue(OutputId.P3_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[2]); SetOutputValue(OutputId.P4_TicketFeeder, ((OutputData)_OutputData).TicketFeeder[3]);*/ SetOutputValue(OutputId.BonusWeaponLamp, ((OutputData)_OutputData).BonusWeaponLamp); SetOutputValue(OutputId.SeatVibrationLamp, ((OutputData)_OutputData).SeatVibrationMotor); SetOutputValue(OutputId.WaterLevelLamp, ((OutputData)_OutputData).WaterLevelLamp); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); SetOutputValue(OutputId.P3_Damaged, ((OutputData)_OutputData).Damaged[2]); SetOutputValue(OutputId.P4_Damaged, ((OutputData)_OutputData).Damaged[3]); if (((OutputData)_OutputData).IsPlaying[0] == 1) SetOutputValue(OutputId.P1_Life, (int)((OutputData)_OutputData).Life[0]); else SetOutputValue(OutputId.P1_Life, 0); if (((OutputData)_OutputData).IsPlaying[1] == 1) SetOutputValue(OutputId.P2_Life, (int)((OutputData)_OutputData).Life[1]); else SetOutputValue(OutputId.P2_Life, 0); if (((OutputData)_OutputData).IsPlaying[2] == 1) SetOutputValue(OutputId.P3_Life, (int)((OutputData)_OutputData).Life[2]); else SetOutputValue(OutputId.P4_Life, 0); if (((OutputData)_OutputData).IsPlaying[3] == 1) SetOutputValue(OutputId.P4_Life, (int)((OutputData)_OutputData).Life[3]); else SetOutputValue(OutputId.P4_Life, 0); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); SetOutputValue(OutputId.P3_Credits, (int)((OutputData)_OutputData).Credits[2]); SetOutputValue(OutputId.P4_Credits, (int)((OutputData)_OutputData).Credits[3]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_Es3Lla.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MemoryX64; using DsCore.Win32; namespace DemulShooterX64 { class Game_Es3Lla : Game { private UInt64 _P1_X_Address; private UInt64 _P1_Y_Address; private UInt64 _P2_X_Address; private UInt64 _P2_Y_Address; private NopStruct _Nop_Axis = new NopStruct(0x000475E3, 2); /// /// Constructor /// public Game_Es3Lla(string RomName): base(RomName, "DomeShooterGame-Win64-Shipping") { _tProcess.Start(); Logger.WriteLog("Waiting for ES3 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero && _TargetProcess.MainWindowHandle != IntPtr.Zero) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); Apply_MemoryHacks(); _ProcessHooked = true; } } } catch (Exception ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); Logger.WriteLog(ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { //Window size Rect TotalRes = new Rect(); Win32API.GetClientRect(_GameWindowHandle, ref TotalRes); double TotalResX = TotalRes.Right - TotalRes.Left; double TotalResY = TotalRes.Bottom - TotalRes.Top; Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0-1600] = 1600 //Y => [0-1800] = 1800 double dMaxX = 1600.0; double dMaxY = 1800.0; PlayerData.RIController.Computed_X = Convert.ToInt32(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt32(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { SetNops(_TargetProcess_MemoryBaseAddress, _Nop_Axis); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } private void GetP1AxisAddress() { byte[] b = ReadBytes((IntPtr)(_TargetProcess_MemoryBaseAddress.ToInt64() + 0x16C7080), 16); UInt64 i = BitConverter.ToUInt64(b, 0); b = ReadBytes((IntPtr)(i + 0xC8), 16); i = BitConverter.ToUInt64(b, 0); b = ReadBytes((IntPtr)(i + 0x558), 16); i = BitConverter.ToUInt64(b, 0); _P1_X_Address = i + 0x2C0; _P1_Y_Address = i + 0x2C4; _P2_X_Address = _P1_X_Address + 0x700; _P2_Y_Address = _P1_X_Address + 0x700; //Logger.WriteLog(_P1_X_Address.ToString("X16")); //Logger.WriteLog(_P1_Y_Address.ToString("X16")); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((Int16)PlayerData.RIController.Computed_Y); GetP1AxisAddress(); if (PlayerData.ID == 1) { //Write Axis WriteBytes((IntPtr)_P1_X_Address, bufferX); WriteBytes((IntPtr)_P1_Y_Address, bufferY); //Inputs /*if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_DOWN) { Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_BTN_Offset, 0x10); } else if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_UP) { Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_BTN_Offset, 0xEF); } if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_DOWN) { Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_BTN_Offset, 0x20); } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_UP) { Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_BTN_Offset, 0xDF); } if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_DOWN) { Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_BTN_Offset, 0x40); } else if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_UP) { Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_BTN_Offset, 0xBF); }*/ } else if (PlayerData.ID == 2) { //Write Axis WriteBytes((IntPtr)_P2_X_Address, bufferX); WriteBytes((IntPtr)_P2_Y_Address, bufferY); //Inputs /*if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_DOWN) { Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P2_BTN_Offset, 0x10); } else if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_UP) { Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P2_BTN_Offset, 0xEF); } if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_DOWN) { Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P2_BTN_Offset, 0x20); } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_UP) { Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P2_BTN_Offset, 0xDF); } if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_DOWN) { Apply_OR_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P2_BTN_Offset, 0x40); } else if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_UP) { Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P2_BTN_Offset, 0xBF); }*/ } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_Es3Tc5.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { public class Game_Es3Tc5 : Game { /*** MEMORY ADDRESSES **/ //Standalone App Hack private UInt64 _Standalone_Axis_CaveAddress = 0; private const Int32 _STANDALONE_X_OFFSET = 0; private const Int32 _STANDALONE_Y_OFFSET = 0x08; private const Int32 _STANDALONE_MAXX_OFFSET = 0x10; private const Int32 _STANDALONE_MAXY_OFFSET = 0x18; private UInt64 SCREEN_WIDTH_OFFSET = 0x0185E388; private UInt64 SCREEN_HEIGHT_OFFSET = 0x0185E38C; private UInt64 GUN_MAX_X_OFFSET = 0x01697E78; private UInt64 GUN_MAX_Y_OFFSET = 0x01697E7C; private int _Gun_Max_X = 0; private int _Gun_Max_Y = 0; private UInt64 _Standalone_Trigger_Ptr_Offset = 0x185E4D0; private UInt64 _Standalone_Trigger_BaseAddress = 0; private NopStruct _Standalone_Nop_Trigger_On = new NopStruct(0xA24357, 11); private NopStruct _Standalone_Nop_Trigger_Off = new NopStruct(0xA24462, 11); //Because of 64bits process, I only know how to alloc memory and use a long jump (14 bytes instruction !) //In this case, I have to use a 15 bytes long "0xCC" between 2 function to write the long jump //So the original hack will short jump (not enough available bytes) to the place where I can put the long jump private UInt64 _Standalone_Injection_Offset = 0x0006B063; private UInt64 _LongJump_Offset = 0x000ABA41; private UInt64 _Standalone_Injection_Return_Offset = 0x0006B069; //JConfig Hack private UInt64 _JVS_TriggerButton_CaveAddress = 0; private UInt64 _JVS_Trigger_LongJump_Offset = 0x000ABB21; private UInt64 _JVS_Trigger_Injection_Offset = 0xA55035; private UInt64 _JVS_Trigger_Injection_Return_Offset = 0xA5503C; private UInt64 _JVS_WeaponButton_CaveAddress = 0; private UInt64 _JVS_Weapon_LongJump_Offset = 0x000AB821; private UInt64 _JVS_Weapon_Injection_Offset = 0xA56E4E; private UInt64 _JVS_Weapon_Injection_Return_Offset = 0xA56E54; private UInt64 _JVS_AxisX_CaveAddress = 0; private UInt64 _JVS_AxisY_CaveAddress = 0; private UInt64 _JVS_Axis_LongJump_Offset = 0x000ABA41; private UInt64 _JVS_Axis_Injection_Offset = 0x6B33A; private UInt64 _JVS_Axis_Injection_Return_Offset = 0x6B342; private float _FloatXvalue = 0; private float _FloatYvalue = 0; //Check instruction for game loaded private const UInt64 ROM_LOADED_CHECK_INSTRUCTION_OFFSET = 0x0006B060; //Custom Outputs private int _P1_LastWeapon = 0; private int _P1_LastRecoil = 0; //Flag to differenciate standalone/Jconfig hack to do private bool _IsStandalone = true; /// /// Constructor /// public Game_Es3Tc5(String RomName) : base(RomName, "TimeCrisisGame-Win64-Shipping") { _KnownMd5Prints.Add("TimeCrisisGame-Win64-Shipping.exe - Original Dump", "5297b9296708d4f83181f244ee2bc3db"); _tProcess.Start(); Logger.WriteLog("Waiting for Namco ES3 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { UInt64 aTest = (UInt64)_TargetProcess_MemoryBaseAddress + SCREEN_WIDTH_OFFSET; byte[] buffer = ReadBytes((IntPtr)aTest, 4); int x = BitConverter.ToInt32(buffer, 0); aTest = (UInt64)_TargetProcess_MemoryBaseAddress + SCREEN_HEIGHT_OFFSET; buffer = ReadBytes((IntPtr)aTest, 4); int y = BitConverter.ToInt32(buffer, 0); aTest = (UInt64)_TargetProcess_MemoryBaseAddress + GUN_MAX_X_OFFSET; buffer = ReadBytes((IntPtr)aTest, 4); _Gun_Max_X = BitConverter.ToInt32(buffer, 0); aTest = (UInt64)_TargetProcess_MemoryBaseAddress + GUN_MAX_Y_OFFSET; buffer = ReadBytes((IntPtr)aTest, 4); _Gun_Max_Y = BitConverter.ToInt32(buffer, 0); _Standalone_Trigger_BaseAddress = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Standalone_Trigger_Ptr_Offset), new UInt64[ ]{ 0 }); if(x != 0 && y != 0 && _Standalone_Trigger_BaseAddress != 0) { //Check for Rslaucher ( = Jconfig use) Process[] procs = Process.GetProcessesByName("rslauncher"); if (procs.Length > 0) _IsStandalone = false; _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Screen size detected by game = [ " + x.ToString() + " x " + y.ToString() + " ]"); Logger.WriteLog("Maximum axis values = [ " + _Gun_Max_X.ToString() + " ; " + _Gun_Max_Y.ToString() + " ]"); Logger.WriteLog("Trigger Address = 0x" + _Standalone_Trigger_BaseAddress.ToString("X16")); Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); Logger.WriteLog("IsStandalone = " + _IsStandalone.ToString()); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("ROM not Loaded..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Fullscreen mode causes issue with windows size, so for now this will only work with fullscreen mode /// Game resolution will be read in memory /// /// /// /*public override bool ClientScale(PlayerSettings PlayerData) { //Convert Screen location to Client location if (_TargetProcess != null) { //Window size Rect TotalRes = new Rect(); Win32API.GetWindowRect(_TargetProcess.MainWindowHandle, ref TotalRes); Logger.WriteLog("Window position (Px) = [ " + TotalRes.Left + ";" + TotalRes.Top + " ]"); PlayerData.RIController.Computed_X = PlayerData.RIController.Computed_X - TotalRes.Left; PlayerData.RIController.Computed_Y = PlayerData.RIController.Computed_Y - TotalRes.Top; Logger.WriteLog("Onclient window position (Px) = [ " + PlayerData.RIController.Computed_X + "x" + PlayerData.RIController.Computed_Y + " ]"); } return true; }*/ /// /// Convert client area pointer location to Game speciffic data for memory injection /// Windowed mode is not working (can't get window size) so full screen mode only is simpler.... /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { if (_IsStandalone) { return true; } else { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); _FloatXvalue = PlayerData.RIController.Computed_X / (float)TotalResX; _FloatYvalue = PlayerData.RIController.Computed_Y / (float)TotalResY; Logger.WriteLog("Game scale result (float) = [ " + _FloatXvalue.ToString() + "; " + _FloatYvalue.ToString() + " ]"); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { //Standalone (old) hack if (_IsStandalone) { SetHackAxis(); UInt64 aTest = (UInt64)_TargetProcess_MemoryBaseAddress + GUN_MAX_X_OFFSET; byte[] buffer = ReadBytes((IntPtr)aTest, 4); WriteBytes((IntPtr)(_Standalone_Axis_CaveAddress + _STANDALONE_MAXX_OFFSET), buffer); aTest = (UInt64)_TargetProcess_MemoryBaseAddress + GUN_MAX_Y_OFFSET; buffer = ReadBytes((IntPtr)aTest, 4); WriteBytes((IntPtr)(_Standalone_Axis_CaveAddress + _STANDALONE_MAXY_OFFSET), buffer); } else { //JVS input memory hack Create_JVS_DataBank(); SetHack_JVS_Trigger(); SetHack_JVS_Weapon(); SetHack_JVS_Axis(); SetNops(_TargetProcess_MemoryBaseAddress, _Standalone_Nop_Trigger_On); //JConfig does not disable mouse so we will to avoid conflict with lightgun handling } Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// The game is working well with mouse but causes issue with a lightgun : /// Except in the menu (everything OK) it read Absolute values as relative movements -> stick in the corners /// This is a first try to inject data into memory on the fly, as I can't find a unique procedure for Axis writing. /// private void SetHackAxis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _Standalone_Axis_CaveAddress = CaveMemory.CaveAddress + 0x40; Logger.WriteLog("_Standalone_Axis_CaveAddress = 0x" + _Standalone_Axis_CaveAddress.ToString("X16")); //push rax CaveMemory.Write_StrBytes("50"); //mov eax,[rsp+60] CaveMemory.Write_StrBytes("8B 44 24 60"); //cmp eax,[RIP+45] => (MAx_X) CaveMemory.Write_StrBytes("3B 05 45 00 00 00"); //je AxisX CaveMemory.Write_StrBytes("74 0B"); //cmp eax,[RIP+45] => (MAx_Y) CaveMemory.Write_StrBytes("3B 05 45 00 00 00"); //je AxisY CaveMemory.Write_StrBytes("74 0D"); //pop rax CaveMemory.Write_StrBytes("58"); //jmp exit CaveMemory.Write_StrBytes("EB 12"); //AxisX: //pop rax CaveMemory.Write_StrBytes("58"); ////mov rax, [RIP+20] (X ==> _Standalone_Axis_CaveAddress + 0) CaveMemory.Write_StrBytes("48 8B 05 20 00 00 00"); //jmp exit CaveMemory.Write_StrBytes("EB 08"); //AxisY: //pop rax CaveMemory.Write_StrBytes("58"); ////mov rax, [RIP+1E] (Y ==> _Standalone_Axis_CaveAddress + 4) CaveMemory.Write_StrBytes("48 8B 05 1E 00 00 00"); //Exit: //mov [rdi],eax CaveMemory.Write_StrBytes("89 07"); //add rsp,20 CaveMemory.Write_StrBytes("48 83 C4 20"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Standalone_Injection_Return_Offset); Logger.WriteLog("Adding P1 Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection //1st Step : writing the long jump IntPtr ProcessHandle = _TargetProcess.Handle; List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _LongJump_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); //2nd step : writing the short jump Buffer = new List(); Buffer.Add(0xE9); Buffer.Add(0xD9); Buffer.Add(0x09); Buffer.Add(0x04); Buffer.Add(0x00); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Standalone_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// The game is working well with mouse but causes issue with a lightgun : /// Except in the menu (everything OK) it read Absolute values as relative movements -> stick in the corners /// This is a first try to inject data into memory on the fly, as I can't find a unique procedure for Axis writing. /// private void SetHackAxis_v2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _Standalone_Axis_CaveAddress = CaveMemory.CaveAddress + 0x40; Logger.WriteLog("_P1_Axis_CaveAddress = 0x" + _Standalone_Axis_CaveAddress.ToString("X16")); //push rax CaveMemory.Write_StrBytes("50"); //mov eax,[rsp+60] CaveMemory.Write_StrBytes("8B 44 24 60"); //cmp eax,[RIP+45] => (MAx_X) CaveMemory.Write_StrBytes("3B 05 45 00 00 00"); //je AxisX CaveMemory.Write_StrBytes("74 0B"); //cmp eax,[RIP+45] => (MAx_Y) CaveMemory.Write_StrBytes("3B 05 45 00 00 00"); //je AxisY CaveMemory.Write_StrBytes("74 0D"); //pop rax CaveMemory.Write_StrBytes("58"); //jmp exit CaveMemory.Write_StrBytes("EB 12"); //AxisX: //pop rax CaveMemory.Write_StrBytes("58"); ////mov rax, [RIP+20] (X ==> _P1_Axis_CaveAddress + 0) CaveMemory.Write_StrBytes("48 8B 05 20 00 00 00"); //jmp exit CaveMemory.Write_StrBytes("EB 08"); //AxisY: //pop rax CaveMemory.Write_StrBytes("58"); ////mov rax, [RIP+1E] (Y ==> _P1_Axis_CaveAddress + 4) CaveMemory.Write_StrBytes("48 8B 05 1E 00 00 00"); //Exit: //mov [rdi],eax CaveMemory.Write_StrBytes("89 07"); //add rsp,20 CaveMemory.Write_StrBytes("48 83 C4 20"); //jmp back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _Standalone_Injection_Return_Offset); Logger.WriteLog("Adding P1 Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection //1st Step : writing the long jump IntPtr ProcessHandle = _TargetProcess.Handle; List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Axis_LongJump_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); //2nd step : writing the short jump Buffer = new List(); Buffer.Add(0xE9); Buffer.Add(0xD9); Buffer.Add(0x09); Buffer.Add(0x04); Buffer.Add(0x00); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _Standalone_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// As a standalone, buttons events are handled by Windows Message (WM_xBUTTONDOWN / WM_xBUTTONUP, x = M/L/R) /// 1st Method is to block the writing of the byte and do it ourselves (byte address is known by pointer) /// 2nd method if needed, is to inject code @+A25AC3 (check RAX value to get good call) to replace read value /// private void SetHack_Buttons_v2() { SetNops(_TargetProcess_MemoryBaseAddress, _Standalone_Nop_Trigger_On); SetNops(_TargetProcess_MemoryBaseAddress, _Standalone_Nop_Trigger_Off); } private void Create_JVS_DataBank() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); _JVS_AxisX_CaveAddress = CaveMemory.CaveAddress; _JVS_AxisY_CaveAddress = CaveMemory.CaveAddress + 08; _JVS_TriggerButton_CaveAddress = CaveMemory.CaveAddress + 0x10; _JVS_WeaponButton_CaveAddress = CaveMemory.CaveAddress + 0x18; Logger.WriteLog("Custom data will be stored at : 0x" + CaveMemory.CaveAddress.ToString("X16")); } /// /// Safest way is to replace the reading part of the "Trigger" byte with our own value /// private void SetHack_JVS_Trigger() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //mov rax,[JVS_Trigger_Btn] CaveMemory.Write_StrBytes("48 A1"); byte[] b = BitConverter.GetBytes(_JVS_TriggerButton_CaveAddress); CaveMemory.Write_Bytes(b); //mov r8d, rax CaveMemory.Write_StrBytes("4C 8B C0"); //pop rax CaveMemory.Write_StrBytes("58"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Injection_Return_Offset); Logger.WriteLog("Adding JVS Trigger COdecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection //1st Step : writing the long jump IntPtr ProcessHandle = _TargetProcess.Handle; List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_LongJump_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); //2nd step : writing the short jump Buffer = new List(); Buffer.Add(0xE9); Buffer.Add(0xE7); Buffer.Add(0x6A); Buffer.Add(0x65); Buffer.Add(0xFF); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Trigger_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Weapon is read along with other buttons on a loaded WORD /// So we will just patch the corresponding bit with our own value /// private void SetHack_JVS_Weapon() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //mov rax,[TimeCrisisGame-Win64-Shipping.exe+1860938] CaveMemory.Write_StrBytes("48 A1"); byte[] b = BitConverter.GetBytes((UInt64)((UInt64)_TargetProcess_MemoryBaseAddress + 0x1860938)); CaveMemory.Write_Bytes(b); //eax,FF7FFFFF CaveMemory.Write_StrBytes("25 FF FF 7F FF"); //mov ebx, eax CaveMemory.Write_StrBytes("8B D8"); //mov eax, [_JVS_Weapon_Btn] CaveMemory.Write_StrBytes("48 A1"); b = BitConverter.GetBytes(_JVS_WeaponButton_CaveAddress); CaveMemory.Write_Bytes(b); //or ebx, eax CaveMemory.Write_StrBytes("09 C3"); //pop rax CaveMemory.Write_StrBytes("58"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Weapon_Injection_Return_Offset); Logger.WriteLog("Adding JVS Weapon Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection //1st Step : writing the long jump IntPtr ProcessHandle = _TargetProcess.Handle; List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Weapon_LongJump_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); //2nd step : writing the short jump Buffer = new List(); Buffer.Add(0xE9); Buffer.Add(0xCE); Buffer.Add(0x49); Buffer.Add(0x65); Buffer.Add(0xFF); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Weapon_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// On my computer axis are not working with JConfig, as the base values (Int) are sent to the game but for unknown reason the float computed value is "Not A Number" /// On this procedure, multiple calls are made to compute the value but only 2 have NaN (0xFFC00000) values, we will overrid result with our own float value. /// The lower bytes of RAX seem to be constant values for X (0x2251) and Y (0x22C1) /// If the "auto" check for X/Y is not working well, this will need more code to check between the 2 changing values /// private void SetHack_JVS_Axis() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //divss xmm0,xmm1 CaveMemory.Write_StrBytes("F3 0F 5E C1"); //cmp [rsp+50], ffc00000 CaveMemory.Write_StrBytes("81 7C 24 58 00 00 C0 FF"); //jne OriginalCode CaveMemory.Write_StrBytes("0F 85 43 00 00 00"); //Hack Part: //and eax, 0xFFFF CaveMemory.Write_StrBytes("25 FF FF 00 00"); //cmp eax, 0x2251 CaveMemory.Write_StrBytes("3D 51 22 00 00"); //je Axis_X CaveMemory.Write_StrBytes("0F 84 10 00 00 00"); //cmp eax, 0x22C1 CaveMemory.Write_StrBytes("3D C1 22 00 00"); //je Axis_Y CaveMemory.Write_StrBytes("0F 84 16 00 00 00"); //jmp OriginalCode (default) CaveMemory.Write_StrBytes("E9 23 00 00 00"); //Axis_X: //mov rax, [AxisX_CaveAddress] CaveMemory.Write_StrBytes("48 A1"); byte[] b = BitConverter.GetBytes(_JVS_AxisX_CaveAddress); CaveMemory.Write_Bytes(b); //mov [rdi], eax CaveMemory.Write_StrBytes("89 07"); //jmp exit CaveMemory.Write_StrBytes("E9 16 00 00 00"); //Axis_Y: //mov rax, [AxisY_CaveAddress] CaveMemory.Write_StrBytes("48 A1"); b = BitConverter.GetBytes(_JVS_AxisY_CaveAddress); CaveMemory.Write_Bytes(b); //mov [rdi], eax CaveMemory.Write_StrBytes("89 07"); //jmp exit CaveMemory.Write_StrBytes("0F 84 04 00 00 00"); //OriginalCode: //movss [rdi], xmm0 CaveMemory.Write_StrBytes("F3 0F 11 07"); //pop rax CaveMemory.Write_StrBytes("58"); //Jump back CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Axis_Injection_Return_Offset); Logger.WriteLog("Adding JVS Axis Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection //1st Step : writing the long jump IntPtr ProcessHandle = _TargetProcess.Handle; List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Axis_LongJump_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); //2nd step : writing the short jump Buffer = new List(); Buffer.Add(0xE9); Buffer.Add(0x02); Buffer.Add(0x07); Buffer.Add(0x04); Buffer.Add(0x00); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _JVS_Axis_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (_IsStandalone) { byte[] bufferX = BitConverter.GetBytes(PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes(PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((IntPtr)(_Standalone_Axis_CaveAddress + _STANDALONE_X_OFFSET), bufferX); WriteBytes((IntPtr)(_Standalone_Axis_CaveAddress + _STANDALONE_Y_OFFSET), bufferY); } } else { byte[] bufferX = BitConverter.GetBytes(_FloatXvalue); byte[] bufferY = BitConverter.GetBytes(_FloatYvalue); if (PlayerData.ID == 1) { WriteBytes((IntPtr)_JVS_AxisX_CaveAddress, bufferX); WriteBytes((IntPtr)_JVS_AxisY_CaveAddress, bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) WriteByte((IntPtr)(_JVS_TriggerButton_CaveAddress), 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) WriteByte((IntPtr)(_JVS_TriggerButton_CaveAddress), 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) WriteByte((IntPtr)(_JVS_WeaponButton_CaveAddress + 2), 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) WriteByte((IntPtr)(_JVS_WeaponButton_CaveAddress + 2), 0x00); //Game is handling inputs via WM_ message. //Lightguns (Aimtrak, Sinden...) are seen as mice and are sending WM_LBUTTON message when triger is pressed //Gamepads are not, so n that case we can send the message as if they were mice buttons /*if (PlayerData.RIController.DeviceType != RawInputDeviceType.RIM_TYPEMOUSE) { if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) //Win32API.PostMessage(_TargetProcess.MainWindowHandle, 0x201, IntPtr.Zero, IntPtr.Zero); //WM_LBUTTONDOWN WriteByte((IntPtr)(_JVS_Trigger_CaveAddress + 0x10), 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) //Win32API.PostMessage(_TargetProcess.MainWindowHandle, 0x202, IntPtr.Zero, IntPtr.Zero); //WM_LBUTTON_UP WriteByte((IntPtr)(_JVS_Trigger_CaveAddress + 0x10), 0x00); } */ } } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.LmpSpeaker)); _Outputs.Add(new GameOutput(OutputId.LmpBillboard)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte bLEDs = ReadByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x18609B9)); SetOutputValue(OutputId.LmpBillboard, bLEDs >> 6 & 0x01); SetOutputValue(OutputId.LmpSpeaker, bLEDs >> 6 & 0x01); //There are many ways to compute Original Recoil : //#1 - @Offset +1860630, the byte is set to 1 after a bullet is fired and then shortly after reset back to 0 (almost non-readable spead, very fast refresh rate needed) //#2 - @Offset +18609B8, the byte is changing between 0x40 and 0x80 for each bullet fired ( start is 0x00 ) //#3 - @InstructionOffset +A56F7b, the option#1 byte is set to 1 (reset to zero @+A56F83). An interception codecave might be possible. //#1 (need high speed refresh) //byte bRecoil = ReadByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x1860630)); //SetOutputValue(OutputId.P1_GunRecoil, bRecoil); //#2 //Only Jconfig with emulated JVS will get these events if (!_IsStandalone) { byte bRecoil = ReadByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x1860630)); if (bRecoil != 0) { if (_P1_LastRecoil == 0x40 && bRecoil == 0x80) { SetOutputValue(OutputId.P1_GunRecoil, 1); SetOutputValue(OutputId.P1_CtmRecoil, 1); } else if (_P1_LastRecoil == 0x80 && bRecoil == 0x40) { SetOutputValue(OutputId.P1_GunRecoil, 1); SetOutputValue(OutputId.P1_CtmRecoil, 1); } } _P1_LastRecoil = bRecoil; } int P1_Life = 0; int P1_Ammo = 0; int P1_Weapon = 0; int P1_Clip = 0; UInt64 PtrAmmoLife = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x0185BE18), new UInt64[] { 0x1C, 0x08, 0x70, 0x1FC }); //To compute custom outputs, we will read Life and Ammo values //Pointers are constantly changing, removed, created, etc....so the value might not exist //On top of that, Ammo number can also be decreased when changing Weapon, so this is a supplementary check to do if (PtrAmmoLife != 0) { //Life is value x 2 in memory.... //Also, can be found statically at P1_Life = ReadByte((IntPtr)(PtrAmmoLife + 0x10)) >> 1; //Life also available in fixed pointer @BaseMemory + 0x01860B78 P1_Weapon = ReadByte((IntPtr)(PtrAmmoLife + 0x118)); P1_Ammo = ReadByte((IntPtr)(PtrAmmoLife + 0x170)); //Computing custom Recoil with the following way (= using Ammo count) will not work during STAGE 2 with unimited ammo weapon if (P1_Weapon == _P1_LastWeapon) { //Custom Recoil //As we don't have a "STATUS" variable telling us if the player is playing or not, //we will smoothly filter for decrease to limit false recoil events if the game sets back Ammo to default value after game-over if (_IsStandalone && P1_Ammo == _P1_LastAmmo - 1) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; } //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } _P1_LastAmmo = P1_Ammo; _P1_LastLife = P1_Life; _P1_LastWeapon = P1_Weapon; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_Flycast.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; namespace DemulShooterX64 { public class Game_Flycast : Game { private const String GAMEDATA_FOLDER = @"MemoryData\flycast"; /* * Output status with derived class : * Ninja Assault (ninjaslt, ninjaslta, ninjasltj, ninjasltu) => working. Need better filter for player playing (force life = 0 at start ?) * Naomi games : * - confmiss => working * - deathcox => working. Need better filter for player playing * - hotd2, hotd2o => working * - hotd2p => no controls on game ? * - hotd2e => need to find memory values * - lupinsho => working * Pokasuka ghost / Manic Panic ghost => I/O emulation not done (time out error) * Atomiswave : no outputs * */ //Output values protected UInt64 _GameRAMPtr_Offset = 0x2087490; protected UInt64 _GameRAM_Address = 0; public Game_Flycast(String RomName) : base(RomName, "flycast") { _KnownMd5Prints.Add("Flycast v2.0", "84b08b9aa61d8c46ff47abcc77f690f7"); _KnownMd5Prints.Add("Flycast v2.1", "cf56b386e1a9e82f5a92f8aadb2b6df9"); _KnownMd5Prints.Add("Flycast v2.2", "1fa2952ada82345ae743bc9110c6dbec"); _KnownMd5Prints.Add("Flycast v2.3", "d5e819796226078c6ce51dc8f304550b"); _KnownMd5Prints.Add("Flycast v2.4", "3ac610caf584b9891de6b8a8924862e5"); _KnownMd5Prints.Add("Flycast v2.5", "c978b832b137ced294a13948a2115d37"); _KnownMd5Prints.Add("Flycast v2.6", "24ddc8b6b9bfb31970b71d1e80721085"); _tProcess.Start(); Logger.WriteLog("Waiting for Flycast " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { CheckExeMd5(); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); _GameRAM_Address = BitConverter.ToUInt64(ReadBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _GameRAMPtr_Offset), 8), 0); if (_GameRAM_Address != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); Logger.WriteLog("Game RAM loaded at 0x" + _GameRAM_Address.ToString("X16")); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("ROM not Loaded..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } } } ================================================ FILE: DemulShooterX64/Games/Game_FlycastAtomiswave.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.MameOutput; using DsCore.Config; namespace DemulShooterX64 { public class Game_FlycastAtomiswave : Game_Flycast { /// /// Constructor /// public Game_FlycastAtomiswave(String RomName) : base(RomName) { } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { /*if (_RomName.Equals("claychal")) Compute_Confmiss_Outputs(); else if (_RomName.Equals("sprtshot")) Compute_Deathcox_Outputs(); else if (_RomName.Equals("xtrmhunt")) Compute_Hotd2_Outputs(0x00096FA0); else if (_RomName.Equals("xtrmhnt2")) Compute_Hotd2_Outputs(0x00096FA0);*/ } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_FlycastNaomi.cs ================================================ using System; using System.Collections.Generic; using DsCore.Config; using DsCore.MameOutput; namespace DemulShooterX64 { public class Game_FlycastNaomi : Game_Flycast { /// /// Constructor /// public Game_FlycastNaomi(String RomName) : base(RomName) { } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (_RomName.Equals("confmiss")) Compute_Confmiss_Outputs(); else if (_RomName.Equals("deathcox")) Compute_Deathcox_Outputs(); else if (_RomName.Equals("deathcoxo")) Compute_Deathcoxo_Outputs(); else if (_RomName.Equals("hotd2")) Compute_Hotd2_Outputs(0x00096FA0); /*else if (_RomName.Equals("hotd2e")) //Need to find memory values, no available on Demul Compute_Hotd2_Outputs(0x00096FA0);*/ else if (_RomName.Equals("hotd2o")) Compute_Hotd2_Outputs(0x00096F58); else if (_RomName.Equals("hotd2p")) Compute_Hotd2_Outputs(0x00082D00); else if (_RomName.Equals("lupinsho")) Compute_Lupinsho_Outputs(); else if (_RomName.Equals("mok")) Compute_Mok_Outputs(); } private void Compute_Confmiss_Outputs() { //Player status : //[0] = Calibration/InGame //[1] = InGame //[2] = Continue //[4] = Game Over / Attract Mode / Menu UInt64 P1_Status_Address = _GameRAM_Address + (UInt64)(BitConverter.ToUInt32(ReadBytes((IntPtr)(_GameRAM_Address + 0x2FBAC), 4), 0) & 0x01FFFFFF); UInt64 P2_Status_Address = P1_Status_Address + 0x40; int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; UInt64 P1_Ammo_Address = _GameRAM_Address + (UInt64)(BitConverter.ToUInt32(ReadBytes((IntPtr)(_GameRAM_Address + 0x2AA50), 4), 0) & 0x01FFFFFF) - 0x14; // = P1_Status_Address + 0xB4C ? UInt64 P2_Ammo_Address = _GameRAM_Address + (UInt64)(BitConverter.ToUInt32(ReadBytes((IntPtr)(_GameRAM_Address + 0x2AA50), 4), 0) & 0x01FFFFFF) + 0x100; UInt64 Credits_Address = _GameRAM_Address + (UInt64)(BitConverter.ToUInt32(ReadBytes((IntPtr)(_GameRAM_Address + 0x2F88C), 4), 0) & 0x01FFFFFF); if (ReadByte((IntPtr)P1_Status_Address) == 0 || ReadByte((IntPtr)P1_Status_Address) == 1) { P1_Life = ReadByte((IntPtr)(P1_Status_Address + 0x14)); P1_Ammo = ReadByte((IntPtr)P1_Ammo_Address); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte((IntPtr)P2_Status_Address) == 0 || ReadByte((IntPtr)P2_Status_Address) == 1) { P2_Life = ReadByte((IntPtr)(P2_Status_Address + 0x14)); P2_Ammo = ReadByte((IntPtr)P2_Ammo_Address); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x000152AEA)) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x000152AEA)) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)Credits_Address)); } private void Compute_Deathcox_Outputs() { //InGame Status : 0 = AttractMode/Demo/Menu, 1 = InGame UInt64 InGame_Address = _GameRAM_Address + 0x000982B8; int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; UInt64 P1_Ammo_Address = _GameRAM_Address + 0x0018E395; UInt64 P2_Ammo_Address = P1_Ammo_Address + 0x3C; UInt64 Credits_Address = _GameRAM_Address + 0x009917C; //P1 and P2 Enable : Display ammo and life when it's 0 ( not reliable but well...) //UInt64 P1_Enable_Address = _GameRAM_Address + 0x00096634; //UInt64 P2_Enable_Address = _GameRAM_Address + 0x00096638; //---> Ammo + 0xC => Byte going from 0 to 1 when alive...use for status ??? UInt64 P1_Enable_Address = P1_Ammo_Address + 0xC; UInt64 P2_Enable_Address = P2_Ammo_Address + 0xC; if (ReadByte((IntPtr)P1_Enable_Address) == 1 && ReadByte((IntPtr)InGame_Address) == 1) { P1_Life = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(Credits_Address + 0x04), 4), 0) * 100); P1_Ammo = ReadByte((IntPtr)P1_Ammo_Address); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte((IntPtr)P2_Enable_Address) == 1 && ReadByte((IntPtr)InGame_Address) == 1) { P2_Life = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(Credits_Address + 0x08), 4), 0) * 100); P2_Ammo = ReadByte((IntPtr)P2_Ammo_Address); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Genuine Outputs //Genuine Outputs memory location still need to be find on this rom //SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x0001C8D16)) >> 7 & 0x01); //SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x0001C8D16)) >> 4 & 0x01); //In the meantime, there is some values that can be used, but does not blink Lamps on the attract/menu screen SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x000098258))); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x00009825C))); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)Credits_Address)); } private void Compute_Deathcoxo_Outputs() { //InGame Status : 0 = AttractMode/Demo/Menu, 1 = InGame UInt64 InGame_Address = _GameRAM_Address + 0x00096680; int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; UInt64 P1_Ammo_Address = _GameRAM_Address + 0x0018BFC9; UInt64 P2_Ammo_Address = P1_Ammo_Address + 0x3C; UInt64 Credits_Address = _GameRAM_Address + 0x00974A8; //P1 and P2 Enable : Display ammo and life when it's 0 ( not reliable but well...) //UInt64 P1_Enable_Address = _GameRAM_Address + 0x00096634; //UInt64 P2_Enable_Address = _GameRAM_Address + 0x00096638; //---> Ammo + 0xC => Byte going from 0 to 1 when alive...use for status ??? UInt64 P1_Enable_Address = P1_Ammo_Address + 0xC; UInt64 P2_Enable_Address = P2_Ammo_Address + 0xC; if (ReadByte((IntPtr)P1_Enable_Address) == 1 && ReadByte((IntPtr)InGame_Address) == 1) { P1_Life = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(Credits_Address + 0x04), 4), 0) * 100); P1_Ammo = ReadByte((IntPtr)P1_Ammo_Address); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte((IntPtr)P2_Enable_Address) == 1 && ReadByte((IntPtr)InGame_Address) == 1) { P2_Life = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(Credits_Address + 0x08), 4), 0) * 100); P2_Ammo = ReadByte((IntPtr)P2_Ammo_Address); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x0001C8D16)) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x0001C8D16)) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)Credits_Address)); } private void Compute_Hotd2_Outputs(UInt32 DataPtr) { //Player status : //[4] = Continue Screen //[5] = InGame //[6] = Game Over //[9] = Menu or Attract Mode UInt64 P1_Status_Address = _GameRAM_Address + (UInt64)(BitConverter.ToUInt32(ReadBytes((IntPtr)(_GameRAM_Address + DataPtr), 4), 0) & 0x01FFFFFF) + 0x04; UInt64 P2_Status_Address = P1_Status_Address + 0x100; int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; if (ReadByte((IntPtr)P1_Status_Address) == 5) { P1_Life = ReadByte((IntPtr)(P1_Status_Address + 0x0C)); P1_Ammo = ReadByte((IntPtr)(P1_Status_Address + 0x20)); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (ReadByte((IntPtr)P2_Status_Address) == 5) { P2_Life = ReadByte((IntPtr)(P2_Status_Address + 0x0C)); P2_Ammo = ReadByte((IntPtr)(P2_Status_Address + 0x20)); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(P1_Status_Address + 0xFB)) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(P2_Status_Address + 0xFB)) >> 6 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)(P1_Status_Address + 0x75C))); } private void Compute_Lupinsho_Outputs() { int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Game Status (float) : //0 : Title Screen //1 : Gameplay //2 : Demo play float GameStatus = BitConverter.ToSingle(ReadBytes((IntPtr)(_GameRAM_Address + 0x00A4C780), 4), 0); if (GameStatus == 1.0f) { //Check if P1 and P2 are active to display their information float P1_Active = BitConverter.ToSingle(ReadBytes((IntPtr)(_GameRAM_Address + 0x00A4C760), 4), 0); float P2_Active = BitConverter.ToSingle(ReadBytes((IntPtr)(_GameRAM_Address + 0x00A4C764), 4), 0); if (P1_Active == 1.0f) { P1_Life = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(_GameRAM_Address + 0x00A4C8B0), 4), 0)); P1_Ammo = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(_GameRAM_Address + 0x00A4C830), 4), 0)); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Active == 1.0f) { P2_Life = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(_GameRAM_Address + 0x00A4C8B4), 4), 0)); P2_Ammo = (int)(BitConverter.ToSingle(ReadBytes((IntPtr)(_GameRAM_Address + 0x00A4C834), 4), 0)); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x00B77C16)) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x00B77C16)) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)(_GameRAM_Address + 0x00B69FF8))); } private void Compute_Mok_Outputs() { //Player status : UInt64 P1_Data_Address = _GameRAM_Address + (UInt64)(BitConverter.ToUInt32(ReadBytes((IntPtr)(_GameRAM_Address + 0x00023464), 4), 0) & 0x01FFFFFF); UInt64 P2_Data_Address = P1_Data_Address + 0x64; int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Player Status : //1 : Title Screen //2,3,4 : Demo //5 : Attract Mode //6 : Game Over //7 : Playing (cut scene) //8 : Playing //9 : continue Screen Byte P1_Status = ReadByte((IntPtr)(P1_Data_Address + 0x48)); Byte P2_Status = ReadByte((IntPtr)(P2_Data_Address + 0x48)); if (P1_Status == 8 || P1_Status == 7) { P1_Life = ReadByte((IntPtr)(P1_Data_Address + 0x5C)); P1_Ammo = ReadByte((IntPtr)(P1_Data_Address + 0x58)); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 8 || P2_Status == 7) { P2_Life = ReadByte((IntPtr)(P2_Data_Address + 0x5C)); P2_Ammo = ReadByte((IntPtr)(P2_Data_Address + 0x58)); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x000EC396)) >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(_GameRAM_Address + 0x000EC396)) >> 4 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)(P1_Data_Address + 0x7C))); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_FlycastNinjaslt.cs ================================================ using System; using System.Collections.Generic; using DsCore.Config; using DsCore.MameOutput; namespace DemulShooterX64 { public class Game_FlycastNinjaslt : Game_Flycast { private UInt32 _Outputs_Outputs_Offset = 0x0022C42D; private UInt32 _Outputs_Credits_Offset = 0x00480D0C; private UInt32 _Outputs_PlayerData_Offset = 0x003D9D30; private int _P1_LastDammage = 0; private int _P2_LastDammage = 0; /// /// Constructor /// public Game_FlycastNinjaslt(String RomName) : base(RomName) { if (RomName.Equals("ninjaslt")) { _Outputs_Outputs_Offset = 0x0022C42D; _Outputs_Credits_Offset = 0x00480D0C; //_Outputs_PlayerData_Offset = 0x003D9D30; Demul _Outputs_PlayerData_Offset = 0x003D9CB0; } else if (RomName.Equals("ninjaslta")) { _Outputs_Outputs_Offset = 0x0022C4AD; _Outputs_Credits_Offset = 0x00480D8C; _Outputs_PlayerData_Offset = 0x003D9D30; } else if (RomName.Equals("ninjasltj")) { _Outputs_Outputs_Offset = 0x0022C3F1; _Outputs_Credits_Offset = 0x00480CD4; _Outputs_PlayerData_Offset = 0x003D9C78; } else if (RomName.Equals("ninjasltu")) { _Outputs_Outputs_Offset = 0x0022C4AD; _Outputs_Credits_Offset = 0x00480D8C; _Outputs_PlayerData_Offset = 0x003D9D30; } } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { UInt64 Outputs_Address = _GameRAM_Address + _Outputs_Outputs_Offset; int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; //Check if the game is in Gameplay mode if (ReadByte((IntPtr)(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x1A)) == 1) { //Didn't find any reliable "player state", but Life seem to stay at 0 when not playing, so we will use that //Note that at start, life may be > 0 if the player has never entered a game :( P1_Life = (int)BitConverter.ToInt16(ReadBytes((IntPtr)(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x54), 2), 0); P2_Life = (int)BitConverter.ToInt16(ReadBytes((IntPtr)(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x56), 2), 0); //For custom dammaged : //1) Solution 1 : Decrease life = small delay between the hit and the life beeing lost /*//[Damaged] custom Output if (_P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); //[Damaged] custom Output if (_P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1);*/ //2) solution 2 : Read a byte value wich is != 0 when hit (invicibility duration ?) but the "1" state duration is long and may trigger the output multiple times int P1_Dammage = ReadByte((IntPtr)(_GameRAM_Address + _Outputs_PlayerData_Offset + 0xBC)); int P2_Dammage = ReadByte((IntPtr)(_GameRAM_Address + _Outputs_PlayerData_Offset + 0xBE)); if (P1_Dammage != 0 & _P1_LastDammage == 0) SetOutputValue(OutputId.P1_Damaged, 1); if (P2_Dammage != 0 & _P2_LastDammage == 0) SetOutputValue(OutputId.P2_Damaged, 1); _P1_LastDammage = P1_Dammage; _P2_LastDammage = P2_Dammage; if (P1_Life > 0) { P1_Ammo = (int)BitConverter.ToInt16(ReadBytes((IntPtr)(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x62), 2), 0); //Custom Recoil if (P1_Ammo < _P1_LastAmmo) SetOutputValue(OutputId.P1_CtmRecoil, 1); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; } if (P2_Life > 0) { P2_Ammo = (int)BitConverter.ToInt16(ReadBytes((IntPtr)(_GameRAM_Address + _Outputs_PlayerData_Offset + 0x64), 2), 0); //Custom Recoil if (P2_Ammo < _P2_LastAmmo) SetOutputValue(OutputId.P2_CtmRecoil, 1); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; } } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); //Genuine Outputs SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)Outputs_Address) >> 6 & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)Outputs_Address) >> 4 & 0x01); SetOutputValue(OutputId.P1_GunRecoil, ReadByte((IntPtr)Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_GunRecoil, ReadByte((IntPtr)Outputs_Address) >> 5 & 0x01); //Custom recoil will be activated just like original one //REMOVED !! The recoil is activated when shooting offscreen !! /*SetOutputValue(OutputId.P1_CtmRecoil, ReadByte((IntPtr)Outputs_Address) >> 7 & 0x01); SetOutputValue(OutputId.P2_CtmRecoil, ReadByte((IntPtr)Outputs_Address) >> 5 & 0x01);*/ //Credits SetOutputValue(OutputId.Credits, ReadByte((IntPtr)(_GameRAM_Address + _Outputs_Credits_Offset))); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_NuLuigiMansion.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MemoryX64; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { public class Game_NuLuigiMansion : Game { /*** MEMORY ADDRESSES **/ private UInt64 _P1_X_Address = 0; private UInt64 _P1_Y_Address = 0; private UInt64 _P1_Buttons_Address = 0; private const UInt64 P1_INJECTION_OFFSET = 0x00017E76; private const UInt64 P1_INJECTION_RETURN_OFFSET = 0x00017E92; private UInt64 _P2_X_Address = 0; private UInt64 _P2_Y_Address = 0; private UInt64 _P2_Buttons_Address = 0; private const UInt64 P2_INJECTION_OFFSET = 0x00017EDA; private const UInt64 P2_INJECTION_RETURN_OFFSET = 0x00017EF6; //Check instruction for game loaded private const int ROM_LOADED_CHECK_INSTRUCTION_OFFSET = 0x17E6E; /// /// Constructor /// public Game_NuLuigiMansion(String RomName) : base(RomName, "vacuum") { _KnownMd5Prints.Add("VACUUM.EXE Teknoparrot dump", "8ddfab1cd2140670d9437738c9c331c8"); _tProcess.Start(); Logger.WriteLog("Waiting for SEGA Nu " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { Int64 aTest = _TargetProcess_MemoryBaseAddress.ToInt64() + ROM_LOADED_CHECK_INSTRUCTION_OFFSET; byte[] buffer = ReadBytes((IntPtr)aTest, 2); if (buffer[0] == 0x74 && buffer[1] == 0x58) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; } else { Logger.WriteLog("ROM not Loaded..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { //Window size Rect TotalRes = new Rect(); Win32API.GetClientRect(_TargetProcess.MainWindowHandle, ref TotalRes); double TotalResX = TotalRes.Right - TotalRes.Left; double TotalResY = TotalRes.Bottom - TotalRes.Top; Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0 - 1920] //Y => [0 - 1080] double dMaxX = 1920.0; double dMaxY = 1080.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { SetHackP1(); SetHackP2(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Axis values and Buttons states are written by Teknoparrot DLL, we can't block it. /// So instead we're targeting the instructions which are reading the values to make them read ou own instead. /// As usual, many buttons are sharing the sam Byte so we are filtering to block only gun buttons and still allow both START buttons /// private void SetHackP1() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ Logger.WriteLog("_P1_Data_Address = 0x" + (CaveMemory.CaveAddress + 0x50).ToString("X16")); _P1_X_Address = CaveMemory.CaveAddress + 0x50; _P1_Y_Address = CaveMemory.CaveAddress + 0x60; _P1_Buttons_Address = CaveMemory.CaveAddress + 0x70; List Buffer = new List(); //mov r8d, [RIP+100] (==> _P1_X_Address) CaveMemory.Write_StrBytes("44 8B 05 49 00 00 00"); //mov r9d, [RIP+100] (==> _P1_Y_Address) CaveMemory.Write_StrBytes("44 8B 0D 52 00 00 00"); //mov r10d, [rax+ 000000BC] CaveMemory.Write_StrBytes("44 8B 90 BC 00 00 00"); //mov r11d, [rax+ 000000C0] CaveMemory.Write_StrBytes("44 8B 98 C0 00 00 00"); //and r10d, 0x1000 CaveMemory.Write_StrBytes("41 81 E2 00 10 00 00"); //push rbx CaveMemory.Write_StrBytes("53"); //mov rbx, [RIP+100] (==> _P1_Button_Address) CaveMemory.Write_StrBytes("48 8B 1D 45 00 00 00"); //or r10d, rbx CaveMemory.Write_StrBytes("49 09 DA"); //pop rbx CaveMemory.Write_StrBytes("5B"); CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + P1_INJECTION_RETURN_OFFSET); Logger.WriteLog("Adding P1 Buttons Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + P1_INJECTION_OFFSET), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } private void SetHackP2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); Logger.WriteLog("_P2_Data_Address = 0x" + (CaveMemory.CaveAddress + 0x50).ToString("X16")); _P2_X_Address = CaveMemory.CaveAddress + 0x50; _P2_Y_Address = CaveMemory.CaveAddress + 0x60; _P2_Buttons_Address = CaveMemory.CaveAddress + 0x70; List Buffer = new List(); //mov r8d, [RIP+100] (==> _P2_X_Address) CaveMemory.Write_StrBytes("44 8B 05 49 00 00 00"); //mov r9d, [RIP+100] (==> _P2_Y_Address) CaveMemory.Write_StrBytes("44 8B 0D 52 00 00 00"); //mov r10d, [rax+ 000000D0] CaveMemory.Write_StrBytes("44 8B 90 D0 00 00 00"); //mov r11d, [rax+ 000000D4] CaveMemory.Write_StrBytes("44 8B 98 D4 00 00 00"); //and r10d, 0x1000 CaveMemory.Write_StrBytes("41 81 E2 00 10 00 00"); //push rbx CaveMemory.Write_StrBytes("53"); //mov rbx, [RIP+100] (==> _P2_Button_Address) CaveMemory.Write_StrBytes("48 8B 1D 45 00 00 00"); //or r10d, rbx CaveMemory.Write_StrBytes("49 09 DA"); //pop rbx CaveMemory.Write_StrBytes("5B"); CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + P2_INJECTION_RETURN_OFFSET); Logger.WriteLog("Adding P2 Buttons Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + P2_INJECTION_OFFSET), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { WriteBytes((IntPtr)(_P1_X_Address), BitConverter.GetBytes(PlayerData.RIController.Computed_X)); WriteBytes((IntPtr)(_P1_Y_Address), BitConverter.GetBytes(PlayerData.RIController.Computed_Y)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_P1_Buttons_Address + 1), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_P1_Buttons_Address + 1), 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_P1_Buttons_Address + 1), 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_P1_Buttons_Address + 1), 0xDF); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)(_P2_X_Address), BitConverter.GetBytes(PlayerData.RIController.Computed_X)); WriteBytes((IntPtr)(_P2_Y_Address), BitConverter.GetBytes(PlayerData.RIController.Computed_Y)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_P2_Buttons_Address + 1), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_P2_Buttons_Address + 1), 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_P2_Buttons_Address + 1), 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_P2_Buttons_Address + 1), 0xDF); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_NuLuigiMansion_v2.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { public class Game_NuLuigiMansion_v2 : Game { private UInt64 _Input_BaseAddress; /*** MEMORY ADDRESSES **/ private const UInt64 P1_X_OFFSET = 0x2C; private const UInt64 P1_Y_OFFSET = 0x30; private const UInt64 P2_X_OFFSET = 0x40; private const UInt64 P2_Y_OFFSET = 0x44; private UInt64 _P1_Buttons_CaveAddress = 0; private UInt64 _P2_Buttons_CaveAddress = 0; private UInt64 _P1_Injection_Offset = 0x00017EB8; private UInt64 _P1_Injection_Return_Offset = 0x00017EC8; private UInt64 _P2_Injection_Offset = 0x00017F1D; private UInt64 _P2_Injection_Return_Offset = 0x00017F2D; //Check instruction for game loaded private const UInt64 ROM_LOADED_CHECK_INSTRUCTION_OFFSET = 0x00017E6E; /// /// Constructor /// public Game_NuLuigiMansion_v2(String RomName) : base(RomName, "vacuum") { _KnownMd5Prints.Add("VACUUM.EXE - Original Dump", "5120bbe464b35f4cc894238bd9f9e11b"); _KnownMd5Prints.Add("VACUUM.EXE - 'SpeedFix'", "8ddfab1cd2140670d9437738c9c331c8"); _KnownMd5Prints.Add("VACUUM.EXE - For JConfig", "63c70cf8b080c1972e9e753f258e9507"); _tProcess.Start(); Logger.WriteLog("Waiting for SEGA Nu " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { UInt64 aTest = (UInt64)_TargetProcess_MemoryBaseAddress + ROM_LOADED_CHECK_INSTRUCTION_OFFSET; byte[] buffer = ReadBytes((IntPtr)aTest, 2); if (buffer[0] == 0x74 && buffer[1] == 0x58) { aTest = (UInt64)_TargetProcess_MemoryBaseAddress + 0x004F6CB8; Logger.WriteLog("aTest = 0x" + aTest.ToString("X16")); buffer = ReadBytes((IntPtr)aTest, 8); _Input_BaseAddress = BitConverter.ToUInt64(buffer, 0); Logger.WriteLog("_Input_BaseAddress (1st step) = 0x" + _Input_BaseAddress.ToString("X16")); buffer = ReadBytes((IntPtr)_Input_BaseAddress, 8); _Input_BaseAddress = BitConverter.ToUInt64(buffer, 0); Logger.WriteLog("_Input_BaseAddress = 0x" + _Input_BaseAddress.ToString("X16")); if (_Input_BaseAddress != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } else { Logger.WriteLog("ROM not Loaded..."); } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0 - 1920] //Y => [0 - 1080] double dMaxX = 1920.0; double dMaxY = 1080.0; PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(dMaxX * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(Math.Round(dMaxY * PlayerData.RIController.Computed_Y / TotalResY)); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { SetHackP1(); SetHackP2(); byte[] InitX = BitConverter.GetBytes((int)960); byte[] InitY = BitConverter.GetBytes((int)540); WriteBytes((IntPtr)(_Input_BaseAddress + P1_X_OFFSET), InitX); WriteBytes((IntPtr)(_Input_BaseAddress + P1_Y_OFFSET), InitY); WriteBytes((IntPtr)(_Input_BaseAddress + P1_X_OFFSET), InitX); WriteBytes((IntPtr)(_Input_BaseAddress + P1_Y_OFFSET), InitY); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Axis values and Buttons states are written by Teknoparrot DLL, we can't block it. /// So instead we're targeting the instructions which are reading the values to make them read ou own instead. /// As usual, many buttons are sharing the sam Byte so we are filtering to block only gun buttons and still allow both START buttons /// private void SetHackP1() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); /* Because of 64bit asm, I dont know how to load a 64bit data segment address into a register. * But there is an instruction to load address with an offset from RIP (instruction pointer) * That's why data are stored just after the code (to know the offset) * */ _P1_Buttons_CaveAddress = CaveMemory.CaveAddress + 0x30; Logger.WriteLog("_P1_Buttons_CaveAddress = 0x" + _P1_Buttons_CaveAddress.ToString("X16")); //push rbx CaveMemory.Write_StrBytes("53"); //mov rbx, [RIP+100] (==> _P1_Button_CaveAddress) CaveMemory.Write_StrBytes("48 8B 1D 28 00 00 00"); //and r10d, 0x1000 CaveMemory.Write_StrBytes("41 81 E2 00 10 00 00"); //or r10d, rbx CaveMemory.Write_StrBytes("49 09 DA"); //pop rbx CaveMemory.Write_StrBytes("5B"); //mov [rax+34],r10d CaveMemory.Write_StrBytes("44 89 50 34"); //mov [rax+38],r11d CaveMemory.Write_StrBytes("44 89 58 38"); CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Injection_Return_Offset); Logger.WriteLog("Adding P1 Buttons Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } private void SetHackP2() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); _P2_Buttons_CaveAddress = CaveMemory.CaveAddress + 0x30; Logger.WriteLog("_P2_Buttons_CaveAddress = 0x" + _P2_Buttons_CaveAddress.ToString("X16")); //push rbx CaveMemory.Write_StrBytes("53"); //mov rbx, [RIP+100] (==> _P2_Button_CaveAddress) CaveMemory.Write_StrBytes("48 8B 1D 28 00 00 00"); //and r10d, 0x1000 CaveMemory.Write_StrBytes("41 81 E2 00 10 00 00"); //or r10d, rbx CaveMemory.Write_StrBytes("49 09 DA"); //pop rbx CaveMemory.Write_StrBytes("5B"); //mov [rax+48],r10d CaveMemory.Write_StrBytes("44 89 50 48"); //mov [rax+4c],r11d CaveMemory.Write_StrBytes("44 89 58 4C"); CaveMemory.Write_jmp((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Injection_Return_Offset); Logger.WriteLog("Adding P2 Buttons Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); //Code Injection List Buffer = new List(); IntPtr ProcessHandle = _TargetProcess.Handle; UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(CaveMemory.CaveAddress)); Buffer.Add(0x90); Buffer.Add(0x90); Win32API.WriteProcessMemoryX64(ProcessHandle, (IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Injection_Offset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes(PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes(PlayerData.RIController.Computed_Y); if (PlayerData.ID == 1) { WriteBytes((IntPtr)(_Input_BaseAddress + P1_X_OFFSET), bufferX); WriteBytes((IntPtr)(_Input_BaseAddress + P1_Y_OFFSET), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_P1_Buttons_CaveAddress + 1), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_P1_Buttons_CaveAddress + 1), 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_P1_Buttons_CaveAddress + 1), 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_P1_Buttons_CaveAddress + 1), 0xDF); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)(_Input_BaseAddress + P2_X_OFFSET), bufferX); WriteBytes((IntPtr)(_Input_BaseAddress + P2_Y_OFFSET), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 1), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 1), 0xBF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 1), 0x20); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 1), 0xDF); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_G)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_B)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_R)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_G)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun_B)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun_R)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun_G)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun_B)); _Outputs.Add(new GameOutput(OutputId.P1_LmpWindow_R)); _Outputs.Add(new GameOutput(OutputId.P1_LmpWindow_G)); _Outputs.Add(new GameOutput(OutputId.P1_LmpWindow_B)); _Outputs.Add(new GameOutput(OutputId.P2_LmpWindow_R)); _Outputs.Add(new GameOutput(OutputId.P2_LmpWindow_G)); _Outputs.Add(new GameOutput(OutputId.P2_LmpWindow_B)); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P2_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { UInt64 Ptr1 = ReadPtr((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x004F6C98)); Ptr1 = ReadPtr((IntPtr)Ptr1); UInt64 Ptr2 = ReadPtr((IntPtr)(Ptr1 + 0x18)); SetOutputValue(OutputId.P1_LmpStart, ReadByte((IntPtr)(Ptr2 + 0x4C)) & 0x01); SetOutputValue(OutputId.P2_LmpStart, ReadByte((IntPtr)(Ptr2 + 0x50)) & 0x01); SetOutputValue(OutputId.P1_Lmp_R, ReadByte((IntPtr)(Ptr1 + 0x101))); SetOutputValue(OutputId.P1_Lmp_G, ReadByte((IntPtr)(Ptr1 + 0x102))); SetOutputValue(OutputId.P1_Lmp_B, ReadByte((IntPtr)(Ptr1 + 0x103))); SetOutputValue(OutputId.P2_Lmp_R, ReadByte((IntPtr)(Ptr1 + 0x105))); SetOutputValue(OutputId.P2_Lmp_G, ReadByte((IntPtr)(Ptr1 + 0x106))); SetOutputValue(OutputId.P2_Lmp_B, ReadByte((IntPtr)(Ptr1 + 0x107))); SetOutputValue(OutputId.P1_LmpGun_R, ReadByte((IntPtr)(Ptr1 + 0x1AD))); SetOutputValue(OutputId.P1_LmpGun_G, ReadByte((IntPtr)(Ptr1 + 0x1AE))); SetOutputValue(OutputId.P1_LmpGun_B, ReadByte((IntPtr)(Ptr1 + 0x1AF))); SetOutputValue(OutputId.P2_LmpGun_R, ReadByte((IntPtr)(Ptr1 + 0x1B1))); SetOutputValue(OutputId.P2_LmpGun_G, ReadByte((IntPtr)(Ptr1 + 0x1B2))); SetOutputValue(OutputId.P2_LmpGun_B, ReadByte((IntPtr)(Ptr1 + 0x1B3))); SetOutputValue(OutputId.P1_LmpWindow_R, ReadByte((IntPtr)(Ptr1 + 0x16D))); SetOutputValue(OutputId.P1_LmpWindow_G, ReadByte((IntPtr)(Ptr1 + 0x16E))); SetOutputValue(OutputId.P1_LmpWindow_B, ReadByte((IntPtr)(Ptr1 + 0x16F))); SetOutputValue(OutputId.P2_LmpWindow_R, ReadByte((IntPtr)(Ptr1 + 0x18D))); SetOutputValue(OutputId.P2_LmpWindow_G, ReadByte((IntPtr)(Ptr1 + 0x18E))); SetOutputValue(OutputId.P2_LmpWindow_B, ReadByte((IntPtr)(Ptr1 + 0x18F))); SetOutputValue(OutputId.P1_GunMotor, ReadByte((IntPtr)(Ptr2 + 0x2C))); SetOutputValue(OutputId.P2_GunMotor, ReadByte((IntPtr)(Ptr2 + 0x30))); //For recoil, I found 2 bytes that are changed. //The second one seems to be some kind of "duration" (3-2-1-0) of the recoil effect //The recoil seems to fire each time a ghost is sucked in the controller Byte Recoil1 = ReadByte((IntPtr)(Ptr2 + 0x44)); Byte Recoil2 = ReadByte((IntPtr)(Ptr2 + 0x48)); SetOutputValue(OutputId.P1_GunRecoil, Recoil1); SetOutputValue(OutputId.P2_GunRecoil, Recoil2); //Changing the real output value to a simple 0/1 recoil effect for the custom recoil output if (Recoil1 != 0) SetOutputValue(OutputId.P1_CtmRecoil, 1); else SetOutputValue(OutputId.P1_CtmRecoil, 0); if (Recoil2 != 0) SetOutputValue(OutputId.P2_CtmRecoil, 1); else SetOutputValue(OutputId.P2_CtmRecoil, 0); //In-game values are pointer affected only during a game (not valid in menus) //Player status : //[1] = Playing //[0] = Not Playing UInt32 P1_Status = 0; UInt32 P2_Status = 0; int P1_Life = 0; int P2_Life = 0; int P1_Ammo = 0; int P2_Ammo = 0; int P1_Clip = 0; int P2_Clip = 0; UInt64 Ptr3 = ReadPtr((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x004FB0C0)); if (Ptr3 != 0) { Ptr3 = ReadPtr((IntPtr)(Ptr3 + 0x48)); if (Ptr3 != 0) { P1_Status = ReadByte((IntPtr)(Ptr3 + 0x548)); P2_Status = ReadByte((IntPtr)(Ptr3 + 0x560)); } } if (P1_Status == 1) { P1_Life = ReadByte((IntPtr)(Ptr3 + 0x550)); P1_Ammo = ReadByte((IntPtr)(Ptr3 + 0x57C)); //[Clip Empty] custom Output if (P1_Ammo > 0) P1_Clip = 1; //[Damaged] custom Output if (P1_Life < _P1_LastLife) SetOutputValue(OutputId.P1_Damaged, 1); } if (P2_Status == 1) { P2_Life = ReadByte((IntPtr)(Ptr3 + 0x568)); P2_Ammo = ReadByte((IntPtr)(Ptr3 + 0x588)); //[Clip Empty] custom Output if (P2_Ammo > 0) P2_Clip = 1; //[Damaged] custom Output if (P2_Life < _P2_LastLife) SetOutputValue(OutputId.P2_Damaged, 1); } _P1_LastAmmo = P1_Ammo; _P2_LastAmmo = P2_Ammo; _P1_LastLife = P1_Life; _P2_LastLife = P2_Life; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); SetOutputValue(OutputId.P2_Ammo, P2_Ammo); SetOutputValue(OutputId.P1_Clip, P1_Clip); SetOutputValue(OutputId.P2_Clip, P2_Clip); SetOutputValue(OutputId.P1_Life, P1_Life); SetOutputValue(OutputId.P2_Life, P2_Life); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + 0x004E19A8))); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_RtNerfArcade.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { internal class Game_RtNerfArcade : Game__Unity { private class InputData : Base_InputData { public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public UInt32[] Credits = null; public UInt16 P1_Lmp_Start = 0; public UInt16 P1_Lmp_SeatPuck = 0; public UInt16 P1_Lmp_SeatMarquee = 0; public UInt16 P1_Lmp_SeatRear_R = 0; public UInt16 P1_Lmp_SeatRear_O = 0; public UInt16 P1_Lmp_SeatRear_B = 0; public UInt16 P2_Lmp_Start = 0; public UInt16 P2_Lmp_SeatPuck = 0; public UInt16 P2_Lmp_SeatMarquee = 0; public UInt16 P2_Lmp_SeatRear_R = 0; public UInt16 P2_Lmp_SeatRear_O = 0; public UInt16 P2_Lmp_SeatRear_B = 0; public UInt16 Cab_Lmp_R = 0; public UInt16 Cab_Lmp_G = 0; public UInt16 Cab_Lmp_B = 0; public UInt16 Cab_Lmp_RearSeat = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_RtNerfArcade(String RomName) : base(RomName, "Nerf", "Nerf") { _KnownMd5Prints.Add("Nerf Arcade v1.55", "7f40b5a56501507b9e899f1d58401817"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_SeatPuck)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_SeatMarquee)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_SeatSpeaker_R)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_SeatSpeaker_O)); _Outputs.Add(new GameOutput(OutputId.P1_Lmp_SeatSpeaker_B)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_SeatPuck)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_SeatMarquee)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_SeatSpeaker_R)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_SeatSpeaker_O)); _Outputs.Add(new GameOutput(OutputId.P2_Lmp_SeatSpeaker_B)); _Outputs.Add(new GameOutput(OutputId.Lmp_TMolding_R)); _Outputs.Add(new GameOutput(OutputId.Lmp_TMolding_G)); _Outputs.Add(new GameOutput(OutputId.Lmp_TMolding_B)); _Outputs.Add(new GameOutput(OutputId.Lmp_SeatDownLight)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Credits)); _Outputs.Add(new GameOutput(OutputId.P2_Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P1_Credits, (int)((OutputData)_OutputData).Credits[0]); SetOutputValue(OutputId.P2_Credits, (int)((OutputData)_OutputData).Credits[1]); //Lamps SetOutputValue(OutputId.P1_LmpStart, ((OutputData)_OutputData).P1_Lmp_Start); SetOutputValue(OutputId.P1_Lmp_SeatPuck, ((OutputData)_OutputData).P1_Lmp_SeatPuck); SetOutputValue(OutputId.P1_Lmp_SeatMarquee, ((OutputData)_OutputData).P1_Lmp_SeatMarquee); SetOutputValue(OutputId.P1_Lmp_SeatSpeaker_R, ((OutputData)_OutputData).P1_Lmp_SeatRear_R); SetOutputValue(OutputId.P1_Lmp_SeatSpeaker_O, ((OutputData)_OutputData).P1_Lmp_SeatRear_O); SetOutputValue(OutputId.P1_Lmp_SeatSpeaker_B, ((OutputData)_OutputData).P1_Lmp_SeatRear_B); SetOutputValue(OutputId.P2_LmpStart, ((OutputData)_OutputData).P2_Lmp_Start); SetOutputValue(OutputId.P2_Lmp_SeatPuck, ((OutputData)_OutputData).P2_Lmp_SeatPuck); SetOutputValue(OutputId.P2_Lmp_SeatMarquee, ((OutputData)_OutputData).P2_Lmp_SeatMarquee); SetOutputValue(OutputId.P2_Lmp_SeatSpeaker_R, ((OutputData)_OutputData).P2_Lmp_SeatRear_R); SetOutputValue(OutputId.P2_Lmp_SeatSpeaker_O, ((OutputData)_OutputData).P2_Lmp_SeatRear_O); SetOutputValue(OutputId.P2_Lmp_SeatSpeaker_B, ((OutputData)_OutputData).P2_Lmp_SeatRear_B); SetOutputValue(OutputId.Lmp_TMolding_R, ((OutputData)_OutputData).Cab_Lmp_R); SetOutputValue(OutputId.Lmp_TMolding_G, ((OutputData)_OutputData).Cab_Lmp_G); SetOutputValue(OutputId.Lmp_TMolding_B, ((OutputData)_OutputData).Cab_Lmp_B); SetOutputValue(OutputId.Lmp_SeatDownLight, ((OutputData)_OutputData).Cab_Lmp_RearSeat); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_S357DarkEscape.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { public class Game_S357DarkEscape : Game { //MEMORY ADDRESSES /*private UInt64 _GameCode_Ptr_Offset = 0x03C37700; private UInt64 _GameCode_Address = 0; private byte[] _GameLoadedInstruction = new byte[] { 0x48, 0x83, 0xEC, 0x28 };*/ private UInt64 _P1_Axis_Address = 0x33048A750; //4bytes Little-indian private UInt64 _P2_Axis_Address = 0x33048CA38; //4bytes Little-indian private UInt64 _Buttons_Address = 0x300986C45; //Outputs private UInt64 _Outputs_Address = 0x300987370; private UInt64 _Credits_Address = 0x300986C13; private int _P1_LastRumble = 0; private int _P2_LastRumble = 0; /// /// Constructor /// public Game_S357DarkEscape(String RomName) : base(RomName, "rpcs3-gun") { _KnownMd5Prints.Add("RPCS3 v0.0.27 fork for System 357, GUN version", "3321f7771ae74e8027f0d4e18167d635"); _KnownMd5Prints.Add("RPCS3 v0.0.37-2ef2310e Alpha (TeknoParrot)", "56B69E6A8D95FE0AC7BF7BB5D57321DC"); _tProcess.Start(); Logger.WriteLog("Waiting for RPCS3 Dark Escape 4D " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Contains("DarkEscape")|| FindGameWindow_Contains("RPCS3 via TeknoParrot")) { Check_PatchedFiles_Ok(); CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } /*_GameCode_Address = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _GameCode_Ptr_Offset), new UInt64[] { 0x230, 0x00, 0x30, 0x00 }); if (_GameCode_Address != 0) { Logger.WriteLog("EBOT.BIN PPU cache memory segment address = 0x" + _GameCode_Address.ToString("X16")); byte[] TestLoadedInstruction = ReadBytes((IntPtr)_GameCode_Address, 4); if (TestLoadedInstruction[0] == 0x48 && TestLoadedInstruction[1] == 0x83 && TestLoadedInstruction[2] == 0xEC && TestLoadedInstruction[3] == 0x28) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { string s = string.Empty; foreach (byte b in TestLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Read bytes at address 0x" + _GameCode_Address.ToString("X16") + " : " + s); s = string.Empty; foreach (byte b in _GameLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Expected : " + s); Logger.WriteLog("ROM not Loaded..."); } } else { Logger.WriteLog("ROM not Loaded..."); }*/ } } } catch (Exception) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region PPU cache check //Check if pre-patched PPU cache files are present in the cache folder //If not, the game won't be able to be control by DemulShooter private void Check_PatchedFiles_Ok() { String EmulatorBasePath = _TargetProcess.MainModule.FileName.Substring(0, _TargetProcess.MainModule.FileName.Length - 13); Logger.WriteLog("Emulator path = " + EmulatorBasePath); String PPU_CachePath = EmulatorBasePath + @"cache\SCEEXE000\ppu-gfm17oJj1cUecjZQ8dVv46oQv2iW-EBOOT.BIN"; if (!Directory.Exists(PPU_CachePath)) { Logger.WriteLog("Error : PPU cache folder not found (" + PPU_CachePath + ")"); return; } string CPU_Type = string.Empty; foreach(String File in Directory.GetFiles(PPU_CachePath)) { String sFile = Path.GetFileName(File); if (sFile.StartsWith("v5-kusa-2LAp0iYJZAhpor5W6oVaQW-00001G")) { try { CPU_Type = sFile.Split(new string[] { ".obj.gz" }, StringSplitOptions.None)[0].Split(new string[] { "0001G-" }, StringSplitOptions.None)[1]; } catch { Logger.WriteLog("Error : Impossible to detect LLVM CPU type"); } if (CPU_Type != string.Empty) break; } } if (CPU_Type != string.Empty) { Logger.WriteLog("LLVM PPU processor is " + CPU_Type); CompareMd5Hash(PPU_CachePath + @"\v5-kusa-Nk1ztL1zd9hqYxZvsSzanK-00001G-" + CPU_Type + ".obj.gz", "f13de35d74c5cd7c7b7fe016d8efd722"); CompareMd5Hash(PPU_CachePath + @"\v5-kusa-qkvp7tpoTGacuznXZ31HmQ-00001G-" + CPU_Type + ".obj.gz", "68074e86eb3cffdb47b9ef1c54c71526"); CompareMd5Hash(PPU_CachePath + @"\v5-kusa-YS0mZH4vvSfUP58FQH907h-00001G-" + CPU_Type + ".obj.gz", "fd4c9e1ed924517747a4baba45fd3e47"); } else { Logger.WriteLog("WARNING : PPU cache not found => DemulShooter may not work correctly"); } } private void CompareMd5Hash(String Filename, String AwaitedMd5) { if (!File.Exists(Filename)) { Logger.WriteLog("WARNING : PPU cache file not found : " + Filename + " => DemulShooter may not work correctly"); } else { using (var md5 = MD5.Create()) { using (var stream = File.OpenRead(Filename)) { var hash = md5.ComputeHash(stream); String StrHash = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); Logger.WriteLog(Filename + " MD5 Checksum = " + StrHash); if (StrHash != AwaitedMd5) Logger.WriteLog(@"/!\ MD5 Hash unknown, DemulShooter may not work correctly /!\"); else Logger.WriteLog("MD5 Check OK"); } } } } #endregion #region Screen /// /// Replacing original function using TargetProcess main window handle by this one, using a specific window handle /// /// /// public override bool ClientScale(PlayerSettings PlayerData) { //Convert Screen location to Client location if (_TargetProcess != null) { //Window size Rect TotalRes = new Rect(); Win32API.GetWindowRect(_GameWindowHandle, ref TotalRes); Logger.WriteLog("Window position (Px) = [ " + TotalRes.Left + ";" + TotalRes.Top + " ]"); PlayerData.RIController.Computed_X = PlayerData.RIController.Computed_X - TotalRes.Left; PlayerData.RIController.Computed_Y = PlayerData.RIController.Computed_Y - TotalRes.Top; Logger.WriteLog("Onclient window position (Px) = [ " + PlayerData.RIController.Computed_X + "x" + PlayerData.RIController.Computed_Y + " ]"); } return true; } /// /// Convert client area pointer location to Game speciffic data for memory injection /// Windowed mode is not working (can't get window size) so full screen mode only is simpler.... /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 1280.0; double dMaxY = 720.0; PlayerData.RIController.Computed_X = Convert.ToInt32(dMaxX * PlayerData.RIController.Computed_X / TotalResX); PlayerData.RIController.Computed_Y = Convert.ToInt32(dMaxY * PlayerData.RIController.Computed_Y / TotalResY); //The game is blocking axis values so that the cursor is not close to the border, or goes to the UI at the bottom /*if (PlayerData.RIController.Computed_X < 111) PlayerData.RIController.Computed_X = 111; if (PlayerData.RIController.Computed_Y < 58) PlayerData.RIController.Computed_Y = 58; if (PlayerData.RIController.Computed_X > 1177) PlayerData.RIController.Computed_X = 1177; if (PlayerData.RIController.Computed_Y > 662) PlayerData.RIController.Computed_Y = 662;*/ if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Input /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { byte[] bufferX = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt32)PlayerData.RIController.Computed_Y); Array.Reverse(bufferX); Array.Reverse(bufferY); if (PlayerData.ID == 1) { WriteBytes((IntPtr)(_P1_Axis_Address), bufferX); WriteBytes((IntPtr)(_P1_Axis_Address + 4), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xBF); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)(_P2_Axis_Address), bufferX); WriteBytes((IntPtr)(_P2_Axis_Address + 4), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x10); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x08); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xF7); } } } /// /// Low-level Keyboard hook callback. /// This is used to replace system keys : /// Byte #1 /// 0x10 -> Down /// 0x20 -> Up /// 0x40 -> Service /// 0x02 -> Enter /// Byte #2 /// 0x10 -> 2P Trigger Left /// 0x20 -> Start 1 /// 0x40 -> 1P Trigger Right /// 0x80 -> 1P Trigger Left /// 0x02 -> 2D/3D Switch /// 0x04 -> Start 2 /// 0x08 -> 2P Trigger Right /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P1_Start) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x20); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P2_Start) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x04); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_3D_Switch) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x02); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Down) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x10); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Up) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x20); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Service) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x40); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Enter) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x02); } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P1_Start) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xDF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P2_Start) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xFB); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_3D_Switch) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xFD); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Down) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xEF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Up) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xDF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Service) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xBF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Enter) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xFD); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.Lmp2D3D)); _Outputs.Add(new GameOutput(OutputId.LmpRoom)); // _Outputs.Add(new GameOutput(OutputId.P1_AirFront)); _Outputs.Add(new GameOutput(OutputId.P2_AirFront)); _Outputs.Add(new GameOutput(OutputId.P1_Fan)); _Outputs.Add(new GameOutput(OutputId.P2_Fan)); _Outputs.Add(new GameOutput(OutputId.VibrationSeat)); //Custom Outputs _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); /*_Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0));*/ _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //LEDs byte bLEDs = ReadByte((IntPtr)(_Outputs_Address + 1)); SetOutputValue(OutputId.P1_LmpStart, bLEDs >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bLEDs >> 6 & 0x01); SetOutputValue(OutputId.Lmp2D3D, bLEDs >> 5 & 0x01); SetOutputValue(OutputId.LmpRoom, ReadByte((IntPtr)(_Outputs_Address + 18)) & 0x0F); //Analog ? Goes from 0xF0 to 0xFF and back to F0 //Other byte bMech = ReadByte((IntPtr)_Outputs_Address); SetOutputValue(OutputId.P1_AirFront, bMech >> 3 & 0x01); SetOutputValue(OutputId.P2_AirFront, bMech >> 2 & 0x01); int FanValue = 0; if ((bMech >> 1 & 0x01) == 1) { //Analog ? value on the analog byte is 0xFF = OFF, 0xF8 = LOW, 0xF4 = HIGH FanValue = 0x0F - (ReadByte((IntPtr)(_Outputs_Address + 16)) & 0x0F); } SetOutputValue(OutputId.P1_Fan, FanValue); FanValue = 0; if ((bMech & 0x01) == 1) { //Analog ? value on the analog byte is 0xFF = OFF, 0xF8 = LOW, 0xF4 = HIGH int i = ReadByte((IntPtr)(_Outputs_Address + 19)) >> 4 & 0x0F; FanValue = 0x0F - i; //Analog ? 0xFF = OFF, 0x8F = LOW, 0x4F = HIGH } SetOutputValue(OutputId.P2_Fan, FanValue); //Seat vibration is coded on 2 Bits, and can have 4 Values (OFF/LOW/MEDIUM/HIGH) int SeatVbr = bLEDs >> 1 & 0x03; if (SeatVbr == 0) SetOutputValue(OutputId.VibrationSeat, 0); else if (SeatVbr == 1) SetOutputValue(OutputId.VibrationSeat, 2); else if (SeatVbr == 2) SetOutputValue(OutputId.VibrationSeat, 1); else if (SeatVbr == 3) SetOutputValue(OutputId.VibrationSeat, 3); //Rumble is using bit-changing values on a bi-stable state (not monostable) on the higher 4bits of the byte // [Bit3] [Bit2] [Bit1] [Bit0] //P1 rumble activation swap 1 from Bit3/Bit2 (i.e : 8 -> 7-> 8-> 7) //P2 rumble activation swap 1 from Bit1/Bit0 (i.e : 1 -> 0-> 1-> 0) int P1_Rumble = bMech & 0xC0; int P2_Rumble = bMech & 0x30; if (_P1_LastRumble != 0 && P1_Rumble != _P1_LastRumble && P1_Rumble != 0) { SetOutputValue(OutputId.P1_GunMotor, 1); SetOutputValue(OutputId.P1_CtmRecoil, 1); } else SetOutputValue(OutputId.P1_GunMotor, 0); if (_P2_LastRumble != 0 && P2_Rumble != _P2_LastRumble && P2_Rumble != 0) { SetOutputValue(OutputId.P2_GunMotor, 1); SetOutputValue(OutputId.P2_CtmRecoil, 1); } else SetOutputValue(OutputId.P2_GunMotor, 0); _P1_LastRumble = P1_Rumble; _P2_LastRumble = P2_Rumble; SetOutputValue(OutputId.Credits, ReadByte((IntPtr)_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_S357DeadStormPirates.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { public class Game_S357DeadStormPirates : Game { //MEMORY ADDRESSES /*private UInt64 _GameCode_Ptr_Offset = 0x03C37700; private UInt64 _GameCode_Address = 0; private byte[] _GameLoadedInstruction = new byte[] { 0x48, 0x83, 0xEC, 0x28 };*/ private UInt64 _P1_Axis_Address = 0x33434CCC4; private UInt64 _P2_Axis_Address = 0x3342F3744; private UInt64 _Buttons_Address = 0x301348BBC; //Outputs private UInt64 _Outputs_Address = 0x301348658; private UInt64 _Credits_Address = 0x301348B8B; private int _P1_LastRumble = 0; private int _P2_LastRumble = 0; /// /// Constructor /// public Game_S357DeadStormPirates(String RomName): base(RomName, "rpcs3-gun") { _KnownMd5Prints.Add("RPCS3 v0.0.27 fork for System 357, GUN version", "3321f7771ae74e8027f0d4e18167d635"); _KnownMd5Prints.Add("RPCS3 v0.0.37-2ef2310e Alpha (TeknoParrot)", "56B69E6A8D95FE0AC7BF7BB5D57321DC"); _tProcess.Start(); Logger.WriteLog("Waiting for RPCS3 DeadStorm Pirates " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Contains("Deadstorm Pirates Special Edition") || FindGameWindow_Contains("RPCS3 via TeknoParrot")) { Check_PatchedFiles_Ok(); CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } /*_GameCode_Address = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _GameCode_Ptr_Offset), new UInt64[] { 0x230, 0x00, 0x00, 0x30, 0x00 }); if (_GameCode_Address != 0) { Logger.WriteLog("EBOT.BIN PPU cache memory segment address = 0x" + _GameCode_Address.ToString("X16")); byte[] TestLoadedInstruction = ReadBytes((IntPtr)_GameCode_Address, 4); if (TestLoadedInstruction[0] == 0x48 && TestLoadedInstruction[1] == 0x83 && TestLoadedInstruction[2] == 0xEC && TestLoadedInstruction[3] == 0x28) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { string s = string.Empty; foreach (byte b in TestLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Read bytes at address 0x" + _GameCode_Address.ToString("X16") + " : " + s); s = string.Empty; foreach (byte b in _GameLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Expected : " + s); Logger.WriteLog("ROM not Loaded..."); } } else { Logger.WriteLog("ROM not Loaded..."); }*/ } } } catch (Exception) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region PPU cache check //Check if pre-patched PPU cache files are present in the cache folder //If not, the game won't be able to be control by DemulShooter private void Check_PatchedFiles_Ok() { String EmulatorBasePath = _TargetProcess.MainModule.FileName.Substring(0, _TargetProcess.MainModule.FileName.Length - 13); Logger.WriteLog("Emulator path = " + EmulatorBasePath); String PPU_CachePath = EmulatorBasePath + @"cache\SCEEXE000\ppu-obiMX8TqMzUsChXLV1Ln5TAJegSZ-EBOOT.BIN"; if (!Directory.Exists(PPU_CachePath)) { Logger.WriteLog("Error : PPU cache folder not found (" + PPU_CachePath + ")"); return; } string CPU_Type = string.Empty; foreach (String File in Directory.GetFiles(PPU_CachePath)) { String sFile = Path.GetFileName(File); if (sFile.StartsWith("v5-kusa-0v5qK7ZgNQz7zP6MmKCY5U-00001G")) { try { CPU_Type = sFile.Split(new string[] { ".obj.gz" }, StringSplitOptions.None)[0].Split(new string[] { "0001G-" }, StringSplitOptions.None)[1]; } catch { Logger.WriteLog("Error : Impossible to detect LLVM CPU type"); } if (CPU_Type != string.Empty) break; } } if (CPU_Type != string.Empty) { Logger.WriteLog("LLVM PPU processor is " + CPU_Type); CompareMd5Hash(PPU_CachePath + @"\v5-kusa-i7aj8qVvJVSC4AVE3TbxET-00001G-" + CPU_Type + ".obj.gz", "6220bb0b28f76847c34dc0988e8c8e82"); CompareMd5Hash(PPU_CachePath + @"\v5-kusa-RfXcExAKkmrdG4qseqx2b8-00001G-" + CPU_Type + ".obj.gz", "1e3f2024a1d48c11f02412882ed3e702"); } else { Logger.WriteLog("WARNING : PPU cache not found => DemulShooter may not work correctly"); } } private void CompareMd5Hash(String Filename, String AwaitedMd5) { if (!File.Exists(Filename)) { Logger.WriteLog("WARNING : PPU cache file not found : " + Filename + " => DemulShooter may not work correctly"); } else { using (var md5 = MD5.Create()) { using (var stream = File.OpenRead(Filename)) { var hash = md5.ComputeHash(stream); String StrHash = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); Logger.WriteLog(Filename + " MD5 Checksum = " + StrHash); if (StrHash != AwaitedMd5) Logger.WriteLog(@"/!\ MD5 Hash unknown, DemulShooter may not work correctly /!\"); else Logger.WriteLog("MD5 Check OK"); } } } } #endregion #region Screen /// /// Replacing original function using TargetProcess main window handle by this one, using a specific window handle /// /// /// public override bool ClientScale(PlayerSettings PlayerData) { //Convert Screen location to Client location if (_TargetProcess != null) { //Window size Rect TotalRes = new Rect(); Win32API.GetWindowRect(_GameWindowHandle, ref TotalRes); Logger.WriteLog("Window position (Px) = [ " + TotalRes.Left + ";" + TotalRes.Top + " ]"); PlayerData.RIController.Computed_X = PlayerData.RIController.Computed_X - TotalRes.Left; PlayerData.RIController.Computed_Y = PlayerData.RIController.Computed_Y - TotalRes.Top; Logger.WriteLog("Onclient window position (Px) = [ " + PlayerData.RIController.Computed_X + "x" + PlayerData.RIController.Computed_Y + " ]"); } return true; } /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 1280.0; double dMaxY = 720.0; PlayerData.RIController.Computed_X = Convert.ToInt32(dMaxX * PlayerData.RIController.Computed_X / TotalResX); PlayerData.RIController.Computed_Y = Convert.ToInt32(dMaxY * PlayerData.RIController.Computed_Y / TotalResY); //The game is blocking axis values so that the cursor is not close to the border, or goes to the UI at the bottom /*if (PlayerData.RIController.Computed_X < 64) PlayerData.RIController.Computed_X = 64; if (PlayerData.RIController.Computed_Y < 64) PlayerData.RIController.Computed_Y = 64; if (PlayerData.RIController.Computed_X > 1216) PlayerData.RIController.Computed_X = 1216; if (PlayerData.RIController.Computed_Y > 592) PlayerData.RIController.Computed_Y = 592;*/ if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { float fX = (float)PlayerData.RIController.Computed_X; float fY = (float)PlayerData.RIController.Computed_Y; byte[] bufferX = BitConverter.GetBytes(fX); byte[] bufferY = BitConverter.GetBytes(fY); Array.Reverse(bufferX); Array.Reverse(bufferY); if (PlayerData.ID == 1) { WriteBytes((IntPtr)(_P1_Axis_Address), bufferX); WriteBytes((IntPtr)(_P1_Axis_Address + 4), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xBF); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)(_P2_Axis_Address), bufferX); WriteBytes((IntPtr)(_P2_Axis_Address + 4), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x10); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x08); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xF7); } } } /// /// Low-level Keyboard hook callback. /// This is used to replace system keys : /// Byte #1 /// 0x10 -> Down /// 0x20 -> Up /// 0x40 -> Service /// 0x02 -> Enter /// Byte #2 /// 0x10 -> 2P Trigger Left /// 0x20 -> Start 1 /// 0x40 -> 1P Trigger Right /// 0x80 -> 1P Trigger Left /// 0x04 -> Start 2 /// 0x08 -> 2P Trigger Right /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P1_Start) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x20); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P2_Start) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x04); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Down) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x10); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Up) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x20); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Service) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x40); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Enter) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x02); } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P1_Start) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xDF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P2_Start) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xFB); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Down) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xEF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Up) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xDF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Service) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xBF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Enter) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xFD); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); /*_Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0));*/ _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Rumble is using bit-changing values on a bi-stable state (not monostable) on the higher 4bits of the byte // [Bit3] [Bit2] [Bit1] [Bit0] //P1 rumble activation swap 1 from Bit3/Bit2 (i.e : 8 -> 7-> 8-> 7) //P2 rumble activation swap 1 from Bit1/Bit0 (i.e : 1 -> 0-> 1-> 0) byte bRumble = ReadByte((IntPtr)_Outputs_Address); int P1_Rumble = bRumble & 0xC0; int P2_Rumble = bRumble & 0x30; if (_P1_LastRumble != 0 && P1_Rumble != _P1_LastRumble && P1_Rumble != 0) { SetOutputValue(OutputId.P1_GunMotor, 1); SetOutputValue(OutputId.P1_CtmRecoil, 1); } else SetOutputValue(OutputId.P1_GunMotor, 0); if (_P2_LastRumble != 0 && P2_Rumble != _P2_LastRumble && P2_Rumble != 0) { SetOutputValue(OutputId.P2_GunMotor, 1); SetOutputValue(OutputId.P2_CtmRecoil, 1); } else SetOutputValue(OutputId.P2_GunMotor, 0); _P1_LastRumble = P1_Rumble; _P2_LastRumble = P2_Rumble; byte bLEDs = ReadByte((IntPtr)(_Outputs_Address + 1)); SetOutputValue(OutputId.P1_LmpStart, bLEDs >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bLEDs >> 6 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_S357RazingStorm.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; namespace DemulShooterX64.Games { public class Game_S357RazingStorm : Game { //MEMORY ADDRESSES //Outputs private UInt64 _Outputs_Address = 0x300D043C8; private UInt64 _Credits_Address = 0x300D048F8; private int _P1_LastRumble = 0; private int _P2_LastRumble = 0; /// /// Constructor /// public Game_S357RazingStorm(String RomName) : base(RomName, "rpcs3") { _KnownMd5Prints.Add("RPCS3 v0.0.37-2ef2310e Alpha (TeknoParrot)", "56B69E6A8D95FE0AC7BF7BB5D57321DC"); _tProcess.Start(); Logger.WriteLog("Waiting for RPCS3 " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Contains("RPCS3 via TeknoParrot")) { CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } /*_GameCode_Address = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _GameCode_Ptr_Offset), new UInt64[] { 0x230, 0x00, 0x00, 0x30, 0x00 }); if (_GameCode_Address != 0) { Logger.WriteLog("EBOT.BIN PPU cache memory segment address = 0x" + _GameCode_Address.ToString("X16")); byte[] TestLoadedInstruction = ReadBytes((IntPtr)_GameCode_Address, 4); if (TestLoadedInstruction[0] == 0x48 && TestLoadedInstruction[1] == 0x83 && TestLoadedInstruction[2] == 0xEC && TestLoadedInstruction[3] == 0x28) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { string s = string.Empty; foreach (byte b in TestLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Read bytes at address 0x" + _GameCode_Address.ToString("X16") + " : " + s); s = string.Empty; foreach (byte b in _GameLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Expected : " + s); Logger.WriteLog("ROM not Loaded..."); } } else { Logger.WriteLog("ROM not Loaded..."); }*/ } } } catch (Exception) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P2_LmpGun)); _Outputs.Add(new GameOutput(OutputId.P1_LmpFront)); _Outputs.Add(new GameOutput(OutputId.P2_LmpFront)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { byte bOutput1 = ReadByte((IntPtr)_Outputs_Address); byte bOutput2 = ReadByte((IntPtr)_Outputs_Address + 1); //Rumble is using bit-changing values on a bi-stable state (not monostable) on the higher 4bits of the byte // [Bit3] [Bit2] [Bit1] [Bit0] //P1 Recoil activation swap 1 from Bit3/Bit2 (i.e : 8 -> 4-> 8-> 4) //P2 Recoil activation swap 1 from Bit1/Bit0 (i.e : 1 -> 2-> 1-> 2) int P1_Rumble = bOutput1 & 0xC0; int P2_Rumble = bOutput1 & 0x30; if (_P1_LastRumble != 0 && P1_Rumble != _P1_LastRumble && P1_Rumble != 0) { SetOutputValue(OutputId.P1_GunMotor, 1); SetOutputValue(OutputId.P1_CtmRecoil, 1); } else SetOutputValue(OutputId.P1_GunMotor, 0); if (_P2_LastRumble != 0 && P2_Rumble != _P2_LastRumble && P2_Rumble != 0) { SetOutputValue(OutputId.P2_GunMotor, 1); SetOutputValue(OutputId.P2_CtmRecoil, 1); } else SetOutputValue(OutputId.P2_GunMotor, 0); _P1_LastRumble = P1_Rumble; _P2_LastRumble = P2_Rumble; SetOutputValue(OutputId.P1_LmpStart, bOutput1 >> 3 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bOutput1 >> 2 & 0x01); SetOutputValue(OutputId.P1_LmpGun, bOutput2 >> 1 & 0x01); SetOutputValue(OutputId.P2_LmpGun, bOutput2 & 0x01); SetOutputValue(OutputId.P1_LmpFront, bOutput2 >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpFront, bOutput2 >> 6 & 0x01); SetOutputValue(OutputId.Credits, ReadByte((IntPtr)_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_S357SailorZombie.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.RawInput; using DsCore.Win32; namespace DemulShooterX64 { public class Game_S357SailorZombie : Game { //MEMORY ADDRESSES /*private UInt64 _GameCode_Ptr_Offset = 0x03C37700; private UInt64 _GameCode_Address = 0; private byte[] _GameLoadedInstruction = new byte[] { 0x48, 0x83, 0xEC, 0x28 };*/ private UInt64 _P1_Axis_Address = 0x336CA6E00; private UInt64 _P2_Axis_Address = 0x336CA6E28; private UInt64 _Buttons_Address = 0x300D91B1D; //Outputs private UInt64 _Outputs_Address = 0x300D92248; private UInt64 _Credits_Address = 0x300D91AEB; private int _P1_LastRumble = 0; private int _P2_LastRumble = 0; /// /// Constructor /// public Game_S357SailorZombie(String RomName) : base(RomName, "rpcs3-gun") { _KnownMd5Prints.Add("RPCS3 v0.0.27 fork for System 357, GUN version", "3321f7771ae74e8027f0d4e18167d635"); _KnownMd5Prints.Add("RPCS3 v0.0.37-2ef2310e Alpha (TeknoParrot)", "56B69E6A8D95FE0AC7BF7BB5D57321DC"); _tProcess.Start(); Logger.WriteLog("Waiting for RPCS3 Sailor Zombie " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Contains("Sailor zombie") || FindGameWindow_Contains("RPCS3 via TeknoParrot")) { Check_PatchedFiles_Ok(); CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } /*_GameCode_Address = ReadPtrChain((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _GameCode_Ptr_Offset), new UInt64[] { 0xB0, 0x00, 0x00, 0x30, 0x00 }); if (_GameCode_Address != 0) { Logger.WriteLog("EBOT.BIN PPU cache memory segment address = 0x" + _GameCode_Address.ToString("X16")); byte[] TestLoadedInstruction = ReadBytes((IntPtr)_GameCode_Address, 4); if (TestLoadedInstruction[0] == 0x48 && TestLoadedInstruction[1] == 0x83 && TestLoadedInstruction[2] == 0xEC && TestLoadedInstruction[3] == 0x28) { Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("MainWindowHandle = 0x" + _TargetProcess.MainWindowHandle.ToString("X16")); Logger.WriteLog("MainWindowTitle" + _TargetProcess.MainWindowTitle); CheckExeMd5(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { string s = string.Empty; foreach (byte b in TestLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Read bytes at address 0x" + _GameCode_Address.ToString("X16") + " : " + s); s = string.Empty; foreach (byte b in _GameLoadedInstruction) s += b.ToString("X2") + " "; Logger.WriteLog("Expected : " + s); Logger.WriteLog("ROM not Loaded..."); } } else { Logger.WriteLog("ROM not Loaded..."); }*/ } } } catch (Exception) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region PPU cache check //Check if pre-patched PPU cache files are present in the cache folder //If not, the game won't be able to be control by DemulShooter private void Check_PatchedFiles_Ok() { String EmulatorBasePath = _TargetProcess.MainModule.FileName.Substring(0, _TargetProcess.MainModule.FileName.Length - 13); Logger.WriteLog("Emulator path = " + EmulatorBasePath); String PPU_CachePath = EmulatorBasePath + @"cache\SCEEXE000\ppu-se1PtZVS5iF9A6M5Y1vu0wNqiASu-EBOOT.BIN"; if (!Directory.Exists(PPU_CachePath)) { Logger.WriteLog("Error : PPU cache folder not found (" + PPU_CachePath + ")"); return; } string CPU_Type = string.Empty; foreach (String File in Directory.GetFiles(PPU_CachePath)) { String sFile = Path.GetFileName(File); if (sFile.StartsWith("v5-kusa-1jdW1xvsTpXqZVxiPnPS3q-00001G")) { try { CPU_Type = sFile.Split(new string[] { ".obj.gz" }, StringSplitOptions.None)[0].Split(new string[] { "0001G-" }, StringSplitOptions.None)[1]; } catch { Logger.WriteLog("Error : Impossible to detect LLVM CPU type"); } if (CPU_Type != string.Empty) break; } } if (CPU_Type != string.Empty) { Logger.WriteLog("LLVM PPU processor is " + CPU_Type); CompareMd5Hash(PPU_CachePath + @"\v5-kusa-aQwdyxfu7HwmKZwJpsrgjQ-00001G-" + CPU_Type + ".obj.gz", "cbb6d336413f1d068e64d90eb800d429"); CompareMd5Hash(PPU_CachePath + @"\v5-kusa-rvYuWdKw9d618McpKm75ir-00001G-" + CPU_Type + ".obj.gz", "c13883a893110edb87029d1e61949555"); } else { Logger.WriteLog("WARNING : PPU cache not found => DemulShooter may not work correctly"); } } private void CompareMd5Hash(String Filename, String AwaitedMd5) { if (!File.Exists(Filename)) { Logger.WriteLog("WARNING : PPU cache file not found : " + Filename + " => DemulShooter may not work correctly"); } else { using (var md5 = MD5.Create()) { using (var stream = File.OpenRead(Filename)) { var hash = md5.ComputeHash(stream); String StrHash = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); Logger.WriteLog(Filename + " MD5 Checksum = " + StrHash); if (StrHash != AwaitedMd5) Logger.WriteLog(@"/!\ MD5 Hash unknown, DemulShooter may not work correctly /!\"); else Logger.WriteLog("MD5 Check OK"); } } } } #endregion #region Screen /// /// Replacing original function using TargetProcess main window handle by this one, using a specific window handle /// /// /// public override bool ClientScale(PlayerSettings PlayerData) { //Convert Screen location to Client location if (_TargetProcess != null) { //Window size Rect TotalRes = new Rect(); Win32API.GetWindowRect(_GameWindowHandle, ref TotalRes); Logger.WriteLog("Window position (Px) = [ " + TotalRes.Left + ";" + TotalRes.Top + " ]"); PlayerData.RIController.Computed_X = PlayerData.RIController.Computed_X - TotalRes.Left; PlayerData.RIController.Computed_Y = PlayerData.RIController.Computed_Y - TotalRes.Top; Logger.WriteLog("Onclient window position (Px) = [ " + PlayerData.RIController.Computed_X + "x" + PlayerData.RIController.Computed_Y + " ]"); } return true; } /// /// Convert client area pointer location to Game speciffic data for memory injection /// Windowed mode is not working (can't get window size) so full screen mode only is simpler.... /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); double dMaxX = 1920.0; double dMaxY = 1080.0; PlayerData.RIController.Computed_X = Convert.ToInt32(dMaxX * PlayerData.RIController.Computed_X / TotalResX); PlayerData.RIController.Computed_Y = Convert.ToInt32(dMaxY * PlayerData.RIController.Computed_Y / TotalResY); if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)dMaxX) PlayerData.RIController.Computed_X = (int)dMaxX; if (PlayerData.RIController.Computed_Y > (int)dMaxY) PlayerData.RIController.Computed_Y = (int)dMaxY; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Input /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack) { byte[] bufferX = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((UInt16)PlayerData.RIController.Computed_Y); Array.Reverse(bufferX); Array.Reverse(bufferY); if (PlayerData.ID == 1) { WriteBytes((IntPtr)(_P1_Axis_Address), bufferX); WriteBytes((IntPtr)(_P1_Axis_Address + 2), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x80); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0x7F); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xBF); } else if (PlayerData.ID == 2) { WriteBytes((IntPtr)(_P2_Axis_Address), bufferX); WriteBytes((IntPtr)(_P2_Axis_Address + 2), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x10); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xEF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x08); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xF7); } } } /// /// Low-level Keyboard hook callback. /// This is used to replace system keys : /// Byte #1 /// 0x10 -> Down /// 0x20 -> Up /// 0x40 -> Service /// 0x02 -> Enter /// Byte #2 /// 0x10 -> 2P Trigger Left /// 0x20 -> Start 1 /// 0x40 -> 1P Trigger Right /// 0x80 -> 1P Trigger Left /// 0x04 -> Start 2 /// 0x08 -> 2P Trigger Right /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (!_DisableInputHack) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P1_Start) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x20); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P2_Start) Apply_OR_ByteMask((IntPtr)(_Buttons_Address + 1), 0x04); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Down) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x10); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Up) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x20); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Service) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x40); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Enter) Apply_OR_ByteMask((IntPtr)_Buttons_Address, 0x02); } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P1_Start) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xDF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_P2_Start) Apply_AND_ByteMask((IntPtr)(_Buttons_Address + 1), 0xFB); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Down) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xEF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Up) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xDF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Service) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xBF); else if (s.scanCode == Configurator.GetInstance().DIK_Rpcs3_Enter) Apply_AND_ByteMask((IntPtr)_Buttons_Address, 0xFD); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P2_GunMotor)); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.LmpRoom)); // _Outputs.Add(new GameOutput(OutputId.P1_AirFront)); _Outputs.Add(new GameOutput(OutputId.P2_AirFront)); _Outputs.Add(new GameOutput(OutputId.VibrationSeat)); //Custom Outputs _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); /*_Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, MameOutputHelper.CustomDamageDelay, 100, 0));*/ _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //LEDs byte bLEDs = ReadByte((IntPtr)(_Outputs_Address + 1)); SetOutputValue(OutputId.P1_LmpStart, bLEDs >> 7 & 0x01); SetOutputValue(OutputId.P2_LmpStart, bLEDs >> 6 & 0x01); SetOutputValue(OutputId.LmpRoom, ReadByte((IntPtr)(_Outputs_Address + 18)) & 0x0F); //Analog ? Goes from 0xF0 to 0xFF and back to F0 //Other byte bMech = ReadByte((IntPtr)_Outputs_Address); SetOutputValue(OutputId.P1_AirFront, bMech >> 3 & 0x01); SetOutputValue(OutputId.P2_AirFront, bMech >> 2 & 0x01); //Seat vibration is coded on 2 Bits, and can have 4 Values (OFF/LOW/MEDIUM/HIGH) int SeatVbr = bLEDs >> 1 & 0x03; if (SeatVbr == 0) SetOutputValue(OutputId.VibrationSeat, 0); else if (SeatVbr == 1) SetOutputValue(OutputId.VibrationSeat, 2); else if (SeatVbr == 2) SetOutputValue(OutputId.VibrationSeat, 1); else if (SeatVbr == 3) SetOutputValue(OutputId.VibrationSeat, 3); //Rumble is using bit-changing values on a bi-stable state (not monostable) on the higher 4bits of the byte // [Bit3] [Bit2] [Bit1] [Bit0] //P1 rumble activation swap 1 from Bit3/Bit2 (i.e : 8 -> 7-> 8-> 7) //P2 rumble activation swap 1 from Bit1/Bit0 (i.e : 1 -> 0-> 1-> 0) int P1_Rumble = bMech & 0xC0; int P2_Rumble = bMech & 0x30; if (_P1_LastRumble != 0 && P1_Rumble != _P1_LastRumble && P1_Rumble != 0) { SetOutputValue(OutputId.P1_GunMotor, 1); SetOutputValue(OutputId.P1_CtmRecoil, 1); } else SetOutputValue(OutputId.P1_GunMotor, 0); if (_P2_LastRumble != 0 && P2_Rumble != _P2_LastRumble && P2_Rumble != 0) { SetOutputValue(OutputId.P2_GunMotor, 1); SetOutputValue(OutputId.P2_CtmRecoil, 1); } else SetOutputValue(OutputId.P2_GunMotor, 0); _P1_LastRumble = P1_Rumble; _P2_LastRumble = P2_Rumble; SetOutputValue(OutputId.Credits, ReadByte((IntPtr)_Credits_Address)); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_WndBhapc.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MemoryX64; using DsCore.Win32; namespace DemulShooterX64 { public class Game_WndBhapc : Game { /*** MEMORY ADDRESSES **/ private const UInt64 P1_X_OFFSET = 0x000000B8; private const UInt64 P1_Y_OFFSET = 0x000000BC; private const UInt64 P1_STRUCT_PTR_OFFSET = 0x0123A658; private const UInt64 P1_MOUSEDELTAX_PTR_OFFSET = 0x01285E70; private NopStruct _Nop_P1_Axis_1 = new NopStruct(0x00124814, 7); private NopStruct _Nop_P1_Axis_2 = new NopStruct(0x00124F7B, 7); private UInt64 _P1_StructAddress = 0; private UInt64 _P1_MouseDeltaX_Address = 0; //Custom data to inject private float _P1_X_Value; private float _P1_Y_Value; private float _P2_X_Value; private float _P2_Y_Value; //Even when changing coordinates, it's not working in-game as long as ONE of the native RAWINPUT axis delta is not modified //So we will modify one of them with some custom delta value (value itself doesn't matter, it's just to change it) private float _P1_LastX; private float _P1_DeltaX; /// /// Constructor /// public Game_WndBhapc(String RomName) : base(RomName, "Buck") { _KnownMd5Prints.Add("Big Buck Hunter Arcade v5.3.6 - PLAZA", "2a6f04726a2471adf68a27386898eabc"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); if (_TargetProcess_MemoryBaseAddress != null) { byte[] bBuffer = ReadBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + P1_STRUCT_PTR_OFFSET), 8); _P1_StructAddress = BitConverter.ToUInt64(bBuffer, 0); bBuffer = ReadBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + P1_MOUSEDELTAX_PTR_OFFSET), 8); _P1_MouseDeltaX_Address = BitConverter.ToUInt64(bBuffer, 0) + 8; if (_P1_StructAddress != 0 && _P1_MouseDeltaX_Address != 0) { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); Logger.WriteLog("P1_StructAddress = 0x" + _P1_StructAddress.ToString("X16")); Logger.WriteLog("P1_MouseDeltaXAddress = 0x" + _P1_MouseDeltaX_Address.ToString("X16")); CheckExeMd5(); Apply_MemoryHacks(); _ProcessHooked = true; } } } } catch (Exception ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); Logger.WriteLog(ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { //Window size Rect TotalRes = new Rect(); Win32API.GetClientRect(_GameWindowHandle, ref TotalRes); double TotalResX = TotalRes.Right - TotalRes.Left; double TotalResY = TotalRes.Bottom - TotalRes.Top; Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [0 -> ClientWidth] //Y => [ClientHeight -> 0] PlayerData.RIController.Computed_Y = (int)TotalResY - PlayerData.RIController.Computed_Y; if (PlayerData.RIController.Computed_X < 0) PlayerData.RIController.Computed_X = 0; if (PlayerData.RIController.Computed_Y < 0) PlayerData.RIController.Computed_Y = 0; if (PlayerData.RIController.Computed_X > (int)TotalResX) PlayerData.RIController.Computed_X = (int)TotalResX; if (PlayerData.RIController.Computed_Y > (int)TotalResY) PlayerData.RIController.Computed_Y = (int)TotalResY; if (PlayerData.ID == 1) { _P1_X_Value = (float)(PlayerData.RIController.Computed_X); _P1_Y_Value = (float)(PlayerData.RIController.Computed_Y); _P1_DeltaX = _P1_X_Value - _P1_LastX; _P1_LastX = _P1_X_Value; } else if (PlayerData.ID == 2) { _P2_X_Value = (float)(PlayerData.RIController.Computed_X); _P2_Y_Value = (float)(PlayerData.RIController.Computed_Y); } return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P1_Axis_1); SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P1_Axis_2); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } #endregion #region Inputs public override void SendInput(PlayerSettings PlayerData) { if (PlayerData.ID == 1) { //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(_P1_X_Value); WriteBytes((IntPtr)(_P1_StructAddress + P1_X_OFFSET), buffer); buffer = BitConverter.GetBytes(_P1_Y_Value); WriteBytes((IntPtr)(_P1_StructAddress + P1_Y_OFFSET), buffer); //Modifying native RAWINPUT mouse delta handling so that the game accepts our values buffer = BitConverter.GetBytes(_P1_DeltaX); WriteBytes((IntPtr)_P1_MouseDeltaX_Address, buffer); /* //Inputs if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_DOWN) { } else if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_UP) { } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_DOWN) { Win32.SendMessage(_TargetProcess.MainWindowHandle, 0x0204, IntPtr.Zero, IntPtr.Zero); } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_UP) { Win32.SendMessage(_TargetProcess.MainWindowHandle, 0x0205, IntPtr.Zero, IntPtr.Zero); } if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_DOWN) { } else if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_UP) { }*/ } else if (PlayerData.ID == 2) { } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_WndBigBuckHunterUltimate.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MameOutput; using DsCore.MemoryX64; using DsCore.Win32; using DsCore.RawInput; namespace DemulShooterX64 { public class Game_WndBigBuckHunterUltimate : Game { /*** MEMORY ADDRESSES **/ private UInt64 _Hud_SetReticleVisible_Function_Offset = 0x0060C320; private UInt64 _BaseFirearm_ToggleRendering_Function_Offset = 0x0067B890; private UInt64 _Cursor_SetVisible_Function_Offset = 0x0236CA10; private UInt64 _ControlManager_ValidateControler_Function_Offset = 0x0066F520; private UInt64 _Unity_Screen_GetWidth_FunctionPointer_Offset = 0x03241AC0; private UInt64 _Unity_Screen_GetHeight_FunctionPointer_Offset = 0x03241AC8; private UInt64 _ForceUseMouse_Offset = 0x006762BE; private UInt64 _ForceWeaponRenderingOff_Offset = 0x0067B9C9; private UInt64 _ForceWeaponDisplayZAxis_1_Offset = 0x0067A108; private UInt64 _ForceWeaponDisplayZAxis_2_Offset = 0x0067A112; private NopStruct _Nop_ValidateController = new NopStruct(0x00066F62D, 2); private NopStruct _Nop_PlayMuzzleFlash = new NopStruct(0x00067CD0C, 5); private InjectionStruct _Axis_InjectionStruct = new InjectionStruct(0x00676506, 5); private InjectionStruct _GetResolution_InjectionStruct = new InjectionStruct(0x006765BA, 14); private InjectionStruct _Buttons_InjectionStruct = new InjectionStruct(0x00678DB5, 15); private InjectionStruct _NoGuns_InjectionStruct = new InjectionStruct(0x0067A231, 14); private InjectionStruct _NoCrosshair_InjectionStruct = new InjectionStruct(0x0060C008, 7); private InjectionStruct _NoCursor_InjectionStruct = new InjectionStruct(0x0067407C, 7); private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x0067A859, 17); private UInt64 _Axis_TrampolineJmp_Offset = 0x006766E1; private UInt64 _NoCrosshair_TrampolineJmp_Offset = 0x0060DE60; private UInt64 _NoCursor_TrampolineJmp_Offset = 0x006740F2; //Custom Values private UInt64 _P1_Axis_CaveAddress = 0; private UInt64 _P2_Axis_CaveAddress = 0; private UInt64 _Buttons_CaveAddress = 0; private UInt64 _ScreenWidth_CaveAddress = 0; private UInt64 _ScreenHeight_CaveAddress = 0; //Outputs private UInt64 _Recoil_CaveAddress = 0; private IntPtr _GameAssemblyDll_BaseAddress = IntPtr.Zero; /// /// Constructor /// public Game_WndBigBuckHunterUltimate(String RomName) : base(RomName, "BBH") { _KnownMd5Prints.Add("Big Buck Hunter Ultimate Trophy - Original release", "344902fbe7cc36087212a38f329123f7"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { ProcessModuleCollection c = _TargetProcess.Modules; foreach (ProcessModule m in c) { if (m.ModuleName.ToLower().Equals("gameassembly.dll")) { _GameAssemblyDll_BaseAddress = m.BaseAddress; if (_GameAssemblyDll_BaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals("BigBuckHunter_UltimateTrophy")) { String AssemblyDllPath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", "GameAssembly.dll"); CheckMd5(AssemblyDllPath); Apply_MemoryHacks(); Apply_NoCursorHack(); if (_HideGuns) Apply_NoGunsMemoryHack(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Changing game resolution in options will not change the desktop res but the Unity "screen" size //This results in an offset with real aim if desktop res is different from the game res //For that, we will read the Unity screen res from our memory to rescale inputs as needed double UnityScreenWidth = 1920.0; double UnityScreenHeight = 1080.0; if (_ScreenWidth_CaveAddress != 0 && _ScreenHeight_CaveAddress != 0) { Int32 iWidth = BitConverter.ToInt32(ReadBytes((IntPtr)_ScreenWidth_CaveAddress, 4), 0); Int32 iHeight = BitConverter.ToInt32(ReadBytes((IntPtr)_ScreenHeight_CaveAddress, 4), 0); if (iWidth != 0 && iHeight != 0) { UnityScreenWidth = (double)iWidth; UnityScreenHeight = (double)iHeight; } } PlayerData.RIController.Computed_X = Convert.ToInt16(Math.Round(UnityScreenWidth * PlayerData.RIController.Computed_X / TotalResX)); PlayerData.RIController.Computed_Y = Convert.ToInt16(UnityScreenHeight - Math.Round(UnityScreenHeight * PlayerData.RIController.Computed_Y / TotalResY)); int X_Value = PlayerData.RIController.Computed_X; int Y_Value = (int)TotalResY - PlayerData.RIController.Computed_Y; if (X_Value < 0) X_Value = 0; if (Y_Value < 0) Y_Value = 0; if (X_Value > (int)UnityScreenWidth) X_Value = (int)UnityScreenWidth; if (Y_Value > (int)UnityScreenHeight) Y_Value = (int)UnityScreenHeight; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Axis_CaveAddress = _InputsDatabank_Address; _P2_Axis_CaveAddress = _InputsDatabank_Address + 0x08; _Buttons_CaveAddress = _InputsDatabank_Address + 0x10; _ScreenWidth_CaveAddress = _InputsDatabank_Address + 0x20; _ScreenHeight_CaveAddress = _InputsDatabank_Address + 0x28; SetHack_GetResolution(); SetHack_InGameAxis(); SetHack_Buttons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// When in Fullscreen mode, if resolution is changed in game option, the game keeps the desktop rez and changes Unity Screen width/Heigh /// Resulting in an offset in aiming when data is computed based on the desktop resolution (in fullscreen) /// Injecting a codecave at the end of BBH_character.PlayerAvatar.GetInputScreenPos() will allow us to call Screen.Width and Screen.Height to store it for Demulshooter /// private void SetHack_GetResolution() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov rax,[GameAssembly.dll+_Unity_Screen_GetWidth_FunctionPointer_Offset] CaveMemory.Write_StrBytes("48 A1"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt64)_GameAssemblyDll_BaseAddress + _Unity_Screen_GetWidth_FunctionPointer_Offset)); //call rax CaveMemory.Write_StrBytes("FF D0"); //mov [_ScreenWidth_CaveAddress], rax CaveMemory.Write_StrBytes("48 A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_ScreenWidth_CaveAddress)); //mov rax,[GameAssembly.dll+_Unity_Screen_GetHeight_FunctionPointer_Offset] CaveMemory.Write_StrBytes("48 A1"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt64)_GameAssemblyDll_BaseAddress + _Unity_Screen_GetHeight_FunctionPointer_Offset)); //call rax CaveMemory.Write_StrBytes("FF D0"); //mov [_ScreenHeight_CaveAddress], rax CaveMemory.Write_StrBytes("48 A3"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_ScreenHeight_CaveAddress)); //mov eax,2AAAAAAB CaveMemory.Write_StrBytes("B8 AB AA AA 2A"); //imul edi CaveMemory.Write_StrBytes("F7 EF"); //cvtdq2ps xmm0,xmm0 CaveMemory.Write_StrBytes("0F 5B C0"); //sar edx,1 CaveMemory.Write_StrBytes("D1 FA"); //mov eax,edx CaveMemory.Write_StrBytes("8B C2"); //Inject it CaveMemory.InjectToOffset(_GetResolution_InjectionStruct, "Resolution"); } private void SetHack_InGameAxis() { //ControllerManager.Validatecontroller() force return 1 even if call to Rewired.Player.get_JoystickCount is 0 //SetNops(_GameAssemblyDll_BaseAddress, _Nop_ValidateController); //Replace ControllerManager.Validatecontroller() function by return 1 WriteBytes((IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _ControlManager_ValidateControler_Function_Offset), new byte[] { 0x48, 0xB8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x90, 0x90 }); //Forcing BBH_character.PlayerAvatar.GetInputScreenPos() to use Mouse Cursor Position WriteBytes((IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _ForceUseMouse_Offset), new byte[] { 0xE9, 0xBE, 0x01, 0x00, 0x00, 0x90}); //At that point the BBH_character.PlayerAvatar.GetInputScreenPos() will get MousePosition and store it //as Vector2 in [RBX+48] / [RBX+4C] //Replacing values with our custom ones Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rcx CaveMemory.Write_StrBytes("51"); //mov rcx,[rbx+70] CaveMemory.Write_StrBytes("48 8B 4B 70"); //Getteing either PlayerID (rcx+24) or ControllerID(rcx+28) //Using PlayerID: both guns will need to be set in DemulShooter to play not-simultaneous multiplayer : game will pick P1 device for player 1 turn and P2 device for player 2 turn. //Using ControllerID: both guns will be needed for simultaneous multiplayer, but only P1 Device will be used at each player turn when they are separate. //Choosing solution #2 as it's more how the game should play //movzx rcx,byte ptr [rcx+28] CaveMemory.Write_StrBytes("48 0F B6 49 28"); //shl rcx,03 CaveMemory.Write_StrBytes("48 C1 E1 03"); //mov rax, _P1_Axis_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Axis_CaveAddress)); //add rax,rcx CaveMemory.Write_StrBytes("48 01 C8"); //mov rax,[rax] CaveMemory.Write_StrBytes("48 8B 00"); //mov [rbx+48],rax CaveMemory.Write_StrBytes("48 89 43 48"); //pop rcx CaveMemory.Write_StrBytes("59"); //Inject it CaveMemory.InjectToOffset_WithTrampoline(_Axis_InjectionStruct, _Axis_TrampolineJmp_Offset, "Axis"); } /// /// At the end of PlayerAvatar.GetButtonDown(), we replace the return value by our own /// private void SetHack_Buttons() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov rbx,[rdi+70] CaveMemory.Write_StrBytes("48 8B 5F 70"); //Getteing either PlayerID (rbx+24) or ControllerID(rbx+28) //Using PlayerID, both guns will need to be set in DemulShooter to play not-simultaneous multiplayer : game will pick P1 device for player 1 turn and P2 device for player 2 turn //Using ControllerID, both guns will be needed for simultaneous multiplayer, but only P1 Device will be used at each player turn when they are separate. //Choosing solution #2 as it's more how the game should play //movzx rbx,byte ptr [rbx+28] CaveMemory.Write_StrBytes("48 0F B6 5B 28"); //shl rbx,04 CaveMemory.Write_StrBytes("48 C1 E3 04"); //mov rax, _Buttons_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Buttons_CaveAddress)); //add rax,rbx CaveMemory.Write_StrBytes("48 01 D8"); //add rax,rbp CaveMemory.Write_StrBytes("48 01 E8"); //mov rbx,rax CaveMemory.Write_StrBytes("48 8B D8"); //xor rax,rax CaveMemory.Write_StrBytes("48 31 C0"); //mov al,[rbx] CaveMemory.Write_StrBytes("8A 03"); //mov byte ptr [rbx],00 CaveMemory.Write_StrBytes("C6 03 00"); //mov rbx,[rsp+40] CaveMemory.Write_StrBytes("48 8B 5C 24 40"); //mov rbp,[rsp+48] CaveMemory.Write_StrBytes("48 8B 6C 24 48"); //mov rsi,[rsp+50] CaveMemory.Write_StrBytes("48 8B 74 24 50"); //InjectIt CaveMemory.InjectToOffset(_Buttons_InjectionStruct, "Buttons"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _Recoil_CaveAddress = _OutputsDatabank_Address; SetHack_Recoil(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Intercepting recoil event in BaseFirearm.fire() procedure /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov r8d,[r8+28] CaveMemory.Write_StrBytes("45 8B 40 28"); //mov r9, _Recoil_CaveAddress CaveMemory.Write_StrBytes("49 B9"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_Recoil_CaveAddress)); //add r9,r8 CaveMemory.Write_StrBytes("4D 01 C1"); //mov byte ptr [r9],01 CaveMemory.Write_StrBytes("41 C6 01 01"); //xor r9d,r9d CaveMemory.Write_StrBytes("45 33 C9"); //xor edx,edx CaveMemory.Write_StrBytes("33 D2"); //mov rcx,rax CaveMemory.Write_StrBytes("48 8B C8"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// Inserting a call at the end of Hud.Update() procedure to force call Hud.SetReticleVisible(false) /// protected override void Apply_NoCrosshairMemoryHack() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rdx CaveMemory.Write_StrBytes("52"); //push r8 CaveMemory.Write_StrBytes("41 50"); //xor r8,r8 CaveMemory.Write_StrBytes("4D 31 C0"); //xor rdx,rdx CaveMemory.Write_StrBytes("48 31 D2"); //mov rcx,rbx CaveMemory.Write_StrBytes("48 8B CB"); /*//mov rax, GameAssembly.dll+60C320 CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes((UInt64)_GameAssemblyDll_BaseAddress + _Hud_SetReticleVisible_Function_Offset)); //call rax CaveMemory.Write_StrBytes("FF D0");*/ //Call Hud.SetReticleVisible() CaveMemory.Write_call_absolute((UInt64)_GameAssemblyDll_BaseAddress + _Hud_SetReticleVisible_Function_Offset); //pop r8 CaveMemory.Write_StrBytes("41 58"); //pop rdx CaveMemory.Write_StrBytes("5A"); //add rsp,00000090 CaveMemory.Write_StrBytes("48 81 C4 90 00 00 00"); //Inject it CaveMemory.InjectToOffset_WithTrampoline(_NoCrosshair_InjectionStruct, _NoCrosshair_TrampolineJmp_Offset, "No Cursor"); } private void Apply_NoGunsMemoryHack() { //Crossbow are not hidden with the following parts of the hack (calling Basefirearm.ToggleRendering()) //So adding a second hack, which is changing the z position of the 3d model so that it's not visible //(changing X or Y is having an impact on the aiming position, but z (toward or behind screen) is not // //Note that setting Z position to 0 is good enough for the crossbow not to be visible, but guns are longer and the tip can be seen //Changing for negative value will require more bytes and codecave, so we will just keep the next part following which was originally working for guns WriteBytes((IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _ForceWeaponDisplayZAxis_1_Offset), new byte[] { 0x31, 0xC0, 0x90 }); WriteBytes((IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _ForceWeaponDisplayZAxis_2_Offset), new byte[] { 0x31, 0xC0, 0x90 }); //First, noping the call to PlayMuzzleFlash() to remove muzzle flash effect SetNops(_GameAssemblyDll_BaseAddress, _Nop_PlayMuzzleFlash); //Then force Basefirearm.ToggleRendering() to always have FALSE as value WriteBytes((IntPtr)((UInt64)_GameAssemblyDll_BaseAddress + _ForceWeaponRenderingOff_Offset), new byte[] { 0x30, 0xD2, 0x90 }); //Last, injecting in Basefirearm.Update() procedure a call to the modified Basefirearm.ToggleRendering() Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov rcx,rbx CaveMemory.Write_StrBytes("48 8B CB"); //call BaseFirearm.ToggleRendering() CaveMemory.Write_call_absolute((UInt64)_GameAssemblyDll_BaseAddress + _BaseFirearm_ToggleRendering_Function_Offset); //movaps xmm6,[rsp+20] CaveMemory.Write_StrBytes("0F 28 74 24 20"); //mov rbx,[rsp+40] CaveMemory.Write_StrBytes("48 8B 5C 24 40"); //add rsp,30 CaveMemory.Write_StrBytes("48 83 C4 30"); //Inject it CaveMemory.InjectToOffset(_NoGuns_InjectionStruct, "No Guns"); } /// /// Adding a call to Cursor.SetVisible(False) in SteamManager.Update() procedure /// Choosing that procedure because it's started fast at start and always runs in game /// private void Apply_NoCursorHack() { Codecave CaveMemory = new Codecave(_TargetProcess, _GameAssemblyDll_BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //push rax CaveMemory.Write_StrBytes("50"); //xor rcx,rcx CaveMemory.Write_StrBytes("48 31 C9"); //call Cursor.SetVisible(False) CaveMemory.Write_call_absolute((UInt64)_GameAssemblyDll_BaseAddress + _Cursor_SetVisible_Function_Offset); //pop rax CaveMemory.Write_StrBytes("58"); //mov rcx,[rax+000000B8] CaveMemory.Write_StrBytes("48 8B 88 B8 00 00 00"); //Inject it CaveMemory.InjectToOffset_WithTrampoline(_NoCursor_InjectionStruct, _NoCursor_TrampolineJmp_Offset, "No Cursor"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { byte[] bufferX = BitConverter.GetBytes((float)PlayerData.RIController.Computed_X); byte[] bufferY = BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y); int PlayerAxisIndex = (PlayerData.ID - 1) * 8; int PlayerButtonsIndex = (PlayerData.ID - 1) * 0x10; WriteBytes((IntPtr)(_P1_Axis_CaveAddress + (UInt64)PlayerAxisIndex), bufferX); WriteBytes((IntPtr)(_P1_Axis_CaveAddress + (UInt64)PlayerAxisIndex + 4), bufferY); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { WriteByte((IntPtr)(_Buttons_CaveAddress + (UInt64)PlayerButtonsIndex + 0x02), 1); WriteByte((IntPtr)(_Buttons_CaveAddress + (UInt64)PlayerButtonsIndex + 0x04), 1); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) { WriteByte((IntPtr)(_Buttons_CaveAddress + (UInt64)PlayerButtonsIndex + 0x02), 0); WriteByte((IntPtr)(_Buttons_CaveAddress + (UInt64)PlayerButtonsIndex + 0x04), 0); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) { WriteByte((IntPtr)(_Buttons_CaveAddress + (UInt64)PlayerButtonsIndex + 0x0C), 1); } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) { WriteByte((IntPtr)(_Buttons_CaveAddress + (UInt64)PlayerButtonsIndex + 0x0C), 0); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (ReadByte((IntPtr)(_Recoil_CaveAddress)) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte((IntPtr)(_Recoil_CaveAddress), 0x00); } if (ReadByte((IntPtr)(_Recoil_CaveAddress + 1)) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte((IntPtr)(_Recoil_CaveAddress + 1), 0x00); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_WndBlueEstate.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.MemoryX64; using DsCore.Win32; using System.Runtime.InteropServices; using DsCore.RawInput; using System.Text; using DsCore.MameOutput; using System.Collections.Generic; namespace DemulShooterX64 { class Game_WndBlueEstate : Game { private const String GAMEDATA_FOLDER = @"MemoryData\windows\bestate"; //MEMORY ADDRESSES private InjectionStruct _XinputGetState_InjectionStruct = new InjectionStruct(0x0099FF7D, 24); private InjectionStruct _MouseButtons_InjectionStruct = new InjectionStruct(0x00995EC0, 15); private InjectionStruct _Recoil_InjectionStruct = new InjectionStruct(0x009DA951, 16); private InjectionStruct _Damaged_InjectionStruct = new InjectionStruct(0x009DD642, 16); private InjectionStruct _PlayerInfo_InjectionStruct = new InjectionStruct(0x009DD666, 14); private UInt64 _P1_X_Patch_Offset = 0x00A25EC7; private UInt64 _P1_Y_Patch_Offset = 0x00A25E98; private UInt64 _P2_Axis_Patch_Offset = 0x00A26297; private UInt64 _ScePadRead_Patch_Offset = 0x0099FFAB; private UInt64 _ForceP2InControllerSelectScreen_Offset = 0x00A29114; private UInt64 _BlockP1InControllerSelectScreen_Offset = 0x00A29171; private NopStruct _Nop_P2Axis_1 = new NopStruct(0x00A2624C, 6); private NopStruct _Nop_P2Axis_2 = new NopStruct(0x00A26284, 2); private NopStruct _Nop_P2Axis_3 = new NopStruct(0x00A262DC, 2); private NopStruct _Nop_P1BlockButton = new NopStruct(0x00A2921B, 5); private UInt64 _P1_Buttons_CaveAddress; private UInt64 _P1_X_CaveAddress; private UInt64 _P1_Y_CaveAddress; private UInt64 _P2_Buttons_CaveAddress; private UInt64 _P2_X_CaveAddress; private UInt64 _P2_Y_CaveAddress; private UInt64 _P1_Recoil_CaveAddress; private UInt64 _P2_Recoil_CaveAddress; private UInt64 _P1_Damaged_CaveAddress; private UInt64 _P2_Damaged_CaveAddress; private UInt64 _P1_Life_CaveAddress; private UInt64 _P2_Life_CaveAddress; private UInt64 _P1_Ammo_CaveAddress; private UInt64 _P2_Ammo_CaveAddress; /// /// Constructor /// public Game_WndBlueEstate(String RomName) : base(RomName, "BEGame") { _KnownMd5Prints.Add("Blue Estate - CODEX x64", "0eaf35ce7de0b8d41490abc86b588f66"); _KnownMd5Prints.Add("Blue Estate - STEAM x64", "5db6275cfd6d1294e3b08aec6bc46eb5"); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; Logger.WriteLog(_TargetProcess_MemoryBaseAddress.ToString("X16")); if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { /* Wait until Splash Screen is closed and real windows displayed */ /* Game Windows classname = "LaunchUnrealUWindowsClient" */ StringBuilder ClassName = new StringBuilder(256); int nRet = Win32API.GetClassName(_TargetProcess.MainWindowHandle, ClassName, ClassName.Capacity); if (nRet != 0 && ClassName.ToString() != "SplashScreenClass") { _GameWindowHandle = _TargetProcess.MainWindowHandle; Logger.WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); Logger.WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); ReadGameDataFromMd5Hash(GAMEDATA_FOLDER); Apply_MemoryHacks(); _ProcessHooked = true; RaiseGameHookedEvent(); } } } } catch (Exception Ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); Logger.WriteLog(Ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { Rect TotalRes = new Rect(); Win32API.GetClientRect(_TargetProcess.MainWindowHandle, ref TotalRes); int TotalResX = TotalRes.Right - TotalRes.Left; int TotalResY = TotalRes.Bottom - TotalRes.Top; Logger.WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //X => [-1 ; 1] float //Y => [-1 ; 1] float float X_Value = (2.0f * PlayerData.RIController.Computed_X / TotalResX) - 1.0f; float Y_Value = (2.0f * PlayerData.RIController.Computed_Y / TotalResY) - 1.0f; if (X_Value < -1.0f) X_Value = -1.0f; if (Y_Value < -1.0f) Y_Value = -1.0f; if (X_Value > 1.0f) X_Value = 1.0f; if (Y_Value > 1.0f) Y_Value = 1.0f; PlayerData.RIController.Computed_X = (int)(X_Value * 1000.0f); PlayerData.RIController.Computed_Y = (int)(Y_Value * 1000.0f); return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region Memory Hack protected override void Apply_InputsMemoryHack() { Create_InputsDataBank(); _P1_Buttons_CaveAddress = _InputsDatabank_Address; _P1_X_CaveAddress = _InputsDatabank_Address + 0x10; _P1_Y_CaveAddress = _InputsDatabank_Address + 0x14; _P2_Buttons_CaveAddress = _InputsDatabank_Address + 0x20; _P2_X_CaveAddress = _InputsDatabank_Address + 0x30; _P2_Y_CaveAddress = _InputsDatabank_Address + 0x34; SetHack_P1Axis(); SetHack_P2Axis(); //Block button disabling for P1 SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P1BlockButton); SetHack_XInputGetState(); //Jump over the calling of ScePad reading procedure if no Xinput is detected WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _ScePadRead_Patch_Offset), 0xEB); //block P2 coordinates changes SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P2Axis_1); SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P2Axis_2); SetNops(_TargetProcess_MemoryBaseAddress, _Nop_P2Axis_3); //Force use P2 controller to START P2 in controller select screen //nop + mov al,1 + nop WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _ForceP2InControllerSelectScreen_Offset), new byte[] { 0x90, 0x90, 0xB0, 0x01, 0x90, 0x90, 0x90, 0x90, 0x90 }); //Block all P1 inputs in controller select screen WriteByte((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _BlockP1InControllerSelectScreen_Offset), 0xEB); //Block MouseButton down/up events to remove unwanted inputs from lightgun //Use custom WM_APP message to send our inputs instead of mouse WM SetHack_MouseButtons(); Logger.WriteLog("Inputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// Replace Xmm0 value computed by the game, by our own [-1.0, +1.0] value /// private void SetHack_P1Axis() { //push rax //mov rax, [_P1_X_CaveAddress] //movss xmm0,[rax] //pop rax //nop //nop //nop //nop //nop //nop WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Y_Patch_Offset), new byte[] { 0x50, 0x48, 0xB8 }); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Y_Patch_Offset + 3), BitConverter.GetBytes(_P1_X_CaveAddress)); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_Y_Patch_Offset + 11), new byte[] { 0xF3, 0x0F, 0x10, 0x00, 0x58, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); //push rax //mov rax, [_P1_Y_CaveAddress] //movss xmm0,[rax] //pop rax //jmp BEGame.exe+A25F0D //nop //nop //nop WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_X_Patch_Offset), new byte[] { 0x50, 0x48, 0xB8 }); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_X_Patch_Offset + 3), BitConverter.GetBytes(_P1_Y_CaveAddress)); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P1_X_Patch_Offset + 11), new byte[] { 0xF3, 0x0F, 0x10, 0x00, 0x58, 0xEB, 0x34, 0x90, 0x90, 0x90}); } /// /// Load custom values intoi XInput buffer result and replace initial call by success return /// [rsp+60] has XINPUT STRUCTURE /// [rdx+04] has BUTTONS AND TRIGGERS /// [rdx+0C] has RIGHT PAD /// private void SetHack_XInputGetState() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //lea rdx,[rsp+60] CaveMemory.Write_StrBytes("48 8D 54 24 60"); //xor rax,rax CaveMemory.Write_StrBytes("48 31 C0"); //mov eax,[_P2_Buttons_CaveAddress] CaveMemory.Write_StrBytes("A1"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Buttons_CaveAddress)); //mov [rdx+04],eax CaveMemory.Write_StrBytes("89 42 04"); //mov [rdx+0C],00000000 CaveMemory.Write_StrBytes("C7 42 0C 00 00 00 00"); //xor eax,eax CaveMemory.Write_StrBytes("31 C0"); //mov cl,01 CaveMemory.Write_StrBytes("B1 01"); //Inject it CaveMemory.InjectToOffset(_XinputGetState_InjectionStruct, "XInput"); } /// /// Loading X or Y CaveAddress instead of original value, based on ECX /// private void SetHack_P2Axis() { //push rcx //shl rcx, 02 //mov rax, _P2_X_CaveAddress //add rax, rcx //pop rcx //movss xmm0, [rax] WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Axis_Patch_Offset), new byte[] { 0x50, 0x51, 0x48, 0xC1, 0xE1, 0x02, 0x48, 0xB8 }); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Axis_Patch_Offset + 8), BitConverter.GetBytes(_P2_X_CaveAddress)); WriteBytes((IntPtr)((UInt64)_TargetProcess_MemoryBaseAddress + _P2_Axis_Patch_Offset + 16), new byte[] { 0x48, 0x01, 0xC8, 0x59, 0xF3, 0x0F, 0x10, 0x00, 0x58, 0xEB, 0x25 }); } /// /// At the start of the wndProc procedure : /// Setting MSg to 0 if it's any of WM_LBUTTONDOWN/UP WM_RBUTTONDOWN/UP /// Using WM_APP+Index Msg for custom Inputs to replace original mouse Msg /// That way, any mouse lightgun would not be controlling buttons anymore /// private void SetHack_MouseButtons() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp edx,00008000 CaveMemory.Write_StrBytes("81 FA 00 80 00 00"); //jle Check_WmButton CaveMemory.Write_StrBytes("7E 08"); //sub edx,00007E00 CaveMemory.Write_StrBytes("81 EA 00 7E 00 00"); //jmp Exit CaveMemory.Write_StrBytes("EB 25"); //cmp edx,00000201 CaveMemory.Write_StrBytes("81 FA 01 02 00 00"); //je Block CaveMemory.Write_StrBytes("74 18"); //cmp edx,00000202 CaveMemory.Write_StrBytes("81 FA 02 02 00 00"); //je Block CaveMemory.Write_StrBytes("74 10"); //cmp edx,00000204 CaveMemory.Write_StrBytes("81 FA 04 02 00 00"); //je Block CaveMemory.Write_StrBytes("74 08"); //cmp edx,00000205 CaveMemory.Write_StrBytes("81 FA 05 02 00 00"); //jne Exit CaveMemory.Write_StrBytes("75 05"); //mov edx,00000000 CaveMemory.Write_StrBytes("BA 00 00 00 00"); //mov [rsp+10],rbx CaveMemory.Write_StrBytes("48 89 5C 24 10"); //mov [rsp+18],rbp CaveMemory.Write_StrBytes("48 89 6C 24 18"); //push rsi CaveMemory.Write_StrBytes("56"); //push r12 CaveMemory.Write_StrBytes("41 54"); //push r13 CaveMemory.Write_StrBytes("41 55"); //Inject it CaveMemory.InjectToOffset(_MouseButtons_InjectionStruct, "Mouse Buttons"); } protected override void Apply_OutputsMemoryHack() { Create_OutputsDataBank(); _P1_Recoil_CaveAddress = _OutputsDatabank_Address; _P2_Recoil_CaveAddress = _OutputsDatabank_Address + 0x04; _P1_Damaged_CaveAddress = _OutputsDatabank_Address + 0x08; _P2_Damaged_CaveAddress = _OutputsDatabank_Address + 0x0C; _P1_Life_CaveAddress = _OutputsDatabank_Address + 0x10; _P1_Ammo_CaveAddress = _OutputsDatabank_Address + 0x14; _P2_Life_CaveAddress = _OutputsDatabank_Address + 0x18; _P2_Ammo_CaveAddress = _OutputsDatabank_Address + 0x1C; SetHack_Recoil(); SetHack_Damage(); SetHack_PlayerInfo(); Logger.WriteLog("Outputs Memory Hack complete !"); Logger.WriteLog("-"); } /// /// In ABEWeaponexecHasAmmo() called before a shot : /// RCX looks like a "Weapon Struct", and Ammo value is available in [RCX+384] /// RCX is changing when the gun is changing /// /// Trying to get info if it's for a player (also used for ennemies) and if it's P1 or P2 /// /// Method1 (by Ducon2016) : /// [RAX] should be 0x1B8 for players check /// Then [RCX+0x120] can be check between 0 (P1) and 0.4f(P2) /// /// Method2: /// [RCX+0xC8] is pointing to what look like to be a "player" owner struct /// [RCX+0xC8] + 0x04C => Owner ID (1 or 2) /// [RCX+0xC8] + 0x374 => Owner Life /// To differentiate Onwer type (player / Ennemy) we can check either : /// - [RCX+0xC8] + 0x7C => 0x16 / 0x2F /// - [RCX+0xC8] + 0xC0 => 00 01 03 05 / 01 01 03 07 /// - [RCX+0xC8] + 0xE0 => 00 00 00 00 00 00 00 00 / 01 00 00 00 04 00 00 00 /// private void SetHack_Recoil() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //mov rax,[rdi] CaveMemory.Write_StrBytes("48 8B 07"); //mov r8d,[rsp+48] CaveMemory.Write_StrBytes("44 8B 44 24 48"); //movzx edx,byte ptr [rsp+40] CaveMemory.Write_StrBytes("0F B6 54 24 40"); //mov rcx,rdi CaveMemory.Write_StrBytes("48 8B CF"); //cmp rcx,00 CaveMemory.Write_StrBytes("48 83 F9 00"); //je Exit CaveMemory.Write_StrBytes("74 2E"); //mov rcx,[rcx+000000C8] CaveMemory.Write_StrBytes("48 8B 89 C8 00 00 00"); //cmp rcx,00 CaveMemory.Write_StrBytes("48 83 F9 00"); //je Exit CaveMemory.Write_StrBytes("74 09"); //cmp byte ptr [rcx+7C],2F CaveMemory.Write_StrBytes("80 79 7C 2F"); //jne Exit CaveMemory.Write_StrBytes("75 28"); //cmp dword ptr [rcx+4C],01 CaveMemory.Write_StrBytes("83 79 4C 01"); //jne Player2 CaveMemory.Write_StrBytes("75 0C"); //mov rax,_P1_Recoil_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Recoil_CaveAddress)); //jmp SetFlag CaveMemory.Write_StrBytes("EB 10"); //Player2: //cmp dword ptr [rcx+4C],02 CaveMemory.Write_StrBytes("83 79 4C 02"); //jne Exit CaveMemory.Write_StrBytes("75 10"); //mov rax,_P2_Recoil_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Recoil_CaveAddress)); //SetFlag: //mov byte ptr [rax],01 CaveMemory.Write_StrBytes("C6 00 01"); //mov rax,[rdi] CaveMemory.Write_StrBytes("48 8B 07"); //mov rcx,rdi CaveMemory.Write_StrBytes("48 8B CF"); //Inject it CaveMemory.InjectToOffset(_Recoil_InjectionStruct, "Recoil"); } /// /// In ABEPlayerPawnexecPlayerPadHitEffect() called before a shot : /// RDI has the value of RCX at the start of the function, same "Player" owner structure as previously used for recoil /// [RDI+0x04C] => Player ID (1 or 2) /// [RDI+0x374] => Player Life /// private void SetHack_Damage() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //cmp rdi,00 CaveMemory.Write_StrBytes("48 83 FF 00"); //je Exit CaveMemory.Write_StrBytes("74 22"); //cmp dword ptr [rdi+4C],02 CaveMemory.Write_StrBytes("83 7F 4C 02"); //je Player2 CaveMemory.Write_StrBytes("74 0C"); //mov rax,_P1_Damage_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Damaged_CaveAddress)); //jmp SetFlag CaveMemory.Write_StrBytes("EB 0A"); //Player2: //mov rax,_P2_Damage_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Damaged_CaveAddress)); //Setflag: //mov byte ptr [rax],01 CaveMemory.Write_StrBytes("C6 00 01"); //mov rax,[rdi] CaveMemory.Write_StrBytes("48 8B 07"); //Inject it CaveMemory.InjectToOffset(_Damaged_InjectionStruct, "Damaged"); } /// /// ABEPlayerPawnexecPlayerPadUpdate() is called in a loop /// In it, we can acces the "Player" owner struct in RCX and we can access : /// [RCX+0x04C] => Player ID (1 or 2) /// [RCX+0x07C] => Player playing ? (0x2F) - Not playing is 0x16 /// [RCX+0x374] => Player Life /// [RCX+0x494] => Player Weapon Struct pointer /// +[RCX+0x494]+0x384 => Weapon Ammo /// private void SetHack_PlayerInfo() { Codecave CaveMemory = new Codecave(_TargetProcess, _TargetProcess_MemoryBaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); //sub rsp,20 CaveMemory.Write_StrBytes("48 83 EC 20"); //xorps xmm0,xmm0 CaveMemory.Write_StrBytes("0F 57 C0"); //mov rdi,rcx CaveMemory.Write_StrBytes("48 8B F9"); //cmp dword ptr [rdi+4C],01 CaveMemory.Write_StrBytes("83 7F 4C 01"); //jne Player2 CaveMemory.Write_StrBytes("75 0C"); //mov rax,_P1_Life_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P1_Life_CaveAddress)); //jmp Next CaveMemory.Write_StrBytes("EB 10"); //cmp dword ptr [rdi+4C],02 CaveMemory.Write_StrBytes("83 7F 4C 02"); //jne Exit CaveMemory.Write_StrBytes("75 4D"); //mov rax,_P2_Life_CaveAddress CaveMemory.Write_StrBytes("48 B8"); CaveMemory.Write_Bytes(BitConverter.GetBytes(_P2_Life_CaveAddress)); //Next: //cmp byte ptr [rdi+7C],2F CaveMemory.Write_StrBytes("80 7F 7C 2F"); //je PlayerPlaying CaveMemory.Write_StrBytes("74 0F"); //mov [rax],00000000 CaveMemory.Write_StrBytes("C7 00 00 00 00 00"); //mov [rax+04],00000000 CaveMemory.Write_StrBytes("C7 40 04 00 00 00 00"); //jmp Exit CaveMemory.Write_StrBytes("EB 2E"); //Playing: //xor rcx,rcx CaveMemory.Write_StrBytes("48 31 C9"); //mov ecx,[rdi+00000374] CaveMemory.Write_StrBytes("8B 8F 74 03 00 00"); //mov [rax],ecx CaveMemory.Write_StrBytes("89 08"); //cmp dword ptr [rdi+00000494],00 CaveMemory.Write_StrBytes("83 BF 94 04 00 00 00"); //jne HasWeapon CaveMemory.Write_StrBytes("75 09"); //mov [rax+04],00000000 CaveMemory.Write_StrBytes("C7 40 04 00 00 00 00"); //jmp Exit CaveMemory.Write_StrBytes("EB 11"); //HasWeapon: //mov rcx,[rdi+00000494] CaveMemory.Write_StrBytes("48 8B 8F 94 04 00 00"); //mov rcx,[rcx+00000384] CaveMemory.Write_StrBytes("48 8B 89 84 03 00 00"); //mov [rax+4],ecx CaveMemory.Write_StrBytes("89 48 04"); //Exit: //mov rax,[rdx+24] CaveMemory.Write_StrBytes("48 8B 42 24"); //Inject it CaveMemory.InjectToOffset(_PlayerInfo_InjectionStruct, "PlayerInfo"); } #endregion #region Inputs public override void SendInput(PlayerSettings PlayerData) { float fX = (float)PlayerData.RIController.Computed_X / 1000.0f; float fY = (float)PlayerData.RIController.Computed_Y / 1000.0f; if (PlayerData.ID == 1) { WriteBytes((IntPtr)((UInt64)_P1_X_CaveAddress), BitConverter.GetBytes(fX)); WriteBytes((IntPtr)((UInt64)_P1_Y_CaveAddress), BitConverter.GetBytes(fY)); //Sending WM_APP+Index to simulate mouse click //As the message will be translated to WM_xBUTTONDOWN/UP, Wparam must be set to according pressed button too if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x001, new IntPtr(1), new IntPtr(0)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x002, new IntPtr(0), new IntPtr(0)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x004, new IntPtr(2), new IntPtr(0)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Win32API.PostMessage(_GameWindowHandle, Win32Define.WM_APP | 0x005, new IntPtr(0), new IntPtr(0)); } if (PlayerData.ID == 2) { WriteBytes((IntPtr)((UInt64)_P2_X_CaveAddress), BitConverter.GetBytes(fX)); WriteBytes((IntPtr)((UInt64)_P2_Y_CaveAddress), BitConverter.GetBytes(fY)); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 3), 0xFF); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 3), 0x00); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) { } if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0x40); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0xBF); } } /// /// Writing Axis and Buttons data in memory /// public override IntPtr KeyboardHookCallback(IntPtr KeyboardHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (s.scanCode == HardwareScanCode.DIK_Z) { Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0x10); } if (s.scanCode == HardwareScanCode.DIK_X) { Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0x40); } if (s.scanCode == HardwareScanCode.DIK_C) { Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0x80); } if (s.scanCode == HardwareScanCode.DIK_SPACE) { Apply_OR_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 3), 0xFF); } if (s.scanCode == HardwareScanCode.DIK_0) { Win32API.PostMessage(_GameWindowHandle, 0x8001, new IntPtr(1), new IntPtr(0)); } if (s.scanCode == HardwareScanCode.DIK_9) { Win32API.PostMessage(_GameWindowHandle, 0x201, new IntPtr(1), new IntPtr(0)); } } else if ((UInt32)wParam == Win32Define.WM_KEYUP) { if (s.scanCode == HardwareScanCode.DIK_Z) { Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0xEF); } if (s.scanCode == HardwareScanCode.DIK_X) { Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0xBF); } if (s.scanCode == HardwareScanCode.DIK_C) { Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 2), 0x7F); } if (s.scanCode == HardwareScanCode.DIK_SPACE) { Apply_AND_ByteMask((IntPtr)(_P2_Buttons_CaveAddress + 3), 0x00); } if (s.scanCode == HardwareScanCode.DIK_0) { Win32API.PostMessage(_GameWindowHandle, 0x8002, new IntPtr(0), new IntPtr(0)); } if (s.scanCode == HardwareScanCode.DIK_9) { Win32API.PostMessage(_GameWindowHandle, 0x202, new IntPtr(0), new IntPtr(0)); } } } return Win32API.CallNextHookEx(KeyboardHookID, nCode, wParam, lParam); } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { if (ReadByte((IntPtr)(_P1_Recoil_CaveAddress)) == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); WriteByte((IntPtr)(_P1_Recoil_CaveAddress), 0x00); } if (ReadByte((IntPtr)(_P2_Recoil_CaveAddress)) == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); WriteByte((IntPtr)(_P2_Recoil_CaveAddress), 0x00); } if (ReadByte((IntPtr)(_P1_Damaged_CaveAddress)) == 1) { SetOutputValue(OutputId.P1_Damaged, 1); WriteByte((IntPtr)(_P1_Damaged_CaveAddress), 0x00); } if (ReadByte((IntPtr)(_P2_Damaged_CaveAddress)) == 1) { SetOutputValue(OutputId.P2_Damaged, 1); WriteByte((IntPtr)(_P2_Damaged_CaveAddress), 0x00); } SetOutputValue(OutputId.P1_Life, BitConverter.ToInt32(ReadBytes((IntPtr)_P1_Life_CaveAddress, 4), 0)); SetOutputValue(OutputId.P2_Life, BitConverter.ToInt32(ReadBytes((IntPtr)_P2_Life_CaveAddress, 4), 0)); SetOutputValue(OutputId.P1_Ammo, BitConverter.ToInt32(ReadBytes((IntPtr)_P1_Ammo_CaveAddress, 4), 0)); SetOutputValue(OutputId.P2_Ammo, BitConverter.ToInt32(ReadBytes((IntPtr)_P2_Ammo_CaveAddress, 4), 0)); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_WndDcop.cs ================================================ using System; using System.Collections.Generic; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; namespace DemulShooterX64.Games { internal class Game_WndDcop : Game__Unity { private class InputData : Base_InputData { public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte GunRecoil = 0; public byte DirectHit = 0; public byte Police_LightBar = 0; public byte GreenTestLight = 0; public byte RedLight = 0; public byte WhiteStrobe = 0; public byte GunLight = 0; public byte P1_Life = 0; public byte P1_Ammo = 0; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 1; /// /// Constructor /// public Game_WndDcop(String RomName) : base(RomName, "DCOP", "DCOP") { _KnownMd5Prints.Add("DCOP - Tenoke ISO - Original", "3940b478b0069635b579c8bd2a6729c1"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_GunRecoil)); _Outputs.Add(new GameOutput(OutputId.Lmp_DirectHit)); _Outputs.Add(new GameOutput(OutputId.Lmp_PoliceBar)); _Outputs.Add(new GameOutput(OutputId.Lmp_GreenTestLight)); _Outputs.Add(new GameOutput(OutputId.Lmp_RedLight)); _Outputs.Add(new GameOutput(OutputId.Lmp_WhiteStrobe)); _Outputs.Add(new GameOutput(OutputId.P1_LmpGun)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_GunRecoil, ((OutputData)_OutputData).GunRecoil); SetOutputValue(OutputId.Lmp_DirectHit, ((OutputData)_OutputData).DirectHit); SetOutputValue(OutputId.Lmp_PoliceBar, ((OutputData)_OutputData).Police_LightBar); SetOutputValue(OutputId.Lmp_GreenTestLight, ((OutputData)_OutputData).GreenTestLight); SetOutputValue(OutputId.Lmp_RedLight, ((OutputData)_OutputData).RedLight); SetOutputValue(OutputId.Lmp_WhiteStrobe, ((OutputData)_OutputData).WhiteStrobe); SetOutputValue(OutputId.P1_LmpGun, ((OutputData)_OutputData).GunLight); SetOutputValue(OutputId.P1_Ammo, ((OutputData)_OutputData).P1_Ammo); SetOutputValue(OutputId.P1_Life, ((OutputData)_OutputData).P1_Life); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).GunRecoil); if (_P1_LastLife > ((OutputData)_OutputData).P1_Life) SetOutputValue(OutputId.P1_Damaged, 1); _P1_LastLife = ((OutputData)_OutputData).P1_Life; } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_WndHotdremakeArcade.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64 { public class Game_WndHotdremakeArcade : Game { private const string GAME_WINDOW_TITLE = "The House of the Dead Remake"; private bool _HackEnabled = false; private MMFH_HotdRemakeArcade _Mmfh; /// /// Constructor /// public Game_WndHotdremakeArcade(String RomName) : base(RomName, "The House of the Dead Remake") { _KnownMd5Prints.Add("House of the dead Remake - GOG v1.0.0.4", "4cd32190b44d7c2d6b8ca52d0d372692"); //exe = D5FCA0B03AE64D1D0E75B795BD6CCA9F _KnownMd5Prints.Add("House of the dead Remake - GOG v1.03", "5e7afcfc9ae4aa8396be7007268bfe12"); _KnownMd5Prints.Add("House of the dead Remake - GOG v1.1.3", "21307253063e17c64b4f4030aa7e5d60"); _KnownMd5Prints.Add("House of the dead Remake - STEAM (v1.0)", "cdf5f802dcfb9262fe4851b6292"); _KnownMd5Prints.Add("House of the dead Remake - STEAM (v1.1.3)", "8377939dd51b947d76146f114fe9c61d"); _tProcess.Start(); Logger.WriteLog("Waiting for Windows game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals(GAME_WINDOW_TITLE)) { String AssemblyDllPath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", @"The House of the Dead Remake_Data\Managed\Assembly-CSharp.dll"); CheckMd5(AssemblyDllPath); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); SetHack(); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } catch (Exception Ex) { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe : " + Ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } #region Screen /// /// Convert client area pointer location to Game speciffic data for memory injection /// this one is easy as the value is simply the pixel corresponding point on the window /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Just need to invert Y axis (0 is bottom) PlayerData.RIController.Computed_Y = (int)TotalResY - PlayerData.RIController.Computed_Y; return true; } catch (Exception Ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + Ex.Message.ToString()); } } return false; } #endregion #region Memory Hack /// /// Creating a custom memory bank to store our data /// private void SetHack() { //Creating Shared Memory access _Mmfh = new MMFH_HotdRemakeArcade(); int r = _Mmfh.MMFOpen(); if (r == 0) { _HackEnabled = true; } else Logger.WriteLog("SetHack() => Error opening Mapped Memory File"); } #endregion #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (_HackEnabled && !_DisableInputHack ) { Array.Copy(BitConverter.GetBytes((float)PlayerData.RIController.Computed_X), 0, _Mmfh.Payload, (int)MMFH_HotdRemakeArcade.Payload_Inputs_Index.P1_AxisX + (PlayerData.ID - 1) * 8, 4); Array.Copy(BitConverter.GetBytes((float)PlayerData.RIController.Computed_Y), 0, _Mmfh.Payload, (int)MMFH_HotdRemakeArcade.Payload_Inputs_Index.P1_AxisY + (PlayerData.ID - 1) * 8, 4); if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Inputs_Index.P1_Trigger + (PlayerData.ID - 1)] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Inputs_Index.P1_Trigger + (PlayerData.ID - 1)] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Inputs_Index.P1_Reload + (PlayerData.ID - 1)] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Inputs_Index.P1_Reload + (PlayerData.ID - 1)] = 0; int r = _Mmfh.WriteInputs(); if (r != 0) Logger.WriteLog("SendInput() => Error writing Mapped Memory Inputs : " + r.ToString()); } } /// /// Mouse callback for low level hook /// This is used to block LeftClick events on the window, because double clicking on the upper-left corner /// makes demul switch from Fullscreen to Windowed mode /// /*public override IntPtr MouseHookCallback(IntPtr MouseHookID, int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && (UInt32)wParam == Win32Define.WM_LBUTTONDOWN) { if (_ProcessHooked && Win32API.GetForegroundWindow() == _TargetProcess.MainWindowHandle) { //Just blocking left clicks return new IntPtr(1); } } return Win32API.CallNextHookEx(MouseHookID, nCode, wParam, lParam); }*/ #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { //Gun motor : Is activated for every bullet fired AND when player gets _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P2_LmpStart)); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new GameOutput(OutputId.P1_Clip)); _Outputs.Add(new GameOutput(OutputId.P2_Clip)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new GameOutput(OutputId.Credits)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { int r = _Mmfh.ReadAll(); if (r == 0) { //Start Lamps SetOutputValue(OutputId.P1_LmpStart, _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P1_StartLmp]); SetOutputValue(OutputId.P2_LmpStart, _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P2_StartLmp]); //P1 Life SetOutputValue(OutputId.P1_Life, _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P1_Life]); //P1_Ammo int P1_Ammo = _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P1_Ammo]; SetOutputValue(OutputId.P1_Ammo, P1_Ammo); //[Clip Empty] custom Output if (P1_Ammo > 0) SetOutputValue(OutputId.P1_Clip, 1); else SetOutputValue(OutputId.P1_Clip, 0); //P1_Recoil if (_Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P1_Recoil] == 1) { SetOutputValue(OutputId.P1_CtmRecoil, 1); _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P1_Recoil] = 0; } //P1_Damaged if (_Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P1_Damaged] == 1) { SetOutputValue(OutputId.P1_Damaged, 1); _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P1_Damaged] = 0; } //P2 Life SetOutputValue(OutputId.P2_Life, _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P2_Life]); //P1_Ammo int P2_Ammo = _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P2_Ammo]; SetOutputValue(OutputId.P2_Ammo, P2_Ammo); //[Clip Empty] custom Output if (P2_Ammo > 0) SetOutputValue(OutputId.P2_Clip, 1); else SetOutputValue(OutputId.P2_Clip, 0); //P1_Recoil if (_Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P2_Recoil] == 1) { SetOutputValue(OutputId.P2_CtmRecoil, 1); _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P2_Recoil] = 0; } //P1_Damaged if (_Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P2_Damaged] == 1) { SetOutputValue(OutputId.P2_Damaged, 1); _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.P2_Damaged] = 0; } //Credits SetOutputValue(OutputId.Credits, _Mmfh.Payload[(int)MMFH_HotdRemakeArcade.Payload_Outputs_Index.Credits]); int r2 = _Mmfh.Writeall(); if (r2 != 0) Logger.WriteLog("UpdateOutputValues() => Error writing Mapped Memory File : " + r.ToString()); } else Logger.WriteLog("UpdateOutputValues() => Error reading Mapped Memory File : " + r.ToString()); } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game_WndOpWolfReturn.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DsCore; using DsCore.Config; using DsCore.IPC; using DsCore.MameOutput; using DsCore.RawInput; namespace DemulShooterX64.Games { internal class Game_WndOpWolfReturn : Game__Unity { private class InputData : Base_InputData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Reload = null; public byte[] ChangeWeapon = null; public InputData(int PlayerNumber) : base(PlayerNumber) { } } private class OutputData : Base_OutputData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public byte[] Life = null; public int[] Ammo = null; public OutputData(int PlayerNumber) : base(PlayerNumber) { } } private const int MAX_PLAYERS = 2; /// /// Constructor /// public Game_WndOpWolfReturn(String RomName) : base(RomName, "OperationWolf", "Operation Wolf Returns: First Mission") { _KnownMd5Prints.Add("Operation Wolf Returns - COG", "6c32e74cda2fd1953245158382cf188a"); _InputData = new InputData(MAX_PLAYERS); _OutputData = new OutputData(MAX_PLAYERS); _tProcess.Start(); Logger.WriteLog("Waiting for " + _RomName + " game to hook....."); } #region Inputs /// /// Writing Axis and Buttons data in memory /// public override void SendInput(PlayerSettings PlayerData) { if (!_DisableInputHack && PlayerData.ID <= MAX_PLAYERS) { float AxisX = PlayerData.RIController.Computed_X; float AxisY = PlayerData.RIController.Computed_Y; ((InputData)_InputData).Axis_X[PlayerData.ID - 1] = AxisX; ((InputData)_InputData).Axis_Y[PlayerData.ID - 1] = AxisY; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerUp) != 0) ((InputData)_InputData).Trigger[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionDown) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.ActionUp) != 0) ((InputData)_InputData).ChangeWeapon[PlayerData.ID - 1] = 0; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerDown) != 0) ((InputData)_InputData).Reload[PlayerData.ID - 1] = 1; if ((PlayerData.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OffScreenTriggerUp) != 0) ((InputData)_InputData).Reload[PlayerData.ID - 1] = 0; if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; _Tcpclient.SendMessage(_InputData.ToByteArray()); } } #endregion #region Outputs /// /// Create the Output list that we will be looking for and forward to MameHooker /// protected override void CreateOutputList() { _Outputs = new List(); _Outputs.Add(new GameOutput(OutputId.P1_Ammo)); _Outputs.Add(new GameOutput(OutputId.P2_Ammo)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_CtmRecoil, Configurator.GetInstance().OutputCustomRecoilOnDelay, Configurator.GetInstance().OutputCustomRecoilOffDelay, 0)); _Outputs.Add(new GameOutput(OutputId.P1_Life)); _Outputs.Add(new GameOutput(OutputId.P2_Life)); _Outputs.Add(new AsyncGameOutput(OutputId.P1_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); _Outputs.Add(new AsyncGameOutput(OutputId.P2_Damaged, Configurator.GetInstance().OutputCustomDamagedDelay, 100, 0)); } /// /// Update all Outputs values before sending them to MameHooker /// public override void UpdateOutputValues() { //Nothing to do here, update will be done by the Tcp packet received event } protected override void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { if (e.Packet.GetHeader() == DsTcp_TcpPacket.PacketHeader.Outputs) { _OutputData.Update(e.Packet.GetPayload()); SetOutputValue(OutputId.P1_Ammo, ((OutputData)_OutputData).Ammo[0]); SetOutputValue(OutputId.P2_Ammo, ((OutputData)_OutputData).Ammo[1]); SetOutputValue(OutputId.P1_Life, ((OutputData)_OutputData).Life[0]); SetOutputValue(OutputId.P2_Life, ((OutputData)_OutputData).Life[1]); SetOutputValue(OutputId.P1_CtmRecoil, ((OutputData)_OutputData).Recoil[0]); SetOutputValue(OutputId.P2_CtmRecoil, ((OutputData)_OutputData).Recoil[1]); SetOutputValue(OutputId.P1_Damaged, ((OutputData)_OutputData).Damaged[0]); SetOutputValue(OutputId.P2_Damaged, ((OutputData)_OutputData).Damaged[1]); } } #endregion } } ================================================ FILE: DemulShooterX64/Games/Game__Unity.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.IPC; namespace DemulShooterX64.Games { public class Game__Unity : Game { protected class Base_InputData : DsTcpData { public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public Base_InputData(int PlayerNumber) : base(PlayerNumber) { } } protected class Base_OutputData : DsTcpData { public Base_OutputData(int PlayerNumber) : base(PlayerNumber) { } } protected string _MainWindowTitle = string.Empty; protected DsTcp_Client _Tcpclient; protected Base_OutputData _OutputData; protected Base_InputData _InputData; /// /// Constructor /// public Game__Unity(String RomName, String TargetProcessName, String MainWindowTitle) : base(RomName, TargetProcessName) { _MainWindowTitle = MainWindowTitle; } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// protected override void tProcess_Elapsed(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; //Looking for the game's window based on it's Title _GameWindowHandle = IntPtr.Zero; if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) { // The game may start with other Windows than the main one (BepInEx console, other stuff.....) so we need to filter // the displayed window according to the Title, if DemulShooter is started before the game, to hook the correct one if (FindGameWindow_Equals(_MainWindowTitle)) { String AssemblyDllPath = _TargetProcess.MainModule.FileName.Replace(_Target_Process_Name + ".exe", _Target_Process_Name + @"_Data\Managed\Assembly-CSharp.dll"); CheckMd5(AssemblyDllPath); //Start TcpClient to dial with Unity Game _Tcpclient = new DsTcp_Client("127.0.0.1", DsTcp_Client.DS_TCP_CLIENT_PORT); _Tcpclient.PacketReceived += DsTcp_Client_PacketReceived; _Tcpclient.TcpConnected += DsTcp_client_TcpConnected; _Tcpclient.Connect(); if (_DisableInputHack) Logger.WriteLog("Input Hack disabled"); _ProcessHooked = true; RaiseGameHookedEvent(); } else { Logger.WriteLog("Game Window not found"); return; } } } } catch { Logger.WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; Logger.WriteLog(_Target_Process_Name + ".exe closed"); Application.Exit(); } } } ~Game__Unity() { if (_Tcpclient != null) _Tcpclient.Disconnect(); } #region Screen /// /// Default GameScale functio, just sending coordinate in a similar way Unity Input.MousePosition() would work /// /// /// public override bool GameScale(PlayerSettings PlayerData) { if (_ProcessHandle != IntPtr.Zero) { try { double TotalResX = _ClientRect.Right - _ClientRect.Left; double TotalResY = _ClientRect.Bottom - _ClientRect.Top; Logger.WriteLog("Game Window Rect (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); //Coordinates are Window size range, but inverted Y int X_Value = PlayerData.RIController.Computed_X; int Y_Value = (int)TotalResY - PlayerData.RIController.Computed_Y; if (X_Value < 0) X_Value = 0; if (Y_Value < 0) Y_Value = 0; if (X_Value > (int)TotalResX) X_Value = (int)TotalResX; if (Y_Value > (int)TotalResY) Y_Value = (int)TotalResY; PlayerData.RIController.Computed_X = X_Value; PlayerData.RIController.Computed_Y = Y_Value; return true; } catch (Exception ex) { Logger.WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion /// /// Sending a TCP message on connection /// protected virtual void DsTcp_client_TcpConnected(Object Sender, EventArgs e) { if (_HideCrosshair) _InputData.HideCrosshairs = 1; else _InputData.HideCrosshairs = 0; if (_DisableInputHack) _InputData.EnableInputsHack = 0; else _InputData.EnableInputsHack = 1; _Tcpclient.SendMessage(_InputData.ToByteArray()); } protected virtual void DsTcp_Client_PacketReceived(Object Sender, DsTcp_Client.PacketReceivedEventArgs e) { } } } ================================================ FILE: DemulShooterX64/Program.cs ================================================ using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Globalization; using System.Collections.Generic; namespace DemulShooterX64 { class Program { [DllImport("kernel32.dll")] static extern bool AttachConsole(int dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] static extern bool FreeConsole(); [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); const uint WM_CHAR = 0x0102; const int VK_ENTER = 0x0D; const int ATTACH_PARENT_PROCESS = -1; static IntPtr ConsoleHwnd = IntPtr.Zero; static String strTarget = string.Empty; static String strRom = string.Empty; static void Main(string[] args) { // Attach to the parent process via AttachConsole SDK call AttachConsole(ATTACH_PARENT_PROCESS); ConsoleHwnd = GetConsoleWindow(); bool isVerbose = false; bool isTrace = false; Dictionary _SystemTargets = new Dictionary(){ {"alls","SEGA Amusement Linkage Live System games"}, {"arcadepc","Various modern PC Arcade Games without dedicated system"}, {"es3","Namco ES3 games"}, {"flycast","Flycast v2.0"}, {"rpcs3", "RPCS3 (System 357)"}, {"rawthrill","Raw Thrill Arcade"}, {"seganu","SEGA Nu games"}, {"windows","Windows games"} }; Dictionary _AllsRoms = new Dictionary(){ {"hodsd","House of the Dead : Scarlet Down"} }; Dictionary _ArcadepcRoms = new Dictionary(){ {"drk", "Drakon Realm Keepers"}, {"eai","Elevator Action invasion"}, {"marss", "Mars Sortie"}, {"mib", "Men In Black"}, {"misimp", "Mission Impossible Arcade"}, {"nha","Night Hunter"}, {"racramp", "Raccoon Rampage Arcade"}, {"rha", "Rabbids Hollywood Arcade"}, {"tra", "Tomb Raider Arcade"} }; Dictionary _Es3Roms = new Dictionary(){ {"tc5","Time Crisis 5"} }; Dictionary _FlycastRoms = new Dictionary(){ //{"braveff","Brave Fire Fighters"}, {"claychal","SEGA Clay Challenge"}, {"confmiss","Confidential Mission"}, {"deathcox","Death Crimson OX (USA)"}, {"deathcoxo","Death Crimson OX (Japan)"}, {"hotd2","House of the Dead II (USA)"}, {"hotd2o","House of the Dead II"}, {"hotd2p","House of the Dead II (Prototype)"}, {"lupinsho","Lupin the Third"}, //{"manicpnc","Manic Panic Ghosts"}, {"mok","The Maze of the Kings"}, {"ninjaslt","Ninja Assault (World)"}, {"ninjaslta","Ninja Assault (Asia)"}, {"ninjasltj","Ninja Assault (Japan)"}, {"ninjasltu","Ninja Assault (US)"}, //{"pokasuka","Pokasuka Ghost"}, {"rangrmsn","Ranger Mission"}, {"sprtshot","Sports Shooting USA"}, {"xtrmhunt","Extreme Hunting"}, {"xtrmhnt2","Extreme Hunting 2"} }; Dictionary _RawThrillRoms = new Dictionary(){ {"nerfa","Nerf Arcade"} }; Dictionary _RPCS3_System357Roms = new Dictionary(){ {"deadstorm","Dead Storm Pirates"}, {"de4d","Dark Escape 4D"}, {"razstorm","Razing Storm"}, {"sailorz","Sailor Zombies"} }; Dictionary _SegaNuRoms = new Dictionary(){ {"lma","Luigi's Mansion Arcade"} }; Dictionary _WindowsRoms = new Dictionary(){ //{"bha","Buck Hunt Arcade PC"} {"bbhut", "Big Bick Hunter Ultimate Trophy"}, {"dcop", "DCOP"}, {"hotdra","House of the Dead Remake - Arcade Mod"}, {"opwolfr", "Operation Wolf Returns First Mission"} }; if (args.Length > 0) { for (int i = 0; i < args.Length; i++) { if (args[i].ToLower().Equals("-h") || args[i].ToLower().Equals("--help") || args[i].ToLower().Equals("-?")) { Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("DemulShooterX64 v" + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString()); DateTime CompileTime = new DateTime(Builtin.CompileTime, DateTimeKind.Utc); String CompileDate = CompileTime.ToString("MMMM d", new CultureInfo("en-US")); CompileDate = String.Format("{0}{1}, {2}", CompileDate, GetDaySuffix(CompileTime.Day), CompileTime.ToString("yyyy")); Console.WriteLine("Build date : " + CompileDate); Console.WriteLine(""); Console.WriteLine("usage : DemulShooterX64.exe -target=[target] -rom=[rom] [options]"); Console.WriteLine(""); Console.WriteLine("Supported [target] :"); DisplayDictionnaryList(_SystemTargets); Console.WriteLine(""); Console.WriteLine("Supported [rom] :"); Console.WriteLine("Arcade PRC Roms :"); DisplayDictionnaryList(_ArcadepcRoms); Console.WriteLine(""); Console.WriteLine("ALLS roms :"); DisplayDictionnaryList(_AllsRoms); Console.WriteLine(""); Console.WriteLine("ES3 roms :"); DisplayDictionnaryList(_Es3Roms); Console.WriteLine(""); Console.WriteLine("Flycast roms :"); DisplayDictionnaryList(_FlycastRoms); Console.WriteLine(""); Console.WriteLine("RPCS3 (System 357) roms :"); DisplayDictionnaryList(_RPCS3_System357Roms); Console.WriteLine(""); Console.WriteLine("Raw Thrill roms :"); DisplayDictionnaryList(_RawThrillRoms); Console.WriteLine(""); Console.WriteLine("SEGA Nu roms :"); DisplayDictionnaryList(_SegaNuRoms); Console.WriteLine(""); Console.WriteLine("Windows games :"); DisplayDictionnaryList(_WindowsRoms); Console.WriteLine(""); Console.WriteLine("Supported [options] :"); Console.WriteLine(" -nocrosshair \t\tHide in-game crosshair (game dependant)"); Console.WriteLine(" -noinput \t\tDisable any input hack"); Console.WriteLine(" -nogun \t\tHide in-game weapon model (game dependant)"); Console.WriteLine(" -pname=[ProcessName]\tchange the name of the expected executable instead of expecting specific name like game.exe"); Console.WriteLine(" -profile=[ConfigFile]\tspecify a config file name for DemulShooterX64 to load"); Console.WriteLine(" -? -h --help\t\tShow this help"); Console.WriteLine(" -v --verbose\t\tEnable output to log file"); ExitConsole(); } else if (args[i].ToLower().Equals("-v") || args[i].ToLower().Equals("--verbose")) { isVerbose = true; } else if (args[i].ToLower().Equals("-t") || args[i].ToLower().Equals("--trace")) { isTrace = true; } else if (args[i].ToLower().StartsWith("-target")) { strTarget = (args[i].ToLower().Split('='))[1].Trim(); if (!CheckParameter(strTarget, _SystemTargets) && !strTarget.Equals("wip")) { Console.WriteLine("\nUnsupported [target] parameter : \"" + strTarget + "\". See help for supported targets"); ExitConsole(); } } } if (strTarget == String.Empty) { Console.WriteLine("\n\n\tNo [target] parameter specified. See help for supported targets"); ExitConsole(); } for (int i = 0; i < args.Length; i++) { if (args[i].ToLower().StartsWith("-rom")) { strRom = (args[i].ToLower().Split('='))[1].Trim(); if (strTarget.StartsWith("arcadepc")) { if (!CheckParameter(strRom, _ArcadepcRoms)) { Console.WriteLine("\n\n\tUnsupported Arcade PC rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("alls")) { if (!CheckParameter(strRom, _AllsRoms)) { Console.WriteLine("Unsupported SEGA ALLS rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("es3")) { if (!CheckParameter(strRom, _Es3Roms)) { Console.WriteLine("Unsupported Namco ES3 rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("flycast")) { if (!CheckParameter(strRom, _FlycastRoms)) { Console.WriteLine("Unsupported Flycast rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("rpcs3")) { if (!CheckParameter(strRom, _RPCS3_System357Roms)) { Console.WriteLine("Unsupported RPCS3 rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("rawthrill")) { if (!CheckParameter(strRom, _RawThrillRoms)) { Console.WriteLine("Unsupported Raw Thrill rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("seganu")) { if (!CheckParameter(strRom, _SegaNuRoms)) { Console.WriteLine("Unsupported Saga Nu rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } else if (strTarget.Equals("windows")) { if (!CheckParameter(strRom, _WindowsRoms)) { Console.WriteLine("Unsupported Windows rom parameter : \"" + strRom + "\". See help for supported roms list"); ExitConsole(); } } } } if (strRom == String.Empty) { Console.WriteLine("\n\n\tNo [Rom] parameter specified. See help for supported targets"); ExitConsole(); } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new DemulShooterWindowX64(args, isVerbose, isTrace)); } else { Console.WriteLine("\n\n\tMissing parameter!\n\tSee help with DemulShooterX64.exe -h for correct usage."); ExitConsole(); } } static string GetDaySuffix(int day) { switch (day) { case 1: case 21: case 31: return "st"; case 2: case 22: return "nd"; case 3: case 23: return "rd"; default: return "th"; } } static void DisplayDictionnaryList(Dictionary list) { foreach (KeyValuePair item in list) { Console.Write(" " + item.Key); for (int t = 0; t < (11 - item.Key.Length); t++) Console.Write(" "); Console.WriteLine(item.Value); } } static bool CheckParameter(String param, Dictionary list) { foreach (KeyValuePair Item in list) { if (param.Equals(Item.Key)) return true; } return false; } static void ExitConsole() { FreeConsole(); //Send a {ENTER} key message to the attached console to show prompt SendMessage(ConsoleHwnd, WM_CHAR, (IntPtr)VK_ENTER, IntPtr.Zero); Environment.Exit(0); } } } ================================================ FILE: DemulShooterX64/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("DemulShooterX64")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DemulShooterX64")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("c9aae810-63ce-4227-8f59-ff60f4e4d2d2")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.5.0")] ================================================ FILE: DemulShooterX64/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. // //------------------------------------------------------------------------------ namespace DemulShooterX64.Properties { using System; /// /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. /// // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder // à l'aide d'un outil, tel que ResGen ou Visual Studio. // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen // avec l'option /str ou régénérez votre projet VS. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DemulShooterX64.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Remplace la propriété CurrentUICulture du thread actuel pour toutes /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Recherche une ressource localisée de type System.Drawing.Icon semblable à (Icône). /// internal static System.Drawing.Icon DemulShooter_Hooked_Icon { get { object obj = ResourceManager.GetObject("DemulShooter_Hooked_Icon", resourceCulture); return ((System.Drawing.Icon)(obj)); } } /// /// Recherche une ressource localisée de type System.Drawing.Icon semblable à (Icône). /// internal static System.Drawing.Icon DemulShooter_Icon { get { object obj = ResourceManager.GetObject("DemulShooter_Icon", resourceCulture); return ((System.Drawing.Icon)(obj)); } } /// /// Recherche une ressource localisée de type System.Drawing.Icon semblable à (Icône). /// internal static System.Drawing.Icon DemulShooter_UnHooked_Icon { get { object obj = ResourceManager.GetObject("DemulShooter_UnHooked_Icon", resourceCulture); return ((System.Drawing.Icon)(obj)); } } } } ================================================ FILE: DemulShooterX64/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\DemulShooter_Hooked_Icon.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\DemulShooter_Icon.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\DemulShooter_UnHooked_Icon.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ================================================ FILE: DemulShooterX64/app.config ================================================ ================================================ FILE: DemulShooter_GUI/DemulShooter_GUI.csproj ================================================  Debug x86 8.0.30703 2.0 {20DE55D0-833D-45CA-96C8-DF3BEDB394A8} WinExe Properties DemulShooter_GUI DemulShooter_GUI v4.8 512 x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 false x86 pdbonly true bin\Release\ TRACE prompt 4 false DemulShooter_Icon.ico true bin\Debug\ DEBUG;TRACE full AnyCPU prompt false bin\Release\ TRACE true pdbonly AnyCPU prompt false false false false true bin\x64\Debug\ DEBUG;TRACE full x64 prompt false false false bin\x64\Release\ TRACE true pdbonly x64 prompt false false false UserControl GUI_AnalogCalibration.cs UserControl GUI_Button.cs UserControl GUI_RawInputHID.cs UserControl GUI_RawInputMouse.cs UserControl GUI_Player.cs True True Resources.resx Form Wnd_DemulShooterGui.cs GUI_AnalogCalibration.cs GUI_Button.cs GUI_Player.cs GUI_RawInputHID.cs GUI_RawInputMouse.cs Wnd_DemulShooterGui.cs ResXFileCodeGenerator Designer Resources.Designer.cs SettingsSingleFileGenerator Settings.Designer.cs True Settings.settings True {BD88CC4D-2944-43F8-85C3-A274A45487AF} DsCore ================================================ FILE: DemulShooter_GUI/GUI_AnalogCalibration.Designer.cs ================================================ namespace DemulShooter_GUI { partial class GUI_AnalogCalibration { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur de composants /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { this.Cbox_Player = new System.Windows.Forms.CheckBox(); this.Gbox_P1_Calib = new System.Windows.Forms.GroupBox(); this.Btn_Init_Calib = new System.Windows.Forms.Button(); this.Btn_Stop_Calib = new System.Windows.Forms.Button(); this.Btn_Start_Calib = new System.Windows.Forms.Button(); this.label28 = new System.Windows.Forms.Label(); this.Txt_Calib_Ymax = new System.Windows.Forms.TextBox(); this.label29 = new System.Windows.Forms.Label(); this.Txt_Calib_Ymin = new System.Windows.Forms.TextBox(); this.label32 = new System.Windows.Forms.Label(); this.Txt_Calib_Xmax = new System.Windows.Forms.TextBox(); this.label36 = new System.Windows.Forms.Label(); this.Txt_Calib_Xmin = new System.Windows.Forms.TextBox(); this.Gbox_P1_Calib.SuspendLayout(); this.SuspendLayout(); // // Cbox_Player // this.Cbox_Player.AutoSize = true; this.Cbox_Player.Location = new System.Drawing.Point(17, 0); this.Cbox_Player.Margin = new System.Windows.Forms.Padding(4); this.Cbox_Player.Name = "Cbox_Player"; this.Cbox_Player.Size = new System.Drawing.Size(126, 20); this.Cbox_Player.TabIndex = 0; this.Cbox_Player.Text = "Override P1 Axis"; this.Cbox_Player.UseVisualStyleBackColor = true; this.Cbox_Player.CheckedChanged += new System.EventHandler(this.Cbox_Player_CheckedChanged); // // Gbox_P1_Calib // this.Gbox_P1_Calib.Controls.Add(this.Btn_Init_Calib); this.Gbox_P1_Calib.Controls.Add(this.Btn_Stop_Calib); this.Gbox_P1_Calib.Controls.Add(this.Btn_Start_Calib); this.Gbox_P1_Calib.Controls.Add(this.label28); this.Gbox_P1_Calib.Controls.Add(this.Txt_Calib_Ymax); this.Gbox_P1_Calib.Controls.Add(this.label29); this.Gbox_P1_Calib.Controls.Add(this.Txt_Calib_Ymin); this.Gbox_P1_Calib.Controls.Add(this.label32); this.Gbox_P1_Calib.Controls.Add(this.Txt_Calib_Xmax); this.Gbox_P1_Calib.Controls.Add(this.label36); this.Gbox_P1_Calib.Controls.Add(this.Txt_Calib_Xmin); this.Gbox_P1_Calib.Controls.Add(this.Cbox_Player); this.Gbox_P1_Calib.Location = new System.Drawing.Point(4, 4); this.Gbox_P1_Calib.Margin = new System.Windows.Forms.Padding(4); this.Gbox_P1_Calib.Name = "Gbox_P1_Calib"; this.Gbox_P1_Calib.Padding = new System.Windows.Forms.Padding(4); this.Gbox_P1_Calib.Size = new System.Drawing.Size(266, 120); this.Gbox_P1_Calib.TabIndex = 56; this.Gbox_P1_Calib.TabStop = false; this.Gbox_P1_Calib.Text = " "; // // Btn_Init_Calib // this.Btn_Init_Calib.Enabled = false; this.Btn_Init_Calib.Location = new System.Drawing.Point(8, 87); this.Btn_Init_Calib.Margin = new System.Windows.Forms.Padding(4); this.Btn_Init_Calib.Name = "Btn_Init_Calib"; this.Btn_Init_Calib.Size = new System.Drawing.Size(74, 25); this.Btn_Init_Calib.TabIndex = 66; this.Btn_Init_Calib.Text = "Default"; this.Btn_Init_Calib.UseVisualStyleBackColor = true; this.Btn_Init_Calib.Click += new System.EventHandler(this.Btn_Init_Calib_Click); // // Btn_Stop_Calib // this.Btn_Stop_Calib.Enabled = false; this.Btn_Stop_Calib.Location = new System.Drawing.Point(185, 88); this.Btn_Stop_Calib.Margin = new System.Windows.Forms.Padding(4); this.Btn_Stop_Calib.Name = "Btn_Stop_Calib"; this.Btn_Stop_Calib.Size = new System.Drawing.Size(74, 25); this.Btn_Stop_Calib.TabIndex = 65; this.Btn_Stop_Calib.Text = "Stop"; this.Btn_Stop_Calib.UseVisualStyleBackColor = true; this.Btn_Stop_Calib.Click += new System.EventHandler(this.Btn_Stop_Calib_Click); // // Btn_Start_Calib // this.Btn_Start_Calib.Enabled = false; this.Btn_Start_Calib.Location = new System.Drawing.Point(103, 88); this.Btn_Start_Calib.Margin = new System.Windows.Forms.Padding(4); this.Btn_Start_Calib.Name = "Btn_Start_Calib"; this.Btn_Start_Calib.Size = new System.Drawing.Size(74, 25); this.Btn_Start_Calib.TabIndex = 64; this.Btn_Start_Calib.Text = "Start"; this.Btn_Start_Calib.UseVisualStyleBackColor = true; this.Btn_Start_Calib.Click += new System.EventHandler(this.Btn_Start_Calib_Click); // // label28 // this.label28.AutoSize = true; this.label28.Location = new System.Drawing.Point(151, 52); this.label28.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label28.Name = "label28"; this.label28.Size = new System.Drawing.Size(51, 16); this.label28.TabIndex = 63; this.label28.Text = "Y Max :"; // // Txt_Calib_Ymax // this.Txt_Calib_Ymax.Enabled = false; this.Txt_Calib_Ymax.Location = new System.Drawing.Point(205, 49); this.Txt_Calib_Ymax.Margin = new System.Windows.Forms.Padding(4); this.Txt_Calib_Ymax.Name = "Txt_Calib_Ymax"; this.Txt_Calib_Ymax.ReadOnly = true; this.Txt_Calib_Ymax.Size = new System.Drawing.Size(54, 22); this.Txt_Calib_Ymax.TabIndex = 62; this.Txt_Calib_Ymax.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_Calib_Ymax.TextChanged += new System.EventHandler(this.Txt_Calib_Ymax_TextChanged); // // label29 // this.label29.AutoSize = true; this.label29.Location = new System.Drawing.Point(151, 28); this.label29.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label29.Name = "label29"; this.label29.Size = new System.Drawing.Size(47, 16); this.label29.TabIndex = 61; this.label29.Text = "Y Min :"; // // Txt_Calib_Ymin // this.Txt_Calib_Ymin.Enabled = false; this.Txt_Calib_Ymin.Location = new System.Drawing.Point(205, 25); this.Txt_Calib_Ymin.Margin = new System.Windows.Forms.Padding(4); this.Txt_Calib_Ymin.Name = "Txt_Calib_Ymin"; this.Txt_Calib_Ymin.ReadOnly = true; this.Txt_Calib_Ymin.Size = new System.Drawing.Size(54, 22); this.Txt_Calib_Ymin.TabIndex = 60; this.Txt_Calib_Ymin.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_Calib_Ymin.TextChanged += new System.EventHandler(this.Txt_Calib_Ymin_TextChanged); // // label32 // this.label32.AutoSize = true; this.label32.Location = new System.Drawing.Point(10, 52); this.label32.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label32.Name = "label32"; this.label32.Size = new System.Drawing.Size(50, 16); this.label32.TabIndex = 4; this.label32.Text = "X Max :"; // // Txt_Calib_Xmax // this.Txt_Calib_Xmax.Enabled = false; this.Txt_Calib_Xmax.Location = new System.Drawing.Point(64, 49); this.Txt_Calib_Xmax.Margin = new System.Windows.Forms.Padding(4); this.Txt_Calib_Xmax.Name = "Txt_Calib_Xmax"; this.Txt_Calib_Xmax.ReadOnly = true; this.Txt_Calib_Xmax.Size = new System.Drawing.Size(54, 22); this.Txt_Calib_Xmax.TabIndex = 3; this.Txt_Calib_Xmax.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_Calib_Xmax.TextChanged += new System.EventHandler(this.Txt_Calib_Xmax_TextChanged); // // label36 // this.label36.AutoSize = true; this.label36.Location = new System.Drawing.Point(10, 28); this.label36.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label36.Name = "label36"; this.label36.Size = new System.Drawing.Size(46, 16); this.label36.TabIndex = 2; this.label36.Text = "X Min :"; // // Txt_Calib_Xmin // this.Txt_Calib_Xmin.Enabled = false; this.Txt_Calib_Xmin.Location = new System.Drawing.Point(64, 25); this.Txt_Calib_Xmin.Margin = new System.Windows.Forms.Padding(4); this.Txt_Calib_Xmin.Name = "Txt_Calib_Xmin"; this.Txt_Calib_Xmin.ReadOnly = true; this.Txt_Calib_Xmin.Size = new System.Drawing.Size(54, 22); this.Txt_Calib_Xmin.TabIndex = 1; this.Txt_Calib_Xmin.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_Calib_Xmin.TextChanged += new System.EventHandler(this.Txt_Calib_Xmin_TextChanged); // // GUI_AnalogCalibration // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.Gbox_P1_Calib); this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "GUI_AnalogCalibration"; this.Size = new System.Drawing.Size(277, 135); this.Gbox_P1_Calib.ResumeLayout(false); this.Gbox_P1_Calib.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.CheckBox Cbox_Player; private System.Windows.Forms.GroupBox Gbox_P1_Calib; private System.Windows.Forms.Button Btn_Start_Calib; private System.Windows.Forms.Label label28; private System.Windows.Forms.TextBox Txt_Calib_Ymax; private System.Windows.Forms.Label label29; private System.Windows.Forms.TextBox Txt_Calib_Ymin; private System.Windows.Forms.Label label32; private System.Windows.Forms.TextBox Txt_Calib_Xmax; private System.Windows.Forms.Label label36; private System.Windows.Forms.TextBox Txt_Calib_Xmin; private System.Windows.Forms.Button Btn_Init_Calib; private System.Windows.Forms.Button Btn_Stop_Calib; } } ================================================ FILE: DemulShooter_GUI/GUI_AnalogCalibration.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using DsCore.Config; namespace DemulShooter_GUI { public partial class GUI_AnalogCalibration : UserControl { private PlayerSettings _PlayerSettings; private int _Player = 0; private Boolean _IsCalibrationRunning = false; public int Player { get { return _Player; } } public GUI_AnalogCalibration(int Player, PlayerSettings Ps) { InitializeComponent(); _PlayerSettings = Ps; _Player = Player; Cbox_Player.Text = "Override P" + Player.ToString() + " Axis"; Cbox_Player.Checked = _PlayerSettings.AnalogAxisRangeOverride; if (_PlayerSettings.AnalogAxisRangeOverride) { Txt_Calib_Xmin.Enabled = true; Txt_Calib_Xmax.Enabled = true; Txt_Calib_Ymin.Enabled = true; Txt_Calib_Ymax.Enabled = true; Txt_Calib_Xmin.Text = _PlayerSettings.AnalogManual_Xmin.ToString(); Txt_Calib_Ymin.Text = _PlayerSettings.AnalogManual_Ymin.ToString(); Txt_Calib_Xmax.Text = _PlayerSettings.AnalogManual_Xmax.ToString(); Txt_Calib_Ymax.Text = _PlayerSettings.AnalogManual_Ymax.ToString(); } } private void Cbox_Player_CheckedChanged(object sender, EventArgs e) { if (Cbox_Player.Checked) { _PlayerSettings.AnalogAxisRangeOverride = true; Txt_Calib_Xmin.Enabled = true; Txt_Calib_Xmax.Enabled = true; Txt_Calib_Ymin.Enabled = true; Txt_Calib_Ymax.Enabled = true; Btn_Init_Calib.Enabled = true; Btn_Start_Calib.Enabled = true; Txt_Calib_Xmin.Text = _PlayerSettings.AnalogManual_Xmin.ToString(); Txt_Calib_Ymin.Text = _PlayerSettings.AnalogManual_Ymin.ToString(); Txt_Calib_Xmax.Text = _PlayerSettings.AnalogManual_Xmax.ToString(); Txt_Calib_Ymax.Text = _PlayerSettings.AnalogManual_Ymax.ToString(); } else { _PlayerSettings.AnalogAxisRangeOverride = false; Txt_Calib_Xmin.Enabled = false; Txt_Calib_Xmax.Enabled = false; Txt_Calib_Ymin.Enabled = false; Txt_Calib_Ymax.Enabled = false; Txt_Calib_Xmin.Text = String.Empty; Txt_Calib_Ymin.Text = String.Empty; Txt_Calib_Xmax.Text = String.Empty; Txt_Calib_Ymax.Text = String.Empty; Btn_Init_Calib.Enabled = false; Btn_Start_Calib.Enabled = false; Btn_Stop_Calib.Enabled = false; } } private void Txt_Calib_Xmin_TextChanged(object sender, EventArgs e) { /*if (Txt_Calib_Xmin.Text != String.Empty) _PlayerSettings.AnalogManual_Xmin = int.Parse(Txt_Calib_Xmin.Text);*/ } private void Txt_Calib_Xmax_TextChanged(object sender, EventArgs e) { /*if (Txt_Calib_Xmax.Text != String.Empty) _PlayerSettings.AnalogManual_Xmax = int.Parse(Txt_Calib_Xmax.Text);*/ } private void Txt_Calib_Ymin_TextChanged(object sender, EventArgs e) { /*if (Txt_Calib_Ymin.Text != String.Empty) _PlayerSettings.AnalogManual_Ymin = int.Parse(Txt_Calib_Ymin.Text);*/ } private void Txt_Calib_Ymax_TextChanged(object sender, EventArgs e) { /*if (Txt_Calib_Ymin.Text != String.Empty) _PlayerSettings.AnalogManual_Ymax = int.Parse(Txt_Calib_Ymin.Text);*/ } private void Btn_Start_Calib_Click(object sender, EventArgs e) { if (_PlayerSettings.RIController.DeviceType == DsCore.RawInput.RawInputDeviceType.RIM_TYPEHID) { Btn_Init_Calib.Enabled = false; Btn_Start_Calib.Enabled = false; Btn_Stop_Calib.Enabled = true; _PlayerSettings.AnalogManual_Xmin = Int32.MaxValue; _PlayerSettings.AnalogManual_Xmax = Int32.MinValue; _PlayerSettings.AnalogManual_Ymin = Int32.MaxValue; _PlayerSettings.AnalogManual_Ymax = Int32.MinValue; _IsCalibrationRunning = true; } else MessageBox.Show("This kind of device can't be calibrated : " + _PlayerSettings.RIController.DeviceType.ToString(), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } private void Btn_Stop_Calib_Click(object sender, EventArgs e) { Btn_Init_Calib.Enabled = true; Btn_Start_Calib.Enabled = true; Btn_Stop_Calib.Enabled = false; _IsCalibrationRunning = false; } private void Btn_Init_Calib_Click(object sender, EventArgs e) { if (_PlayerSettings.RIController.DeviceType == DsCore.RawInput.RawInputDeviceType.RIM_TYPEHID) { Btn_Start_Calib.Enabled = true; Btn_Stop_Calib.Enabled = false; _PlayerSettings.AnalogManual_Xmin = _PlayerSettings.RIController.Axis_X_Min; _PlayerSettings.AnalogManual_Xmax = _PlayerSettings.RIController.Axis_X_Max; _PlayerSettings.AnalogManual_Ymin = _PlayerSettings.RIController.Axis_Y_Min; _PlayerSettings.AnalogManual_Ymax = _PlayerSettings.RIController.Axis_Y_Max; Txt_Calib_Xmin.Text = _PlayerSettings.RIController.Axis_X_Min.ToString(); Txt_Calib_Xmax.Text = _PlayerSettings.RIController.Axis_X_Max.ToString(); Txt_Calib_Ymin.Text = _PlayerSettings.RIController.Axis_Y_Min.ToString(); Txt_Calib_Ymax.Text = _PlayerSettings.RIController.Axis_Y_Max.ToString(); } else MessageBox.Show("This kind of device can't be calibrated : " + _PlayerSettings.RIController.DeviceType.ToString(), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } public void UpdateValues() { if (Cbox_Player.Checked && _IsCalibrationRunning) { if (_PlayerSettings.RIController.Computed_X < _PlayerSettings.AnalogManual_Xmin) _PlayerSettings.AnalogManual_Xmin = _PlayerSettings.RIController.Computed_X; if (_PlayerSettings.RIController.Computed_X > _PlayerSettings.AnalogManual_Xmax) _PlayerSettings.AnalogManual_Xmax = _PlayerSettings.RIController.Computed_X; if (_PlayerSettings.RIController.Computed_Y < _PlayerSettings.AnalogManual_Ymin) _PlayerSettings.AnalogManual_Ymin = _PlayerSettings.RIController.Computed_Y; if (_PlayerSettings.RIController.Computed_Y > _PlayerSettings.AnalogManual_Ymax) _PlayerSettings.AnalogManual_Ymax = _PlayerSettings.RIController.Computed_Y; Txt_Calib_Xmin.Text = _PlayerSettings.AnalogManual_Xmin.ToString(); Txt_Calib_Ymin.Text = _PlayerSettings.AnalogManual_Ymin.ToString(); Txt_Calib_Xmax.Text = _PlayerSettings.AnalogManual_Xmax.ToString(); Txt_Calib_Ymax.Text = _PlayerSettings.AnalogManual_Ymax.ToString(); } } } } ================================================ FILE: DemulShooter_GUI/GUI_AnalogCalibration.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DemulShooter_GUI/GUI_Button.Designer.cs ================================================ namespace DemulShooter_GUI { partial class GUI_Button { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur de composants /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { this.Pnl_Background = new System.Windows.Forms.Panel(); this.Lbl_Number = new System.Windows.Forms.Label(); this.Pnl_Background.SuspendLayout(); this.SuspendLayout(); // // Pnl_Background // this.Pnl_Background.BackColor = System.Drawing.Color.Crimson; this.Pnl_Background.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.Pnl_Background.Controls.Add(this.Lbl_Number); this.Pnl_Background.Dock = System.Windows.Forms.DockStyle.Fill; this.Pnl_Background.Location = new System.Drawing.Point(0, 0); this.Pnl_Background.Name = "Pnl_Background"; this.Pnl_Background.Size = new System.Drawing.Size(26, 26); this.Pnl_Background.TabIndex = 1; // // Lbl_Number // this.Lbl_Number.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.Lbl_Number.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Lbl_Number.ForeColor = System.Drawing.Color.White; this.Lbl_Number.Location = new System.Drawing.Point(-2, 0); this.Lbl_Number.Name = "Lbl_Number"; this.Lbl_Number.Size = new System.Drawing.Size(26, 22); this.Lbl_Number.TabIndex = 0; this.Lbl_Number.Text = "1"; this.Lbl_Number.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // Gui_Button // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.Pnl_Background); this.Name = "Gui_Button"; this.Size = new System.Drawing.Size(26, 26); this.Pnl_Background.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Panel Pnl_Background; private System.Windows.Forms.Label Lbl_Number; } } ================================================ FILE: DemulShooter_GUI/GUI_Button.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DemulShooter_GUI { public partial class GUI_Button : UserControl { private int _Number; public GUI_Button(int Number) { InitializeComponent(); _Number = Number; Lbl_Number.Text = Number.ToString(); } public void Activate(bool isActivated) { if (isActivated) Pnl_Background.BackColor = Color.Green; else Pnl_Background.BackColor = Color.Crimson; } } } ================================================ FILE: DemulShooter_GUI/GUI_Button.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DemulShooter_GUI/GUI_Player.Designer.cs ================================================ namespace DemulShooter_GUI { partial class GUI_Player { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur de composants /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { this.Cbo_Device = new System.Windows.Forms.ComboBox(); this.Lbl_Player = new System.Windows.Forms.Label(); this.Pnl_Options = new System.Windows.Forms.Panel(); this.Lbl_ProductManu = new System.Windows.Forms.Label(); this.SuspendLayout(); // // Cbo_Device // this.Cbo_Device.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.Cbo_Device.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbo_Device.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Cbo_Device.FormattingEnabled = true; this.Cbo_Device.Location = new System.Drawing.Point(13, 27); this.Cbo_Device.Margin = new System.Windows.Forms.Padding(4); this.Cbo_Device.Name = "Cbo_Device"; this.Cbo_Device.Size = new System.Drawing.Size(554, 23); this.Cbo_Device.TabIndex = 40; this.Cbo_Device.SelectionChangeCommitted += new System.EventHandler(this.Cbo_Device_SelectionChangeCommitted); // // Lbl_Player // this.Lbl_Player.AutoSize = true; this.Lbl_Player.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Lbl_Player.Location = new System.Drawing.Point(10, 7); this.Lbl_Player.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_Player.Name = "Lbl_Player"; this.Lbl_Player.Size = new System.Drawing.Size(74, 13); this.Lbl_Player.TabIndex = 41; this.Lbl_Player.Text = "P1 Device :"; // // Pnl_Options // this.Pnl_Options.Location = new System.Drawing.Point(0, 57); this.Pnl_Options.Name = "Pnl_Options"; this.Pnl_Options.Size = new System.Drawing.Size(576, 202); this.Pnl_Options.TabIndex = 42; // // Lbl_ProductManu // this.Lbl_ProductManu.AutoSize = true; this.Lbl_ProductManu.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Lbl_ProductManu.Location = new System.Drawing.Point(81, 7); this.Lbl_ProductManu.Name = "Lbl_ProductManu"; this.Lbl_ProductManu.Size = new System.Drawing.Size(0, 13); this.Lbl_ProductManu.TabIndex = 43; // // GUI_Player // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.Lbl_ProductManu); this.Controls.Add(this.Pnl_Options); this.Controls.Add(this.Cbo_Device); this.Controls.Add(this.Lbl_Player); this.Name = "GUI_Player"; this.Size = new System.Drawing.Size(576, 259); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.ComboBox Cbo_Device; private System.Windows.Forms.Label Lbl_Player; private System.Windows.Forms.Panel Pnl_Options; private System.Windows.Forms.Label Lbl_ProductManu; } } ================================================ FILE: DemulShooter_GUI/GUI_Player.cs ================================================ using System; using System.Windows.Forms; using DsCore.Config; using DsCore.RawInput; using DsCore; namespace DemulShooter_GUI { public partial class GUI_Player : UserControl { private const String XINPUT_DEVICE_PREFIX = "XInput Gamepad #"; private PlayerSettings _PlayerData; private RawInputController[] _AvailableControllers; GUI_RawInputMouse _RimData; GUI_RawInputHID _RihData; public GUI_Player(PlayerSettings PlayerData, RawInputController[] AvailableControllers) { InitializeComponent(); _PlayerData = PlayerData; _AvailableControllers = AvailableControllers; _RimData = new GUI_RawInputMouse(); _RihData = new GUI_RawInputHID(); _RimData.Visible = false; _RihData.Visible = false; Pnl_Options.Controls.Add(_RimData); Pnl_Options.Controls.Add(_RihData); Logger.WriteLog("Initializing Player" + _PlayerData.ID.ToString() + " devices:"); Lbl_Player.Text = "P" + _PlayerData.ID.ToString() + " Device :"; //GUI init AddDevice(""); foreach (RawInputController Controller in AvailableControllers) { try { Logger.WriteLog("Adding " + Controller.DeviceName); AddDevice("[" + Controller.DHidUsage + "] " + Controller.ManufacturerName + " - " + Controller.ProductName ); } catch (Exception ex) { Logger.WriteLog("GUI_Player() ERROR : " + ex.Message.ToString()); } } if (_PlayerData.DeviceName.Length > 0) { Logger.WriteLog("Current selected device : " + _PlayerData.DeviceName); for (int i = 0; i < _AvailableControllers.Length; i++) { if (_PlayerData.DeviceName == _AvailableControllers[i].DeviceName) { Cbo_Device.SelectedItem = Cbo_Device.Items[i+1]; SelectRawInputController(_AvailableControllers[i].DeviceName); } } } else Logger.WriteLog("Current selected device : [None]"); } /// /// Add a device in the drop down list /// /// Name of the device to display public void AddDevice(string DeviceName) { Cbo_Device.Items.Add(DeviceName); } /// /// Display whethet the vibration are enabled or not for the gamepad /// /// True or False public void SetVibrationEnabled(bool Enabled) { //Chk_VibrationEnable.Checked = Enabled; } /// /// Return a RawInputController based on a Device Name /// /// The Device Name corresponding to the wanted Device Controller /// a RawInputController if found in the list, otherwise NULL private RawInputController GetRawInputControllerFromDeviceName(String RawInputDeviceName) { if (RawInputDeviceName.Length > 0) { foreach (RawInputController c in _AvailableControllers) { if (c.DeviceName == RawInputDeviceName) return c; } } return null; } /// /// Select a RawInputController and set values (selected Axis and Buttons) /// /// DeviceName string of the wanted RawInputController private void SelectRawInputController(String RawInputDeviceName) { _PlayerData.RIController = GetRawInputControllerFromDeviceName(RawInputDeviceName); if (_PlayerData.RIController != null) { if (_PlayerData.RIController.DeviceType == RawInputDeviceType.RIM_TYPEMOUSE) { _RihData.Visible = false; _RimData.Visible = true; _RimData.UpdateData(_PlayerData); } else if (_PlayerData.RIController.DeviceType == RawInputDeviceType.RIM_TYPEHID) { _RihData.Visible = true; _RimData.Visible = false; _RihData.UpdateData(_PlayerData); } else { _RihData.Visible = false; _RimData.Visible = false; } Lbl_ProductManu.Text = _PlayerData.RIController.DeviceName; //_PlayerData.RIController.ManufacturerName + " " + _PlayerData.RIController.ProductName; } } /// /// Clear all buttons and axis values if no controller is selected /// private void ClearRawInputController() { _PlayerData.RIController = null; Lbl_ProductManu.Text = ""; _RihData.Visible = false; _RimData.Visible = false; } #region GUI private void Cbo_Device_SelectionChangeCommitted(object sender, EventArgs e) { if (Cbo_Device.Text.Length > 0) SelectRawInputController(_AvailableControllers[Cbo_Device.SelectedIndex - 1].DeviceName); else ClearRawInputController(); } /// /// Called by the main window chen a RawInput event occurs to update graphical representation. /// public void UpdateGui() { if (_RihData.Visible) _RihData.UpdateGui(); else if (_RimData.Visible) _RimData.UpdateGui(); } #endregion } } ================================================ FILE: DemulShooter_GUI/GUI_Player.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DemulShooter_GUI/GUI_RawInputHID.Designer.cs ================================================ namespace DemulShooter_GUI { partial class GUI_RawInputHID { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur de composants /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { this.Gbox_HIDOptions = new System.Windows.Forms.GroupBox(); this.label1 = new System.Windows.Forms.Label(); this.Cbox_HID_YAxis = new System.Windows.Forms.ComboBox(); this.label29 = new System.Windows.Forms.Label(); this.Cbox_HID_XAxis = new System.Windows.Forms.ComboBox(); this.Cbox_HID_OffScreenButton = new System.Windows.Forms.ComboBox(); this.Cbox_HID_ActionButton = new System.Windows.Forms.ComboBox(); this.Cbox_HID_OnScreenButton = new System.Windows.Forms.ComboBox(); this.label30 = new System.Windows.Forms.Label(); this.label31 = new System.Windows.Forms.Label(); this.label32 = new System.Windows.Forms.Label(); this.Pnl_AxisViewer = new System.Windows.Forms.Panel(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.Pnl_ButtonsViewer = new System.Windows.Forms.FlowLayoutPanel(); this.Chk_InvertX = new System.Windows.Forms.CheckBox(); this.Chk_InvertY = new System.Windows.Forms.CheckBox(); this.Gbox_HIDOptions.SuspendLayout(); this.groupBox1.SuspendLayout(); this.SuspendLayout(); // // Gbox_HIDOptions // this.Gbox_HIDOptions.Controls.Add(this.Chk_InvertY); this.Gbox_HIDOptions.Controls.Add(this.Chk_InvertX); this.Gbox_HIDOptions.Controls.Add(this.label1); this.Gbox_HIDOptions.Controls.Add(this.Cbox_HID_YAxis); this.Gbox_HIDOptions.Controls.Add(this.label29); this.Gbox_HIDOptions.Controls.Add(this.Cbox_HID_XAxis); this.Gbox_HIDOptions.Controls.Add(this.Cbox_HID_OffScreenButton); this.Gbox_HIDOptions.Controls.Add(this.Cbox_HID_ActionButton); this.Gbox_HIDOptions.Controls.Add(this.Cbox_HID_OnScreenButton); this.Gbox_HIDOptions.Controls.Add(this.label30); this.Gbox_HIDOptions.Controls.Add(this.label31); this.Gbox_HIDOptions.Controls.Add(this.label32); this.Gbox_HIDOptions.Location = new System.Drawing.Point(3, 3); this.Gbox_HIDOptions.Name = "Gbox_HIDOptions"; this.Gbox_HIDOptions.Size = new System.Drawing.Size(223, 189); this.Gbox_HIDOptions.TabIndex = 43; this.Gbox_HIDOptions.TabStop = false; this.Gbox_HIDOptions.Text = "Device Options"; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(6, 156); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(42, 13); this.label1.TabIndex = 14; this.label1.Text = "Y Axis :"; // // Cbox_HID_YAxis // this.Cbox_HID_YAxis.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_HID_YAxis.FormattingEnabled = true; this.Cbox_HID_YAxis.Items.AddRange(new object[] { "Left Stick", "Right Stick"}); this.Cbox_HID_YAxis.Location = new System.Drawing.Point(54, 153); this.Cbox_HID_YAxis.Name = "Cbox_HID_YAxis"; this.Cbox_HID_YAxis.Size = new System.Drawing.Size(57, 21); this.Cbox_HID_YAxis.TabIndex = 13; this.Cbox_HID_YAxis.SelectionChangeCommitted += new System.EventHandler(this.Cbox_HID_YAxis_SelectionChangeCommitted); // // label29 // this.label29.AutoSize = true; this.label29.Location = new System.Drawing.Point(6, 129); this.label29.Name = "label29"; this.label29.Size = new System.Drawing.Size(42, 13); this.label29.TabIndex = 7; this.label29.Text = "X Axis :"; // // Cbox_HID_XAxis // this.Cbox_HID_XAxis.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_HID_XAxis.FormattingEnabled = true; this.Cbox_HID_XAxis.Items.AddRange(new object[] { "Left Stick", "Right Stick"}); this.Cbox_HID_XAxis.Location = new System.Drawing.Point(54, 126); this.Cbox_HID_XAxis.Name = "Cbox_HID_XAxis"; this.Cbox_HID_XAxis.Size = new System.Drawing.Size(57, 21); this.Cbox_HID_XAxis.TabIndex = 6; this.Cbox_HID_XAxis.SelectionChangeCommitted += new System.EventHandler(this.Cbox_HID_XAxis_SelectionChangeCommitted); // // Cbox_HID_OffScreenButton // this.Cbox_HID_OffScreenButton.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_HID_OffScreenButton.FormattingEnabled = true; this.Cbox_HID_OffScreenButton.Items.AddRange(new object[] { "A", "B", "X", "Y", "R Shoulder", "L Shoulder", "R Thumb", "L Thumb"}); this.Cbox_HID_OffScreenButton.Location = new System.Drawing.Point(109, 86); this.Cbox_HID_OffScreenButton.Name = "Cbox_HID_OffScreenButton"; this.Cbox_HID_OffScreenButton.Size = new System.Drawing.Size(101, 21); this.Cbox_HID_OffScreenButton.TabIndex = 5; this.Cbox_HID_OffScreenButton.SelectionChangeCommitted += new System.EventHandler(this.Cbox_HID_OffScreenButton_SelectionChangeCommitted); // // Cbox_HID_ActionButton // this.Cbox_HID_ActionButton.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_HID_ActionButton.FormattingEnabled = true; this.Cbox_HID_ActionButton.Items.AddRange(new object[] { "A", "B", "X", "Y", "R Shoulder", "L Shoulder", "R Thumb", "L Thumb"}); this.Cbox_HID_ActionButton.Location = new System.Drawing.Point(109, 59); this.Cbox_HID_ActionButton.Name = "Cbox_HID_ActionButton"; this.Cbox_HID_ActionButton.Size = new System.Drawing.Size(101, 21); this.Cbox_HID_ActionButton.TabIndex = 4; this.Cbox_HID_ActionButton.SelectionChangeCommitted += new System.EventHandler(this.Cbox_HID_ActionButton_SelectionChangeCommitted); // // Cbox_HID_OnScreenButton // this.Cbox_HID_OnScreenButton.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_HID_OnScreenButton.FormattingEnabled = true; this.Cbox_HID_OnScreenButton.Items.AddRange(new object[] { "A", "B", "X", "Y", "R Shoulder", "L Shoulder", "R Thumb", "L Thumb"}); this.Cbox_HID_OnScreenButton.Location = new System.Drawing.Point(109, 32); this.Cbox_HID_OnScreenButton.Name = "Cbox_HID_OnScreenButton"; this.Cbox_HID_OnScreenButton.Size = new System.Drawing.Size(101, 21); this.Cbox_HID_OnScreenButton.TabIndex = 3; this.Cbox_HID_OnScreenButton.SelectionChangeCommitted += new System.EventHandler(this.Cbox_HID_OnScreenButton_SelectionChangeCommitted); // // label30 // this.label30.AutoSize = true; this.label30.Location = new System.Drawing.Point(4, 89); this.label30.Name = "label30"; this.label30.Size = new System.Drawing.Size(98, 13); this.label30.TabIndex = 2; this.label30.Text = "Off Screen Button:"; // // label31 // this.label31.AutoSize = true; this.label31.Location = new System.Drawing.Point(4, 62); this.label31.Name = "label31"; this.label31.Size = new System.Drawing.Size(74, 13); this.label31.TabIndex = 1; this.label31.Text = "Action Button:"; // // label32 // this.label32.AutoSize = true; this.label32.Location = new System.Drawing.Point(4, 35); this.label32.Name = "label32"; this.label32.Size = new System.Drawing.Size(80, 13); this.label32.TabIndex = 0; this.label32.Text = "Trigger Button :"; // // Pnl_AxisViewer // this.Pnl_AxisViewer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Pnl_AxisViewer.Location = new System.Drawing.Point(17, 32); this.Pnl_AxisViewer.Name = "Pnl_AxisViewer"; this.Pnl_AxisViewer.Size = new System.Drawing.Size(146, 142); this.Pnl_AxisViewer.TabIndex = 44; this.Pnl_AxisViewer.Paint += new System.Windows.Forms.PaintEventHandler(this.Pnl_AxisViewer_Paint); // // groupBox1 // this.groupBox1.Controls.Add(this.Pnl_ButtonsViewer); this.groupBox1.Controls.Add(this.Pnl_AxisViewer); this.groupBox1.Location = new System.Drawing.Point(232, 3); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(341, 189); this.groupBox1.TabIndex = 45; this.groupBox1.TabStop = false; this.groupBox1.Text = "Device Preview"; // // Pnl_ButtonsViewer // this.Pnl_ButtonsViewer.Location = new System.Drawing.Point(169, 32); this.Pnl_ButtonsViewer.Name = "Pnl_ButtonsViewer"; this.Pnl_ButtonsViewer.Size = new System.Drawing.Size(166, 142); this.Pnl_ButtonsViewer.TabIndex = 46; // // Chk_InvertX // this.Chk_InvertX.AutoSize = true; this.Chk_InvertX.Location = new System.Drawing.Point(135, 130); this.Chk_InvertX.Name = "Chk_InvertX"; this.Chk_InvertX.Size = new System.Drawing.Size(75, 17); this.Chk_InvertX.TabIndex = 47; this.Chk_InvertX.Text = "Invert Axis"; this.Chk_InvertX.UseVisualStyleBackColor = true; this.Chk_InvertX.CheckedChanged += new System.EventHandler(this.Chk_InvertX_CheckedChanged); // // Chk_InvertY // this.Chk_InvertY.AutoSize = true; this.Chk_InvertY.Location = new System.Drawing.Point(135, 156); this.Chk_InvertY.Name = "Chk_InvertY"; this.Chk_InvertY.Size = new System.Drawing.Size(75, 17); this.Chk_InvertY.TabIndex = 48; this.Chk_InvertY.Text = "Invert Axis"; this.Chk_InvertY.UseVisualStyleBackColor = true; this.Chk_InvertY.CheckedChanged += new System.EventHandler(this.Chk_InvertY_CheckedChanged); // // GUI_RawInputHID // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.groupBox1); this.Controls.Add(this.Gbox_HIDOptions); this.Name = "GUI_RawInputHID"; this.Size = new System.Drawing.Size(576, 202); this.Gbox_HIDOptions.ResumeLayout(false); this.Gbox_HIDOptions.PerformLayout(); this.groupBox1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.GroupBox Gbox_HIDOptions; private System.Windows.Forms.Label label29; private System.Windows.Forms.ComboBox Cbox_HID_XAxis; private System.Windows.Forms.ComboBox Cbox_HID_OffScreenButton; private System.Windows.Forms.ComboBox Cbox_HID_ActionButton; private System.Windows.Forms.ComboBox Cbox_HID_OnScreenButton; private System.Windows.Forms.Label label30; private System.Windows.Forms.Label label31; private System.Windows.Forms.Label label32; private System.Windows.Forms.Label label1; private System.Windows.Forms.ComboBox Cbox_HID_YAxis; private System.Windows.Forms.Panel Pnl_AxisViewer; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.FlowLayoutPanel Pnl_ButtonsViewer; private System.Windows.Forms.CheckBox Chk_InvertX; private System.Windows.Forms.CheckBox Chk_InvertY; } } ================================================ FILE: DemulShooter_GUI/GUI_RawInputHID.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using DsCore.Config; namespace DemulShooter_GUI { public partial class GUI_RawInputHID : UserControl { private List _Buttons = new List(); private PlayerSettings _PlayerData; public GUI_RawInputHID() { InitializeComponent(); } public void UpdateData(PlayerSettings PlayerData) { _PlayerData = PlayerData; //Force virtual mouse buttons to OFF with joypad _PlayerData.isVirtualMouseButtonsEnabled = false; //Clear controls Cbox_HID_XAxis.Items.Clear(); Cbox_HID_YAxis.Items.Clear(); Cbox_HID_ActionButton.Items.Clear(); Cbox_HID_OffScreenButton.Items.Clear(); Cbox_HID_OnScreenButton.Items.Clear(); _Buttons.Clear(); Pnl_ButtonsViewer.Controls.Clear(); //Fill device-related values int indexX = 0; int indexY = 0; Cbox_HID_XAxis.Items.Add(""); Cbox_HID_YAxis.Items.Add(""); for (int i = 0; i < PlayerData.RIController.AxisList.Count; i++) { Cbox_HID_XAxis.Items.Add("0x" + PlayerData.RIController.AxisList[i].ToString("X2")); if (_PlayerData.RIController.Selected_AxisX == PlayerData.RIController.AxisList[i]) indexX = i + 1; Cbox_HID_YAxis.Items.Add("0x" + PlayerData.RIController.AxisList[i].ToString("X2")); if (_PlayerData.RIController.Selected_AxisY == PlayerData.RIController.AxisList[i]) indexY = i + 1; } Cbox_HID_XAxis.SelectedItem = Cbox_HID_XAxis.Items[indexX]; Cbox_HID_YAxis.SelectedItem = Cbox_HID_YAxis.Items[indexY]; Chk_InvertX.Checked = _PlayerData.InvertAxis_X; Chk_InvertY.Checked = _PlayerData.InvertAxis_Y; int indexBtnAction = 0; int indexBtnOnScreen = 0; int indexBtnOffScreen = 0; Cbox_HID_ActionButton.Items.Add(""); Cbox_HID_OnScreenButton.Items.Add(""); Cbox_HID_OffScreenButton.Items.Add(""); for (int i = 0; i < PlayerData.RIController.NumberOfButtons; i++) { _Buttons.Add(new GUI_Button(i + 1)); Pnl_ButtonsViewer.Controls.Add(_Buttons[i]); Cbox_HID_ActionButton.Items.Add(i + 1); if (_PlayerData.RIController.Selected_ActionButton == (i + 1)) indexBtnAction = i + 1; Cbox_HID_OnScreenButton.Items.Add(i+1); if (_PlayerData.RIController.Selected_OnScreenTriggerButton == (i + 1)) indexBtnOnScreen = i + 1; Cbox_HID_OffScreenButton.Items.Add(i+1); if (_PlayerData.RIController.Selected_OffScreenTriggerButton == (i + 1)) indexBtnOffScreen = i + 1; } Cbox_HID_ActionButton.SelectedItem = Cbox_HID_ActionButton.Items[indexBtnAction]; Cbox_HID_OnScreenButton.SelectedItem = Cbox_HID_OnScreenButton.Items[indexBtnOnScreen]; Cbox_HID_OffScreenButton.SelectedItem = Cbox_HID_OffScreenButton.Items[indexBtnOffScreen]; } private void Cbox_HID_OnScreenButton_SelectionChangeCommitted(object sender, EventArgs e) { if (Cbox_HID_OnScreenButton.Text.Length > 0) _PlayerData.RIController.Selected_OnScreenTriggerButton = int.Parse(Cbox_HID_OnScreenButton.Text); else _PlayerData.RIController.Selected_OnScreenTriggerButton = 99; } private void Cbox_HID_ActionButton_SelectionChangeCommitted(object sender, EventArgs e) { if (Cbox_HID_ActionButton.Text.Length > 0) _PlayerData.RIController.Selected_ActionButton = int.Parse(Cbox_HID_ActionButton.Text); else _PlayerData.RIController.Selected_ActionButton = 99; } private void Cbox_HID_OffScreenButton_SelectionChangeCommitted(object sender, EventArgs e) { if (Cbox_HID_OffScreenButton.Text.Length > 0) _PlayerData.RIController.Selected_OffScreenTriggerButton = int.Parse(Cbox_HID_OffScreenButton.Text); else _PlayerData.RIController.Selected_OffScreenTriggerButton = 99; } private void Cbox_HID_XAxis_SelectionChangeCommitted(object sender, EventArgs e) { if (Cbox_HID_XAxis.Text.Length > 0) _PlayerData.RIController.Selected_AxisX = ushort.Parse(Cbox_HID_XAxis.Text.Substring(2), System.Globalization.NumberStyles.HexNumber); else _PlayerData.RIController.Selected_AxisX = 0; } private void Cbox_HID_YAxis_SelectionChangeCommitted(object sender, EventArgs e) { if (Cbox_HID_YAxis.Text.Length > 0) _PlayerData.RIController.Selected_AxisY = ushort.Parse(Cbox_HID_YAxis.Text.Substring(2), System.Globalization.NumberStyles.HexNumber); else _PlayerData.RIController.Selected_AxisY = 0; } private void Chk_InvertX_CheckedChanged(object sender, EventArgs e) { _PlayerData.InvertAxis_X = Chk_InvertX.Checked; } private void Chk_InvertY_CheckedChanged(object sender, EventArgs e) { _PlayerData.InvertAxis_Y = Chk_InvertY.Checked; } #region GUI Animation public void UpdateGui() { for (int i = 0; i < _PlayerData.RIController.Hid_Buttons.Length; i++) { SetButtonState(i, _PlayerData.RIController.Hid_Buttons[i]); } Pnl_AxisViewer.Invalidate(); } public void SetButtonState(int ButtonID, bool ButtonState) { if (ButtonID <= _Buttons.Count) { _Buttons[ButtonID].Activate(ButtonState); } } private void Pnl_AxisViewer_Paint(object sender, PaintEventArgs e) { base.OnPaint(e); double XRange = (double)(_PlayerData.RIController.Axis_X_Max - _PlayerData.RIController.Axis_X_Min); double YRange = (double)(_PlayerData.RIController.Axis_Y_Max - _PlayerData.RIController.Axis_Y_Min); double XPositionPercent = (double)(_PlayerData.RIController.Computed_X - _PlayerData.RIController.Axis_X_Min) / XRange; double YPositionPercent = (double)(_PlayerData.RIController.Computed_Y - _PlayerData.RIController.Axis_Y_Min) / YRange; int X = 0; int Y = 0; try { X = Convert.ToInt32(Math.Round((double)Pnl_AxisViewer.Width * XPositionPercent)); Y = Convert.ToInt32(Math.Round((double)Pnl_AxisViewer.Height * YPositionPercent)); if (_PlayerData.InvertAxis_X) X = Pnl_AxisViewer.Width - X; if (_PlayerData.InvertAxis_Y) Y = Pnl_AxisViewer.Width - Y; } catch { } // Draw Crosshair Graphics g = e.Graphics; SolidBrush b = new SolidBrush(Color.Black); Pen p = new Pen(b, 1); g.DrawLine(p, new PointF(X - 4, Y), new PointF(X + 4, Y)); g.DrawLine(p, new PointF(X, Y + 4), new PointF(X, Y - 4)); g.Dispose(); } #endregion } } ================================================ FILE: DemulShooter_GUI/GUI_RawInputHID.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DemulShooter_GUI/GUI_RawInputMouse.Designer.cs ================================================ namespace DemulShooter_GUI { partial class GUI_RawInputMouse { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur de composants /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { this.Gbox_GunOptions = new System.Windows.Forms.GroupBox(); this.label2 = new System.Windows.Forms.Label(); this.Txt_VirtualLeftBtn = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.Txt_VirtualRightBtn = new System.Windows.Forms.TextBox(); this.Lbl_MidButonEnable = new System.Windows.Forms.Label(); this.Chk_VirtualMiddleBtn = new System.Windows.Forms.CheckBox(); this.label36 = new System.Windows.Forms.Label(); this.Txt_VirtualMiddleBtn = new System.Windows.Forms.TextBox(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.Pnl_ButtonsViewer = new System.Windows.Forms.FlowLayoutPanel(); this.Pnl_AxisViewer = new System.Windows.Forms.Panel(); this.Gbox_GunOptions.SuspendLayout(); this.groupBox1.SuspendLayout(); this.SuspendLayout(); // // Gbox_GunOptions // this.Gbox_GunOptions.Controls.Add(this.label2); this.Gbox_GunOptions.Controls.Add(this.Txt_VirtualLeftBtn); this.Gbox_GunOptions.Controls.Add(this.label1); this.Gbox_GunOptions.Controls.Add(this.Txt_VirtualRightBtn); this.Gbox_GunOptions.Controls.Add(this.Lbl_MidButonEnable); this.Gbox_GunOptions.Controls.Add(this.Chk_VirtualMiddleBtn); this.Gbox_GunOptions.Controls.Add(this.label36); this.Gbox_GunOptions.Controls.Add(this.Txt_VirtualMiddleBtn); this.Gbox_GunOptions.Location = new System.Drawing.Point(3, 3); this.Gbox_GunOptions.Name = "Gbox_GunOptions"; this.Gbox_GunOptions.Size = new System.Drawing.Size(223, 189); this.Gbox_GunOptions.TabIndex = 44; this.Gbox_GunOptions.TabStop = false; this.Gbox_GunOptions.Text = "Device Options"; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(6, 80); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(97, 13); this.label2.TabIndex = 58; this.label2.Text = "\"Left Mouse\" Key :"; // // Txt_VirtualLeftBtn // this.Txt_VirtualLeftBtn.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_VirtualLeftBtn.Enabled = false; this.Txt_VirtualLeftBtn.Location = new System.Drawing.Point(122, 77); this.Txt_VirtualLeftBtn.Name = "Txt_VirtualLeftBtn"; this.Txt_VirtualLeftBtn.ReadOnly = true; this.Txt_VirtualLeftBtn.Size = new System.Drawing.Size(84, 20); this.Txt_VirtualLeftBtn.TabIndex = 59; this.Txt_VirtualLeftBtn.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DIK_MouseClick); // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(6, 152); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(107, 13); this.label1.TabIndex = 56; this.label1.Text = "\"Right Mouse\" Key :"; // // Txt_VirtualRightBtn // this.Txt_VirtualRightBtn.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_VirtualRightBtn.Enabled = false; this.Txt_VirtualRightBtn.Location = new System.Drawing.Point(122, 149); this.Txt_VirtualRightBtn.Name = "Txt_VirtualRightBtn"; this.Txt_VirtualRightBtn.ReadOnly = true; this.Txt_VirtualRightBtn.Size = new System.Drawing.Size(84, 20); this.Txt_VirtualRightBtn.TabIndex = 57; this.Txt_VirtualRightBtn.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DIK_MouseClick); // // Lbl_MidButonEnable // this.Lbl_MidButonEnable.AutoSize = true; this.Lbl_MidButonEnable.Location = new System.Drawing.Point(30, 39); this.Lbl_MidButonEnable.Name = "Lbl_MidButonEnable"; this.Lbl_MidButonEnable.Size = new System.Drawing.Size(154, 13); this.Lbl_MidButonEnable.TabIndex = 13; this.Lbl_MidButonEnable.Text = "Enable \"Virtual\" mouse buttons"; // // Chk_VirtualMiddleBtn // this.Chk_VirtualMiddleBtn.AutoSize = true; this.Chk_VirtualMiddleBtn.Location = new System.Drawing.Point(9, 39); this.Chk_VirtualMiddleBtn.Name = "Chk_VirtualMiddleBtn"; this.Chk_VirtualMiddleBtn.Size = new System.Drawing.Size(15, 14); this.Chk_VirtualMiddleBtn.TabIndex = 55; this.Chk_VirtualMiddleBtn.UseVisualStyleBackColor = true; this.Chk_VirtualMiddleBtn.CheckedChanged += new System.EventHandler(this.Chk_VirtualMiddleBtn_CheckedChanged); // // label36 // this.label36.AutoSize = true; this.label36.Location = new System.Drawing.Point(6, 116); this.label36.Name = "label36"; this.label36.Size = new System.Drawing.Size(110, 13); this.label36.TabIndex = 13; this.label36.Text = "\"Middle Mouse\" Key :"; // // Txt_VirtualMiddleBtn // this.Txt_VirtualMiddleBtn.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_VirtualMiddleBtn.Enabled = false; this.Txt_VirtualMiddleBtn.Location = new System.Drawing.Point(122, 113); this.Txt_VirtualMiddleBtn.Name = "Txt_VirtualMiddleBtn"; this.Txt_VirtualMiddleBtn.ReadOnly = true; this.Txt_VirtualMiddleBtn.Size = new System.Drawing.Size(84, 20); this.Txt_VirtualMiddleBtn.TabIndex = 54; this.Txt_VirtualMiddleBtn.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DIK_MouseClick); // // groupBox1 // this.groupBox1.Controls.Add(this.Pnl_ButtonsViewer); this.groupBox1.Controls.Add(this.Pnl_AxisViewer); this.groupBox1.Location = new System.Drawing.Point(232, 3); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(341, 189); this.groupBox1.TabIndex = 46; this.groupBox1.TabStop = false; this.groupBox1.Text = "Device Preview"; // // Pnl_ButtonsViewer // this.Pnl_ButtonsViewer.Location = new System.Drawing.Point(199, 32); this.Pnl_ButtonsViewer.Name = "Pnl_ButtonsViewer"; this.Pnl_ButtonsViewer.Size = new System.Drawing.Size(136, 142); this.Pnl_ButtonsViewer.TabIndex = 47; // // Pnl_AxisViewer // this.Pnl_AxisViewer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Pnl_AxisViewer.Location = new System.Drawing.Point(17, 32); this.Pnl_AxisViewer.Name = "Pnl_AxisViewer"; this.Pnl_AxisViewer.Size = new System.Drawing.Size(146, 142); this.Pnl_AxisViewer.TabIndex = 44; this.Pnl_AxisViewer.Paint += new System.Windows.Forms.PaintEventHandler(this.Pnl_AxisViewer_Paint); // // GUI_RawInputMouse // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.groupBox1); this.Controls.Add(this.Gbox_GunOptions); this.Name = "GUI_RawInputMouse"; this.Size = new System.Drawing.Size(576, 202); this.Gbox_GunOptions.ResumeLayout(false); this.Gbox_GunOptions.PerformLayout(); this.groupBox1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.GroupBox Gbox_GunOptions; private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox Txt_VirtualRightBtn; private System.Windows.Forms.Label Lbl_MidButonEnable; private System.Windows.Forms.CheckBox Chk_VirtualMiddleBtn; private System.Windows.Forms.Label label36; private System.Windows.Forms.TextBox Txt_VirtualMiddleBtn; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.Panel Pnl_AxisViewer; private System.Windows.Forms.FlowLayoutPanel Pnl_ButtonsViewer; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox Txt_VirtualLeftBtn; } } ================================================ FILE: DemulShooter_GUI/GUI_RawInputMouse.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore.Config; using DsCore.Win32; namespace DemulShooter_GUI { public partial class GUI_RawInputMouse : UserControl { private List _Buttons = new List(); PlayerSettings _PlayerData; // DIK registering TextBox private TextBox _SelectedTextBox; private String _SelectedTextBoxTextBackup = String.Empty; private bool _Start_KeyRecord = false; private Win32API.HookProc _KeyboardHookProc; private IntPtr _KeyboardHookID = IntPtr.Zero; public GUI_RawInputMouse() { InitializeComponent(); //Installing KeyboardHook for DIK TextBox _KeyboardHookProc = new Win32API.HookProc(GuiKeyboardHookCallback); using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) _KeyboardHookID = Win32API.SetWindowsHookEx(Win32Define.WH_KEYBOARD_LL, _KeyboardHookProc, Win32API.GetModuleHandle(curModule.ModuleName), 0); if (_KeyboardHookID == IntPtr.Zero) { MessageBox.Show("Failed to register LowLevel Keyboard Hook.", "DemulShooter Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } public void UpdateData(PlayerSettings PlayerData) { _PlayerData = PlayerData; _Buttons.Clear(); Pnl_ButtonsViewer.Controls.Clear(); for (int i = 0; i < _PlayerData.RIController.NumberOfButtons; i++) { _Buttons.Add(new GUI_Button(i + 1)); Pnl_ButtonsViewer.Controls.Add(_Buttons[i]); } Txt_VirtualLeftBtn.Text = GetKeyStringFromScanCode((int)_PlayerData.DIK_VirtualMouseButton_Left); Txt_VirtualMiddleBtn.Text = GetKeyStringFromScanCode((int)_PlayerData.DIK_VirtualMouseButton_Middle); Txt_VirtualRightBtn.Text = GetKeyStringFromScanCode((int)_PlayerData.DIK_VirtualMouseButton_Right); if (!_PlayerData.isVirtualMouseButtonsEnabled) { Chk_VirtualMiddleBtn.Checked = false; Txt_VirtualMiddleBtn.Enabled = false; Txt_VirtualRightBtn.Enabled = false; } else { Chk_VirtualMiddleBtn.Checked = true; Txt_VirtualMiddleBtn.Enabled = true; Txt_VirtualRightBtn.Enabled = true; } } private void Chk_VirtualMiddleBtn_CheckedChanged(object sender, EventArgs e) { _PlayerData.isVirtualMouseButtonsEnabled = Chk_VirtualMiddleBtn.Checked; if (!_PlayerData.isVirtualMouseButtonsEnabled) { Txt_VirtualLeftBtn.Enabled = false; Txt_VirtualMiddleBtn.Enabled = false; Txt_VirtualRightBtn.Enabled = false; } else { Txt_VirtualLeftBtn.Enabled = true; Txt_VirtualMiddleBtn.Enabled = true; Txt_VirtualRightBtn.Enabled = true; } } /// /// Generic "MouseClick" procedure for TextBox, to be able to set keys for config /// public void TXT_DIK_MouseClick(object sender, MouseEventArgs e) { if (_SelectedTextBox != null && _SelectedTextBox != ((TextBox)sender)) { _SelectedTextBox.Text = _SelectedTextBoxTextBackup; } _SelectedTextBox = ((TextBox)sender); _SelectedTextBoxTextBackup = _SelectedTextBox.Text; _SelectedTextBox.Text = ""; _Start_KeyRecord = true; } /// /// Keyboard hook for the GUI part, to detect buttons for config /// private IntPtr GuiKeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (_Start_KeyRecord) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); //_SelectedTextBox.Text = s.scanCode.ToString(); _SelectedTextBox.Text = GetKeyStringFromVkCode(s.vkCode); if (_SelectedTextBox == Txt_VirtualLeftBtn) _PlayerData.DIK_VirtualMouseButton_Left = s.scanCode; else if (_SelectedTextBox == Txt_VirtualMiddleBtn) _PlayerData.DIK_VirtualMouseButton_Middle = s.scanCode; else if (_SelectedTextBox == Txt_VirtualRightBtn) _PlayerData.DIK_VirtualMouseButton_Right = s.scanCode; _SelectedTextBox = null; _Start_KeyRecord = false; return new IntPtr(1); } } } return Win32API.CallNextHookEx(_KeyboardHookID, nCode, wParam, lParam); } private String GetKeyStringFromScanCode(int ScanCode) { uint Vk = Win32API.MapVirtualKey((uint)ScanCode, VirtualKeyMapType.MAPVK_VSC_TO_VK); return GetKeyStringFromVkCode((int)Vk); } private String GetKeyStringFromVkCode(int vkCode) { KeysConverter kc = new KeysConverter(); return kc.ConvertToString((Keys)vkCode); } #region GUI Animation public void UpdateGui() { for (int i = 0; i < _PlayerData.RIController.Hid_Buttons.Length; i++ ) { SetButtonState(i, _PlayerData.RIController.Hid_Buttons[i]); } Pnl_AxisViewer.Invalidate(); } public void SetButtonState(int ButtonID, bool ButtonState) { if (ButtonID <= _Buttons.Count) { _Buttons[ButtonID].Activate(ButtonState); } } private void Pnl_AxisViewer_Paint(object sender, PaintEventArgs e) { base.OnPaint(e); int X = Pnl_AxisViewer.Width / 2; int Y = Pnl_AxisViewer.Height / 2; try { int Xratio = (int)_PlayerData.RIController.Axis_X_Max / Pnl_AxisViewer.Width; int Yratio = (int)_PlayerData.RIController.Axis_Y_Max / Pnl_AxisViewer.Height; X = _PlayerData.RIController.Computed_X / Xratio; Y = _PlayerData.RIController.Computed_Y / Xratio; } catch { } // Draw Crosshair Graphics g = e.Graphics; SolidBrush b = new SolidBrush(Color.Black); Pen p = new Pen(b, 1); g.DrawLine(p, new PointF(X - 4, Y), new PointF(X + 4, Y)); g.DrawLine(p, new PointF(X, Y + 4), new PointF(X, Y - 4)); g.Dispose(); } #endregion } } ================================================ FILE: DemulShooter_GUI/GUI_RawInputMouse.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DemulShooter_GUI/Game_Bhapc - Copy.cs ================================================ using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Collections.Generic; using System.Windows.Forms; using Microsoft.Win32; using System.Runtime.InteropServices; namespace DemulShooter { class Game_Bhapc : Game { private const string FOLDER_GAMEDATA = @"MemoryData\windows"; /*** MEMORY ADDRESSES **/ protected int _P1_X_Offset = 0x000000B8; protected int _P1_Y_Offset = 0x000000BC; protected int _P1_StructPtr_Offset = 0x0123A658; protected string _P1_Axis_Nop_Offset_1 = "0x00124814|7"; protected string _P1_Axis_Nop_Offset_2 = "0x00124F7B|7"; protected int _P1_Axis_Injection_Offset = 0x00124F73; protected int _P1_Axis_Injection_Return_Offset = 0x00124F82; protected Int64 _P1_StructAddress = 0; protected Int64 _P1_X_Address; protected Int64 _P1_Y_Address; protected Int64 _P2_X_Address; protected Int64 _P2_Y_Address; //Custom data to inject protected float _P1_X_Value; protected float _P1_Y_Value; protected float _P2_X_Value; protected float _P2_Y_Value; /// /// Constructor /// public Game_Bhapc(string RomName, bool Verbose) : base() { GetScreenResolution(); _RomName = RomName; _VerboseEnable = Verbose; _ProcessHooked = false; _Target_Process_Name = "Buck"; ReadGameData(); _tProcess = new Timer(); _tProcess.Interval = 500; _tProcess.Tick += new EventHandler(tProcess_Tick); _tProcess.Enabled = true; _tProcess.Start(); WriteLog("Waiting for Windows Game " + _RomName + " game to hook....."); } /// /// Timer event when looking for Process (auto-Hook and auto-close) /// private void tProcess_Tick(Object Sender, EventArgs e) { if (!_ProcessHooked) { try { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length > 0) { _TargetProcess = processes[0]; _ProcessHandle = _TargetProcess.Handle; _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); if (_TargetProcess_MemoryBaseAddress != null) { byte[] bBuffer = ReadBytes_x64((Int64)_TargetProcess_MemoryBaseAddress + _P1_StructPtr_Offset, 8); _P1_StructAddress = BitConverter.ToInt64(bBuffer, 0); if (_P1_StructAddress != 0) { _ProcessHooked = true; WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X16")); WriteLog("P1_StructAddress = 0x" + _P1_StructAddress.ToString("X16")); ApplyKeyboardHook(); SetHack(); } } } } catch (Exception ex) { WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); WriteLog(ex.Message.ToString()); } } else { Process[] processes = Process.GetProcessesByName(_Target_Process_Name); if (processes.Length <= 0) { _ProcessHooked = false; _TargetProcess = null; _ProcessHandle = IntPtr.Zero; _TargetProcess_MemoryBaseAddress = IntPtr.Zero; WriteLog(_Target_Process_Name + ".exe closed"); Environment.Exit(0); } } } #region Screen public override bool GameScale(MouseInfo Mouse, int Player) { if (_ProcessHandle != IntPtr.Zero) { try { //Window size Win32.Rect TotalRes = new Win32.Rect(); Win32.GetClientRect(_TargetProcess.MainWindowHandle, ref TotalRes); double TotalResX = TotalRes.Right - TotalRes.Left; double TotalResY = TotalRes.Bottom - TotalRes.Top; //X => [0 - ClientWidth] //Y => [ClientHeight - 0] Mouse.pTarget.Y = (int)TotalResY - Mouse.pTarget.Y; if (Mouse.pTarget.X < 0) Mouse.pTarget.X = 0; if (Mouse.pTarget.Y < 0) Mouse.pTarget.Y = 0; if (Mouse.pTarget.X > (int)TotalResX) Mouse.pTarget.X = (int)TotalResX; if (Mouse.pTarget.Y > (int)TotalResY) Mouse.pTarget.Y = (int)TotalResY; if (Player == 1) { _P1_X_Value = (float)(Mouse.pTarget.X); _P1_Y_Value = (float)(Mouse.pTarget.Y); } else if (Player == 2) { _P2_X_Value = (float)(Mouse.pTarget.X); _P2_Y_Value = (float)(Mouse.pTarget.Y); } return true; } catch (Exception ex) { WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); } } return false; } #endregion #region MemoryHack [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "ReadProcessMemory")] public static extern bool ReadProcessMemory_x64(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "WriteProcessMemory")] public static extern bool WriteProcessMemory_x64(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten); protected Byte[] ReadBytes_x64(Int64 X64_Address, int Bytes) { byte[] Buffer = new byte[Bytes]; int bytesRead = 0; if (!ReadProcessMemory_x64((int)_ProcessHandle, X64_Address, Buffer, Buffer.Length, ref bytesRead)) { WriteLog("Cannot read memory at address 0x" + X64_Address.ToString("X8")); } return Buffer; } protected bool WriteByte_x64(Int64 Address, byte Value) { int bytesWritten = 0; Byte[] Buffer = { Value }; if (WriteProcessMemory_x64((int)_ProcessHandle, Address, Buffer, 1, ref bytesWritten)) { if (bytesWritten == 1) return true; else return false; } else return false; } protected bool WriteBytes_x64(Int64 Address, byte[] Buffer) { int bytesWritten = 0; if (WriteProcessMemory_x64((int)_ProcessHandle, Address, Buffer, Buffer.Length, ref bytesWritten)) { if (bytesWritten == Buffer.Length) return true; else return false; } else return false; } protected void SetNops_x64(Int64 BaseAddress, string OffsetAndNumber) { if (OffsetAndNumber != null) { try { int n = int.Parse((OffsetAndNumber.Split('|'))[1]); int address = int.Parse((OffsetAndNumber.Split('|'))[0].Substring(3).Trim(), NumberStyles.HexNumber); for (int i = 0; i < n; i++) { WriteByte_x64(BaseAddress + address + i, 0x90); } } catch { WriteLog("Impossible de traiter le NOP : " + OffsetAndNumber); } } } private void SetHack() { SetNops_x64((Int64)_TargetProcess_MemoryBaseAddress, _P1_Axis_Nop_Offset_1); //SetNops_x64((Int64)_TargetProcess_MemoryBaseAddress, _P1_Axis_Nop_Offset_2); SetHack_Data(); SetHack_P1Axis(); WriteLog("Memory Hack complete !"); WriteLog("-"); } /*** Creating a custom memory bank to store our data ***/ private void SetHack_Data() { //1st Codecave : storing our Axis Data Memory DataCaveMemory = new Memory(_TargetProcess, _TargetProcess.MainModule.BaseAddress); DataCaveMemory.Open(); DataCaveMemory.Alloc(0x800); _P1_X_Address = (Int64)DataCaveMemory.CaveAddress; _P1_Y_Address = (Int64)DataCaveMemory.CaveAddress + 0x04; _P2_X_Address = (Int64)DataCaveMemory.CaveAddress + 0x10; _P2_Y_Address = (Int64)DataCaveMemory.CaveAddress + 0x14; WriteLog("Custom data will be stored at : 0x" + _P1_X_Address.ToString("X16")); } /*** Creating a custom memory bank to store our data ***/ private void SetHack_P1Axis() { Memory CaveMemory = new Memory(_TargetProcess, _TargetProcess.MainModule.BaseAddress); CaveMemory.Open(); CaveMemory.Alloc(0x800); WriteLog("Adding P1_InGame_Y CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); List Buffer = new List(); //push rax CaveMemory.Write_StrBytes("50"); //mov rax, _P1_X_Address CaveMemory.Write_StrBytes("48 A1"); byte[] b = BitConverter.GetBytes(_P1_X_Address); CaveMemory.Write_Bytes(b); //mov [rsp+000000B0], rax CaveMemory.Write_StrBytes("48 89 84 24 B0 00 00 00"); //pop rax CaveMemory.Write_StrBytes("58"); //mov rcx, [rsp+0x000000B0] CaveMemory.Write_StrBytes("48 8B 8C 24 B0 00 00 00"); //mov [rax+0x000000B8], rcx CaveMemory.Write_StrBytes("48 89 88 B8 00 00 00"); //jmp Buck.exe+_P1_Axis_Injection_Return_Offset CaveMemory.Write_StrBytes("FF 25 00 00 00 00"); b = BitConverter.GetBytes((Int64)_TargetProcess_MemoryBaseAddress + _P1_Axis_Injection_Return_Offset); CaveMemory.Write_Bytes(b); WriteLog("Adding P1_Axis CodeCave at : 0x" + CaveMemory.CaveAddress.ToString("X16")); //Code injection IntPtr ProcessHandle = _TargetProcess.Handle; int bytesWritten = 0; Int64 jumpTo = 0; jumpTo = (Int64)CaveMemory.CaveAddress - ((Int64)_TargetProcess_MemoryBaseAddress + _P1_Axis_Injection_Offset) - 5; Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); Buffer.Add(0x90); Buffer.Add(0x90); Buffer.Add(0x90); //WriteProcessMemory_x64((int)ProcessHandle, (Int64)_TargetProcess_MemoryBaseAddress + _P1_Axis_Injection_Offset, Buffer.ToArray(), Buffer.Count, ref bytesWritten); } // Mouse callback for low level hook /*protected override IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && (UInt32)wParam == Win32.WM_MBUTTONDOWN) { //Just blocking middle clicks return new IntPtr(1); } else if (nCode >= 0 && (UInt32)wParam == Win32.WM_RBUTTONDOWN) { //Just blocking right clicks => if not P1 reload play animation but does not reload return new IntPtr(1); } return Win32.CallNextHookEx(_MouseHookID, nCode, wParam, lParam); }*/ public override void SendInput(MouseInfo mouse, int Player) { if (Player == 1) { //Setting Values in memory for the Codecave to read it byte[] buffer = BitConverter.GetBytes(_P1_X_Value); WriteBytes_x64(_P1_X_Address, buffer); buffer = BitConverter.GetBytes(_P1_Y_Value); WriteBytes_x64(_P1_Y_Address, buffer); /*//Inputs if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_DOWN) { SendKeyDown(_P1_Trigger_DIK); } else if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_UP) { SendKeyUp(_P1_Trigger_DIK); } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_DOWN) { if (P1_Next_Dir.Equals("left")) SendKeyDown(_P1_Left_DIK); else SendKeyDown(_P1_Right_DIK); } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_UP) { if (P1_Next_Dir.Equals("left")) { SendKeyUp(_P1_Left_DIK); P1_Next_Dir = "right"; } else { SendKeyUp(_P1_Right_DIK); P1_Next_Dir = "left"; } } if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_DOWN) { SendKeyDown(_P1_Reload_DIK); } else if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_UP) { SendKeyUp(_P1_Reload_DIK); }*/ } else if (Player == 2) { /* //Inputs if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_DOWN) { SendKeyDown(_P2_Trigger_DIK); } else if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_UP) { SendKeyUp(_P2_Trigger_DIK); } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_DOWN) { if (P2_Next_Dir.Equals("left")) SendKeyDown(_P2_Left_DIK); else SendKeyDown(_P2_Right_DIK); } else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_UP) { if (P2_Next_Dir.Equals("left")) { SendKeyUp(_P2_Left_DIK); P2_Next_Dir = "right"; } else { SendKeyUp(_P2_Right_DIK); P2_Next_Dir = "left"; } } if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_DOWN) { SendKeyDown(_P2_Reload_DIK); } else if (mouse.button == Win32.RI_MOUSE_RIGHT_BUTTON_UP) { SendKeyUp(_P2_Reload_DIK); }*/ } } #endregion // Keyboard will be used to use Grenade protected override IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { Win32.KBDLLHOOKSTRUCT s = (Win32.KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.KBDLLHOOKSTRUCT)); if ((UInt32)wParam == Win32.WM_KEYDOWN) { switch (s.scanCode) { case 0x22: //G { try { byte[] bBuffer = ReadBytes_x64((Int64)_TargetProcess_MemoryBaseAddress + 0x01285E70, 8); Int64 MouseDeltaX = BitConverter.ToInt64(bBuffer, 0) + 8; WriteLog("MouseDeltaX = 0x" + MouseDeltaX.ToString("X16")); bBuffer = ReadBytes_x64(MouseDeltaX, 4); //WriteLog(bBuffer[0].ToString("X2") + ", " + bBuffer[1].ToString("X2") + ", " + bBuffer[2].ToString("X2") + ", " + bBuffer[3].ToString("X2")); float f = BitConverter.ToSingle(bBuffer, 0); WriteLog("MouseDeltaX value = " + f.ToString()); f = f + 10.0f; bBuffer = BitConverter.GetBytes(f); WriteBytes_x64(MouseDeltaX, bBuffer); } catch (Exception ex) { WriteLog("Convertion error : " + ex.Message.ToString()); } } break; default: break; } } /*else if ((UInt32)wParam == Win32.WM_KEYUP) { switch (s.scanCode) { case _P1_Grenade_ScanCode: { Apply_AND_ByteMask((int)_TargetProcess_MemoryBaseAddress + _P1_Buttons_Offset, 0xFB); } break; default: break; } }*/ } return Win32.CallNextHookEx(_KeyboardHookID, nCode, wParam, lParam); } } } ================================================ FILE: DemulShooter_GUI/Program.cs ================================================ using System; using System.Windows.Forms; namespace DemulShooter_GUI { static class Program { /// /// Application entry point /// [STAThread] static void Main(string[] args) { bool isVerbose = false; if (args.Length > 0) { for (int i = 0; i < args.Length; i++) { if (args[i].ToLower().Equals("-v") || args[i].ToLower().Equals("--verbose")) { isVerbose = true; } } } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Wnd_DemulShooterGui(isVerbose)); } } } ================================================ FILE: DemulShooter_GUI/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("DemulShooter GUI")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DemulShooter GUI")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("0b646fe6-b0fc-40c9-ad26-a5f8661f4cea")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : [assembly: AssemblyVersion("17.3.0")] ================================================ FILE: DemulShooter_GUI/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. // //------------------------------------------------------------------------------ namespace DemulShooter_GUI.Properties { using System; /// /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. /// // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder // à l'aide d'un outil, tel que ResGen ou Visual Studio. // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen // avec l'option /str ou régénérez votre projet VS. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DemulShooter_GUI.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Remplace la propriété CurrentUICulture du thread actuel pour toutes /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Recherche une ressource localisée de type System.Byte[]. /// internal static byte[] Crosshair { get { object obj = ResourceManager.GetObject("Crosshair", resourceCulture); return ((byte[])(obj)); } } /// /// Recherche une ressource localisée de type System.Byte[]. /// internal static byte[] dinput8_blocker { get { object obj = ResourceManager.GetObject("dinput8_blocker", resourceCulture); return ((byte[])(obj)); } } /// /// Recherche une chaîne localisée semblable à [Wiimote1] ///Device = DInput/0/Keyboard Mouse ///Buttons/A = `Click 2` ///Buttons/B = `Click 0` ///Buttons/1 = 1 ///Buttons/2 = 2 ///Buttons/- = SUBTRACT ///Buttons/+ = ADD ///Buttons/Home = HOME ///Shake/X = `Click 1` ///Shake/Y = `Click 1` ///Shake/Z = `Click 1` ///Extension = Nunchuk ///Nunchuk/Buttons/C = LCONTROL ///Nunchuk/Buttons/Z = LSHIFT ///Nunchuk/Stick/Up = W ///Nunchuk/Stick/Down = S ///Nunchuk/Stick/Left = A ///Nunchuk/Stick/Right = D ///D-Pad/Up = UP ///D-Pad/Down = DOWN ///D-Pad/Left = LEFT ///D-Pad/Right = RIGHT ///Source = 1 ///IR/Left [le reste de la chaîne a été tronqué]";. /// internal static string WiimoteNew_v4 { get { return ResourceManager.GetString("WiimoteNew_v4", resourceCulture); } } /// /// Recherche une chaîne localisée semblable à [Wiimote1] ///Device = DInput/0/Keyboard Mouse ///Buttons/A = `Click 2` ///Buttons/B = `Click 0` ///Buttons/1 = `1` ///Buttons/2 = `2` ///Buttons/- = `3` ///Buttons/+ = `4` ///Buttons/Home = `5` ///IR/Up = `Cursor Y-` ///IR/Down = Cursor Y+ ///IR/Left = Cursor X- ///IR/Right = Cursor X+ ///Shake/X = `Click 1` ///Shake/Y = `Click 1` ///Shake/Z = `Click 1` ///Extension = Nunchuk ///Nunchuk/Buttons/C = LCONTROL ///Nunchuk/Buttons/Z = LSHIFT ///Nunchuk/Stick/Up = W ///Nunchuk/Stick/Down = S ///Nunchuk/Stick/Left = A ///Nunchuk/Stick/Right = D ///D-Pad/Up = [le reste de la chaîne a été tronqué]";. /// internal static string WiimoteNew_v5 { get { return ResourceManager.GetString("WiimoteNew_v5", resourceCulture); } } /// /// Recherche une ressource localisée de type System.Byte[]. /// internal static byte[] xinput1_3 { get { object obj = ResourceManager.GetObject("xinput1_3", resourceCulture); return ((byte[])(obj)); } } } } ================================================ FILE: DemulShooter_GUI/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\Crosshair.cur;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\dinput8.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\WiimoteNew_v4.ini;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 ..\Resources\WiimoteNew_v5.ini;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 ..\Resources\xinput1_3.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DemulShooter_GUI/Properties/Settings.Designer.cs ================================================ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. // //------------------------------------------------------------------------------ namespace DemulShooter_GUI.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } } } ================================================ FILE: DemulShooter_GUI/Properties/Settings.settings ================================================  ================================================ FILE: DemulShooter_GUI/Resources/WiimoteNew_v4.ini ================================================ [Wiimote1] Device = DInput/0/Keyboard Mouse Buttons/A = `Click 2` Buttons/B = `Click 0` Buttons/1 = 1 Buttons/2 = 2 Buttons/- = SUBTRACT Buttons/+ = ADD Buttons/Home = HOME Shake/X = `Click 1` Shake/Y = `Click 1` Shake/Z = `Click 1` Extension = Nunchuk Nunchuk/Buttons/C = LCONTROL Nunchuk/Buttons/Z = LSHIFT Nunchuk/Stick/Up = W Nunchuk/Stick/Down = S Nunchuk/Stick/Left = A Nunchuk/Stick/Right = D D-Pad/Up = UP D-Pad/Down = DOWN D-Pad/Left = LEFT D-Pad/Right = RIGHT Source = 1 IR/Left = Cursor X- IR/Right = Cursor X+ IR/Down = Cursor Y+ IR/Up = Cursor Y- [Wiimote2] Source = 1 Device = SDL/0/ATRAK Device #2 Buttons/A = `Button 0` Buttons/B = `Button 1` Buttons/+ = `Button 2` Buttons/- = `Button 5` IR/Up = `Axis 1-` IR/Down = `Axis 1+` Shake/X = `Button 3` Shake/Y = `Button 3` Shake/Z = `Button 3` Extension = Nunchuk Nunchuk/Buttons/C = LCONTROL Nunchuk/Buttons/Z = LSHIFT Nunchuk/Stick/Up = W Nunchuk/Stick/Down = S Nunchuk/Stick/Left = A Nunchuk/Stick/Right = D D-Pad/Right = `Button 4` IR/Left = `Axis 0-` IR/Right = `Axis 0+` [Wiimote3] UDP Wiimote/Update_Accel = 0 UDP Wiimote/Update_IR = 0 UDP Wiimote/Update_Butt = 0 UDP Wiimote/Update_Nunchuk = 0 Source = 0 [Wiimote4] Source = 0 [Wiimote5] UDP Wiimote/Update_Accel = 0 UDP Wiimote/Update_IR = 0 UDP Wiimote/Update_Butt = 0 UDP Wiimote/Update_Nunchuk = 0 [BalanceBoard] Source = 0 ================================================ FILE: DemulShooter_GUI/Resources/WiimoteNew_v5.ini ================================================ [Wiimote1] Device = DInput/0/Keyboard Mouse Buttons/A = `Click 2` Buttons/B = `Click 0` Buttons/1 = `1` Buttons/2 = `2` Buttons/- = `3` Buttons/+ = `4` Buttons/Home = `5` IR/Up = `Cursor Y-` IR/Down = Cursor Y+ IR/Left = Cursor X- IR/Right = Cursor X+ Shake/X = `Click 1` Shake/Y = `Click 1` Shake/Z = `Click 1` Extension = Nunchuk Nunchuk/Buttons/C = LCONTROL Nunchuk/Buttons/Z = LSHIFT Nunchuk/Stick/Up = W Nunchuk/Stick/Down = S Nunchuk/Stick/Left = A Nunchuk/Stick/Right = D D-Pad/Up = UP D-Pad/Down = DOWN D-Pad/Left = LEFT D-Pad/Right = RIGHT Source = 1 Swing/Left = `6` Tilt/Left = `7` [Wiimote2] Source = 1 Device = DInput/0/Keyboard Mouse Buttons/A = D Buttons/B = S IR/Up = `DInput/0/ATRAK Device #2:Axis Y-` IR/Down = `DInput/0/ATRAK Device #2:Axis Y+` IR/Left = `DInput/0/ATRAK Device #2:Axis X-` IR/Right = `DInput/0/ATRAK Device #2:Axis X+` Shake/X = F Shake/Y = F Shake/Z = F D-Pad/Up = `NUMPAD8` D-Pad/Down = `NUMPAD2` D-Pad/Left = `NUMPAD4` D-Pad/Right = `NUMPAD6` [Wiimote3] Source = 0 [Wiimote4] Source = 0 [BalanceBoard] Source = 0 ================================================ FILE: DemulShooter_GUI/Wnd_DemulShooterGui.Designer.cs ================================================ namespace DemulShooter_GUI { partial class Wnd_DemulShooterGui { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur Windows Form /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Wnd_DemulShooterGui)); this.Btn_Save_P1 = new System.Windows.Forms.Button(); this.tabControl1 = new System.Windows.Forms.TabControl(); this.Tab_P1 = new System.Windows.Forms.TabPage(); this.Tab_P2 = new System.Windows.Forms.TabPage(); this.Btn_SaveP2 = new System.Windows.Forms.Button(); this.Tab_P3 = new System.Windows.Forms.TabPage(); this.Btn_Save_P3 = new System.Windows.Forms.Button(); this.Tab_P4 = new System.Windows.Forms.TabPage(); this.Btn_Save_P4 = new System.Windows.Forms.Button(); this.Tab_AnalogCalib = new System.Windows.Forms.TabPage(); this.TableLayout_Calib = new System.Windows.Forms.TableLayoutPanel(); this.Btn_SaveAnalog = new System.Windows.Forms.Button(); this.Tab_ActLAbs = new System.Windows.Forms.TabPage(); this.Btn_ActLabs_Save = new System.Windows.Forms.Button(); this.groupBox3 = new System.Windows.Forms.GroupBox(); this.label47 = new System.Windows.Forms.Label(); this.Txt_ActLabs_Y4 = new System.Windows.Forms.TextBox(); this.label48 = new System.Windows.Forms.Label(); this.Txt_ActLabs_X4 = new System.Windows.Forms.TextBox(); this.label49 = new System.Windows.Forms.Label(); this.Txt_ActLabs_Y3 = new System.Windows.Forms.TextBox(); this.label50 = new System.Windows.Forms.Label(); this.Txt_ActLabs_X3 = new System.Windows.Forms.TextBox(); this.label18 = new System.Windows.Forms.Label(); this.Txt_ActLabs_Y2 = new System.Windows.Forms.TextBox(); this.label19 = new System.Windows.Forms.Label(); this.Txt_ActLabs_X2 = new System.Windows.Forms.TextBox(); this.Chk_DspCorrectedCrosshair = new System.Windows.Forms.CheckBox(); this.label17 = new System.Windows.Forms.Label(); this.Txt_ActLabs_Y1 = new System.Windows.Forms.TextBox(); this.label16 = new System.Windows.Forms.Label(); this.Txt_ActLabs_X1 = new System.Windows.Forms.TextBox(); this.Cb_ActLabsOffset = new System.Windows.Forms.CheckBox(); this.label15 = new System.Windows.Forms.Label(); this.Tab_Dolphin = new System.Windows.Forms.TabPage(); this.label2 = new System.Windows.Forms.Label(); this.Btn_Dolphin5 = new System.Windows.Forms.Button(); this.Tab_EAInvasion = new System.Windows.Forms.TabPage(); this.groupBox17 = new System.Windows.Forms.GroupBox(); this.Txt_EAI_Settings = new System.Windows.Forms.TextBox(); this.label76 = new System.Windows.Forms.Label(); this.Txt_EAI_P1_Start = new System.Windows.Forms.TextBox(); this.Btn_EAI_Save = new System.Windows.Forms.Button(); this.Txt_EAI_P1_Credits = new System.Windows.Forms.TextBox(); this.label69 = new System.Windows.Forms.Label(); this.Txt_EAI_Enter = new System.Windows.Forms.TextBox(); this.label70 = new System.Windows.Forms.Label(); this.Txt_EAI_Up = new System.Windows.Forms.TextBox(); this.Txt_EAI_Down = new System.Windows.Forms.TextBox(); this.label71 = new System.Windows.Forms.Label(); this.label72 = new System.Windows.Forms.Label(); this.label73 = new System.Windows.Forms.Label(); this.Txt_EAI_P2_Start = new System.Windows.Forms.TextBox(); this.label74 = new System.Windows.Forms.Label(); this.label75 = new System.Windows.Forms.Label(); this.Txt_EAI_P2_Credits = new System.Windows.Forms.TextBox(); this.groupBox16 = new System.Windows.Forms.GroupBox(); this.Btn_EAI_Patch = new System.Windows.Forms.Button(); this.Txt_EAI_FolderPath = new System.Windows.Forms.TextBox(); this.Btn_EAI_Open = new System.Windows.Forms.Button(); this.label68 = new System.Windows.Forms.Label(); this.Tab_GSOZ = new System.Windows.Forms.TabPage(); this.Btn_Save_Gsoz = new System.Windows.Forms.Button(); this.label34 = new System.Windows.Forms.Label(); this.groupBox6 = new System.Windows.Forms.GroupBox(); this.label33 = new System.Windows.Forms.Label(); this.TXT_GSOZ_PEDAL_2 = new System.Windows.Forms.TextBox(); this.Chk_GundamP2Pedal = new System.Windows.Forms.CheckBox(); this.groupBox5 = new System.Windows.Forms.GroupBox(); this.label9 = new System.Windows.Forms.Label(); this.TXT_GSOZ_PEDAL_1 = new System.Windows.Forms.TextBox(); this.Chk_GundamP1Pedal = new System.Windows.Forms.CheckBox(); this.Tab_HeavyFire = new System.Windows.Forms.TabPage(); this.Rdo_HF_MiddleGrenade = new System.Windows.Forms.RadioButton(); this.Rdo_HF_MiddleCover = new System.Windows.Forms.RadioButton(); this.Gbox_HF_Grenade = new System.Windows.Forms.GroupBox(); this.label27 = new System.Windows.Forms.Label(); this.Gbox_HF_Cover = new System.Windows.Forms.GroupBox(); this.label40 = new System.Windows.Forms.Label(); this.label45 = new System.Windows.Forms.Label(); this.Chk_HF_ReverseCover = new System.Windows.Forms.CheckBox(); this.TrackBar_HF_Cover = new System.Windows.Forms.TrackBar(); this.Btn_HF_Save = new System.Windows.Forms.Button(); this.label41 = new System.Windows.Forms.Label(); this.Tab_LethalEnforcer3 = new System.Windows.Forms.TabPage(); this.Btn_Save_Le3 = new System.Windows.Forms.Button(); this.label58 = new System.Windows.Forms.Label(); this.groupBox14 = new System.Windows.Forms.GroupBox(); this.label59 = new System.Windows.Forms.Label(); this.TXT_LE3_PEDAL_2 = new System.Windows.Forms.TextBox(); this.Chk_Le3_EnablePedal2 = new System.Windows.Forms.CheckBox(); this.groupBox15 = new System.Windows.Forms.GroupBox(); this.label60 = new System.Windows.Forms.Label(); this.TXT_LE3_PEDAL_1 = new System.Windows.Forms.TextBox(); this.Chk_Le3_EnablePedal1 = new System.Windows.Forms.CheckBox(); this.Tab_M2 = new System.Windows.Forms.TabPage(); this.groupBox2 = new System.Windows.Forms.GroupBox(); this.panel1 = new System.Windows.Forms.Panel(); this.Cbox_M2_Flash = new System.Windows.Forms.CheckBox(); this.label14 = new System.Windows.Forms.Label(); this.TXT_CH_P1 = new System.Windows.Forms.TextBox(); this.label13 = new System.Windows.Forms.Label(); this.TXT_CH_P2 = new System.Windows.Forms.TextBox(); this.label11 = new System.Windows.Forms.Label(); this.TXT_CH_VIS = new System.Windows.Forms.TextBox(); this.Btn_M2Scripts = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.Tab_MissionImpossible = new System.Windows.Forms.TabPage(); this.groupBox4 = new System.Windows.Forms.GroupBox(); this.Rdo_MIA_Merge = new System.Windows.Forms.RadioButton(); this.Rdo_MIA_Separate = new System.Windows.Forms.RadioButton(); this.label42 = new System.Windows.Forms.Label(); this.Btn_MisImp_Save = new System.Windows.Forms.Button(); this.Tab_OpGhost = new System.Windows.Forms.TabPage(); this.label67 = new System.Windows.Forms.Label(); this.groupBox18 = new System.Windows.Forms.GroupBox(); this.Cbox_OpGhost_CreditsToContinue = new System.Windows.Forms.ComboBox(); this.label66 = new System.Windows.Forms.Label(); this.Cbox_OpGhost_CreditsToStart = new System.Windows.Forms.ComboBox(); this.label65 = new System.Windows.Forms.Label(); this.Cbox_OpGhost_CreditsByCoin = new System.Windows.Forms.ComboBox(); this.label64 = new System.Windows.Forms.Label(); this.Cbox_OpGhost_Freeplay = new System.Windows.Forms.ComboBox(); this.label62 = new System.Windows.Forms.Label(); this.Btn_Save_OpGhost = new System.Windows.Forms.Button(); this.Gbox_OpGhost_Buttons = new System.Windows.Forms.GroupBox(); this.label61 = new System.Windows.Forms.Label(); this.TXT_OPGHOST_ACTION_P2 = new System.Windows.Forms.TextBox(); this.label63 = new System.Windows.Forms.Label(); this.TXT_OPGHOST_ACTION_P1 = new System.Windows.Forms.TextBox(); this.Chk_OpGhost_SeparateButton = new System.Windows.Forms.CheckBox(); this.Tab_UnityPlugins = new System.Windows.Forms.TabPage(); this.label36 = new System.Windows.Forms.Label(); this.Tab_Raccoon = new System.Windows.Forms.TabPage(); this.groupBox21 = new System.Windows.Forms.GroupBox(); this.Btn_Raccoon_Patch = new System.Windows.Forms.Button(); this.Txt_Raccoon_FolderPath = new System.Windows.Forms.TextBox(); this.Btn_Raccoon_Open = new System.Windows.Forms.Button(); this.Tab_RPCS3 = new System.Windows.Forms.TabPage(); this.Txt_Rpcs3_Save = new System.Windows.Forms.Button(); this.groupBox13 = new System.Windows.Forms.GroupBox(); this.Btn_Rpcs3_RazingStorm = new System.Windows.Forms.Button(); this.Btn_Rpcs3_SailorZombies = new System.Windows.Forms.Button(); this.Btn_Rpcs3_DarkEscape = new System.Windows.Forms.Button(); this.Btn_Rpcs3_DeadStorm = new System.Windows.Forms.Button(); this.groupBox12 = new System.Windows.Forms.GroupBox(); this.label57 = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); this.label37 = new System.Windows.Forms.Label(); this.Txt_Rpcs3_P1_Start = new System.Windows.Forms.TextBox(); this.Txt_Rpcs3_Service = new System.Windows.Forms.TextBox(); this.Txt_Rpcs3_P2_Start = new System.Windows.Forms.TextBox(); this.label56 = new System.Windows.Forms.Label(); this.label38 = new System.Windows.Forms.Label(); this.label55 = new System.Windows.Forms.Label(); this.Txt_Rpcs3_Enter = new System.Windows.Forms.TextBox(); this.Txt_Rpcs3_3D_Switch = new System.Windows.Forms.TextBox(); this.label54 = new System.Windows.Forms.Label(); this.label51 = new System.Windows.Forms.Label(); this.label53 = new System.Windows.Forms.Label(); this.Txt_Rpcs3_Up = new System.Windows.Forms.TextBox(); this.label52 = new System.Windows.Forms.Label(); this.Txt_Rpcs3_Down = new System.Windows.Forms.TextBox(); this.Tab_SHA = new System.Windows.Forms.TabPage(); this.label35 = new System.Windows.Forms.Label(); this.TXT_P1_S = new System.Windows.Forms.TextBox(); this.TXT_P1_T = new System.Windows.Forms.TextBox(); this.label8 = new System.Windows.Forms.Label(); this.TXT_SERVICE = new System.Windows.Forms.TextBox(); this.Save_Sha_Keys = new System.Windows.Forms.Button(); this.label7 = new System.Windows.Forms.Label(); this.TXT_EXIT = new System.Windows.Forms.TextBox(); this.TXT_TEST = new System.Windows.Forms.TextBox(); this.label12 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.TXT_P2_S = new System.Windows.Forms.TextBox(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.TXT_P2_T = new System.Windows.Forms.TextBox(); this.Tab_Outputs = new System.Windows.Forms.TabPage(); this.groupBox19 = new System.Windows.Forms.GroupBox(); this.Cbox_NetOutputs = new System.Windows.Forms.CheckBox(); this.Cbox_WmOutputs = new System.Windows.Forms.CheckBox(); this.Cbox_Outputs = new System.Windows.Forms.CheckBox(); this.Btn_SaveOutput = new System.Windows.Forms.Button(); this.Grp_Outputs = new System.Windows.Forms.GroupBox(); this.label21 = new System.Windows.Forms.Label(); this.label24 = new System.Windows.Forms.Label(); this.Txt_OutputRecoilOff = new System.Windows.Forms.TextBox(); this.label23 = new System.Windows.Forms.Label(); this.label20 = new System.Windows.Forms.Label(); this.label10 = new System.Windows.Forms.Label(); this.label22 = new System.Windows.Forms.Label(); this.Txt_OutputRecoilOn = new System.Windows.Forms.TextBox(); this.label25 = new System.Windows.Forms.Label(); this.Txt_OutputDamaged = new System.Windows.Forms.TextBox(); this.label26 = new System.Windows.Forms.Label(); this.Txt_OutputDelay = new System.Windows.Forms.TextBox(); this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog(); this.Bgw_XInput = new System.ComponentModel.BackgroundWorker(); this.Cbo_PageSettings = new System.Windows.Forms.ComboBox(); this.label39 = new System.Windows.Forms.Label(); this.Btn_Dcop = new System.Windows.Forms.Button(); this.Btn_Drk = new System.Windows.Forms.Button(); this.Btn_MarsS = new System.Windows.Forms.Button(); this.Btn_Mib = new System.Windows.Forms.Button(); this.Btn_Mia = new System.Windows.Forms.Button(); this.Btn_Nerfa = new System.Windows.Forms.Button(); this.Btn_Nha = new System.Windows.Forms.Button(); this.Btn_Owr = new System.Windows.Forms.Button(); this.Btn_PvZ = new System.Windows.Forms.Button(); this.Btn_Rha = new System.Windows.Forms.Button(); this.Btn_Tra = new System.Windows.Forms.Button(); this.Btn_Pbx = new System.Windows.Forms.Button(); this.Btn_Wws = new System.Windows.Forms.Button(); this.tabControl1.SuspendLayout(); this.Tab_P1.SuspendLayout(); this.Tab_P2.SuspendLayout(); this.Tab_P3.SuspendLayout(); this.Tab_P4.SuspendLayout(); this.Tab_AnalogCalib.SuspendLayout(); this.Tab_ActLAbs.SuspendLayout(); this.groupBox3.SuspendLayout(); this.Tab_Dolphin.SuspendLayout(); this.Tab_EAInvasion.SuspendLayout(); this.groupBox17.SuspendLayout(); this.groupBox16.SuspendLayout(); this.Tab_GSOZ.SuspendLayout(); this.groupBox6.SuspendLayout(); this.groupBox5.SuspendLayout(); this.Tab_HeavyFire.SuspendLayout(); this.Gbox_HF_Grenade.SuspendLayout(); this.Gbox_HF_Cover.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.TrackBar_HF_Cover)).BeginInit(); this.Tab_LethalEnforcer3.SuspendLayout(); this.groupBox14.SuspendLayout(); this.groupBox15.SuspendLayout(); this.Tab_M2.SuspendLayout(); this.groupBox2.SuspendLayout(); this.Tab_MissionImpossible.SuspendLayout(); this.groupBox4.SuspendLayout(); this.Tab_OpGhost.SuspendLayout(); this.groupBox18.SuspendLayout(); this.Gbox_OpGhost_Buttons.SuspendLayout(); this.Tab_UnityPlugins.SuspendLayout(); this.Tab_Raccoon.SuspendLayout(); this.groupBox21.SuspendLayout(); this.Tab_RPCS3.SuspendLayout(); this.groupBox13.SuspendLayout(); this.groupBox12.SuspendLayout(); this.Tab_SHA.SuspendLayout(); this.Tab_Outputs.SuspendLayout(); this.groupBox19.SuspendLayout(); this.Grp_Outputs.SuspendLayout(); this.SuspendLayout(); // // Btn_Save_P1 // this.Btn_Save_P1.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Save_P1.Location = new System.Drawing.Point(217, 284); this.Btn_Save_P1.Margin = new System.Windows.Forms.Padding(4); this.Btn_Save_P1.Name = "Btn_Save_P1"; this.Btn_Save_P1.Size = new System.Drawing.Size(145, 37); this.Btn_Save_P1.TabIndex = 32; this.Btn_Save_P1.Text = "Save Config"; this.Btn_Save_P1.UseVisualStyleBackColor = true; this.Btn_Save_P1.Click += new System.EventHandler(this.Btn_Save_Cfg_Click); // // tabControl1 // this.tabControl1.Appearance = System.Windows.Forms.TabAppearance.FlatButtons; this.tabControl1.Controls.Add(this.Tab_P1); this.tabControl1.Controls.Add(this.Tab_P2); this.tabControl1.Controls.Add(this.Tab_P3); this.tabControl1.Controls.Add(this.Tab_P4); this.tabControl1.Controls.Add(this.Tab_AnalogCalib); this.tabControl1.Controls.Add(this.Tab_ActLAbs); this.tabControl1.Controls.Add(this.Tab_Dolphin); this.tabControl1.Controls.Add(this.Tab_EAInvasion); this.tabControl1.Controls.Add(this.Tab_GSOZ); this.tabControl1.Controls.Add(this.Tab_HeavyFire); this.tabControl1.Controls.Add(this.Tab_LethalEnforcer3); this.tabControl1.Controls.Add(this.Tab_M2); this.tabControl1.Controls.Add(this.Tab_MissionImpossible); this.tabControl1.Controls.Add(this.Tab_OpGhost); this.tabControl1.Controls.Add(this.Tab_Raccoon); this.tabControl1.Controls.Add(this.Tab_RPCS3); this.tabControl1.Controls.Add(this.Tab_SHA); this.tabControl1.Controls.Add(this.Tab_UnityPlugins); this.tabControl1.Controls.Add(this.Tab_Outputs); this.tabControl1.ItemSize = new System.Drawing.Size(0, 1); this.tabControl1.Location = new System.Drawing.Point(2, 47); this.tabControl1.Margin = new System.Windows.Forms.Padding(4); this.tabControl1.Name = "tabControl1"; this.tabControl1.SelectedIndex = 0; this.tabControl1.Size = new System.Drawing.Size(589, 343); this.tabControl1.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; this.tabControl1.TabIndex = 38; // // Tab_P1 // this.Tab_P1.BackColor = System.Drawing.SystemColors.Control; this.Tab_P1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_P1.Controls.Add(this.Btn_Save_P1); this.Tab_P1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Tab_P1.Location = new System.Drawing.Point(4, 5); this.Tab_P1.Margin = new System.Windows.Forms.Padding(4); this.Tab_P1.Name = "Tab_P1"; this.Tab_P1.Padding = new System.Windows.Forms.Padding(4); this.Tab_P1.Size = new System.Drawing.Size(581, 334); this.Tab_P1.TabIndex = 0; this.Tab_P1.Text = "P1 Config"; // // Tab_P2 // this.Tab_P2.BackColor = System.Drawing.SystemColors.Control; this.Tab_P2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_P2.Controls.Add(this.Btn_SaveP2); this.Tab_P2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Tab_P2.Location = new System.Drawing.Point(4, 5); this.Tab_P2.Name = "Tab_P2"; this.Tab_P2.Padding = new System.Windows.Forms.Padding(3); this.Tab_P2.Size = new System.Drawing.Size(581, 334); this.Tab_P2.TabIndex = 7; this.Tab_P2.Text = "P2 Config"; // // Btn_SaveP2 // this.Btn_SaveP2.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_SaveP2.Location = new System.Drawing.Point(217, 284); this.Btn_SaveP2.Margin = new System.Windows.Forms.Padding(4); this.Btn_SaveP2.Name = "Btn_SaveP2"; this.Btn_SaveP2.Size = new System.Drawing.Size(145, 37); this.Btn_SaveP2.TabIndex = 38; this.Btn_SaveP2.Text = "Save Config"; this.Btn_SaveP2.UseVisualStyleBackColor = true; this.Btn_SaveP2.Click += new System.EventHandler(this.Btn_Save_Cfg_Click); // // Tab_P3 // this.Tab_P3.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_P3.Controls.Add(this.Btn_Save_P3); this.Tab_P3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Tab_P3.Location = new System.Drawing.Point(4, 5); this.Tab_P3.Name = "Tab_P3"; this.Tab_P3.Padding = new System.Windows.Forms.Padding(3); this.Tab_P3.Size = new System.Drawing.Size(581, 334); this.Tab_P3.TabIndex = 11; this.Tab_P3.Text = "P3 Config"; this.Tab_P3.UseVisualStyleBackColor = true; // // Btn_Save_P3 // this.Btn_Save_P3.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Save_P3.Location = new System.Drawing.Point(217, 284); this.Btn_Save_P3.Margin = new System.Windows.Forms.Padding(4); this.Btn_Save_P3.Name = "Btn_Save_P3"; this.Btn_Save_P3.Size = new System.Drawing.Size(145, 37); this.Btn_Save_P3.TabIndex = 41; this.Btn_Save_P3.Text = "Save Config"; this.Btn_Save_P3.UseVisualStyleBackColor = true; this.Btn_Save_P3.Click += new System.EventHandler(this.Btn_Save_Cfg_Click); // // Tab_P4 // this.Tab_P4.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_P4.Controls.Add(this.Btn_Save_P4); this.Tab_P4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Tab_P4.Location = new System.Drawing.Point(4, 5); this.Tab_P4.Name = "Tab_P4"; this.Tab_P4.Padding = new System.Windows.Forms.Padding(3); this.Tab_P4.Size = new System.Drawing.Size(581, 334); this.Tab_P4.TabIndex = 12; this.Tab_P4.Text = "P4 Config"; this.Tab_P4.UseVisualStyleBackColor = true; // // Btn_Save_P4 // this.Btn_Save_P4.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Save_P4.Location = new System.Drawing.Point(217, 284); this.Btn_Save_P4.Margin = new System.Windows.Forms.Padding(4); this.Btn_Save_P4.Name = "Btn_Save_P4"; this.Btn_Save_P4.Size = new System.Drawing.Size(145, 37); this.Btn_Save_P4.TabIndex = 41; this.Btn_Save_P4.Text = "Save Config"; this.Btn_Save_P4.UseVisualStyleBackColor = true; this.Btn_Save_P4.Click += new System.EventHandler(this.Btn_Save_Cfg_Click); // // Tab_AnalogCalib // this.Tab_AnalogCalib.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_AnalogCalib.Controls.Add(this.TableLayout_Calib); this.Tab_AnalogCalib.Controls.Add(this.Btn_SaveAnalog); this.Tab_AnalogCalib.Location = new System.Drawing.Point(4, 5); this.Tab_AnalogCalib.Name = "Tab_AnalogCalib"; this.Tab_AnalogCalib.Size = new System.Drawing.Size(581, 334); this.Tab_AnalogCalib.TabIndex = 14; this.Tab_AnalogCalib.Text = "Analog device calibration"; this.Tab_AnalogCalib.UseVisualStyleBackColor = true; this.Tab_AnalogCalib.Click += new System.EventHandler(this.Tab_AnalogCalib_Click); // // TableLayout_Calib // this.TableLayout_Calib.ColumnCount = 2; this.TableLayout_Calib.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.TableLayout_Calib.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.TableLayout_Calib.Location = new System.Drawing.Point(5, 3); this.TableLayout_Calib.Name = "TableLayout_Calib"; this.TableLayout_Calib.RowCount = 2; this.TableLayout_Calib.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.TableLayout_Calib.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.TableLayout_Calib.Size = new System.Drawing.Size(569, 274); this.TableLayout_Calib.TabIndex = 34; // // Btn_SaveAnalog // this.Btn_SaveAnalog.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_SaveAnalog.Location = new System.Drawing.Point(217, 284); this.Btn_SaveAnalog.Margin = new System.Windows.Forms.Padding(4); this.Btn_SaveAnalog.Name = "Btn_SaveAnalog"; this.Btn_SaveAnalog.Size = new System.Drawing.Size(145, 37); this.Btn_SaveAnalog.TabIndex = 33; this.Btn_SaveAnalog.Text = "Save Config"; this.Btn_SaveAnalog.UseVisualStyleBackColor = true; this.Btn_SaveAnalog.Click += new System.EventHandler(this.Btn_SaveAnalog_Click); // // Tab_ActLAbs // this.Tab_ActLAbs.BackColor = System.Drawing.SystemColors.Control; this.Tab_ActLAbs.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_ActLAbs.Controls.Add(this.Btn_ActLabs_Save); this.Tab_ActLAbs.Controls.Add(this.groupBox3); this.Tab_ActLAbs.Controls.Add(this.label15); this.Tab_ActLAbs.Location = new System.Drawing.Point(4, 5); this.Tab_ActLAbs.Name = "Tab_ActLAbs"; this.Tab_ActLAbs.Size = new System.Drawing.Size(581, 334); this.Tab_ActLAbs.TabIndex = 6; this.Tab_ActLAbs.Text = "Calibration"; // // Btn_ActLabs_Save // this.Btn_ActLabs_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_ActLabs_Save.Location = new System.Drawing.Point(217, 284); this.Btn_ActLabs_Save.Margin = new System.Windows.Forms.Padding(4); this.Btn_ActLabs_Save.Name = "Btn_ActLabs_Save"; this.Btn_ActLabs_Save.Size = new System.Drawing.Size(145, 37); this.Btn_ActLabs_Save.TabIndex = 54; this.Btn_ActLabs_Save.Text = "Save Config"; this.Btn_ActLabs_Save.UseVisualStyleBackColor = true; this.Btn_ActLabs_Save.Click += new System.EventHandler(this.Btn_ActLabs_Save_Click); // // groupBox3 // this.groupBox3.Controls.Add(this.label47); this.groupBox3.Controls.Add(this.Txt_ActLabs_Y4); this.groupBox3.Controls.Add(this.label48); this.groupBox3.Controls.Add(this.Txt_ActLabs_X4); this.groupBox3.Controls.Add(this.label49); this.groupBox3.Controls.Add(this.Txt_ActLabs_Y3); this.groupBox3.Controls.Add(this.label50); this.groupBox3.Controls.Add(this.Txt_ActLabs_X3); this.groupBox3.Controls.Add(this.label18); this.groupBox3.Controls.Add(this.Txt_ActLabs_Y2); this.groupBox3.Controls.Add(this.label19); this.groupBox3.Controls.Add(this.Txt_ActLabs_X2); this.groupBox3.Controls.Add(this.Chk_DspCorrectedCrosshair); this.groupBox3.Controls.Add(this.label17); this.groupBox3.Controls.Add(this.Txt_ActLabs_Y1); this.groupBox3.Controls.Add(this.label16); this.groupBox3.Controls.Add(this.Txt_ActLabs_X1); this.groupBox3.Controls.Add(this.Cb_ActLabsOffset); this.groupBox3.Location = new System.Drawing.Point(5, 102); this.groupBox3.Name = "groupBox3"; this.groupBox3.Size = new System.Drawing.Size(569, 175); this.groupBox3.TabIndex = 53; this.groupBox3.TabStop = false; this.groupBox3.Text = " "; // // label47 // this.label47.AutoSize = true; this.label47.Location = new System.Drawing.Point(279, 114); this.label47.Name = "label47"; this.label47.Size = new System.Drawing.Size(76, 16); this.label47.TabIndex = 67; this.label47.Text = "P4 Y offset :"; // // Txt_ActLabs_Y4 // this.Txt_ActLabs_Y4.Enabled = false; this.Txt_ActLabs_Y4.Location = new System.Drawing.Point(361, 111); this.Txt_ActLabs_Y4.Name = "Txt_ActLabs_Y4"; this.Txt_ActLabs_Y4.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_Y4.TabIndex = 66; this.Txt_ActLabs_Y4.Text = "0"; this.Txt_ActLabs_Y4.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_Y4.TextChanged += new System.EventHandler(this.Txt_ActLabs_Y4_TextChanged); // // label48 // this.label48.AutoSize = true; this.label48.Location = new System.Drawing.Point(279, 90); this.label48.Name = "label48"; this.label48.Size = new System.Drawing.Size(75, 16); this.label48.TabIndex = 65; this.label48.Text = "P4 X offset :"; // // Txt_ActLabs_X4 // this.Txt_ActLabs_X4.Enabled = false; this.Txt_ActLabs_X4.Location = new System.Drawing.Point(361, 87); this.Txt_ActLabs_X4.Name = "Txt_ActLabs_X4"; this.Txt_ActLabs_X4.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_X4.TabIndex = 64; this.Txt_ActLabs_X4.Text = "0"; this.Txt_ActLabs_X4.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_X4.TextChanged += new System.EventHandler(this.Txt_ActLabs_X4_TextChanged); // // label49 // this.label49.AutoSize = true; this.label49.Location = new System.Drawing.Point(71, 114); this.label49.Name = "label49"; this.label49.Size = new System.Drawing.Size(76, 16); this.label49.TabIndex = 63; this.label49.Text = "P3 Y offset :"; // // Txt_ActLabs_Y3 // this.Txt_ActLabs_Y3.Enabled = false; this.Txt_ActLabs_Y3.Location = new System.Drawing.Point(153, 111); this.Txt_ActLabs_Y3.Name = "Txt_ActLabs_Y3"; this.Txt_ActLabs_Y3.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_Y3.TabIndex = 62; this.Txt_ActLabs_Y3.Text = "0"; this.Txt_ActLabs_Y3.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_Y3.TextChanged += new System.EventHandler(this.Txt_ActLabs_Y3_TextChanged); // // label50 // this.label50.AutoSize = true; this.label50.Location = new System.Drawing.Point(71, 90); this.label50.Name = "label50"; this.label50.Size = new System.Drawing.Size(75, 16); this.label50.TabIndex = 61; this.label50.Text = "P3 X offset :"; // // Txt_ActLabs_X3 // this.Txt_ActLabs_X3.Enabled = false; this.Txt_ActLabs_X3.Location = new System.Drawing.Point(153, 87); this.Txt_ActLabs_X3.Name = "Txt_ActLabs_X3"; this.Txt_ActLabs_X3.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_X3.TabIndex = 60; this.Txt_ActLabs_X3.Text = "0"; this.Txt_ActLabs_X3.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_X3.TextChanged += new System.EventHandler(this.Txt_ActLabs_X3_TextChanged); // // label18 // this.label18.AutoSize = true; this.label18.Location = new System.Drawing.Point(279, 55); this.label18.Name = "label18"; this.label18.Size = new System.Drawing.Size(76, 16); this.label18.TabIndex = 59; this.label18.Text = "P2 Y offset :"; // // Txt_ActLabs_Y2 // this.Txt_ActLabs_Y2.Enabled = false; this.Txt_ActLabs_Y2.Location = new System.Drawing.Point(361, 52); this.Txt_ActLabs_Y2.Name = "Txt_ActLabs_Y2"; this.Txt_ActLabs_Y2.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_Y2.TabIndex = 58; this.Txt_ActLabs_Y2.Text = "0"; this.Txt_ActLabs_Y2.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_Y2.TextChanged += new System.EventHandler(this.Txt_ActLabs_Y2_TextChanged); // // label19 // this.label19.AutoSize = true; this.label19.Location = new System.Drawing.Point(279, 31); this.label19.Name = "label19"; this.label19.Size = new System.Drawing.Size(75, 16); this.label19.TabIndex = 57; this.label19.Text = "P2 X offset :"; // // Txt_ActLabs_X2 // this.Txt_ActLabs_X2.Enabled = false; this.Txt_ActLabs_X2.Location = new System.Drawing.Point(361, 28); this.Txt_ActLabs_X2.Name = "Txt_ActLabs_X2"; this.Txt_ActLabs_X2.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_X2.TabIndex = 56; this.Txt_ActLabs_X2.Text = "0"; this.Txt_ActLabs_X2.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_X2.TextChanged += new System.EventHandler(this.Txt_ActLabs_X2_TextChanged); // // Chk_DspCorrectedCrosshair // this.Chk_DspCorrectedCrosshair.AutoSize = true; this.Chk_DspCorrectedCrosshair.Checked = true; this.Chk_DspCorrectedCrosshair.CheckState = System.Windows.Forms.CheckState.Checked; this.Chk_DspCorrectedCrosshair.Location = new System.Drawing.Point(13, 149); this.Chk_DspCorrectedCrosshair.Name = "Chk_DspCorrectedCrosshair"; this.Chk_DspCorrectedCrosshair.Size = new System.Drawing.Size(295, 20); this.Chk_DspCorrectedCrosshair.TabIndex = 55; this.Chk_DspCorrectedCrosshair.Text = "Show a mark on shoot for \"corrected\" location"; this.Chk_DspCorrectedCrosshair.UseVisualStyleBackColor = true; this.Chk_DspCorrectedCrosshair.CheckedChanged += new System.EventHandler(this.Chk_DspCorrectedCrosshair_CheckedChanged); // // label17 // this.label17.AutoSize = true; this.label17.Location = new System.Drawing.Point(71, 55); this.label17.Name = "label17"; this.label17.Size = new System.Drawing.Size(76, 16); this.label17.TabIndex = 4; this.label17.Text = "P1 Y offset :"; // // Txt_ActLabs_Y1 // this.Txt_ActLabs_Y1.Enabled = false; this.Txt_ActLabs_Y1.Location = new System.Drawing.Point(153, 52); this.Txt_ActLabs_Y1.Name = "Txt_ActLabs_Y1"; this.Txt_ActLabs_Y1.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_Y1.TabIndex = 3; this.Txt_ActLabs_Y1.Text = "0"; this.Txt_ActLabs_Y1.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_Y1.TextChanged += new System.EventHandler(this.Txt_ActLabs_Y1_TextChanged); // // label16 // this.label16.AutoSize = true; this.label16.Location = new System.Drawing.Point(71, 31); this.label16.Name = "label16"; this.label16.Size = new System.Drawing.Size(75, 16); this.label16.TabIndex = 2; this.label16.Text = "P1 X offset :"; // // Txt_ActLabs_X1 // this.Txt_ActLabs_X1.Enabled = false; this.Txt_ActLabs_X1.Location = new System.Drawing.Point(153, 28); this.Txt_ActLabs_X1.Name = "Txt_ActLabs_X1"; this.Txt_ActLabs_X1.Size = new System.Drawing.Size(75, 22); this.Txt_ActLabs_X1.TabIndex = 1; this.Txt_ActLabs_X1.Text = "0"; this.Txt_ActLabs_X1.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_ActLabs_X1.TextChanged += new System.EventHandler(this.Txt_ActLabs_X1_TextChanged); // // Cb_ActLabsOffset // this.Cb_ActLabsOffset.AutoSize = true; this.Cb_ActLabsOffset.Location = new System.Drawing.Point(13, 0); this.Cb_ActLabsOffset.Name = "Cb_ActLabsOffset"; this.Cb_ActLabsOffset.Size = new System.Drawing.Size(264, 20); this.Cb_ActLabsOffset.TabIndex = 0; this.Cb_ActLabsOffset.Text = "Enable X / Y offset (offsets are in pixels)"; this.Cb_ActLabsOffset.UseVisualStyleBackColor = true; this.Cb_ActLabsOffset.CheckedChanged += new System.EventHandler(this.Cb_ActLabsOffset_CheckedChanged); // // label15 // this.label15.AutoSize = true; this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label15.Location = new System.Drawing.Point(15, 13); this.label15.Name = "label15"; this.label15.Size = new System.Drawing.Size(372, 75); this.label15.TabIndex = 52; this.label15.Text = resources.GetString("label15.Text"); // // Tab_Dolphin // this.Tab_Dolphin.BackColor = System.Drawing.SystemColors.Control; this.Tab_Dolphin.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_Dolphin.Controls.Add(this.label2); this.Tab_Dolphin.Controls.Add(this.Btn_Dolphin5); this.Tab_Dolphin.Location = new System.Drawing.Point(4, 5); this.Tab_Dolphin.Name = "Tab_Dolphin"; this.Tab_Dolphin.Padding = new System.Windows.Forms.Padding(3); this.Tab_Dolphin.Size = new System.Drawing.Size(581, 334); this.Tab_Dolphin.TabIndex = 4; this.Tab_Dolphin.Text = "Dolphin"; // // label2 // this.label2.AutoSize = true; this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label2.Location = new System.Drawing.Point(15, 13); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(320, 60); this.label2.TabIndex = 50; this.label2.Text = "Click the button to copy the file \"WiimoteNew.ini\" to \r\n\"MyDocuments\\Dolphin Emul" + "ator\\Config\\WiimoteNew.ini\"\r\n\r\nExisting file will be backed-up and overwritten !" + ""; // // Btn_Dolphin5 // this.Btn_Dolphin5.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Dolphin5.Location = new System.Drawing.Point(147, 203); this.Btn_Dolphin5.Margin = new System.Windows.Forms.Padding(4); this.Btn_Dolphin5.Name = "Btn_Dolphin5"; this.Btn_Dolphin5.Size = new System.Drawing.Size(307, 46); this.Btn_Dolphin5.TabIndex = 49; this.Btn_Dolphin5.Text = "Install Wiimote configuration"; this.Btn_Dolphin5.UseVisualStyleBackColor = true; this.Btn_Dolphin5.Click += new System.EventHandler(this.Btn_Dolphin5_Click); // // Tab_EAInvasion // this.Tab_EAInvasion.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_EAInvasion.Controls.Add(this.groupBox17); this.Tab_EAInvasion.Controls.Add(this.groupBox16); this.Tab_EAInvasion.Controls.Add(this.label68); this.Tab_EAInvasion.Location = new System.Drawing.Point(4, 5); this.Tab_EAInvasion.Name = "Tab_EAInvasion"; this.Tab_EAInvasion.Size = new System.Drawing.Size(581, 334); this.Tab_EAInvasion.TabIndex = 20; this.Tab_EAInvasion.Text = "Elevator Action Invasion"; this.Tab_EAInvasion.UseVisualStyleBackColor = true; // // groupBox17 // this.groupBox17.Controls.Add(this.Txt_EAI_Settings); this.groupBox17.Controls.Add(this.label76); this.groupBox17.Controls.Add(this.Txt_EAI_P1_Start); this.groupBox17.Controls.Add(this.Btn_EAI_Save); this.groupBox17.Controls.Add(this.Txt_EAI_P1_Credits); this.groupBox17.Controls.Add(this.label69); this.groupBox17.Controls.Add(this.Txt_EAI_Enter); this.groupBox17.Controls.Add(this.label70); this.groupBox17.Controls.Add(this.Txt_EAI_Up); this.groupBox17.Controls.Add(this.Txt_EAI_Down); this.groupBox17.Controls.Add(this.label71); this.groupBox17.Controls.Add(this.label72); this.groupBox17.Controls.Add(this.label73); this.groupBox17.Controls.Add(this.Txt_EAI_P2_Start); this.groupBox17.Controls.Add(this.label74); this.groupBox17.Controls.Add(this.label75); this.groupBox17.Controls.Add(this.Txt_EAI_P2_Credits); this.groupBox17.Location = new System.Drawing.Point(5, 126); this.groupBox17.Name = "groupBox17"; this.groupBox17.Size = new System.Drawing.Size(569, 201); this.groupBox17.TabIndex = 72; this.groupBox17.TabStop = false; this.groupBox17.Text = "To set a key, click a box then push the key on your keyboard :"; // // Txt_EAI_Settings // this.Txt_EAI_Settings.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_Settings.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_Settings.Location = new System.Drawing.Point(403, 36); this.Txt_EAI_Settings.Name = "Txt_EAI_Settings"; this.Txt_EAI_Settings.ReadOnly = true; this.Txt_EAI_Settings.Size = new System.Drawing.Size(127, 21); this.Txt_EAI_Settings.TabIndex = 85; this.Txt_EAI_Settings.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_Settings.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label76 // this.label76.AutoSize = true; this.label76.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label76.Location = new System.Drawing.Point(330, 39); this.label76.Name = "label76"; this.label76.Size = new System.Drawing.Size(72, 15); this.label76.TabIndex = 86; this.label76.Text = "SETTINGS :"; // // Txt_EAI_P1_Start // this.Txt_EAI_P1_Start.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_P1_Start.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_P1_Start.Location = new System.Drawing.Point(109, 36); this.Txt_EAI_P1_Start.Name = "Txt_EAI_P1_Start"; this.Txt_EAI_P1_Start.ReadOnly = true; this.Txt_EAI_P1_Start.Size = new System.Drawing.Size(127, 21); this.Txt_EAI_P1_Start.TabIndex = 71; this.Txt_EAI_P1_Start.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_P1_Start.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Btn_EAI_Save // this.Btn_EAI_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_EAI_Save.Location = new System.Drawing.Point(157, 157); this.Btn_EAI_Save.Margin = new System.Windows.Forms.Padding(4); this.Btn_EAI_Save.Name = "Btn_EAI_Save"; this.Btn_EAI_Save.Size = new System.Drawing.Size(251, 37); this.Btn_EAI_Save.TabIndex = 67; this.Btn_EAI_Save.Text = "Save Key config"; this.Btn_EAI_Save.UseVisualStyleBackColor = true; this.Btn_EAI_Save.Click += new System.EventHandler(this.Btn_EAI_Save_Click); // // Txt_EAI_P1_Credits // this.Txt_EAI_P1_Credits.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_P1_Credits.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_P1_Credits.Location = new System.Drawing.Point(109, 63); this.Txt_EAI_P1_Credits.Name = "Txt_EAI_P1_Credits"; this.Txt_EAI_P1_Credits.ReadOnly = true; this.Txt_EAI_P1_Credits.Size = new System.Drawing.Size(127, 21); this.Txt_EAI_P1_Credits.TabIndex = 72; this.Txt_EAI_P1_Credits.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_P1_Credits.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label69 // this.label69.AutoSize = true; this.label69.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label69.Location = new System.Drawing.Point(330, 120); this.label69.Name = "label69"; this.label69.Size = new System.Drawing.Size(54, 15); this.label69.TabIndex = 84; this.label69.Text = "ENTER :"; // // Txt_EAI_Enter // this.Txt_EAI_Enter.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_Enter.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_Enter.Location = new System.Drawing.Point(403, 117); this.Txt_EAI_Enter.Name = "Txt_EAI_Enter"; this.Txt_EAI_Enter.ReadOnly = true; this.Txt_EAI_Enter.Size = new System.Drawing.Size(128, 21); this.Txt_EAI_Enter.TabIndex = 73; this.Txt_EAI_Enter.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_Enter.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label70 // this.label70.AutoSize = true; this.label70.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label70.Location = new System.Drawing.Point(330, 93); this.label70.Name = "label70"; this.label70.Size = new System.Drawing.Size(51, 15); this.label70.TabIndex = 83; this.label70.Text = "DOWN :"; // // Txt_EAI_Up // this.Txt_EAI_Up.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_Up.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_Up.Location = new System.Drawing.Point(403, 63); this.Txt_EAI_Up.Name = "Txt_EAI_Up"; this.Txt_EAI_Up.ReadOnly = true; this.Txt_EAI_Up.Size = new System.Drawing.Size(127, 21); this.Txt_EAI_Up.TabIndex = 78; this.Txt_EAI_Up.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_Up.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Txt_EAI_Down // this.Txt_EAI_Down.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_Down.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_Down.Location = new System.Drawing.Point(403, 90); this.Txt_EAI_Down.Name = "Txt_EAI_Down"; this.Txt_EAI_Down.ReadOnly = true; this.Txt_EAI_Down.Size = new System.Drawing.Size(128, 21); this.Txt_EAI_Down.TabIndex = 74; this.Txt_EAI_Down.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_Down.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label71 // this.label71.AutoSize = true; this.label71.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label71.Location = new System.Drawing.Point(35, 66); this.label71.Name = "label71"; this.label71.Size = new System.Drawing.Size(69, 15); this.label71.TabIndex = 79; this.label71.Text = "P1 Credits :"; // // label72 // this.label72.AutoSize = true; this.label72.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label72.Location = new System.Drawing.Point(330, 66); this.label72.Name = "label72"; this.label72.Size = new System.Drawing.Size(27, 15); this.label72.TabIndex = 82; this.label72.Text = "UP:"; // // label73 // this.label73.AutoSize = true; this.label73.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label73.Location = new System.Drawing.Point(35, 39); this.label73.Name = "label73"; this.label73.Size = new System.Drawing.Size(56, 15); this.label73.TabIndex = 77; this.label73.Text = "P1 Start :"; // // Txt_EAI_P2_Start // this.Txt_EAI_P2_Start.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_P2_Start.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_P2_Start.Location = new System.Drawing.Point(109, 90); this.Txt_EAI_P2_Start.Name = "Txt_EAI_P2_Start"; this.Txt_EAI_P2_Start.ReadOnly = true; this.Txt_EAI_P2_Start.Size = new System.Drawing.Size(127, 21); this.Txt_EAI_P2_Start.TabIndex = 75; this.Txt_EAI_P2_Start.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_P2_Start.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label74 // this.label74.AutoSize = true; this.label74.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label74.Location = new System.Drawing.Point(35, 93); this.label74.Name = "label74"; this.label74.Size = new System.Drawing.Size(56, 15); this.label74.TabIndex = 80; this.label74.Text = "P2 Start :"; // // label75 // this.label75.AutoSize = true; this.label75.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label75.Location = new System.Drawing.Point(35, 120); this.label75.Name = "label75"; this.label75.Size = new System.Drawing.Size(69, 15); this.label75.TabIndex = 81; this.label75.Text = "P2 Credits :"; // // Txt_EAI_P2_Credits // this.Txt_EAI_P2_Credits.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_EAI_P2_Credits.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_EAI_P2_Credits.Location = new System.Drawing.Point(109, 117); this.Txt_EAI_P2_Credits.Name = "Txt_EAI_P2_Credits"; this.Txt_EAI_P2_Credits.ReadOnly = true; this.Txt_EAI_P2_Credits.Size = new System.Drawing.Size(127, 21); this.Txt_EAI_P2_Credits.TabIndex = 76; this.Txt_EAI_P2_Credits.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_EAI_P2_Credits.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // groupBox16 // this.groupBox16.Controls.Add(this.Btn_EAI_Patch); this.groupBox16.Controls.Add(this.Txt_EAI_FolderPath); this.groupBox16.Controls.Add(this.Btn_EAI_Open); this.groupBox16.Location = new System.Drawing.Point(5, 3); this.groupBox16.Name = "groupBox16"; this.groupBox16.Size = new System.Drawing.Size(569, 100); this.groupBox16.TabIndex = 71; this.groupBox16.TabStop = false; this.groupBox16.Text = "Select \"ESGame-Win64-Shipping.exe\" directory to patch the files :"; // // Btn_EAI_Patch // this.Btn_EAI_Patch.Enabled = false; this.Btn_EAI_Patch.Location = new System.Drawing.Point(209, 58); this.Btn_EAI_Patch.Name = "Btn_EAI_Patch"; this.Btn_EAI_Patch.Size = new System.Drawing.Size(131, 36); this.Btn_EAI_Patch.TabIndex = 2; this.Btn_EAI_Patch.Text = "Patch !"; this.Btn_EAI_Patch.UseVisualStyleBackColor = true; this.Btn_EAI_Patch.Click += new System.EventHandler(this.Btn_EAI_Patch_Click); // // Txt_EAI_FolderPath // this.Txt_EAI_FolderPath.Location = new System.Drawing.Point(6, 30); this.Txt_EAI_FolderPath.Name = "Txt_EAI_FolderPath"; this.Txt_EAI_FolderPath.Size = new System.Drawing.Size(514, 22); this.Txt_EAI_FolderPath.TabIndex = 1; // // Btn_EAI_Open // this.Btn_EAI_Open.Location = new System.Drawing.Point(526, 29); this.Btn_EAI_Open.Name = "Btn_EAI_Open"; this.Btn_EAI_Open.Size = new System.Drawing.Size(37, 23); this.Btn_EAI_Open.TabIndex = 0; this.Btn_EAI_Open.Text = "..."; this.Btn_EAI_Open.UseVisualStyleBackColor = true; this.Btn_EAI_Open.Click += new System.EventHandler(this.Btn_EAI_Open_Click); // // label68 // this.label68.AutoSize = true; this.label68.Location = new System.Drawing.Point(40, 126); this.label68.Name = "label68"; this.label68.Size = new System.Drawing.Size(0, 16); this.label68.TabIndex = 68; // // Tab_GSOZ // this.Tab_GSOZ.BackColor = System.Drawing.SystemColors.Control; this.Tab_GSOZ.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_GSOZ.Controls.Add(this.Btn_Save_Gsoz); this.Tab_GSOZ.Controls.Add(this.label34); this.Tab_GSOZ.Controls.Add(this.groupBox6); this.Tab_GSOZ.Controls.Add(this.groupBox5); this.Tab_GSOZ.Location = new System.Drawing.Point(4, 5); this.Tab_GSOZ.Name = "Tab_GSOZ"; this.Tab_GSOZ.Size = new System.Drawing.Size(581, 334); this.Tab_GSOZ.TabIndex = 8; this.Tab_GSOZ.Text = "Gundam : SoZ"; // // Btn_Save_Gsoz // this.Btn_Save_Gsoz.Location = new System.Drawing.Point(410, 259); this.Btn_Save_Gsoz.Name = "Btn_Save_Gsoz"; this.Btn_Save_Gsoz.Size = new System.Drawing.Size(145, 37); this.Btn_Save_Gsoz.TabIndex = 56; this.Btn_Save_Gsoz.Text = "Save Config"; this.Btn_Save_Gsoz.UseVisualStyleBackColor = true; this.Btn_Save_Gsoz.Click += new System.EventHandler(this.Btn_Save_Gsoz_Click); // // label34 // this.label34.AutoSize = true; this.label34.Location = new System.Drawing.Point(9, 200); this.label34.Name = "label34"; this.label34.Size = new System.Drawing.Size(317, 96); this.label34.TabIndex = 55; this.label34.Text = "Default : \r\nHide in game by pointing the gun out of screen.\r\n\r\nPedal-Mode :\r\nUse " + "a supplementary button to hide, like Time Crisis.\r\nPointing the gun out of scree" + "n won\'t hide anymore."; // // groupBox6 // this.groupBox6.Controls.Add(this.label33); this.groupBox6.Controls.Add(this.TXT_GSOZ_PEDAL_2); this.groupBox6.Controls.Add(this.Chk_GundamP2Pedal); this.groupBox6.Location = new System.Drawing.Point(12, 103); this.groupBox6.Name = "groupBox6"; this.groupBox6.Size = new System.Drawing.Size(562, 75); this.groupBox6.TabIndex = 54; this.groupBox6.TabStop = false; this.groupBox6.Text = "Player 2 :"; // // label33 // this.label33.AutoSize = true; this.label33.Location = new System.Drawing.Point(228, 36); this.label33.Name = "label33"; this.label33.Size = new System.Drawing.Size(117, 16); this.label33.TabIndex = 54; this.label33.Text = "Set the pedal key :"; // // TXT_GSOZ_PEDAL_2 // this.TXT_GSOZ_PEDAL_2.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_GSOZ_PEDAL_2.Enabled = false; this.TXT_GSOZ_PEDAL_2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_GSOZ_PEDAL_2.Location = new System.Drawing.Point(352, 34); this.TXT_GSOZ_PEDAL_2.Name = "TXT_GSOZ_PEDAL_2"; this.TXT_GSOZ_PEDAL_2.ReadOnly = true; this.TXT_GSOZ_PEDAL_2.Size = new System.Drawing.Size(127, 21); this.TXT_GSOZ_PEDAL_2.TabIndex = 54; this.TXT_GSOZ_PEDAL_2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_GSOZ_PEDAL_2.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Chk_GundamP2Pedal // this.Chk_GundamP2Pedal.AutoSize = true; this.Chk_GundamP2Pedal.Location = new System.Drawing.Point(9, 36); this.Chk_GundamP2Pedal.Name = "Chk_GundamP2Pedal"; this.Chk_GundamP2Pedal.Size = new System.Drawing.Size(157, 20); this.Chk_GundamP2Pedal.TabIndex = 53; this.Chk_GundamP2Pedal.Text = "Enable \"Pedal-Mode\""; this.Chk_GundamP2Pedal.UseVisualStyleBackColor = true; this.Chk_GundamP2Pedal.CheckedChanged += new System.EventHandler(this.Chk_GundamP2Pedal_CheckedChanged); // // groupBox5 // this.groupBox5.Controls.Add(this.label9); this.groupBox5.Controls.Add(this.TXT_GSOZ_PEDAL_1); this.groupBox5.Controls.Add(this.Chk_GundamP1Pedal); this.groupBox5.Location = new System.Drawing.Point(12, 22); this.groupBox5.Name = "groupBox5"; this.groupBox5.Size = new System.Drawing.Size(562, 75); this.groupBox5.TabIndex = 53; this.groupBox5.TabStop = false; this.groupBox5.Text = "Player 1 :"; // // label9 // this.label9.AutoSize = true; this.label9.Location = new System.Drawing.Point(228, 36); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(117, 16); this.label9.TabIndex = 53; this.label9.Text = "Set the pedal key :"; // // TXT_GSOZ_PEDAL_1 // this.TXT_GSOZ_PEDAL_1.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_GSOZ_PEDAL_1.Enabled = false; this.TXT_GSOZ_PEDAL_1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_GSOZ_PEDAL_1.Location = new System.Drawing.Point(352, 34); this.TXT_GSOZ_PEDAL_1.Name = "TXT_GSOZ_PEDAL_1"; this.TXT_GSOZ_PEDAL_1.ReadOnly = true; this.TXT_GSOZ_PEDAL_1.Size = new System.Drawing.Size(127, 21); this.TXT_GSOZ_PEDAL_1.TabIndex = 52; this.TXT_GSOZ_PEDAL_1.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_GSOZ_PEDAL_1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Chk_GundamP1Pedal // this.Chk_GundamP1Pedal.AutoSize = true; this.Chk_GundamP1Pedal.Location = new System.Drawing.Point(9, 36); this.Chk_GundamP1Pedal.Name = "Chk_GundamP1Pedal"; this.Chk_GundamP1Pedal.Size = new System.Drawing.Size(157, 20); this.Chk_GundamP1Pedal.TabIndex = 0; this.Chk_GundamP1Pedal.Text = "Enable \"Pedal-Mode\""; this.Chk_GundamP1Pedal.UseVisualStyleBackColor = true; this.Chk_GundamP1Pedal.CheckedChanged += new System.EventHandler(this.Chk_GundamP1Pedal_CheckedChanged); // // Tab_HeavyFire // this.Tab_HeavyFire.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_HeavyFire.Controls.Add(this.Rdo_HF_MiddleGrenade); this.Tab_HeavyFire.Controls.Add(this.Rdo_HF_MiddleCover); this.Tab_HeavyFire.Controls.Add(this.Gbox_HF_Grenade); this.Tab_HeavyFire.Controls.Add(this.Gbox_HF_Cover); this.Tab_HeavyFire.Controls.Add(this.Btn_HF_Save); this.Tab_HeavyFire.Controls.Add(this.label41); this.Tab_HeavyFire.Location = new System.Drawing.Point(4, 5); this.Tab_HeavyFire.Name = "Tab_HeavyFire"; this.Tab_HeavyFire.Size = new System.Drawing.Size(581, 334); this.Tab_HeavyFire.TabIndex = 9; this.Tab_HeavyFire.Text = "Heavy Fire Afghanistan"; this.Tab_HeavyFire.UseVisualStyleBackColor = true; // // Rdo_HF_MiddleGrenade // this.Rdo_HF_MiddleGrenade.AutoSize = true; this.Rdo_HF_MiddleGrenade.Checked = true; this.Rdo_HF_MiddleGrenade.Location = new System.Drawing.Point(30, 188); this.Rdo_HF_MiddleGrenade.Name = "Rdo_HF_MiddleGrenade"; this.Rdo_HF_MiddleGrenade.Size = new System.Drawing.Size(252, 20); this.Rdo_HF_MiddleGrenade.TabIndex = 18; this.Rdo_HF_MiddleGrenade.TabStop = true; this.Rdo_HF_MiddleGrenade.Text = "Use Middle Click for Player 1 Grenade"; this.Rdo_HF_MiddleGrenade.UseVisualStyleBackColor = true; this.Rdo_HF_MiddleGrenade.CheckedChanged += new System.EventHandler(this.Rdo_HF_MiddleClickGrenade_CheckedChanged); // // Rdo_HF_MiddleCover // this.Rdo_HF_MiddleCover.AutoSize = true; this.Rdo_HF_MiddleCover.Location = new System.Drawing.Point(30, 11); this.Rdo_HF_MiddleCover.Name = "Rdo_HF_MiddleCover"; this.Rdo_HF_MiddleCover.Size = new System.Drawing.Size(235, 20); this.Rdo_HF_MiddleCover.TabIndex = 15; this.Rdo_HF_MiddleCover.TabStop = true; this.Rdo_HF_MiddleCover.Text = "Use Middle Click for Player 1 Cover"; this.Rdo_HF_MiddleCover.UseVisualStyleBackColor = true; this.Rdo_HF_MiddleCover.CheckedChanged += new System.EventHandler(this.Rdo_HF_MiddleCover_CheckedChanged); // // Gbox_HF_Grenade // this.Gbox_HF_Grenade.Controls.Add(this.label27); this.Gbox_HF_Grenade.Location = new System.Drawing.Point(15, 190); this.Gbox_HF_Grenade.Name = "Gbox_HF_Grenade"; this.Gbox_HF_Grenade.Size = new System.Drawing.Size(553, 82); this.Gbox_HF_Grenade.TabIndex = 17; this.Gbox_HF_Grenade.TabStop = false; // // label27 // this.label27.AutoSize = true; this.label27.Location = new System.Drawing.Point(12, 38); this.label27.Name = "label27"; this.label27.Size = new System.Drawing.Size(464, 16); this.label27.TabIndex = 17; this.label27.Text = "Cover and QTE will need original [A] / [S] / [D] / [W] / [SPACE] keyboard keys"; // // Gbox_HF_Cover // this.Gbox_HF_Cover.Controls.Add(this.label40); this.Gbox_HF_Cover.Controls.Add(this.label45); this.Gbox_HF_Cover.Controls.Add(this.Chk_HF_ReverseCover); this.Gbox_HF_Cover.Controls.Add(this.TrackBar_HF_Cover); this.Gbox_HF_Cover.Location = new System.Drawing.Point(15, 14); this.Gbox_HF_Cover.Name = "Gbox_HF_Cover"; this.Gbox_HF_Cover.Size = new System.Drawing.Size(553, 162); this.Gbox_HF_Cover.TabIndex = 16; this.Gbox_HF_Cover.TabStop = false; // // label40 // this.label40.AutoSize = true; this.label40.Location = new System.Drawing.Point(12, 36); this.label40.Name = "label40"; this.label40.Size = new System.Drawing.Size(275, 16); this.label40.TabIndex = 18; this.label40.Text = "Player 1 Grenade will need [G] Keyboard key"; // // label45 // this.label45.AutoSize = true; this.label45.Location = new System.Drawing.Point(12, 109); this.label45.Name = "label45"; this.label45.Size = new System.Drawing.Size(161, 16); this.label45.TabIndex = 10; this.label45.Text = "\"Cover Mode\" Sensibility :"; // // Chk_HF_ReverseCover // this.Chk_HF_ReverseCover.AutoSize = true; this.Chk_HF_ReverseCover.Location = new System.Drawing.Point(15, 76); this.Chk_HF_ReverseCover.Name = "Chk_HF_ReverseCover"; this.Chk_HF_ReverseCover.Size = new System.Drawing.Size(144, 20); this.Chk_HF_ReverseCover.TabIndex = 14; this.Chk_HF_ReverseCover.Text = "Reverse cover side"; this.Chk_HF_ReverseCover.UseVisualStyleBackColor = true; this.Chk_HF_ReverseCover.CheckedChanged += new System.EventHandler(this.Chk_HF_ReverseCover_CheckedChanged); // // TrackBar_HF_Cover // this.TrackBar_HF_Cover.Location = new System.Drawing.Point(180, 109); this.TrackBar_HF_Cover.Maximum = 5; this.TrackBar_HF_Cover.Minimum = 1; this.TrackBar_HF_Cover.Name = "TrackBar_HF_Cover"; this.TrackBar_HF_Cover.Size = new System.Drawing.Size(367, 45); this.TrackBar_HF_Cover.TabIndex = 1; this.TrackBar_HF_Cover.TickFrequency = 25; this.TrackBar_HF_Cover.TickStyle = System.Windows.Forms.TickStyle.None; this.TrackBar_HF_Cover.Value = 3; this.TrackBar_HF_Cover.ValueChanged += new System.EventHandler(this.TrackBar_HF_Cover_ValueChanged); // // Btn_HF_Save // this.Btn_HF_Save.Location = new System.Drawing.Point(217, 284); this.Btn_HF_Save.Name = "Btn_HF_Save"; this.Btn_HF_Save.Size = new System.Drawing.Size(145, 37); this.Btn_HF_Save.TabIndex = 11; this.Btn_HF_Save.Text = "Save"; this.Btn_HF_Save.UseVisualStyleBackColor = true; this.Btn_HF_Save.Click += new System.EventHandler(this.Btn_HF_Save_Click); // // label41 // this.label41.AutoSize = true; this.label41.Location = new System.Drawing.Point(5, 25); this.label41.Name = "label41"; this.label41.Size = new System.Drawing.Size(0, 16); this.label41.TabIndex = 2; // // Tab_LethalEnforcer3 // this.Tab_LethalEnforcer3.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_LethalEnforcer3.Controls.Add(this.Btn_Save_Le3); this.Tab_LethalEnforcer3.Controls.Add(this.label58); this.Tab_LethalEnforcer3.Controls.Add(this.groupBox14); this.Tab_LethalEnforcer3.Controls.Add(this.groupBox15); this.Tab_LethalEnforcer3.Location = new System.Drawing.Point(4, 5); this.Tab_LethalEnforcer3.Name = "Tab_LethalEnforcer3"; this.Tab_LethalEnforcer3.Size = new System.Drawing.Size(581, 334); this.Tab_LethalEnforcer3.TabIndex = 19; this.Tab_LethalEnforcer3.Text = "Lethal Enforcers 3"; this.Tab_LethalEnforcer3.UseVisualStyleBackColor = true; // // Btn_Save_Le3 // this.Btn_Save_Le3.Location = new System.Drawing.Point(410, 259); this.Btn_Save_Le3.Name = "Btn_Save_Le3"; this.Btn_Save_Le3.Size = new System.Drawing.Size(145, 37); this.Btn_Save_Le3.TabIndex = 60; this.Btn_Save_Le3.Text = "Save Config"; this.Btn_Save_Le3.UseVisualStyleBackColor = true; this.Btn_Save_Le3.Click += new System.EventHandler(this.Btn_Save_Le3_Click); // // label58 // this.label58.AutoSize = true; this.label58.Location = new System.Drawing.Point(9, 200); this.label58.Name = "label58"; this.label58.Size = new System.Drawing.Size(317, 96); this.label58.TabIndex = 59; this.label58.Text = "Default : \r\nHide in game by pointing the gun out of screen.\r\n\r\nPedal-Mode :\r\nUse " + "a supplementary button to hide, like Time Crisis.\r\nPointing the gun out of scree" + "n won\'t hide anymore."; // // groupBox14 // this.groupBox14.Controls.Add(this.label59); this.groupBox14.Controls.Add(this.TXT_LE3_PEDAL_2); this.groupBox14.Controls.Add(this.Chk_Le3_EnablePedal2); this.groupBox14.Location = new System.Drawing.Point(12, 103); this.groupBox14.Name = "groupBox14"; this.groupBox14.Size = new System.Drawing.Size(562, 75); this.groupBox14.TabIndex = 58; this.groupBox14.TabStop = false; this.groupBox14.Text = "Player 2 :"; // // label59 // this.label59.AutoSize = true; this.label59.Location = new System.Drawing.Point(228, 36); this.label59.Name = "label59"; this.label59.Size = new System.Drawing.Size(117, 16); this.label59.TabIndex = 54; this.label59.Text = "Set the pedal key :"; // // TXT_LE3_PEDAL_2 // this.TXT_LE3_PEDAL_2.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_LE3_PEDAL_2.Enabled = false; this.TXT_LE3_PEDAL_2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_LE3_PEDAL_2.Location = new System.Drawing.Point(352, 34); this.TXT_LE3_PEDAL_2.Name = "TXT_LE3_PEDAL_2"; this.TXT_LE3_PEDAL_2.ReadOnly = true; this.TXT_LE3_PEDAL_2.Size = new System.Drawing.Size(127, 21); this.TXT_LE3_PEDAL_2.TabIndex = 54; this.TXT_LE3_PEDAL_2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_LE3_PEDAL_2.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Chk_Le3_EnablePedal2 // this.Chk_Le3_EnablePedal2.AutoSize = true; this.Chk_Le3_EnablePedal2.Location = new System.Drawing.Point(9, 36); this.Chk_Le3_EnablePedal2.Name = "Chk_Le3_EnablePedal2"; this.Chk_Le3_EnablePedal2.Size = new System.Drawing.Size(157, 20); this.Chk_Le3_EnablePedal2.TabIndex = 53; this.Chk_Le3_EnablePedal2.Text = "Enable \"Pedal-Mode\""; this.Chk_Le3_EnablePedal2.UseVisualStyleBackColor = true; this.Chk_Le3_EnablePedal2.CheckedChanged += new System.EventHandler(this.Chk_Le3_EnablePedal2_CheckedChanged); // // groupBox15 // this.groupBox15.Controls.Add(this.label60); this.groupBox15.Controls.Add(this.TXT_LE3_PEDAL_1); this.groupBox15.Controls.Add(this.Chk_Le3_EnablePedal1); this.groupBox15.Location = new System.Drawing.Point(12, 22); this.groupBox15.Name = "groupBox15"; this.groupBox15.Size = new System.Drawing.Size(562, 75); this.groupBox15.TabIndex = 57; this.groupBox15.TabStop = false; this.groupBox15.Text = "Player 1 :"; // // label60 // this.label60.AutoSize = true; this.label60.Location = new System.Drawing.Point(228, 36); this.label60.Name = "label60"; this.label60.Size = new System.Drawing.Size(117, 16); this.label60.TabIndex = 53; this.label60.Text = "Set the pedal key :"; // // TXT_LE3_PEDAL_1 // this.TXT_LE3_PEDAL_1.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_LE3_PEDAL_1.Enabled = false; this.TXT_LE3_PEDAL_1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_LE3_PEDAL_1.Location = new System.Drawing.Point(352, 34); this.TXT_LE3_PEDAL_1.Name = "TXT_LE3_PEDAL_1"; this.TXT_LE3_PEDAL_1.ReadOnly = true; this.TXT_LE3_PEDAL_1.Size = new System.Drawing.Size(127, 21); this.TXT_LE3_PEDAL_1.TabIndex = 52; this.TXT_LE3_PEDAL_1.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_LE3_PEDAL_1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Chk_Le3_EnablePedal1 // this.Chk_Le3_EnablePedal1.AutoSize = true; this.Chk_Le3_EnablePedal1.Location = new System.Drawing.Point(9, 36); this.Chk_Le3_EnablePedal1.Name = "Chk_Le3_EnablePedal1"; this.Chk_Le3_EnablePedal1.Size = new System.Drawing.Size(157, 20); this.Chk_Le3_EnablePedal1.TabIndex = 0; this.Chk_Le3_EnablePedal1.Text = "Enable \"Pedal-Mode\""; this.Chk_Le3_EnablePedal1.UseVisualStyleBackColor = true; this.Chk_Le3_EnablePedal1.CheckedChanged += new System.EventHandler(this.Chk_Le3_EnablePedal1_CheckedChanged); // // Tab_M2 // this.Tab_M2.BackColor = System.Drawing.SystemColors.Control; this.Tab_M2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_M2.Controls.Add(this.groupBox2); this.Tab_M2.Controls.Add(this.Btn_M2Scripts); this.Tab_M2.Controls.Add(this.label1); this.Tab_M2.Location = new System.Drawing.Point(4, 5); this.Tab_M2.Name = "Tab_M2"; this.Tab_M2.Padding = new System.Windows.Forms.Padding(3); this.Tab_M2.Size = new System.Drawing.Size(581, 334); this.Tab_M2.TabIndex = 5; this.Tab_M2.Text = "m2emulator"; // // groupBox2 // this.groupBox2.Controls.Add(this.panel1); this.groupBox2.Controls.Add(this.Cbox_M2_Flash); this.groupBox2.Controls.Add(this.label14); this.groupBox2.Controls.Add(this.TXT_CH_P1); this.groupBox2.Controls.Add(this.label13); this.groupBox2.Controls.Add(this.TXT_CH_P2); this.groupBox2.Controls.Add(this.label11); this.groupBox2.Controls.Add(this.TXT_CH_VIS); this.groupBox2.Location = new System.Drawing.Point(9, 105); this.groupBox2.Name = "groupBox2"; this.groupBox2.Size = new System.Drawing.Size(568, 120); this.groupBox2.TabIndex = 59; this.groupBox2.TabStop = false; // // panel1 // this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.panel1.Location = new System.Drawing.Point(282, 14); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(4, 100); this.panel1.TabIndex = 60; // // Cbox_M2_Flash // this.Cbox_M2_Flash.AutoSize = true; this.Cbox_M2_Flash.Checked = true; this.Cbox_M2_Flash.CheckState = System.Windows.Forms.CheckState.Checked; this.Cbox_M2_Flash.Location = new System.Drawing.Point(333, 59); this.Cbox_M2_Flash.Name = "Cbox_M2_Flash"; this.Cbox_M2_Flash.Size = new System.Drawing.Size(191, 20); this.Cbox_M2_Flash.TabIndex = 59; this.Cbox_M2_Flash.Text = "Disable white flash on shoot"; this.Cbox_M2_Flash.UseVisualStyleBackColor = true; // // label14 // this.label14.AutoSize = true; this.label14.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label14.Location = new System.Drawing.Point(14, 87); this.label14.Name = "label14"; this.label14.Size = new System.Drawing.Size(109, 15); this.label14.TabIndex = 58; this.label14.Text = "Crosshair visibility :"; // // TXT_CH_P1 // this.TXT_CH_P1.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_CH_P1.Location = new System.Drawing.Point(145, 30); this.TXT_CH_P1.Name = "TXT_CH_P1"; this.TXT_CH_P1.ReadOnly = true; this.TXT_CH_P1.Size = new System.Drawing.Size(100, 22); this.TXT_CH_P1.TabIndex = 53; this.TXT_CH_P1.Text = "7"; this.TXT_CH_P1.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_CH_P1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label13 // this.label13.AutoSize = true; this.label13.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label13.Location = new System.Drawing.Point(14, 59); this.label13.Name = "label13"; this.label13.Size = new System.Drawing.Size(125, 15); this.label13.TabIndex = 57; this.label13.Text = "P2 change crosshair :"; // // TXT_CH_P2 // this.TXT_CH_P2.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_CH_P2.Location = new System.Drawing.Point(145, 56); this.TXT_CH_P2.Name = "TXT_CH_P2"; this.TXT_CH_P2.ReadOnly = true; this.TXT_CH_P2.Size = new System.Drawing.Size(100, 22); this.TXT_CH_P2.TabIndex = 54; this.TXT_CH_P2.Text = "8"; this.TXT_CH_P2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_CH_P2.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label11 // this.label11.AutoSize = true; this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label11.Location = new System.Drawing.Point(14, 33); this.label11.Name = "label11"; this.label11.Size = new System.Drawing.Size(125, 15); this.label11.TabIndex = 56; this.label11.Text = "P1 change crosshair :"; // // TXT_CH_VIS // this.TXT_CH_VIS.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_CH_VIS.Location = new System.Drawing.Point(145, 84); this.TXT_CH_VIS.Name = "TXT_CH_VIS"; this.TXT_CH_VIS.ReadOnly = true; this.TXT_CH_VIS.Size = new System.Drawing.Size(100, 22); this.TXT_CH_VIS.TabIndex = 55; this.TXT_CH_VIS.Text = "9"; this.TXT_CH_VIS.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_CH_VIS.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Btn_M2Scripts // this.Btn_M2Scripts.Anchor = System.Windows.Forms.AnchorStyles.None; this.Btn_M2Scripts.Location = new System.Drawing.Point(191, 249); this.Btn_M2Scripts.Margin = new System.Windows.Forms.Padding(4); this.Btn_M2Scripts.Name = "Btn_M2Scripts"; this.Btn_M2Scripts.Size = new System.Drawing.Size(202, 57); this.Btn_M2Scripts.TabIndex = 52; this.Btn_M2Scripts.Text = "Install m2emulator crosshair scripts"; this.Btn_M2Scripts.UseVisualStyleBackColor = true; this.Btn_M2Scripts.Click += new System.EventHandler(this.Btn_M2Scripts_Click); // // label1 // this.label1.AutoSize = true; this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.Location = new System.Drawing.Point(15, 13); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(309, 75); this.label1.TabIndex = 51; this.label1.Text = "Click the button to install lua scripts for custom crosshair\r\nin nebula Model2 Em" + "ulator.\r\nYou will be asked for the emulator .exe location\r\n\r\nExisting files will" + " be overwritten !"; // // Tab_MissionImpossible // this.Tab_MissionImpossible.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_MissionImpossible.Controls.Add(this.groupBox4); this.Tab_MissionImpossible.Controls.Add(this.label42); this.Tab_MissionImpossible.Controls.Add(this.Btn_MisImp_Save); this.Tab_MissionImpossible.Location = new System.Drawing.Point(4, 5); this.Tab_MissionImpossible.Name = "Tab_MissionImpossible"; this.Tab_MissionImpossible.Size = new System.Drawing.Size(581, 334); this.Tab_MissionImpossible.TabIndex = 22; this.Tab_MissionImpossible.Text = "Mission Impossible"; this.Tab_MissionImpossible.UseVisualStyleBackColor = true; // // groupBox4 // this.groupBox4.Controls.Add(this.Rdo_MIA_Merge); this.groupBox4.Controls.Add(this.Rdo_MIA_Separate); this.groupBox4.Location = new System.Drawing.Point(10, 3); this.groupBox4.Name = "groupBox4"; this.groupBox4.Size = new System.Drawing.Size(552, 100); this.groupBox4.TabIndex = 37; this.groupBox4.TabStop = false; this.groupBox4.Text = "Triggers configuration :"; // // Rdo_MIA_Merge // this.Rdo_MIA_Merge.AutoSize = true; this.Rdo_MIA_Merge.Checked = true; this.Rdo_MIA_Merge.Location = new System.Drawing.Point(35, 34); this.Rdo_MIA_Merge.Name = "Rdo_MIA_Merge"; this.Rdo_MIA_Merge.Size = new System.Drawing.Size(118, 20); this.Rdo_MIA_Merge.TabIndex = 34; this.Rdo_MIA_Merge.TabStop = true; this.Rdo_MIA_Merge.Text = "Merge Triggers"; this.Rdo_MIA_Merge.UseVisualStyleBackColor = true; this.Rdo_MIA_Merge.CheckedChanged += new System.EventHandler(this.Rdo_MIA_Merge_CheckedChanged); // // Rdo_MIA_Separate // this.Rdo_MIA_Separate.AutoSize = true; this.Rdo_MIA_Separate.Location = new System.Drawing.Point(35, 60); this.Rdo_MIA_Separate.Name = "Rdo_MIA_Separate"; this.Rdo_MIA_Separate.Size = new System.Drawing.Size(135, 20); this.Rdo_MIA_Separate.TabIndex = 35; this.Rdo_MIA_Separate.TabStop = true; this.Rdo_MIA_Separate.Text = "Separate Triggers"; this.Rdo_MIA_Separate.UseVisualStyleBackColor = true; this.Rdo_MIA_Separate.CheckedChanged += new System.EventHandler(this.Rdo_MIA_Separate_CheckedChanged); // // label42 // this.label42.AutoSize = true; this.label42.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label42.Location = new System.Drawing.Point(28, 126); this.label42.Name = "label42"; this.label42.Size = new System.Drawing.Size(522, 120); this.label42.TabIndex = 36; this.label42.Text = resources.GetString("label42.Text"); // // Btn_MisImp_Save // this.Btn_MisImp_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_MisImp_Save.Location = new System.Drawing.Point(217, 284); this.Btn_MisImp_Save.Margin = new System.Windows.Forms.Padding(4); this.Btn_MisImp_Save.Name = "Btn_MisImp_Save"; this.Btn_MisImp_Save.Size = new System.Drawing.Size(145, 37); this.Btn_MisImp_Save.TabIndex = 33; this.Btn_MisImp_Save.Text = "Save Config"; this.Btn_MisImp_Save.UseVisualStyleBackColor = true; this.Btn_MisImp_Save.Click += new System.EventHandler(this.Btn_MisImp_Save_Click); // // Tab_OpGhost // this.Tab_OpGhost.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_OpGhost.Controls.Add(this.label67); this.Tab_OpGhost.Controls.Add(this.groupBox18); this.Tab_OpGhost.Controls.Add(this.Btn_Save_OpGhost); this.Tab_OpGhost.Controls.Add(this.Gbox_OpGhost_Buttons); this.Tab_OpGhost.Location = new System.Drawing.Point(4, 5); this.Tab_OpGhost.Name = "Tab_OpGhost"; this.Tab_OpGhost.Size = new System.Drawing.Size(581, 334); this.Tab_OpGhost.TabIndex = 18; this.Tab_OpGhost.Text = "Operation G.H.O.S.T"; this.Tab_OpGhost.UseVisualStyleBackColor = true; // // label67 // this.label67.AutoSize = true; this.label67.Location = new System.Drawing.Point(7, 239); this.label67.Name = "label67"; this.label67.Size = new System.Drawing.Size(528, 80); this.label67.TabIndex = 66; this.label67.Text = resources.GetString("label67.Text"); // // groupBox18 // this.groupBox18.Controls.Add(this.Cbox_OpGhost_CreditsToContinue); this.groupBox18.Controls.Add(this.label66); this.groupBox18.Controls.Add(this.Cbox_OpGhost_CreditsToStart); this.groupBox18.Controls.Add(this.label65); this.groupBox18.Controls.Add(this.Cbox_OpGhost_CreditsByCoin); this.groupBox18.Controls.Add(this.label64); this.groupBox18.Controls.Add(this.Cbox_OpGhost_Freeplay); this.groupBox18.Controls.Add(this.label62); this.groupBox18.Location = new System.Drawing.Point(10, 15); this.groupBox18.Name = "groupBox18"; this.groupBox18.Size = new System.Drawing.Size(280, 163); this.groupBox18.TabIndex = 65; this.groupBox18.TabStop = false; this.groupBox18.Text = "Coin Settings"; // // Cbox_OpGhost_CreditsToContinue // this.Cbox_OpGhost_CreditsToContinue.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_OpGhost_CreditsToContinue.FormattingEnabled = true; this.Cbox_OpGhost_CreditsToContinue.Items.AddRange(new object[] { "1", "2", "3", "4", "5", "6", "7", "8", "9"}); this.Cbox_OpGhost_CreditsToContinue.Location = new System.Drawing.Point(183, 131); this.Cbox_OpGhost_CreditsToContinue.Name = "Cbox_OpGhost_CreditsToContinue"; this.Cbox_OpGhost_CreditsToContinue.Size = new System.Drawing.Size(77, 24); this.Cbox_OpGhost_CreditsToContinue.TabIndex = 8; this.Cbox_OpGhost_CreditsToContinue.SelectedIndexChanged += new System.EventHandler(this.Cbox_OpGhost_CreditsToContinue_SelectedIndexChanged); // // label66 // this.label66.AutoSize = true; this.label66.Location = new System.Drawing.Point(9, 134); this.label66.Name = "label66"; this.label66.Size = new System.Drawing.Size(167, 16); this.label66.TabIndex = 7; this.label66.Text = "CREDITS TO CONTINUE :"; // // Cbox_OpGhost_CreditsToStart // this.Cbox_OpGhost_CreditsToStart.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_OpGhost_CreditsToStart.FormattingEnabled = true; this.Cbox_OpGhost_CreditsToStart.Items.AddRange(new object[] { "1", "2", "3", "4", "5", "6", "7", "8", "9"}); this.Cbox_OpGhost_CreditsToStart.Location = new System.Drawing.Point(183, 101); this.Cbox_OpGhost_CreditsToStart.Name = "Cbox_OpGhost_CreditsToStart"; this.Cbox_OpGhost_CreditsToStart.Size = new System.Drawing.Size(77, 24); this.Cbox_OpGhost_CreditsToStart.TabIndex = 6; this.Cbox_OpGhost_CreditsToStart.SelectedIndexChanged += new System.EventHandler(this.Cbox_OpGhost_CreditsToStart_SelectedIndexChanged); // // label65 // this.label65.AutoSize = true; this.label65.Location = new System.Drawing.Point(9, 104); this.label65.Name = "label65"; this.label65.Size = new System.Drawing.Size(143, 16); this.label65.TabIndex = 5; this.label65.Text = "CREDITS TO START :"; // // Cbox_OpGhost_CreditsByCoin // this.Cbox_OpGhost_CreditsByCoin.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_OpGhost_CreditsByCoin.FormattingEnabled = true; this.Cbox_OpGhost_CreditsByCoin.Items.AddRange(new object[] { "1", "2", "3", "4", "5", "6", "7", "8", "9"}); this.Cbox_OpGhost_CreditsByCoin.Location = new System.Drawing.Point(183, 71); this.Cbox_OpGhost_CreditsByCoin.Name = "Cbox_OpGhost_CreditsByCoin"; this.Cbox_OpGhost_CreditsByCoin.Size = new System.Drawing.Size(77, 24); this.Cbox_OpGhost_CreditsByCoin.TabIndex = 4; this.Cbox_OpGhost_CreditsByCoin.SelectedIndexChanged += new System.EventHandler(this.Cbox_OpGhost_CreditsByCoin_SelectedIndexChanged); // // label64 // this.label64.AutoSize = true; this.label64.Location = new System.Drawing.Point(7, 74); this.label64.Name = "label64"; this.label64.Size = new System.Drawing.Size(146, 16); this.label64.TabIndex = 3; this.label64.Text = "COIN(S) PER CREDIT :"; // // Cbox_OpGhost_Freeplay // this.Cbox_OpGhost_Freeplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbox_OpGhost_Freeplay.FormattingEnabled = true; this.Cbox_OpGhost_Freeplay.Items.AddRange(new object[] { "OFF", "ON"}); this.Cbox_OpGhost_Freeplay.Location = new System.Drawing.Point(183, 41); this.Cbox_OpGhost_Freeplay.Name = "Cbox_OpGhost_Freeplay"; this.Cbox_OpGhost_Freeplay.Size = new System.Drawing.Size(77, 24); this.Cbox_OpGhost_Freeplay.TabIndex = 2; this.Cbox_OpGhost_Freeplay.SelectedIndexChanged += new System.EventHandler(this.Cbox_OpGhost_Freeplay_SelectedIndexChanged); // // label62 // this.label62.AutoSize = true; this.label62.Location = new System.Drawing.Point(7, 44); this.label62.Name = "label62"; this.label62.Size = new System.Drawing.Size(83, 16); this.label62.TabIndex = 1; this.label62.Text = "FREEPLAY :"; // // Btn_Save_OpGhost // this.Btn_Save_OpGhost.Location = new System.Drawing.Point(376, 149); this.Btn_Save_OpGhost.Name = "Btn_Save_OpGhost"; this.Btn_Save_OpGhost.Size = new System.Drawing.Size(145, 37); this.Btn_Save_OpGhost.TabIndex = 64; this.Btn_Save_OpGhost.Text = "Save Config"; this.Btn_Save_OpGhost.UseVisualStyleBackColor = true; this.Btn_Save_OpGhost.Click += new System.EventHandler(this.Btn_Save_OpGhost_Click); // // Gbox_OpGhost_Buttons // this.Gbox_OpGhost_Buttons.Controls.Add(this.label61); this.Gbox_OpGhost_Buttons.Controls.Add(this.TXT_OPGHOST_ACTION_P2); this.Gbox_OpGhost_Buttons.Controls.Add(this.label63); this.Gbox_OpGhost_Buttons.Controls.Add(this.TXT_OPGHOST_ACTION_P1); this.Gbox_OpGhost_Buttons.Controls.Add(this.Chk_OpGhost_SeparateButton); this.Gbox_OpGhost_Buttons.Location = new System.Drawing.Point(301, 15); this.Gbox_OpGhost_Buttons.Name = "Gbox_OpGhost_Buttons"; this.Gbox_OpGhost_Buttons.Size = new System.Drawing.Size(267, 103); this.Gbox_OpGhost_Buttons.TabIndex = 61; this.Gbox_OpGhost_Buttons.TabStop = false; // // label61 // this.label61.AutoSize = true; this.label61.Location = new System.Drawing.Point(27, 69); this.label61.Name = "label61"; this.label61.Size = new System.Drawing.Size(77, 16); this.label61.TabIndex = 56; this.label61.Text = "Set P2 key :"; // // TXT_OPGHOST_ACTION_P2 // this.TXT_OPGHOST_ACTION_P2.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_OPGHOST_ACTION_P2.Enabled = false; this.TXT_OPGHOST_ACTION_P2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_OPGHOST_ACTION_P2.Location = new System.Drawing.Point(111, 66); this.TXT_OPGHOST_ACTION_P2.Name = "TXT_OPGHOST_ACTION_P2"; this.TXT_OPGHOST_ACTION_P2.ReadOnly = true; this.TXT_OPGHOST_ACTION_P2.Size = new System.Drawing.Size(127, 21); this.TXT_OPGHOST_ACTION_P2.TabIndex = 57; this.TXT_OPGHOST_ACTION_P2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_OPGHOST_ACTION_P2.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label63 // this.label63.AutoSize = true; this.label63.Location = new System.Drawing.Point(27, 40); this.label63.Name = "label63"; this.label63.Size = new System.Drawing.Size(77, 16); this.label63.TabIndex = 53; this.label63.Text = "Set P1 key :"; // // TXT_OPGHOST_ACTION_P1 // this.TXT_OPGHOST_ACTION_P1.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_OPGHOST_ACTION_P1.Enabled = false; this.TXT_OPGHOST_ACTION_P1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_OPGHOST_ACTION_P1.Location = new System.Drawing.Point(111, 37); this.TXT_OPGHOST_ACTION_P1.Name = "TXT_OPGHOST_ACTION_P1"; this.TXT_OPGHOST_ACTION_P1.ReadOnly = true; this.TXT_OPGHOST_ACTION_P1.Size = new System.Drawing.Size(127, 21); this.TXT_OPGHOST_ACTION_P1.TabIndex = 52; this.TXT_OPGHOST_ACTION_P1.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_OPGHOST_ACTION_P1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Chk_OpGhost_SeparateButton // this.Chk_OpGhost_SeparateButton.AutoSize = true; this.Chk_OpGhost_SeparateButton.Location = new System.Drawing.Point(9, 0); this.Chk_OpGhost_SeparateButton.Name = "Chk_OpGhost_SeparateButton"; this.Chk_OpGhost_SeparateButton.Size = new System.Drawing.Size(174, 20); this.Chk_OpGhost_SeparateButton.TabIndex = 0; this.Chk_OpGhost_SeparateButton.Text = "Separate ACTION button"; this.Chk_OpGhost_SeparateButton.UseVisualStyleBackColor = true; this.Chk_OpGhost_SeparateButton.CheckedChanged += new System.EventHandler(this.Chk_OpGhost_SeparateButton_CheckedChanged); // // Tab_UnityPlugins // this.Tab_UnityPlugins.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_UnityPlugins.Controls.Add(this.label36); this.Tab_UnityPlugins.Controls.Add(this.Btn_Wws); this.Tab_UnityPlugins.Controls.Add(this.Btn_Pbx); this.Tab_UnityPlugins.Controls.Add(this.Btn_Tra); this.Tab_UnityPlugins.Controls.Add(this.Btn_Rha); this.Tab_UnityPlugins.Controls.Add(this.Btn_PvZ); this.Tab_UnityPlugins.Controls.Add(this.Btn_Owr); this.Tab_UnityPlugins.Controls.Add(this.Btn_Nha); this.Tab_UnityPlugins.Controls.Add(this.Btn_Nerfa); this.Tab_UnityPlugins.Controls.Add(this.Btn_Mia); this.Tab_UnityPlugins.Controls.Add(this.Btn_Mib); this.Tab_UnityPlugins.Controls.Add(this.Btn_MarsS); this.Tab_UnityPlugins.Controls.Add(this.Btn_Drk); this.Tab_UnityPlugins.Controls.Add(this.Btn_Dcop); this.Tab_UnityPlugins.Location = new System.Drawing.Point(4, 5); this.Tab_UnityPlugins.Name = "Tab_UnityPlugins"; this.Tab_UnityPlugins.Size = new System.Drawing.Size(581, 334); this.Tab_UnityPlugins.TabIndex = 16; this.Tab_UnityPlugins.Text = "tabPage1"; this.Tab_UnityPlugins.UseVisualStyleBackColor = true; // // label36 // this.label36.AutoSize = true; this.label36.Location = new System.Drawing.Point(5, 19); this.label36.Name = "label36"; this.label36.Size = new System.Drawing.Size(196, 16); this.label36.TabIndex = 70; this.label36.Text = "Select the Unity Plugin to install :"; // // Tab_Raccoon // this.Tab_Raccoon.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_Raccoon.Controls.Add(this.groupBox21); this.Tab_Raccoon.Location = new System.Drawing.Point(4, 5); this.Tab_Raccoon.Name = "Tab_Raccoon"; this.Tab_Raccoon.Size = new System.Drawing.Size(581, 334); this.Tab_Raccoon.TabIndex = 21; this.Tab_Raccoon.Text = "Raccoon Rampage"; this.Tab_Raccoon.UseVisualStyleBackColor = true; // // groupBox21 // this.groupBox21.Controls.Add(this.Btn_Raccoon_Patch); this.groupBox21.Controls.Add(this.Txt_Raccoon_FolderPath); this.groupBox21.Controls.Add(this.Btn_Raccoon_Open); this.groupBox21.Location = new System.Drawing.Point(5, 3); this.groupBox21.Name = "groupBox21"; this.groupBox21.Size = new System.Drawing.Size(569, 100); this.groupBox21.TabIndex = 73; this.groupBox21.TabStop = false; this.groupBox21.Text = "Select \"RSGame-Win64-Shipping.exe\" directory to patch the files :"; // // Btn_Raccoon_Patch // this.Btn_Raccoon_Patch.Enabled = false; this.Btn_Raccoon_Patch.Location = new System.Drawing.Point(209, 58); this.Btn_Raccoon_Patch.Name = "Btn_Raccoon_Patch"; this.Btn_Raccoon_Patch.Size = new System.Drawing.Size(131, 36); this.Btn_Raccoon_Patch.TabIndex = 2; this.Btn_Raccoon_Patch.Text = "Patch !"; this.Btn_Raccoon_Patch.UseVisualStyleBackColor = true; this.Btn_Raccoon_Patch.Click += new System.EventHandler(this.Btn_Raccoon_Patch_Click); // // Txt_Raccoon_FolderPath // this.Txt_Raccoon_FolderPath.Location = new System.Drawing.Point(6, 30); this.Txt_Raccoon_FolderPath.Name = "Txt_Raccoon_FolderPath"; this.Txt_Raccoon_FolderPath.Size = new System.Drawing.Size(514, 22); this.Txt_Raccoon_FolderPath.TabIndex = 1; // // Btn_Raccoon_Open // this.Btn_Raccoon_Open.Location = new System.Drawing.Point(526, 29); this.Btn_Raccoon_Open.Name = "Btn_Raccoon_Open"; this.Btn_Raccoon_Open.Size = new System.Drawing.Size(37, 23); this.Btn_Raccoon_Open.TabIndex = 0; this.Btn_Raccoon_Open.Text = "..."; this.Btn_Raccoon_Open.UseVisualStyleBackColor = true; this.Btn_Raccoon_Open.Click += new System.EventHandler(this.Btn_Raccoon_Open_Click); // // Tab_RPCS3 // this.Tab_RPCS3.Controls.Add(this.Txt_Rpcs3_Save); this.Tab_RPCS3.Controls.Add(this.groupBox13); this.Tab_RPCS3.Controls.Add(this.groupBox12); this.Tab_RPCS3.Location = new System.Drawing.Point(4, 5); this.Tab_RPCS3.Name = "Tab_RPCS3"; this.Tab_RPCS3.Size = new System.Drawing.Size(581, 334); this.Tab_RPCS3.TabIndex = 17; this.Tab_RPCS3.Text = "RPCS3 (System 357)"; this.Tab_RPCS3.UseVisualStyleBackColor = true; // // Txt_Rpcs3_Save // this.Txt_Rpcs3_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Txt_Rpcs3_Save.Location = new System.Drawing.Point(217, 284); this.Txt_Rpcs3_Save.Margin = new System.Windows.Forms.Padding(4); this.Txt_Rpcs3_Save.Name = "Txt_Rpcs3_Save"; this.Txt_Rpcs3_Save.Size = new System.Drawing.Size(145, 37); this.Txt_Rpcs3_Save.TabIndex = 71; this.Txt_Rpcs3_Save.Text = "Save Config"; this.Txt_Rpcs3_Save.UseVisualStyleBackColor = true; this.Txt_Rpcs3_Save.Click += new System.EventHandler(this.Txt_Rpcs3_Save_Click); // // groupBox13 // this.groupBox13.Controls.Add(this.Btn_Rpcs3_RazingStorm); this.groupBox13.Controls.Add(this.Btn_Rpcs3_SailorZombies); this.groupBox13.Controls.Add(this.Btn_Rpcs3_DarkEscape); this.groupBox13.Controls.Add(this.Btn_Rpcs3_DeadStorm); this.groupBox13.Location = new System.Drawing.Point(6, 3); this.groupBox13.Name = "groupBox13"; this.groupBox13.Size = new System.Drawing.Size(569, 70); this.groupBox13.TabIndex = 70; this.groupBox13.TabStop = false; this.groupBox13.Text = "PPU Cache Patching :"; // // Btn_Rpcs3_RazingStorm // this.Btn_Rpcs3_RazingStorm.Enabled = false; this.Btn_Rpcs3_RazingStorm.Location = new System.Drawing.Point(429, 36); this.Btn_Rpcs3_RazingStorm.Name = "Btn_Rpcs3_RazingStorm"; this.Btn_Rpcs3_RazingStorm.Size = new System.Drawing.Size(132, 28); this.Btn_Rpcs3_RazingStorm.TabIndex = 3; this.Btn_Rpcs3_RazingStorm.Text = "Razing Storm"; this.Btn_Rpcs3_RazingStorm.UseVisualStyleBackColor = true; this.Btn_Rpcs3_RazingStorm.Click += new System.EventHandler(this.Btn_Rpcs3_RazingStorm_Click); // // Btn_Rpcs3_SailorZombies // this.Btn_Rpcs3_SailorZombies.Location = new System.Drawing.Point(288, 36); this.Btn_Rpcs3_SailorZombies.Name = "Btn_Rpcs3_SailorZombies"; this.Btn_Rpcs3_SailorZombies.Size = new System.Drawing.Size(132, 28); this.Btn_Rpcs3_SailorZombies.TabIndex = 2; this.Btn_Rpcs3_SailorZombies.Text = "Sailor Zombie"; this.Btn_Rpcs3_SailorZombies.UseVisualStyleBackColor = true; this.Btn_Rpcs3_SailorZombies.Click += new System.EventHandler(this.Btn_Rpcs3_SailorZombie_Click); // // Btn_Rpcs3_DarkEscape // this.Btn_Rpcs3_DarkEscape.Location = new System.Drawing.Point(147, 36); this.Btn_Rpcs3_DarkEscape.Name = "Btn_Rpcs3_DarkEscape"; this.Btn_Rpcs3_DarkEscape.Size = new System.Drawing.Size(132, 28); this.Btn_Rpcs3_DarkEscape.TabIndex = 1; this.Btn_Rpcs3_DarkEscape.Text = "Dark Escape 4D"; this.Btn_Rpcs3_DarkEscape.UseVisualStyleBackColor = true; this.Btn_Rpcs3_DarkEscape.Click += new System.EventHandler(this.Btn_Rpcs3_DarkEscape_Click); // // Btn_Rpcs3_DeadStorm // this.Btn_Rpcs3_DeadStorm.Location = new System.Drawing.Point(6, 36); this.Btn_Rpcs3_DeadStorm.Name = "Btn_Rpcs3_DeadStorm"; this.Btn_Rpcs3_DeadStorm.Size = new System.Drawing.Size(132, 28); this.Btn_Rpcs3_DeadStorm.TabIndex = 0; this.Btn_Rpcs3_DeadStorm.Text = "DeadStorm Pirates"; this.Btn_Rpcs3_DeadStorm.UseVisualStyleBackColor = true; this.Btn_Rpcs3_DeadStorm.Click += new System.EventHandler(this.Btn_Rpcs3_DeadStorm_Click); // // groupBox12 // this.groupBox12.Controls.Add(this.label57); this.groupBox12.Controls.Add(this.panel2); this.groupBox12.Controls.Add(this.label37); this.groupBox12.Controls.Add(this.Txt_Rpcs3_P1_Start); this.groupBox12.Controls.Add(this.Txt_Rpcs3_Service); this.groupBox12.Controls.Add(this.Txt_Rpcs3_P2_Start); this.groupBox12.Controls.Add(this.label56); this.groupBox12.Controls.Add(this.label38); this.groupBox12.Controls.Add(this.label55); this.groupBox12.Controls.Add(this.Txt_Rpcs3_Enter); this.groupBox12.Controls.Add(this.Txt_Rpcs3_3D_Switch); this.groupBox12.Controls.Add(this.label54); this.groupBox12.Controls.Add(this.label51); this.groupBox12.Controls.Add(this.label53); this.groupBox12.Controls.Add(this.Txt_Rpcs3_Up); this.groupBox12.Controls.Add(this.label52); this.groupBox12.Controls.Add(this.Txt_Rpcs3_Down); this.groupBox12.Location = new System.Drawing.Point(5, 87); this.groupBox12.Name = "groupBox12"; this.groupBox12.Size = new System.Drawing.Size(569, 185); this.groupBox12.TabIndex = 69; this.groupBox12.TabStop = false; this.groupBox12.Text = "Key configuration : to set a key, click a box then push the key on your keyboard " + ":"; // // label57 // this.label57.AutoSize = true; this.label57.Location = new System.Drawing.Point(66, 36); this.label57.Name = "label57"; this.label57.Size = new System.Drawing.Size(135, 16); this.label57.TabIndex = 69; this.label57.Text = "PLAYER BUTTONS :"; // // panel2 // this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.panel2.Location = new System.Drawing.Point(268, 25); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(4, 150); this.panel2.TabIndex = 68; // // label37 // this.label37.AutoSize = true; this.label37.Location = new System.Drawing.Point(341, 36); this.label37.Name = "label37"; this.label37.Size = new System.Drawing.Size(161, 16); this.label37.TabIndex = 67; this.label37.Text = "TEST MENU BUTTONS :"; // // Txt_Rpcs3_P1_Start // this.Txt_Rpcs3_P1_Start.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_Rpcs3_P1_Start.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_Rpcs3_P1_Start.Location = new System.Drawing.Point(84, 70); this.Txt_Rpcs3_P1_Start.Name = "Txt_Rpcs3_P1_Start"; this.Txt_Rpcs3_P1_Start.ReadOnly = true; this.Txt_Rpcs3_P1_Start.Size = new System.Drawing.Size(127, 21); this.Txt_Rpcs3_P1_Start.TabIndex = 53; this.Txt_Rpcs3_P1_Start.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_Rpcs3_P1_Start.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Txt_Rpcs3_Service // this.Txt_Rpcs3_Service.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_Rpcs3_Service.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_Rpcs3_Service.Location = new System.Drawing.Point(84, 124); this.Txt_Rpcs3_Service.Name = "Txt_Rpcs3_Service"; this.Txt_Rpcs3_Service.ReadOnly = true; this.Txt_Rpcs3_Service.Size = new System.Drawing.Size(127, 21); this.Txt_Rpcs3_Service.TabIndex = 58; this.Txt_Rpcs3_Service.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_Rpcs3_Service.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Txt_Rpcs3_P2_Start // this.Txt_Rpcs3_P2_Start.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_Rpcs3_P2_Start.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_Rpcs3_P2_Start.Location = new System.Drawing.Point(84, 97); this.Txt_Rpcs3_P2_Start.Name = "Txt_Rpcs3_P2_Start"; this.Txt_Rpcs3_P2_Start.ReadOnly = true; this.Txt_Rpcs3_P2_Start.Size = new System.Drawing.Size(127, 21); this.Txt_Rpcs3_P2_Start.TabIndex = 54; this.Txt_Rpcs3_P2_Start.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_Rpcs3_P2_Start.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label56 // this.label56.AutoSize = true; this.label56.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label56.Location = new System.Drawing.Point(10, 127); this.label56.Name = "label56"; this.label56.Size = new System.Drawing.Size(64, 15); this.label56.TabIndex = 63; this.label56.Text = "SERVICE :"; // // label38 // this.label38.AutoSize = true; this.label38.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label38.Location = new System.Drawing.Point(10, 154); this.label38.Name = "label38"; this.label38.Size = new System.Drawing.Size(68, 15); this.label38.TabIndex = 66; this.label38.Text = "3D Switch :"; // // label55 // this.label55.AutoSize = true; this.label55.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label55.Location = new System.Drawing.Point(10, 100); this.label55.Name = "label55"; this.label55.Size = new System.Drawing.Size(56, 15); this.label55.TabIndex = 62; this.label55.Text = "P2 Start :"; // // Txt_Rpcs3_Enter // this.Txt_Rpcs3_Enter.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_Rpcs3_Enter.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_Rpcs3_Enter.Location = new System.Drawing.Point(376, 127); this.Txt_Rpcs3_Enter.Name = "Txt_Rpcs3_Enter"; this.Txt_Rpcs3_Enter.ReadOnly = true; this.Txt_Rpcs3_Enter.Size = new System.Drawing.Size(128, 21); this.Txt_Rpcs3_Enter.TabIndex = 55; this.Txt_Rpcs3_Enter.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_Rpcs3_Enter.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Txt_Rpcs3_3D_Switch // this.Txt_Rpcs3_3D_Switch.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_Rpcs3_3D_Switch.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_Rpcs3_3D_Switch.Location = new System.Drawing.Point(84, 151); this.Txt_Rpcs3_3D_Switch.Name = "Txt_Rpcs3_3D_Switch"; this.Txt_Rpcs3_3D_Switch.ReadOnly = true; this.Txt_Rpcs3_3D_Switch.Size = new System.Drawing.Size(127, 21); this.Txt_Rpcs3_3D_Switch.TabIndex = 57; this.Txt_Rpcs3_3D_Switch.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_Rpcs3_3D_Switch.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label54 // this.label54.AutoSize = true; this.label54.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label54.Location = new System.Drawing.Point(10, 73); this.label54.Name = "label54"; this.label54.Size = new System.Drawing.Size(56, 15); this.label54.TabIndex = 59; this.label54.Text = "P1 Start :"; // // label51 // this.label51.AutoSize = true; this.label51.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label51.Location = new System.Drawing.Point(316, 103); this.label51.Name = "label51"; this.label51.Size = new System.Drawing.Size(51, 15); this.label51.TabIndex = 65; this.label51.Text = "DOWN :"; // // label53 // this.label53.AutoSize = true; this.label53.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label53.Location = new System.Drawing.Point(316, 76); this.label53.Name = "label53"; this.label53.Size = new System.Drawing.Size(30, 15); this.label53.TabIndex = 64; this.label53.Text = "UP :"; // // Txt_Rpcs3_Up // this.Txt_Rpcs3_Up.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_Rpcs3_Up.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_Rpcs3_Up.Location = new System.Drawing.Point(376, 73); this.Txt_Rpcs3_Up.Name = "Txt_Rpcs3_Up"; this.Txt_Rpcs3_Up.ReadOnly = true; this.Txt_Rpcs3_Up.Size = new System.Drawing.Size(127, 21); this.Txt_Rpcs3_Up.TabIndex = 60; this.Txt_Rpcs3_Up.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_Rpcs3_Up.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label52 // this.label52.AutoSize = true; this.label52.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label52.Location = new System.Drawing.Point(316, 130); this.label52.Name = "label52"; this.label52.Size = new System.Drawing.Size(54, 15); this.label52.TabIndex = 61; this.label52.Text = "ENTER :"; // // Txt_Rpcs3_Down // this.Txt_Rpcs3_Down.BackColor = System.Drawing.SystemColors.ControlLightLight; this.Txt_Rpcs3_Down.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Txt_Rpcs3_Down.Location = new System.Drawing.Point(376, 100); this.Txt_Rpcs3_Down.Name = "Txt_Rpcs3_Down"; this.Txt_Rpcs3_Down.ReadOnly = true; this.Txt_Rpcs3_Down.Size = new System.Drawing.Size(128, 21); this.Txt_Rpcs3_Down.TabIndex = 56; this.Txt_Rpcs3_Down.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.Txt_Rpcs3_Down.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Tab_SHA // this.Tab_SHA.BackColor = System.Drawing.SystemColors.Control; this.Tab_SHA.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_SHA.Controls.Add(this.label35); this.Tab_SHA.Controls.Add(this.TXT_P1_S); this.Tab_SHA.Controls.Add(this.TXT_P1_T); this.Tab_SHA.Controls.Add(this.label8); this.Tab_SHA.Controls.Add(this.TXT_SERVICE); this.Tab_SHA.Controls.Add(this.Save_Sha_Keys); this.Tab_SHA.Controls.Add(this.label7); this.Tab_SHA.Controls.Add(this.TXT_EXIT); this.Tab_SHA.Controls.Add(this.TXT_TEST); this.Tab_SHA.Controls.Add(this.label12); this.Tab_SHA.Controls.Add(this.label6); this.Tab_SHA.Controls.Add(this.label5); this.Tab_SHA.Controls.Add(this.TXT_P2_S); this.Tab_SHA.Controls.Add(this.label3); this.Tab_SHA.Controls.Add(this.label4); this.Tab_SHA.Controls.Add(this.TXT_P2_T); this.Tab_SHA.Location = new System.Drawing.Point(4, 5); this.Tab_SHA.Margin = new System.Windows.Forms.Padding(4); this.Tab_SHA.Name = "Tab_SHA"; this.Tab_SHA.Padding = new System.Windows.Forms.Padding(4); this.Tab_SHA.Size = new System.Drawing.Size(581, 334); this.Tab_SHA.TabIndex = 2; this.Tab_SHA.Text = "Silent Hill the Arcade"; // // label35 // this.label35.AutoSize = true; this.label35.Location = new System.Drawing.Point(16, 14); this.label35.Name = "label35"; this.label35.Size = new System.Drawing.Size(370, 16); this.label35.TabIndex = 52; this.label35.Text = "To set a key, click a box then push the key on your keyboard :"; // // TXT_P1_S // this.TXT_P1_S.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_P1_S.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_P1_S.Location = new System.Drawing.Point(86, 48); this.TXT_P1_S.Name = "TXT_P1_S"; this.TXT_P1_S.ReadOnly = true; this.TXT_P1_S.Size = new System.Drawing.Size(127, 21); this.TXT_P1_S.TabIndex = 0; this.TXT_P1_S.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_P1_S.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // TXT_P1_T // this.TXT_P1_T.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_P1_T.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_P1_T.Location = new System.Drawing.Point(86, 80); this.TXT_P1_T.Name = "TXT_P1_T"; this.TXT_P1_T.ReadOnly = true; this.TXT_P1_T.Size = new System.Drawing.Size(127, 21); this.TXT_P1_T.TabIndex = 1; this.TXT_P1_T.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_P1_T.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label8 // this.label8.AutoSize = true; this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label8.Location = new System.Drawing.Point(305, 112); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(64, 15); this.label8.TabIndex = 47; this.label8.Text = "SERVICE :"; // // TXT_SERVICE // this.TXT_SERVICE.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_SERVICE.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_SERVICE.Location = new System.Drawing.Point(378, 109); this.TXT_SERVICE.Name = "TXT_SERVICE"; this.TXT_SERVICE.ReadOnly = true; this.TXT_SERVICE.Size = new System.Drawing.Size(128, 21); this.TXT_SERVICE.TabIndex = 3; this.TXT_SERVICE.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_SERVICE.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Save_Sha_Keys // this.Save_Sha_Keys.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Save_Sha_Keys.Location = new System.Drawing.Point(180, 242); this.Save_Sha_Keys.Margin = new System.Windows.Forms.Padding(4); this.Save_Sha_Keys.Name = "Save_Sha_Keys"; this.Save_Sha_Keys.Size = new System.Drawing.Size(207, 47); this.Save_Sha_Keys.TabIndex = 48; this.Save_Sha_Keys.Text = "Save Key config"; this.Save_Sha_Keys.UseVisualStyleBackColor = true; this.Save_Sha_Keys.Click += new System.EventHandler(this.Save_Sha_Keys_Click); // // label7 // this.label7.AutoSize = true; this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label7.Location = new System.Drawing.Point(305, 80); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(43, 15); this.label7.TabIndex = 46; this.label7.Text = "TEST :"; // // TXT_EXIT // this.TXT_EXIT.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_EXIT.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_EXIT.Location = new System.Drawing.Point(379, 45); this.TXT_EXIT.Name = "TXT_EXIT"; this.TXT_EXIT.ReadOnly = true; this.TXT_EXIT.Size = new System.Drawing.Size(127, 21); this.TXT_EXIT.TabIndex = 40; this.TXT_EXIT.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_EXIT.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // TXT_TEST // this.TXT_TEST.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_TEST.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_TEST.Location = new System.Drawing.Point(378, 77); this.TXT_TEST.Name = "TXT_TEST"; this.TXT_TEST.ReadOnly = true; this.TXT_TEST.Size = new System.Drawing.Size(128, 21); this.TXT_TEST.TabIndex = 4; this.TXT_TEST.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_TEST.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label12 // this.label12.AutoSize = true; this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label12.Location = new System.Drawing.Point(12, 83); this.label12.Name = "label12"; this.label12.Size = new System.Drawing.Size(70, 15); this.label12.TabIndex = 42; this.label12.Text = "P1 Trigger :"; // // label6 // this.label6.AutoSize = true; this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label6.Location = new System.Drawing.Point(305, 48); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(39, 15); this.label6.TabIndex = 45; this.label6.Text = "EXIT :"; // // label5 // this.label5.AutoSize = true; this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label5.Location = new System.Drawing.Point(12, 51); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(56, 15); this.label5.TabIndex = 9; this.label5.Text = "P1 Start :"; // // TXT_P2_S // this.TXT_P2_S.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_P2_S.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_P2_S.Location = new System.Drawing.Point(86, 112); this.TXT_P2_S.Name = "TXT_P2_S"; this.TXT_P2_S.ReadOnly = true; this.TXT_P2_S.Size = new System.Drawing.Size(127, 21); this.TXT_P2_S.TabIndex = 5; this.TXT_P2_S.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_P2_S.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // label3 // this.label3.AutoSize = true; this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label3.Location = new System.Drawing.Point(12, 115); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(56, 15); this.label3.TabIndex = 43; this.label3.Text = "P2 Start :"; // // label4 // this.label4.AutoSize = true; this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label4.Location = new System.Drawing.Point(12, 146); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(70, 15); this.label4.TabIndex = 44; this.label4.Text = "P2 Trigger :"; // // TXT_P2_T // this.TXT_P2_T.BackColor = System.Drawing.SystemColors.ControlLightLight; this.TXT_P2_T.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.TXT_P2_T.Location = new System.Drawing.Point(86, 144); this.TXT_P2_T.Name = "TXT_P2_T"; this.TXT_P2_T.ReadOnly = true; this.TXT_P2_T.Size = new System.Drawing.Size(127, 21); this.TXT_P2_T.TabIndex = 6; this.TXT_P2_T.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TXT_P2_T.MouseClick += new System.Windows.Forms.MouseEventHandler(this.TXT_DirectInput_MouseClick); // // Tab_Outputs // this.Tab_Outputs.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Tab_Outputs.Controls.Add(this.groupBox19); this.Tab_Outputs.Controls.Add(this.Btn_SaveOutput); this.Tab_Outputs.Controls.Add(this.Grp_Outputs); this.Tab_Outputs.Location = new System.Drawing.Point(4, 5); this.Tab_Outputs.Name = "Tab_Outputs"; this.Tab_Outputs.Size = new System.Drawing.Size(581, 334); this.Tab_Outputs.TabIndex = 13; this.Tab_Outputs.Text = "Outputs"; this.Tab_Outputs.UseVisualStyleBackColor = true; // // groupBox19 // this.groupBox19.Controls.Add(this.Cbox_NetOutputs); this.groupBox19.Controls.Add(this.Cbox_WmOutputs); this.groupBox19.Controls.Add(this.Cbox_Outputs); this.groupBox19.Location = new System.Drawing.Point(5, 3); this.groupBox19.Name = "groupBox19"; this.groupBox19.Size = new System.Drawing.Size(568, 101); this.groupBox19.TabIndex = 57; this.groupBox19.TabStop = false; // // Cbox_NetOutputs // this.Cbox_NetOutputs.AutoSize = true; this.Cbox_NetOutputs.Enabled = false; this.Cbox_NetOutputs.Location = new System.Drawing.Point(38, 67); this.Cbox_NetOutputs.Name = "Cbox_NetOutputs"; this.Cbox_NetOutputs.Size = new System.Drawing.Size(123, 20); this.Cbox_NetOutputs.TabIndex = 57; this.Cbox_NetOutputs.Text = "Network Outputs"; this.Cbox_NetOutputs.UseVisualStyleBackColor = true; this.Cbox_NetOutputs.CheckedChanged += new System.EventHandler(this.Cbox_NetOutputs_CheckedChanged); // // Cbox_WmOutputs // this.Cbox_WmOutputs.AutoSize = true; this.Cbox_WmOutputs.Enabled = false; this.Cbox_WmOutputs.Location = new System.Drawing.Point(38, 41); this.Cbox_WmOutputs.Name = "Cbox_WmOutputs"; this.Cbox_WmOutputs.Size = new System.Drawing.Size(314, 20); this.Cbox_WmOutputs.TabIndex = 56; this.Cbox_WmOutputs.Text = "Window Messages outputs (MameHooker, etc...)"; this.Cbox_WmOutputs.UseVisualStyleBackColor = true; this.Cbox_WmOutputs.CheckedChanged += new System.EventHandler(this.Cbox_WmOutputs_CheckedChanged); // // Cbox_Outputs // this.Cbox_Outputs.AutoSize = true; this.Cbox_Outputs.Location = new System.Drawing.Point(13, -1); this.Cbox_Outputs.Name = "Cbox_Outputs"; this.Cbox_Outputs.Size = new System.Drawing.Size(117, 20); this.Cbox_Outputs.TabIndex = 1; this.Cbox_Outputs.Text = "Enable Outputs"; this.Cbox_Outputs.UseVisualStyleBackColor = true; this.Cbox_Outputs.CheckedChanged += new System.EventHandler(this.Cbox_Outputs_CheckedChanged); // // Btn_SaveOutput // this.Btn_SaveOutput.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_SaveOutput.Location = new System.Drawing.Point(217, 283); this.Btn_SaveOutput.Margin = new System.Windows.Forms.Padding(4); this.Btn_SaveOutput.Name = "Btn_SaveOutput"; this.Btn_SaveOutput.Size = new System.Drawing.Size(145, 37); this.Btn_SaveOutput.TabIndex = 40; this.Btn_SaveOutput.Text = "Save Config"; this.Btn_SaveOutput.UseVisualStyleBackColor = true; this.Btn_SaveOutput.Click += new System.EventHandler(this.Btn_Save_Cfg_Click); // // Grp_Outputs // this.Grp_Outputs.Controls.Add(this.label21); this.Grp_Outputs.Controls.Add(this.label24); this.Grp_Outputs.Controls.Add(this.Txt_OutputRecoilOff); this.Grp_Outputs.Controls.Add(this.label23); this.Grp_Outputs.Controls.Add(this.label20); this.Grp_Outputs.Controls.Add(this.label10); this.Grp_Outputs.Controls.Add(this.label22); this.Grp_Outputs.Controls.Add(this.Txt_OutputRecoilOn); this.Grp_Outputs.Controls.Add(this.label25); this.Grp_Outputs.Controls.Add(this.Txt_OutputDamaged); this.Grp_Outputs.Controls.Add(this.label26); this.Grp_Outputs.Controls.Add(this.Txt_OutputDelay); this.Grp_Outputs.Location = new System.Drawing.Point(4, 103); this.Grp_Outputs.Name = "Grp_Outputs"; this.Grp_Outputs.Size = new System.Drawing.Size(569, 173); this.Grp_Outputs.TabIndex = 56; this.Grp_Outputs.TabStop = false; // // label21 // this.label21.AutoSize = true; this.label21.Location = new System.Drawing.Point(396, 130); this.label21.Name = "label21"; this.label21.Size = new System.Drawing.Size(82, 16); this.label21.TabIndex = 69; this.label21.Text = "Milliseconds"; // // label24 // this.label24.AutoSize = true; this.label24.Location = new System.Drawing.Point(10, 130); this.label24.Name = "label24"; this.label24.Size = new System.Drawing.Size(322, 16); this.label24.TabIndex = 68; this.label24.Text = "Length of custom [recoil] generated output OFF state :\r\n"; // // Txt_OutputRecoilOff // this.Txt_OutputRecoilOff.Enabled = false; this.Txt_OutputRecoilOff.Location = new System.Drawing.Point(334, 127); this.Txt_OutputRecoilOff.Name = "Txt_OutputRecoilOff"; this.Txt_OutputRecoilOff.Size = new System.Drawing.Size(56, 22); this.Txt_OutputRecoilOff.TabIndex = 67; this.Txt_OutputRecoilOff.Text = "50"; this.Txt_OutputRecoilOff.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_OutputRecoilOff.TextChanged += new System.EventHandler(this.Txt_OutputRecoilOff_TextChanged); // // label23 // this.label23.AutoSize = true; this.label23.Location = new System.Drawing.Point(372, 62); this.label23.Name = "label23"; this.label23.Size = new System.Drawing.Size(82, 16); this.label23.TabIndex = 66; this.label23.Text = "Milliseconds"; // // label20 // this.label20.AutoSize = true; this.label20.Location = new System.Drawing.Point(10, 62); this.label20.Name = "label20"; this.label20.Size = new System.Drawing.Size(287, 16); this.label20.TabIndex = 65; this.label20.Text = "Length of custom [damaged] generated output :"; // // label10 // this.label10.AutoSize = true; this.label10.Location = new System.Drawing.Point(396, 102); this.label10.Name = "label10"; this.label10.Size = new System.Drawing.Size(82, 16); this.label10.TabIndex = 64; this.label10.Text = "Milliseconds"; // // label22 // this.label22.AutoSize = true; this.label22.Location = new System.Drawing.Point(11, 102); this.label22.Name = "label22"; this.label22.Size = new System.Drawing.Size(316, 16); this.label22.TabIndex = 61; this.label22.Text = "Length of custom [recoil] generated output ON state :"; // // Txt_OutputRecoilOn // this.Txt_OutputRecoilOn.Enabled = false; this.Txt_OutputRecoilOn.Location = new System.Drawing.Point(334, 99); this.Txt_OutputRecoilOn.Name = "Txt_OutputRecoilOn"; this.Txt_OutputRecoilOn.Size = new System.Drawing.Size(56, 22); this.Txt_OutputRecoilOn.TabIndex = 3; this.Txt_OutputRecoilOn.Text = "50"; this.Txt_OutputRecoilOn.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_OutputRecoilOn.TextChanged += new System.EventHandler(this.Txt_OutputRecoilOn_TextChanged); // // label25 // this.label25.AutoSize = true; this.label25.Location = new System.Drawing.Point(372, 34); this.label25.Name = "label25"; this.label25.Size = new System.Drawing.Size(82, 16); this.label25.TabIndex = 4; this.label25.Text = "Milliseconds"; // // Txt_OutputDamaged // this.Txt_OutputDamaged.Enabled = false; this.Txt_OutputDamaged.Location = new System.Drawing.Point(310, 59); this.Txt_OutputDamaged.Name = "Txt_OutputDamaged"; this.Txt_OutputDamaged.Size = new System.Drawing.Size(56, 22); this.Txt_OutputDamaged.TabIndex = 4; this.Txt_OutputDamaged.Text = "200"; this.Txt_OutputDamaged.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_OutputDamaged.TextChanged += new System.EventHandler(this.Txt_OutputDamaged_TextChanged); // // label26 // this.label26.AutoSize = true; this.label26.Location = new System.Drawing.Point(11, 34); this.label26.Name = "label26"; this.label26.Size = new System.Drawing.Size(186, 16); this.label26.TabIndex = 2; this.label26.Text = "Delay between output refresh :\r\n"; // // Txt_OutputDelay // this.Txt_OutputDelay.Enabled = false; this.Txt_OutputDelay.Location = new System.Drawing.Point(310, 31); this.Txt_OutputDelay.Name = "Txt_OutputDelay"; this.Txt_OutputDelay.Size = new System.Drawing.Size(56, 22); this.Txt_OutputDelay.TabIndex = 2; this.Txt_OutputDelay.Text = "1"; this.Txt_OutputDelay.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; this.Txt_OutputDelay.TextChanged += new System.EventHandler(this.Txt_OutputDelay_TextChanged); // // Cbo_PageSettings // this.Cbo_PageSettings.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbo_PageSettings.FormattingEnabled = true; this.Cbo_PageSettings.Items.AddRange(new object[] { "P1 Configuration", "P2 Configuration", "P3 Configuration", "P4 Configuration", "Calibration (Analog devices)", "Calibration (Act Labs lightguns)", "Dolphin", "Elevator Action Invasion", "Gundam : SoZ", "Heavy Fire series", "Lethal Enforcers 3", "m2Emulator", "Mission Impossible", "Operation G.H.O.S.T", "Raccoon Rampage", "RPCS3 (System 357)", "Silent Hill : The Arcade", "Unity Plugins Installation", "Outputs"}); this.Cbo_PageSettings.Location = new System.Drawing.Point(118, 12); this.Cbo_PageSettings.Name = "Cbo_PageSettings"; this.Cbo_PageSettings.Size = new System.Drawing.Size(470, 24); this.Cbo_PageSettings.TabIndex = 39; this.Cbo_PageSettings.SelectionChangeCommitted += new System.EventHandler(this.Cbo_PageSettings_SelectionChangeCommitted); // // label39 // this.label39.AutoSize = true; this.label39.Location = new System.Drawing.Point(8, 15); this.label39.Name = "label39"; this.label39.Size = new System.Drawing.Size(103, 16); this.label39.TabIndex = 40; this.label39.Text = "Page selection :"; // // Btn_Dcop // this.Btn_Dcop.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Dcop.Location = new System.Drawing.Point(41, 54); this.Btn_Dcop.Margin = new System.Windows.Forms.Padding(4); this.Btn_Dcop.Name = "Btn_Dcop"; this.Btn_Dcop.Size = new System.Drawing.Size(118, 48); this.Btn_Dcop.TabIndex = 74; this.Btn_Dcop.Text = "DCOP"; this.Btn_Dcop.UseVisualStyleBackColor = true; this.Btn_Dcop.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Drk // this.Btn_Drk.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Drk.Location = new System.Drawing.Point(167, 54); this.Btn_Drk.Margin = new System.Windows.Forms.Padding(4); this.Btn_Drk.Name = "Btn_Drk"; this.Btn_Drk.Size = new System.Drawing.Size(118, 48); this.Btn_Drk.TabIndex = 75; this.Btn_Drk.Text = "Drakon\r\nRealm Keeper"; this.Btn_Drk.UseVisualStyleBackColor = true; this.Btn_Drk.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_MarsS // this.Btn_MarsS.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_MarsS.Location = new System.Drawing.Point(293, 54); this.Btn_MarsS.Margin = new System.Windows.Forms.Padding(4); this.Btn_MarsS.Name = "Btn_MarsS"; this.Btn_MarsS.Size = new System.Drawing.Size(118, 48); this.Btn_MarsS.TabIndex = 76; this.Btn_MarsS.Text = "Mars Sortie"; this.Btn_MarsS.UseVisualStyleBackColor = true; this.Btn_MarsS.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Mib // this.Btn_Mib.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Mib.Location = new System.Drawing.Point(42, 110); this.Btn_Mib.Margin = new System.Windows.Forms.Padding(4); this.Btn_Mib.Name = "Btn_Mib"; this.Btn_Mib.Size = new System.Drawing.Size(118, 48); this.Btn_Mib.TabIndex = 77; this.Btn_Mib.Text = "M.I.B"; this.Btn_Mib.UseVisualStyleBackColor = true; this.Btn_Mib.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Mia // this.Btn_Mia.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Mia.Location = new System.Drawing.Point(419, 54); this.Btn_Mia.Margin = new System.Windows.Forms.Padding(4); this.Btn_Mia.Name = "Btn_Mia"; this.Btn_Mia.Size = new System.Drawing.Size(118, 48); this.Btn_Mia.TabIndex = 78; this.Btn_Mia.Text = "Mission Impossible"; this.Btn_Mia.UseVisualStyleBackColor = true; this.Btn_Mia.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Nerfa // this.Btn_Nerfa.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Nerfa.Location = new System.Drawing.Point(167, 110); this.Btn_Nerfa.Margin = new System.Windows.Forms.Padding(4); this.Btn_Nerfa.Name = "Btn_Nerfa"; this.Btn_Nerfa.Size = new System.Drawing.Size(118, 48); this.Btn_Nerfa.TabIndex = 79; this.Btn_Nerfa.Text = "Nerf Arcade"; this.Btn_Nerfa.UseVisualStyleBackColor = true; this.Btn_Nerfa.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Nha // this.Btn_Nha.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Nha.Location = new System.Drawing.Point(293, 110); this.Btn_Nha.Margin = new System.Windows.Forms.Padding(4); this.Btn_Nha.Name = "Btn_Nha"; this.Btn_Nha.Size = new System.Drawing.Size(118, 48); this.Btn_Nha.TabIndex = 80; this.Btn_Nha.Text = "Night Hunter"; this.Btn_Nha.UseVisualStyleBackColor = true; this.Btn_Nha.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Owr // this.Btn_Owr.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Owr.Location = new System.Drawing.Point(419, 110); this.Btn_Owr.Margin = new System.Windows.Forms.Padding(4); this.Btn_Owr.Name = "Btn_Owr"; this.Btn_Owr.Size = new System.Drawing.Size(118, 48); this.Btn_Owr.TabIndex = 81; this.Btn_Owr.Text = "Operation Wolf\r\nReturn"; this.Btn_Owr.UseVisualStyleBackColor = true; this.Btn_Owr.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_PvZ // this.Btn_PvZ.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_PvZ.Location = new System.Drawing.Point(41, 166); this.Btn_PvZ.Margin = new System.Windows.Forms.Padding(4); this.Btn_PvZ.Name = "Btn_PvZ"; this.Btn_PvZ.Size = new System.Drawing.Size(118, 48); this.Btn_PvZ.TabIndex = 82; this.Btn_PvZ.Text = "Plant VS Zombies"; this.Btn_PvZ.UseVisualStyleBackColor = true; this.Btn_PvZ.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Rha // this.Btn_Rha.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Rha.Location = new System.Drawing.Point(293, 166); this.Btn_Rha.Margin = new System.Windows.Forms.Padding(4); this.Btn_Rha.Name = "Btn_Rha"; this.Btn_Rha.Size = new System.Drawing.Size(118, 48); this.Btn_Rha.TabIndex = 83; this.Btn_Rha.Text = "Rabbids Hollywood"; this.Btn_Rha.UseVisualStyleBackColor = true; this.Btn_Rha.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Tra // this.Btn_Tra.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Tra.Location = new System.Drawing.Point(419, 166); this.Btn_Tra.Margin = new System.Windows.Forms.Padding(4); this.Btn_Tra.Name = "Btn_Tra"; this.Btn_Tra.Size = new System.Drawing.Size(118, 48); this.Btn_Tra.TabIndex = 84; this.Btn_Tra.Text = "Tomb Raider Arcade"; this.Btn_Tra.UseVisualStyleBackColor = true; this.Btn_Tra.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Pbx // this.Btn_Pbx.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Pbx.Location = new System.Drawing.Point(167, 166); this.Btn_Pbx.Margin = new System.Windows.Forms.Padding(4); this.Btn_Pbx.Name = "Btn_Pbx"; this.Btn_Pbx.Size = new System.Drawing.Size(118, 48); this.Btn_Pbx.TabIndex = 85; this.Btn_Pbx.Text = "Point Blank X"; this.Btn_Pbx.UseVisualStyleBackColor = true; this.Btn_Pbx.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Btn_Wws // this.Btn_Wws.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.Btn_Wws.Location = new System.Drawing.Point(42, 222); this.Btn_Wws.Margin = new System.Windows.Forms.Padding(4); this.Btn_Wws.Name = "Btn_Wws"; this.Btn_Wws.Size = new System.Drawing.Size(118, 48); this.Btn_Wws.TabIndex = 86; this.Btn_Wws.Text = "Wild West Shootout"; this.Btn_Wws.UseVisualStyleBackColor = true; this.Btn_Wws.Click += new System.EventHandler(this.Btn_InstallUnityPlugin_Click); // // Wnd_DemulShooterGui // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(593, 392); this.Controls.Add(this.label39); this.Controls.Add(this.Cbo_PageSettings); this.Controls.Add(this.tabControl1); this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "Wnd_DemulShooterGui"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "DemulShooter"; this.tabControl1.ResumeLayout(false); this.Tab_P1.ResumeLayout(false); this.Tab_P2.ResumeLayout(false); this.Tab_P3.ResumeLayout(false); this.Tab_P4.ResumeLayout(false); this.Tab_AnalogCalib.ResumeLayout(false); this.Tab_ActLAbs.ResumeLayout(false); this.Tab_ActLAbs.PerformLayout(); this.groupBox3.ResumeLayout(false); this.groupBox3.PerformLayout(); this.Tab_Dolphin.ResumeLayout(false); this.Tab_Dolphin.PerformLayout(); this.Tab_EAInvasion.ResumeLayout(false); this.Tab_EAInvasion.PerformLayout(); this.groupBox17.ResumeLayout(false); this.groupBox17.PerformLayout(); this.groupBox16.ResumeLayout(false); this.groupBox16.PerformLayout(); this.Tab_GSOZ.ResumeLayout(false); this.Tab_GSOZ.PerformLayout(); this.groupBox6.ResumeLayout(false); this.groupBox6.PerformLayout(); this.groupBox5.ResumeLayout(false); this.groupBox5.PerformLayout(); this.Tab_HeavyFire.ResumeLayout(false); this.Tab_HeavyFire.PerformLayout(); this.Gbox_HF_Grenade.ResumeLayout(false); this.Gbox_HF_Grenade.PerformLayout(); this.Gbox_HF_Cover.ResumeLayout(false); this.Gbox_HF_Cover.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.TrackBar_HF_Cover)).EndInit(); this.Tab_LethalEnforcer3.ResumeLayout(false); this.Tab_LethalEnforcer3.PerformLayout(); this.groupBox14.ResumeLayout(false); this.groupBox14.PerformLayout(); this.groupBox15.ResumeLayout(false); this.groupBox15.PerformLayout(); this.Tab_M2.ResumeLayout(false); this.Tab_M2.PerformLayout(); this.groupBox2.ResumeLayout(false); this.groupBox2.PerformLayout(); this.Tab_MissionImpossible.ResumeLayout(false); this.Tab_MissionImpossible.PerformLayout(); this.groupBox4.ResumeLayout(false); this.groupBox4.PerformLayout(); this.Tab_OpGhost.ResumeLayout(false); this.Tab_OpGhost.PerformLayout(); this.groupBox18.ResumeLayout(false); this.groupBox18.PerformLayout(); this.Gbox_OpGhost_Buttons.ResumeLayout(false); this.Gbox_OpGhost_Buttons.PerformLayout(); this.Tab_UnityPlugins.ResumeLayout(false); this.Tab_UnityPlugins.PerformLayout(); this.Tab_Raccoon.ResumeLayout(false); this.groupBox21.ResumeLayout(false); this.groupBox21.PerformLayout(); this.Tab_RPCS3.ResumeLayout(false); this.groupBox13.ResumeLayout(false); this.groupBox12.ResumeLayout(false); this.groupBox12.PerformLayout(); this.Tab_SHA.ResumeLayout(false); this.Tab_SHA.PerformLayout(); this.Tab_Outputs.ResumeLayout(false); this.groupBox19.ResumeLayout(false); this.groupBox19.PerformLayout(); this.Grp_Outputs.ResumeLayout(false); this.Grp_Outputs.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Button Btn_Save_P1; private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.TabPage Tab_P1; private System.Windows.Forms.TabPage Tab_SHA; private System.Windows.Forms.Label label5; private System.Windows.Forms.TextBox TXT_P2_T; private System.Windows.Forms.TextBox TXT_P2_S; private System.Windows.Forms.TextBox TXT_TEST; private System.Windows.Forms.TextBox TXT_SERVICE; private System.Windows.Forms.TextBox TXT_P1_T; private System.Windows.Forms.TextBox TXT_P1_S; private System.Windows.Forms.TextBox TXT_EXIT; private System.Windows.Forms.Label label8; private System.Windows.Forms.Label label7; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label12; private System.Windows.Forms.Button Save_Sha_Keys; private System.Windows.Forms.TabPage Tab_Dolphin; private System.Windows.Forms.Label label2; private System.Windows.Forms.Button Btn_Dolphin5; private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog1; private System.Windows.Forms.TabPage Tab_M2; private System.Windows.Forms.Button Btn_M2Scripts; private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox TXT_CH_VIS; private System.Windows.Forms.TextBox TXT_CH_P2; private System.Windows.Forms.TextBox TXT_CH_P1; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.Label label14; private System.Windows.Forms.Label label13; private System.Windows.Forms.Label label11; private System.Windows.Forms.TabPage Tab_ActLAbs; private System.Windows.Forms.GroupBox groupBox3; private System.Windows.Forms.Label label17; private System.Windows.Forms.TextBox Txt_ActLabs_Y1; private System.Windows.Forms.Label label16; private System.Windows.Forms.TextBox Txt_ActLabs_X1; private System.Windows.Forms.CheckBox Cb_ActLabsOffset; private System.Windows.Forms.Label label15; private System.Windows.Forms.Button Btn_ActLabs_Save; private System.Windows.Forms.CheckBox Chk_DspCorrectedCrosshair; private System.Windows.Forms.Label label18; private System.Windows.Forms.TextBox Txt_ActLabs_Y2; private System.Windows.Forms.Label label19; private System.Windows.Forms.TextBox Txt_ActLabs_X2; private System.ComponentModel.BackgroundWorker Bgw_XInput; private System.Windows.Forms.TabPage Tab_P2; private System.Windows.Forms.Button Btn_SaveP2; private System.Windows.Forms.GroupBox groupBox5; private System.Windows.Forms.TextBox TXT_GSOZ_PEDAL_1; private System.Windows.Forms.CheckBox Chk_GundamP1Pedal; private System.Windows.Forms.TextBox TXT_GSOZ_PEDAL_2; private System.Windows.Forms.CheckBox Chk_GundamP2Pedal; private System.Windows.Forms.TabPage Tab_GSOZ; private System.Windows.Forms.Label label34; private System.Windows.Forms.GroupBox groupBox6; private System.Windows.Forms.Label label33; private System.Windows.Forms.Label label9; private System.Windows.Forms.Label label35; private System.Windows.Forms.Button Btn_Save_Gsoz; private System.Windows.Forms.ComboBox Cbo_PageSettings; private System.Windows.Forms.Label label39; private System.Windows.Forms.TabPage Tab_HeavyFire; private System.Windows.Forms.TrackBar TrackBar_HF_Cover; private System.Windows.Forms.Label label41; private System.Windows.Forms.Button Btn_HF_Save; private System.Windows.Forms.Label label45; private System.Windows.Forms.Label label47; private System.Windows.Forms.TextBox Txt_ActLabs_Y4; private System.Windows.Forms.Label label48; private System.Windows.Forms.TextBox Txt_ActLabs_X4; private System.Windows.Forms.Label label49; private System.Windows.Forms.TextBox Txt_ActLabs_Y3; private System.Windows.Forms.Label label50; private System.Windows.Forms.TextBox Txt_ActLabs_X3; private System.Windows.Forms.TabPage Tab_P3; private System.Windows.Forms.TabPage Tab_P4; private System.Windows.Forms.Button Btn_Save_P3; private System.Windows.Forms.Button Btn_Save_P4; private System.Windows.Forms.TabPage Tab_Outputs; private System.Windows.Forms.CheckBox Cbox_Outputs; private System.Windows.Forms.Button Btn_SaveOutput; private System.Windows.Forms.GroupBox Grp_Outputs; private System.Windows.Forms.Label label22; private System.Windows.Forms.TextBox Txt_OutputRecoilOn; private System.Windows.Forms.Label label25; private System.Windows.Forms.TextBox Txt_OutputDamaged; private System.Windows.Forms.Label label26; private System.Windows.Forms.TextBox Txt_OutputDelay; private System.Windows.Forms.Label label23; private System.Windows.Forms.Label label20; private System.Windows.Forms.Label label10; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.CheckBox Cbox_M2_Flash; private System.Windows.Forms.TabPage Tab_AnalogCalib; private System.Windows.Forms.Button Btn_SaveAnalog; private System.Windows.Forms.TableLayoutPanel TableLayout_Calib; private System.Windows.Forms.Label label21; private System.Windows.Forms.Label label24; private System.Windows.Forms.TextBox Txt_OutputRecoilOff; private System.Windows.Forms.TabPage Tab_UnityPlugins; private System.Windows.Forms.Label label36; private System.Windows.Forms.TabPage Tab_RPCS3; private System.Windows.Forms.GroupBox groupBox13; private System.Windows.Forms.Button Btn_Rpcs3_RazingStorm; private System.Windows.Forms.Button Btn_Rpcs3_SailorZombies; private System.Windows.Forms.Button Btn_Rpcs3_DarkEscape; private System.Windows.Forms.Button Btn_Rpcs3_DeadStorm; private System.Windows.Forms.GroupBox groupBox12; private System.Windows.Forms.TextBox Txt_Rpcs3_P1_Start; private System.Windows.Forms.TextBox Txt_Rpcs3_Service; private System.Windows.Forms.TextBox Txt_Rpcs3_P2_Start; private System.Windows.Forms.Label label56; private System.Windows.Forms.Label label38; private System.Windows.Forms.Label label55; private System.Windows.Forms.TextBox Txt_Rpcs3_Enter; private System.Windows.Forms.TextBox Txt_Rpcs3_3D_Switch; private System.Windows.Forms.Label label54; private System.Windows.Forms.Label label51; private System.Windows.Forms.Label label53; private System.Windows.Forms.TextBox Txt_Rpcs3_Up; private System.Windows.Forms.Label label52; private System.Windows.Forms.TextBox Txt_Rpcs3_Down; private System.Windows.Forms.Button Txt_Rpcs3_Save; private System.Windows.Forms.Label label57; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Label label37; private System.Windows.Forms.TabPage Tab_LethalEnforcer3; private System.Windows.Forms.Button Btn_Save_Le3; private System.Windows.Forms.Label label58; private System.Windows.Forms.GroupBox groupBox14; private System.Windows.Forms.Label label59; private System.Windows.Forms.TextBox TXT_LE3_PEDAL_2; private System.Windows.Forms.CheckBox Chk_Le3_EnablePedal2; private System.Windows.Forms.GroupBox groupBox15; private System.Windows.Forms.Label label60; private System.Windows.Forms.TextBox TXT_LE3_PEDAL_1; private System.Windows.Forms.CheckBox Chk_Le3_EnablePedal1; private System.Windows.Forms.TabPage Tab_OpGhost; private System.Windows.Forms.GroupBox groupBox18; private System.Windows.Forms.Button Btn_Save_OpGhost; private System.Windows.Forms.GroupBox Gbox_OpGhost_Buttons; private System.Windows.Forms.Label label61; private System.Windows.Forms.TextBox TXT_OPGHOST_ACTION_P2; private System.Windows.Forms.Label label63; private System.Windows.Forms.TextBox TXT_OPGHOST_ACTION_P1; private System.Windows.Forms.CheckBox Chk_OpGhost_SeparateButton; private System.Windows.Forms.ComboBox Cbox_OpGhost_CreditsToContinue; private System.Windows.Forms.Label label66; private System.Windows.Forms.ComboBox Cbox_OpGhost_CreditsToStart; private System.Windows.Forms.Label label65; private System.Windows.Forms.ComboBox Cbox_OpGhost_CreditsByCoin; private System.Windows.Forms.Label label64; private System.Windows.Forms.ComboBox Cbox_OpGhost_Freeplay; private System.Windows.Forms.Label label62; private System.Windows.Forms.Label label67; private System.Windows.Forms.TabPage Tab_EAInvasion; private System.Windows.Forms.Label label68; private System.Windows.Forms.Button Btn_EAI_Save; private System.Windows.Forms.GroupBox groupBox17; private System.Windows.Forms.TextBox Txt_EAI_Settings; private System.Windows.Forms.Label label76; private System.Windows.Forms.TextBox Txt_EAI_P1_Start; private System.Windows.Forms.TextBox Txt_EAI_P1_Credits; private System.Windows.Forms.Label label69; private System.Windows.Forms.TextBox Txt_EAI_Enter; private System.Windows.Forms.Label label70; private System.Windows.Forms.TextBox Txt_EAI_Up; private System.Windows.Forms.TextBox Txt_EAI_Down; private System.Windows.Forms.Label label71; private System.Windows.Forms.Label label72; private System.Windows.Forms.Label label73; private System.Windows.Forms.TextBox Txt_EAI_P2_Start; private System.Windows.Forms.Label label74; private System.Windows.Forms.Label label75; private System.Windows.Forms.TextBox Txt_EAI_P2_Credits; private System.Windows.Forms.GroupBox groupBox16; private System.Windows.Forms.Button Btn_EAI_Patch; private System.Windows.Forms.TextBox Txt_EAI_FolderPath; private System.Windows.Forms.Button Btn_EAI_Open; private System.Windows.Forms.CheckBox Chk_HF_ReverseCover; private System.Windows.Forms.GroupBox groupBox19; private System.Windows.Forms.CheckBox Cbox_NetOutputs; private System.Windows.Forms.CheckBox Cbox_WmOutputs; private System.Windows.Forms.TabPage Tab_Raccoon; private System.Windows.Forms.GroupBox groupBox21; private System.Windows.Forms.Button Btn_Raccoon_Patch; private System.Windows.Forms.TextBox Txt_Raccoon_FolderPath; private System.Windows.Forms.Button Btn_Raccoon_Open; private System.Windows.Forms.GroupBox Gbox_HF_Cover; private System.Windows.Forms.RadioButton Rdo_HF_MiddleCover; private System.Windows.Forms.GroupBox Gbox_HF_Grenade; private System.Windows.Forms.Label label27; private System.Windows.Forms.Label label40; private System.Windows.Forms.RadioButton Rdo_HF_MiddleGrenade; private System.Windows.Forms.TabPage Tab_MissionImpossible; private System.Windows.Forms.GroupBox groupBox4; private System.Windows.Forms.RadioButton Rdo_MIA_Merge; private System.Windows.Forms.RadioButton Rdo_MIA_Separate; private System.Windows.Forms.Label label42; private System.Windows.Forms.Button Btn_MisImp_Save; private System.Windows.Forms.Button Btn_Dcop; private System.Windows.Forms.Button Btn_Wws; private System.Windows.Forms.Button Btn_Pbx; private System.Windows.Forms.Button Btn_Tra; private System.Windows.Forms.Button Btn_Rha; private System.Windows.Forms.Button Btn_PvZ; private System.Windows.Forms.Button Btn_Owr; private System.Windows.Forms.Button Btn_Nha; private System.Windows.Forms.Button Btn_Nerfa; private System.Windows.Forms.Button Btn_Mia; private System.Windows.Forms.Button Btn_Mib; private System.Windows.Forms.Button Btn_MarsS; private System.Windows.Forms.Button Btn_Drk; } } ================================================ FILE: DemulShooter_GUI/Wnd_DemulShooterGui.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Windows.Forms; using DsCore; using DsCore.Config; using DsCore.RawInput; using DsCore.Win32; using System.Drawing; namespace DemulShooter_GUI { public partial class Wnd_DemulShooterGui : Form { private static Wnd_DemulShooterGui _This; private const string CONF_FILENAME = "config.ini"; //Non-GUI settings (W.I.P -- they should be incliuded next) private int _HookTimeout = 0; //Available RawInput devices (filters by thir Type) private RawInputController[] _AvailableControllers; //Low-Level Hooks private Win32API.HookProc _KeyboardHookProc; private IntPtr _KeyboardHookID = IntPtr.Zero; /*** XInput Controllers data ***/ /*private const String XINPUT_DEVICE_PREFIX = "XInput Gamepad #"; private XInputState[] _XInputStates; private int _XInput_PollingInterval = 20; //ms*/ /**** Directinput data ***/ private bool _Start_KeyRecord = false; private TextBox _SelectedTextBox; private String _SelectedTextBoxTextBackup = String.Empty; /*** Controllers ***/ private List _GUI_Players; private List _GUI_AnalogCalibrations; /*** Timer used to display some kind of crosshair to test aim ***/ private Timer CrosshairAimTimer; /// /// Construcor /// public Wnd_DemulShooterGui(bool IsVerbose) { InitializeComponent(); Logger.IsEnabled = IsVerbose; _This = this; this.Text = "DemulShooter_GUI " + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString(); Logger.WriteLog(""); Logger.WriteLog("---------------- Program Start -- DemulShooter_GUI v" + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString() + " ----------------"); //Finding plugged devices _AvailableControllers = RawInputHelper.GetRawInputDevices(new RawInputDeviceType[] { RawInputDeviceType.RIM_TYPEHID, RawInputDeviceType.RIM_TYPEMOUSE }); Logger.WriteLog("Found " + _AvailableControllers.Length + " available RawInput devices :"); foreach (RawInputController c in _AvailableControllers) { try { Logger.WriteLog(" + [" + c.DeviceType.ToString() + "] " + c.DeviceName); } catch (Exception ex) { Logger.WriteLog("Wnd_DemulShooterGui() ERROR : " + ex.Message.ToString()); } } //Reading config file to get parameters Configurator.GetInstance().ReadDsConfig(AppDomain.CurrentDomain.BaseDirectory + CONF_FILENAME); Configurator.GetInstance().Read_Sha_Conf(); Logger.WriteLog("Initializing GUI [Players] pages..."); int TabPageIndex = 0; _GUI_Players = new List(); foreach (PlayerSettings PlayerData in Configurator.GetInstance().PlayersSettings) { if (PlayerData.Mode == PlayerSettings.PLAYER_MODE_RAWINPUT) { //For each player, create a tab on the GUI GUI_Player gPlayer = new GUI_Player(PlayerData, _AvailableControllers); Logger.WriteLog("Adding PlayerData to the list..."); _GUI_Players.Add(gPlayer); Logger.WriteLog("Adding Player tab to the GUI..."); tabControl1.TabPages[TabPageIndex].Controls.Add(gPlayer); TabPageIndex++; } else { //Logger.WriteLog("P" + Player.ID + " Gamepad ID = " + Player.GamepadID); } } //Fill Analog calibration Tab Logger.WriteLog("Initializing GUI [Analog calibration] page..."); _GUI_AnalogCalibrations = new List(); for (int i = 1; i <= 4; i++) { GUI_AnalogCalibration Calib = new GUI_AnalogCalibration(i, Configurator.GetInstance().GetPlayerSettings(i)); TableLayout_Calib.Controls.Add(Calib); _GUI_AnalogCalibrations.Add(Calib); } //Fill ActLabs tab Logger.WriteLog("Initializing GUI [Act Lab] page..."); Cb_ActLabsOffset.Checked = Configurator.GetInstance().Act_Labs_Offset_Enable; Chk_DspCorrectedCrosshair.Checked = Configurator.GetInstance().Act_Labs_Display_Crosshair; Txt_ActLabs_X1.Text = Configurator.GetInstance().GetPlayerSettings(1).Act_Labs_Offset_X.ToString(); Txt_ActLabs_Y1.Text = Configurator.GetInstance().GetPlayerSettings(1).Act_Labs_Offset_Y.ToString(); Txt_ActLabs_X2.Text = Configurator.GetInstance().GetPlayerSettings(2).Act_Labs_Offset_X.ToString(); Txt_ActLabs_Y2.Text = Configurator.GetInstance().GetPlayerSettings(2).Act_Labs_Offset_Y.ToString(); Txt_ActLabs_X3.Text = Configurator.GetInstance().GetPlayerSettings(3).Act_Labs_Offset_X.ToString(); Txt_ActLabs_Y3.Text = Configurator.GetInstance().GetPlayerSettings(3).Act_Labs_Offset_Y.ToString(); Txt_ActLabs_X4.Text = Configurator.GetInstance().GetPlayerSettings(4).Act_Labs_Offset_X.ToString(); Txt_ActLabs_Y4.Text = Configurator.GetInstance().GetPlayerSettings(4).Act_Labs_Offset_Y.ToString(); //Fill Model2 Tab TXT_CH_P1.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_M2_Crosshair_P1); TXT_CH_P2.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_M2_Crosshair_P2); TXT_CH_VIS.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_M2_Crosshair_Visibility); //Fill Silent Hill tab Logger.WriteLog("Initializing GUI [Silent Hill] pages..."); TXT_P1_S.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Sha_P1_Start); TXT_P1_T.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Sha_P1_Trigger); TXT_P2_S.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Sha_P2_Start); TXT_P2_T.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Sha_P2_Trigger); TXT_EXIT.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Sha_Exit); TXT_SERVICE.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Sha_Service); TXT_TEST.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Sha_Test); //Fill Elevator Action Invasion tab Txt_EAI_P1_Start.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_P1_Start); Txt_EAI_P2_Start.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_P2_Start); Txt_EAI_P1_Credits.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_P1_Credits); Txt_EAI_P2_Credits.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_P2_Credits); Txt_EAI_Settings.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_Settings); Txt_EAI_Up.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_MenuUp); Txt_EAI_Down.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_MenuDown); Txt_EAI_Enter.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Eai_MenuEnter); //Fill Gundam tab Logger.WriteLog("Initializing GUI [Gundam] pages..."); Chk_GundamP1Pedal.Checked = Configurator.GetInstance().Gsoz_Pedal_P1_Enabled; Chk_GundamP2Pedal.Checked = Configurator.GetInstance().Gsoz_Pedal_P2_Enabled; TXT_GSOZ_PEDAL_1.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Gsoz_Pedal_P1); TXT_GSOZ_PEDAL_2.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Gsoz_Pedal_P2); //Fill Heavy Fire series tab Logger.WriteLog("Initializing GUI [Heavy Fire series] pages..."); Gbox_HF_Cover.Enabled = false; Gbox_HF_Grenade.Enabled = false; Rdo_HF_MiddleGrenade.Checked = Configurator.GetInstance().HF_UseMiddleButtonAsGrenade; Rdo_HF_MiddleCover.Checked = !Configurator.GetInstance().HF_UseMiddleButtonAsGrenade; TrackBar_HF_Cover.Value = Configurator.GetInstance().HF_CoverSensibility; Chk_HF_ReverseCover.Checked = Configurator.GetInstance().HF_ReverseCover; //Fill Lethal Enforcers 3 tab Logger.WriteLog("Initializing GUI [Lethal Enforcers 3] pages..."); Chk_Le3_EnablePedal1.Checked = Configurator.GetInstance().Le3_Pedal_P1_Enabled; Chk_Le3_EnablePedal2.Checked = Configurator.GetInstance().Le3_Pedal_P2_Enabled; TXT_LE3_PEDAL_1.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Le3_Pedal_P1); TXT_LE3_PEDAL_2.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Le3_Pedal_P2); //Fill Mission Impossible tab Logger.WriteLog("Initializing GUI [Mission Impossible] pages..."); Rdo_MIA_Merge.Checked = Configurator.GetInstance().MissionImpossible_MergeTriggers; Rdo_MIA_Separate.Checked = !Configurator.GetInstance().MissionImpossible_MergeTriggers; //Fill Operation GHOST Tab Logger.WriteLog("Initializing GUI [Operation G.H.O.S.T] pages..."); if (Configurator.GetInstance().OpGhost_EnableFreeplay) Cbox_OpGhost_Freeplay.SelectedIndex = 1; else Cbox_OpGhost_Freeplay.SelectedIndex = 0; Cbox_OpGhost_CreditsByCoin.SelectedIndex = Configurator.GetInstance().OpGhost_CoinsPerCredits - 1; Cbox_OpGhost_CreditsToStart.SelectedIndex = Configurator.GetInstance().OpGhost_CreditsToStart - 1; Cbox_OpGhost_CreditsToContinue.SelectedIndex = Configurator.GetInstance().OpGhost_CreditsToContinue - 1; Chk_OpGhost_SeparateButton.Checked = Configurator.GetInstance().OpGhost_SeparateButtons; TXT_OPGHOST_ACTION_P1.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_OpGhost_Action_P1); TXT_OPGHOST_ACTION_P2.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_OpGhost_Action_P2); //Fill RPCS3 Tab Logger.WriteLog("Initializing GUI [RPCS3] pages..."); Txt_Rpcs3_P1_Start.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Rpcs3_P1_Start); Txt_Rpcs3_P2_Start.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Rpcs3_P2_Start); Txt_Rpcs3_Service.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Rpcs3_Service); Txt_Rpcs3_Up.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Rpcs3_Up); Txt_Rpcs3_Down.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Rpcs3_Down); Txt_Rpcs3_Enter.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Rpcs3_Enter); Txt_Rpcs3_3D_Switch.Text = GetKeyStringFromScanCode((int)Configurator.GetInstance().DIK_Rpcs3_3D_Switch); //Fill Output Tab Logger.WriteLog("Initializing GUI [Output] pages..."); Cbox_Outputs.Checked = Configurator.GetInstance().OutputEnabled; Cbox_WmOutputs.Checked = Configurator.GetInstance().Wm_OutputEnabled; Cbox_NetOutputs.Checked = Configurator.GetInstance().Net_OutputEnabled; Txt_OutputDelay.Text = Configurator.GetInstance().OutputPollingDelay.ToString(); Txt_OutputRecoilOn.Text = Configurator.GetInstance().OutputCustomRecoilOnDelay.ToString(); Txt_OutputRecoilOff.Text = Configurator.GetInstance().OutputCustomRecoilOffDelay.ToString(); Txt_OutputDamaged.Text = Configurator.GetInstance().OutputCustomDamagedDelay.ToString(); //Non-GUI settings _HookTimeout = Configurator.GetInstance().HookTimeout; // Register to rawinput Logger.WriteLog("Registering to RawInput service..."); RawInputDevice[] rid = new RawInputDevice[3]; rid[0].UsagePage = HidUsagePage.GENERIC; rid[0].Usage = HidUsage.Joystick; rid[0].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[0].hwndTarget = this.Handle; rid[1].UsagePage = HidUsagePage.GENERIC; rid[1].Usage = HidUsage.Mouse; rid[1].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[1].hwndTarget = this.Handle; rid[2].UsagePage = HidUsagePage.GENERIC; rid[2].Usage = HidUsage.Gamepad; rid[2].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[2].hwndTarget = this.Handle; if (!Win32API.RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) { MessageBox.Show("Failed to register raw input device(s).", "DemulShooter Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Application.Exit(); } //Install Low Level keyboard hook Logger.WriteLog("Installing Low-Level Keyboard Hook..."); ApplyKeyboardHook(); Cbo_PageSettings.SelectedIndex = 0; CrosshairAimTimer = new Timer(); CrosshairAimTimer.Tick += new EventHandler(CrosshairAimTimer_Tick); } #region RAW_INPUT /// /// Get movements/click and process to Demul memory or GUI when visible /// private void ProcessRawInputMessage(IntPtr RawInputHandle) { foreach (RawInputController Controller in _AvailableControllers) { if (Controller.isSourceOfRawInputMessage(RawInputHandle)) { foreach (PlayerSettings Player in Configurator.GetInstance().PlayersSettings) { if (Player.DeviceName == Controller.DeviceName) { Controller.ProcessRawInputData(RawInputHandle); if (tabControl1.SelectedTab == Tab_P1 || tabControl1.SelectedTab == Tab_P2 || tabControl1.SelectedTab == Tab_P3 || tabControl1.SelectedTab == Tab_P4) _GUI_Players[Player.ID - 1].UpdateGui(); else if (tabControl1.SelectedTab == Tab_AnalogCalib) _GUI_AnalogCalibrations[Player.ID - 1].UpdateValues(); else if (tabControl1.SelectedTab == Tab_ActLAbs) { if ((Player.RIController.Computed_Buttons & RawInputcontrollerButtonEvent.OnScreenTriggerDown) != 0) { DrawCrosshair(Player); } } } } } } } #endregion /// /// Page Selection /// private void Cbo_PageSettings_SelectionChangeCommitted(object sender, EventArgs e) { tabControl1.SelectTab(Cbo_PageSettings.SelectedIndex); } #region Analog Calibration private void UpdateCalibration(int Player, int CurrentX, int CurrentY) { if (CurrentX > Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Xmax) Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Xmax = CurrentX; if (CurrentX < Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Xmin) Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Xmin = CurrentX; if (CurrentY > Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Ymax) Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Ymax = CurrentY; if (CurrentY < Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Ymin) Configurator.GetInstance().PlayersSettings[Player - 1].AnalogManual_Ymin = CurrentY; } private void Btn_SaveAnalog_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region ActLabs Offset tab private void Cb_ActLabsOffset_CheckedChanged(object sender, EventArgs e) { if (Cb_ActLabsOffset.Checked) { Txt_ActLabs_X1.Enabled = true; Txt_ActLabs_Y1.Enabled = true; Txt_ActLabs_X2.Enabled = true; Txt_ActLabs_Y2.Enabled = true; Txt_ActLabs_X3.Enabled = true; Txt_ActLabs_Y3.Enabled = true; Txt_ActLabs_X4.Enabled = true; Txt_ActLabs_Y4.Enabled = true; Configurator.GetInstance().Act_Labs_Offset_Enable = true; } else { Txt_ActLabs_X1.Enabled = false; Txt_ActLabs_Y1.Enabled = false; Txt_ActLabs_X2.Enabled = false; Txt_ActLabs_Y2.Enabled = false; Txt_ActLabs_X3.Enabled = false; Txt_ActLabs_Y3.Enabled = false; Txt_ActLabs_X4.Enabled = false; Txt_ActLabs_Y4.Enabled = false; Configurator.GetInstance().Act_Labs_Offset_Enable = false; } } private void Chk_DspCorrectedCrosshair_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().Act_Labs_Display_Crosshair = Chk_DspCorrectedCrosshair.Checked; } private void Txt_ActLabs_X1_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().GetPlayerSettings(1).Act_Labs_Offset_X = Convert.ToInt32(Txt_ActLabs_X1.Text); } catch { MessageBox.Show(Txt_ActLabs_X1.Text + " is not a valid X offset value. Please enter a non-decimal number"); Txt_ActLabs_X1.Text = Configurator.GetInstance().GetPlayerSettings(1).Act_Labs_Offset_X.ToString(); } } private void Txt_ActLabs_Y1_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().GetPlayerSettings(1).Act_Labs_Offset_Y = Convert.ToInt32(Txt_ActLabs_Y1.Text); } catch { MessageBox.Show(Txt_ActLabs_Y1.Text + " is not a valid Y offset value. Please enter a non-decimal number"); Txt_ActLabs_Y1.Text = Configurator.GetInstance().GetPlayerSettings(1).Act_Labs_Offset_Y.ToString(); } } private void Txt_ActLabs_X2_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().PlayersSettings[1].Act_Labs_Offset_X = Convert.ToInt32(Txt_ActLabs_X2.Text); } catch { MessageBox.Show(Txt_ActLabs_X2.Text + " is not a valid X offset value. Please enter a non-decimal number"); Txt_ActLabs_X2.Text = Configurator.GetInstance().PlayersSettings[1].Act_Labs_Offset_X.ToString(); } } private void Txt_ActLabs_Y2_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().PlayersSettings[1].Act_Labs_Offset_Y = Convert.ToInt32(Txt_ActLabs_Y2.Text); } catch { MessageBox.Show(Txt_ActLabs_Y2.Text + " is not a valid Y offset value. Please enter a non-decimal number"); Txt_ActLabs_Y2.Text = Configurator.GetInstance().PlayersSettings[1].Act_Labs_Offset_Y.ToString(); } } private void Txt_ActLabs_X3_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().PlayersSettings[2].Act_Labs_Offset_X = Convert.ToInt32(Txt_ActLabs_X3.Text); } catch { MessageBox.Show(Txt_ActLabs_X3.Text + " is not a valid X offset value. Please enter a non-decimal number"); Txt_ActLabs_X3.Text = Configurator.GetInstance().PlayersSettings[2].Act_Labs_Offset_X.ToString(); } } private void Txt_ActLabs_Y3_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().PlayersSettings[2].Act_Labs_Offset_Y = Convert.ToInt32(Txt_ActLabs_Y3.Text); } catch { MessageBox.Show(Txt_ActLabs_Y3.Text + " is not a valid Y offset value. Please enter a non-decimal number"); Txt_ActLabs_Y3.Text = Configurator.GetInstance().PlayersSettings[2].Act_Labs_Offset_Y.ToString(); } } private void Txt_ActLabs_X4_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().PlayersSettings[3].Act_Labs_Offset_X = Convert.ToInt32(Txt_ActLabs_X4.Text); } catch { MessageBox.Show(Txt_ActLabs_X4.Text + " is not a valid X offset value. Please enter a non-decimal number"); Txt_ActLabs_X4.Text = Configurator.GetInstance().PlayersSettings[3].Act_Labs_Offset_X.ToString(); } } private void Txt_ActLabs_Y4_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().PlayersSettings[3].Act_Labs_Offset_Y = Convert.ToInt32(Txt_ActLabs_Y4.Text); } catch { MessageBox.Show(Txt_ActLabs_Y4.Text + " is not a valid Y offset value. Please enter a non-decimal number"); Txt_ActLabs_Y4.Text = Configurator.GetInstance().PlayersSettings[3].Act_Labs_Offset_Y.ToString(); } } private void Btn_ActLabs_Save_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void DrawCrosshair(PlayerSettings Player) { if (Chk_DspCorrectedCrosshair.Checked) { Color CrosshairColor = Color.Crimson; if (Player.ID == 2) CrosshairColor = Color.Blue; else if (Player.ID == 3) CrosshairColor = Color.LimeGreen; else if (Player.ID == 4) CrosshairColor = Color.Gold; Player.RIController.Computed_X = ScreenScale(Player.RIController.Computed_X, Player.RIController.Axis_X_Min, Player.RIController.Axis_X_Max, 0, Screen.PrimaryScreen.WorkingArea.Width); Player.RIController.Computed_Y = ScreenScale(Player.RIController.Computed_Y, Player.RIController.Axis_Y_Min, Player.RIController.Axis_Y_Max, 0, Screen.PrimaryScreen.WorkingArea.Height); if (Configurator.GetInstance().Act_Labs_Offset_Enable) { Player.RIController.Computed_X += Player.Act_Labs_Offset_X; Player.RIController.Computed_Y += Player.Act_Labs_Offset_Y; } IntPtr desktopPtr = Win32API.GetDC(IntPtr.Zero); Graphics g = Graphics.FromHdc(desktopPtr); SolidBrush b = new SolidBrush(CrosshairColor); Pen p = new Pen(b, 2); g.DrawEllipse(p, Player.RIController.Computed_X - 20, Player.RIController.Computed_Y - 20, 40, 40); g.DrawEllipse(p, Player.RIController.Computed_X - 2, Player.RIController.Computed_Y - 2, 4, 4); g.Dispose(); Win32API.ReleaseDC(IntPtr.Zero, desktopPtr); CrosshairAimTimer.Interval = 300; CrosshairAimTimer.Start(); } } private void CrosshairAimTimer_Tick(object sender, EventArgs e) { CrosshairAimTimer.Stop(); Win32API.InvalidateRect(IntPtr.Zero, IntPtr.Zero, true); } /// /// Contains value inside min-max range /// protected int Clamp(int val, int minVal, int maxVal) { if (val > maxVal) return maxVal; else if (val < minVal) return minVal; else return val; } /// /// Transforming 0x0000-0xFFFF absolute rawdata to absolute x,y position on Desktop resolution /// public int ScreenScale(int val, int fromMinVal, int fromMaxVal, int toMinVal, int toMaxVal) { return ScreenScale(val, fromMinVal, fromMinVal, fromMaxVal, toMinVal, toMinVal, toMaxVal); } protected int ScreenScale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal) { double fromRange; double frac; if (fromMaxVal > fromMinVal) { val = Clamp(val, fromMinVal, fromMaxVal); if (val > fromOffVal) { fromRange = (double)(fromMaxVal - fromOffVal); frac = (double)(val - fromOffVal) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMinVal); frac = (double)(val - fromOffVal) / fromRange; } else return toOffVal; } else if (fromMinVal > fromMaxVal) { val = Clamp(val, fromMaxVal, fromMinVal); if (val > fromOffVal) { fromRange = (double)(fromMinVal - fromOffVal); frac = (double)(fromOffVal - val) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMaxVal); frac = (double)(fromOffVal - val) / fromRange; } else return toOffVal; } else return toOffVal; double toRange; if (toMaxVal > toMinVal) { if (frac >= 0) toRange = (double)(toMaxVal - toOffVal); else toRange = (double)(toOffVal - toMinVal); return toOffVal + (int)(toRange * frac); } else { if (frac >= 0) toRange = (double)(toOffVal - toMaxVal); else toRange = (double)(toMinVal - toOffVal); return toOffVal - (int)(toRange * frac); } } #endregion #region Modify/Create SHA key mapping private void Save_Sha_Keys_Click(object sender, EventArgs e) { if (Configurator.GetInstance().Write_Sha_Config()) MessageBox.Show("Key mapping saved !"); else MessageBox.Show("Impossible to save SHA config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region Gundam:SoZ Tab private void Chk_GundamP1Pedal_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().Gsoz_Pedal_P1_Enabled = Chk_GundamP1Pedal.Checked; if (Configurator.GetInstance().Gsoz_Pedal_P1_Enabled) TXT_GSOZ_PEDAL_1.Enabled = true; else TXT_GSOZ_PEDAL_1.Enabled = false; } private void Chk_GundamP2Pedal_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().Gsoz_Pedal_P2_Enabled = Chk_GundamP2Pedal.Checked; if (Configurator.GetInstance().Gsoz_Pedal_P2_Enabled) TXT_GSOZ_PEDAL_2.Enabled = true; else TXT_GSOZ_PEDAL_2.Enabled = false; } private void Btn_Save_Gsoz_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region Dolphin Wiimote configuration private void Btn_Dolphin5_Click(object sender, EventArgs e) { InstallWiimoteconfigFile(DemulShooter_GUI.Properties.Resources.WiimoteNew_v5); } private void InstallWiimoteconfigFile(String ResourceFile) { String Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Dolphin Emulator\Config"; bool IsAimtrak = false; bool Overwritten = false; try { if (File.Exists(Path + @"\WiimoteNew.ini")) { File.Copy(Path + @"\WiimoteNew.ini", Path + @"\WiimoteNew.bak.ini", true); Overwritten = true; } //Copying file using (StreamWriter sw = new StreamWriter(Path + @"\WiimoteNew.ini", false)) { sw.Write(ResourceFile); sw.Close(); } //Modifying Devices Names according to config.ini int P2_Atrak_ID = 0; try { String VID = (Configurator.GetInstance().PlayersSettings[0].DeviceName.ToUpper().Split(new string[] { "VID_" }, System.StringSplitOptions.RemoveEmptyEntries))[1].Substring(0, 4); String PID = (Configurator.GetInstance().PlayersSettings[1].DeviceName.ToUpper().Split(new string[] { "PID_" }, System.StringSplitOptions.RemoveEmptyEntries))[1].Substring(0, 4); if (VID.Equals("D209") && PID.StartsWith("16")) { IsAimtrak = true; P2_Atrak_ID = int.Parse(PID.Substring(2, 2)); int bID = 0x30 + P2_Atrak_ID; using (BinaryWriter fileWriter = new BinaryWriter(File.Open(Path + @"\WiimoteNew.ini", System.IO.FileMode.Open))) { fileWriter.BaseStream.Position = 0x2E7; // set the offset fileWriter.Write((byte)bID); fileWriter.BaseStream.Position = 0x315; // set the offset fileWriter.Write((byte)bID); fileWriter.BaseStream.Position = 0x343; // set the offset fileWriter.Write((byte)bID); fileWriter.BaseStream.Position = 0x372; // set the offset fileWriter.Write((byte)bID); } } } catch { //String m = "P2 Wiimote axis device could not be modified, default is :\n \"DInput/0/ATRAK Device #2\""; //MessageBox.Show(m, "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } //Dialog Message String message = "File \"" + Path + "\\WiimoteNew.ini\" successfully written !"; if (Overwritten) message += "\n\nThe existing WiimoteNew.ini was backed-up to \"WiimoteNew.bak.ini\"\n\n"; MessageBox.Show(message, "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Information); if (IsAimtrak) { message = "Aimtrak detected for Player2 device, Aimtrak ID = " + P2_Atrak_ID; message += "\n\nAccording to DemulShooter config, P2 Wiimote axis device modified to : \n\"DInput/0/ATRAK Device #" + P2_Atrak_ID + "\""; MessageBox.Show(message, "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { message = "No Aimtrak detected for Player2."; message += "\n\nP2 Wiimote axis device will keep default value :\n \"DInput/0/ATRAK Device #2\""; MessageBox.Show(message, "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception ex) { MessageBox.Show("Can't install WiimoteNew.ini : \n\n" + ex.Message.ToString(), "", MessageBoxButtons.OK, MessageBoxIcon.Error); } } #endregion #region Elevator Action Invasion private string _EAI_ExeFilename = "ESGame-Win64-Shipping.exe"; private string _EAI_UsbDllFilename = "UsbPluginsDll.dll"; private void Btn_EAI_Open_Click(object sender, EventArgs e) { using (FolderBrowserDialog fbd = new FolderBrowserDialog()) { fbd.Description = "Select your " + _EAI_ExeFilename + " location :"; if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { if (fbd.SelectedPath != string.Empty) { if (File.Exists(fbd.SelectedPath + "\\" + _EAI_ExeFilename)) { Txt_EAI_FolderPath.Text = fbd.SelectedPath; Btn_EAI_Patch.Enabled = true; } else { MessageBox.Show(_EAI_ExeFilename + " not found in following folder : " + fbd.SelectedPath, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } } private void Btn_EAI_Patch_Click(object sender, EventArgs e) { string ExeFilePath = Txt_EAI_FolderPath.Text + "\\" + _EAI_ExeFilename; string UsbDllFilePath = Txt_EAI_FolderPath.Text + "\\..\\..\\Plugins\\" + _EAI_UsbDllFilename; //Main exe patching try { using (BinaryWriter bw = new BinaryWriter(File.Open(ExeFilePath, FileMode.Open, FileAccess.ReadWrite))) { //Force overriding read data from the COM port by our own //Creating codecave bw.BaseStream.Seek(0x292C9D4, SeekOrigin.Begin); bw.Write(new byte[] { 0x4C, 0x8D, 0x35, 0x95, 0x44, 0x16, 0x01, 0x41, 0x0F, 0xB6, 0x9E, 0xCE, 0x02, 0x00, 0x00, 0xE9, 0xF6, 0x50, 0xE6, 0xFD }); //Injection bw.BaseStream.Seek(0x791AD6, SeekOrigin.Begin); bw.Write(new byte[] { 0xE9, 0xF9, 0xAE, 0x19, 0x02, 0x90, 0x90, 0x90 }); //Calls to the usb dll credits function will not return any good values (pointer not existing, no data to write/read) //There are values that can be used in the main .exe : //For Credits we will force the game to read our own value bw.BaseStream.Seek(0x7B77BF, SeekOrigin.Begin); bw.Write(new byte[] { 0x48, 0x8B, 0xF9, 0x48, 0x8D, 0x05, 0x93, 0x96, 0x2D, 0x03, 0x48, 0xC1, 0xE7, 0x04, 0x48, 0x01, 0xF8, 0x8B, 0x00, 0xE9, 0x81, 0x00, 0x00, 0x00, 0x00 }); //For Credits we will force the game to write to our own values bw.BaseStream.Seek(0x7B767E, SeekOrigin.Begin); bw.Write(new byte[] { 0x48, 0x8D, 0x05, 0xD7, 0x97, 0x2D, 0x03, 0xC1, 0xE5, 0x04, 0x48, 0x01, 0xE8, 0x8B, 0x28, 0x29, 0xD5, 0x89, 0x28, 0xE9, 0x91, 0x00, 0x00, 0x00, 0x90 }); //Force overriding bad read values from GUN api with good flag, and force values to be read from another memory location //which then will be written by any tool to feed data //Creating codecave bw.BaseStream.Seek(0x292C97F, SeekOrigin.Begin); bw.Write(new byte[] { 0xCC, 0xCC, 0xCC, 0x50, 0x53, 0x48, 0xC1, 0xE3, 0x04, 0x48, 0x31, 0xC0, 0x42, 0xC7, 0x44, 0x39, 0x34, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x05, 0xB5, 0x44, 0x16, 0x01, 0x48, 0x01, 0xD8, 0x8B, 0x00, 0x42, 0x89, 0x44, 0x39, 0x14, 0x48, 0x8D, 0x05, 0xA8, 0x44, 0x16, 0x01, 0x48, 0x01, 0xD8, 0x8B, 0x00, 0x42, 0x89, 0x44, 0x39, 0x18, 0x48, 0x8D, 0x05, 0x9B, 0x44, 0x16, 0x01, 0x48, 0x01, 0xD8, 0x8B, 0x00, 0x42, 0x89, 0x44, 0x39, 0x51, 0x5B, 0x58, 0x42, 0x83, 0x7C, 0x39, 0x34, 0x00, 0xE9, 0xD9, 0x60, 0xE8, 0xFD }); //Injection bw.BaseStream.Seek(0x7B2AA7, SeekOrigin.Begin); bw.Write(new byte[] { 0xE9, 0xD6, 0x9E, 0x17, 0x02, 0x90 }); //Force return OK to IsGunCalibrated call bw.BaseStream.Seek(0x772D17, SeekOrigin.Begin); bw.Write(new byte[] { 0xB0, 0x01 }); } } catch (Exception Ex) { MessageBox.Show("Error patching " + ExeFilePath + " : \n" + Ex.Message.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } //USB dll patching try { using (BinaryWriter bw = new BinaryWriter(File.Open(UsbDllFilePath, FileMode.Open, FileAccess.ReadWrite))) { //Force return true for BIND_InitDongle() bw.BaseStream.Seek(0x134CB, SeekOrigin.Begin); bw.Write(new byte[] { 0x90, 0x90, }); //Force return true for BIND_IsDongleVerify() bw.BaseStream.Seek(0x136F7, SeekOrigin.Begin); bw.Write(new byte[] { 0xB0, 0x01 }); //Force return true to BIND_OpenESIODevice without opening COM port at all bw.BaseStream.Seek(0x137B0, SeekOrigin.Begin); bw.Write(new byte[] { 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3 }); //Force return true for BIND_IsExistESIODevice() bw.BaseStream.Seek(0xA5A0, SeekOrigin.Begin); bw.Write(new byte[] { 0xB8, 0x01, 0x00, 0x00, 0x00, 0x90, 0x90 }); //Force return true for BIND_GetESIoData() //That way the game will constantly call the ABPGameState::PArseIoData where we will inject our values bw.BaseStream.Seek(0x12F10, SeekOrigin.Begin); bw.Write(new byte[] { 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3 }); } } catch (Exception Ex) { MessageBox.Show("Error patching " + UsbDllFilePath + " : \n" + Ex.Message.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } MessageBox.Show("Patch Complete !", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information); } private void Btn_EAI_Save_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region Model2 Emulator tab private void Btn_M2Scripts_Click(object sender, EventArgs e) { InstallM2Scripts(); } private void InstallM2Scripts() { if (folderBrowserDialog1.ShowDialog() != DialogResult.OK) return; try { StreamWriter streamWriter = new StreamWriter(folderBrowserDialog1.SelectedPath + "\\scripts\\demulshooter.lua", false); streamWriter.WriteLine("-- This file is automatically generated by Demulshooter\n"); streamWriter.WriteLine("P1_ChangeCrosshairKey=0x" + ((byte)Configurator.GetInstance().DIK_M2_Crosshair_P1).ToString("X2")); streamWriter.WriteLine("P2_ChangeCrosshairKey=0x" + ((byte)Configurator.GetInstance().DIK_M2_Crosshair_P2).ToString("X2")); streamWriter.WriteLine("CrosshairVisibilityKey=0x" + ((byte)Configurator.GetInstance().DIK_M2_Crosshair_Visibility).ToString("X2")); streamWriter.WriteLine(); streamWriter.WriteLine("function lines_from(file)"); streamWriter.WriteLine("\tlines = {}"); streamWriter.WriteLine("\tfor line in io.lines(file) do"); streamWriter.WriteLine("\t\tlines[#lines + 1] = line"); streamWriter.WriteLine("\tend"); streamWriter.WriteLine("\treturn lines"); streamWriter.WriteLine("end"); streamWriter.Close(); Directory.CreateDirectory(folderBrowserDialog1.SelectedPath + "\\artwork\\crosshairs"); foreach (FileInfo file in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "\\m2emulator\\artwork\\crosshairs").GetFiles()) file.CopyTo(folderBrowserDialog1.SelectedPath + "\\artwork\\crosshairs\\" + file.Name, true); String FlashState = "flash"; if (Cbox_M2_Flash.Checked) FlashState = "noflash"; foreach (FileInfo file in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "\\m2emulator\\scripts\\" + FlashState).GetFiles()) file.CopyTo(folderBrowserDialog1.SelectedPath + "\\scripts\\" + file.Name, true); MessageBox.Show("Scripts installed !"); } catch (Exception ex) { MessageBox.Show("Can't install m2emulator lua scripts : \n\n" + ex.Message.ToString(), "", MessageBoxButtons.OK, MessageBoxIcon.Hand); } } #endregion #region Heavy Fire Afghanistan tab private void Rdo_HF_MiddleCover_CheckedChanged(object sender, EventArgs e) { Gbox_HF_Cover.Enabled = Rdo_HF_MiddleCover.Checked; Gbox_HF_Grenade.Enabled = !Rdo_HF_MiddleCover.Checked; Configurator.GetInstance().HF_UseMiddleButtonAsGrenade = !Rdo_HF_MiddleCover.Checked; } private void Rdo_HF_MiddleClickGrenade_CheckedChanged(object sender, EventArgs e) { Gbox_HF_Grenade.Enabled = Rdo_HF_MiddleGrenade.Checked; Gbox_HF_Cover.Enabled = !Rdo_HF_MiddleGrenade.Checked; Configurator.GetInstance().HF_UseMiddleButtonAsGrenade = Rdo_HF_MiddleGrenade.Checked; } private void TrackBar_HF_Cover_ValueChanged(object sender, EventArgs e) { Configurator.GetInstance().HF_CoverSensibility = TrackBar_HF_Cover.Value; } private void Chk_HF_ReverseCover_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().HF_ReverseCover = Chk_HF_ReverseCover.Checked; } private void Btn_HF_Save_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region Lethal Enforcer 3 tab private void Chk_Le3_EnablePedal1_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().Le3_Pedal_P1_Enabled = Chk_Le3_EnablePedal1.Checked; if (Configurator.GetInstance().Le3_Pedal_P1_Enabled) TXT_LE3_PEDAL_1.Enabled = true; else TXT_LE3_PEDAL_1.Enabled = false; } private void Chk_Le3_EnablePedal2_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().Le3_Pedal_P2_Enabled = Chk_Le3_EnablePedal2.Checked; if (Configurator.GetInstance().Le3_Pedal_P2_Enabled) TXT_LE3_PEDAL_2.Enabled = true; else TXT_LE3_PEDAL_2.Enabled = false; } private void Btn_Save_Le3_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #region Mission Impossible Tab private void Rdo_MIA_Merge_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().MissionImpossible_MergeTriggers = Rdo_MIA_Merge.Checked; } private void Rdo_MIA_Separate_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().MissionImpossible_MergeTriggers = Rdo_MIA_Merge.Checked; } private void Btn_MisImp_Save_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #endregion #region Operation Ghost Tab private void Cbox_OpGhost_Freeplay_SelectedIndexChanged(object sender, EventArgs e) { if (Cbox_OpGhost_Freeplay.SelectedIndex == 0) Configurator.GetInstance().OpGhost_EnableFreeplay = false; else Configurator.GetInstance().OpGhost_EnableFreeplay = true; } private void Cbox_OpGhost_CreditsByCoin_SelectedIndexChanged(object sender, EventArgs e) { Configurator.GetInstance().OpGhost_CoinsPerCredits = Cbox_OpGhost_CreditsByCoin.SelectedIndex + 1; } private void Cbox_OpGhost_CreditsToStart_SelectedIndexChanged(object sender, EventArgs e) { Configurator.GetInstance().OpGhost_CreditsToStart = Cbox_OpGhost_CreditsToStart.SelectedIndex + 1; } private void Cbox_OpGhost_CreditsToContinue_SelectedIndexChanged(object sender, EventArgs e) { Configurator.GetInstance().OpGhost_CreditsToContinue = Cbox_OpGhost_CreditsToContinue.SelectedIndex + 1; } private void Chk_OpGhost_SeparateButton_CheckedChanged(object sender, EventArgs e) { Configurator.GetInstance().OpGhost_SeparateButtons = Chk_OpGhost_SeparateButton.Checked; if (Configurator.GetInstance().OpGhost_SeparateButtons) { TXT_OPGHOST_ACTION_P1.Enabled = true; TXT_OPGHOST_ACTION_P2.Enabled = true; } else { TXT_OPGHOST_ACTION_P1.Enabled = false; TXT_OPGHOST_ACTION_P2.Enabled = false; } } private void Btn_Save_OpGhost_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region Unity Plugin Installation Tab private void Btn_InstallUnityPlugin_Click(object sender, EventArgs e) { Button Btn = (Button)sender; string SrcUnityFolder = string.Empty; string ExeName = string.Empty; switch (Btn.Name) { case "Btn_Dcop": SrcUnityFolder = "DCOP"; break; case "Btn_Drk": SrcUnityFolder = "Drakon"; break; case "Btn_MarsS": SrcUnityFolder = "MarsSortie"; break; case "Btn_Mia": SrcUnityFolder = "MissionImpossible"; break; case "Btn_Mib": SrcUnityFolder = "MenInBlack"; break; case "Btn_Nerfa": SrcUnityFolder = "NerfArcade"; break; case "Btn_Nha": SrcUnityFolder = "NightHunter"; break; case "Btn_Owr": SrcUnityFolder = "OperationWolfReturn"; break; case "Btn_PvZ": SrcUnityFolder = "PlantsVsZombies"; break; case "Btn_Pbx": SrcUnityFolder = "PointBlankX"; break; case "Btn_Rha": SrcUnityFolder = "RabbidsHollywood"; break; case "Btn_Tra": SrcUnityFolder = "TombRaider"; break; case "Btn_Wws": SrcUnityFolder = "WildWestShootout"; break; default: break; } folderBrowserDialog1.Description = "Please select the Unity game executable installation folder"; if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) { Install_Unity_Plugin(SrcUnityFolder, folderBrowserDialog1.SelectedPath); } } private void Install_Unity_Plugin(string SrcFolder, string DstFolder) { if (!CloneDirectory(AppDomain.CurrentDomain.BaseDirectory + "\\Unity\\" + SrcFolder, DstFolder)) MessageBox.Show("Impossible to copy Unity plugin in the following folder :\n" + DstFolder, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); else MessageBox.Show("Unity Plugin successfully installed !"); } #endregion #region Raccoon Rampage Tab private string _RACCOON_ExeFilename = "RSGame-Win64-Shipping.exe"; private string _RACCOON_UsbDllFilename = "UsbPluginsDll.dll"; private void Btn_Raccoon_Open_Click(object sender, EventArgs e) { using (FolderBrowserDialog fbd = new FolderBrowserDialog()) { fbd.Description = "Select your " + _RACCOON_ExeFilename + " location :"; if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { if (fbd.SelectedPath != string.Empty) { if (File.Exists(fbd.SelectedPath + "\\" + _RACCOON_ExeFilename)) { Txt_Raccoon_FolderPath.Text = fbd.SelectedPath; Btn_Raccoon_Patch.Enabled = true; } else { MessageBox.Show(_RACCOON_ExeFilename + " not found in following folder : " + fbd.SelectedPath, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } } private void Btn_Raccoon_Patch_Click(object sender, EventArgs e) { string ExeFilePath = Txt_Raccoon_FolderPath.Text + "\\" + _RACCOON_ExeFilename; string UsbDllFilePath = Txt_Raccoon_FolderPath.Text + "\\..\\..\\Plugins\\" + _RACCOON_UsbDllFilename; //USB dll patching try { using (BinaryWriter bw = new BinaryWriter(File.Open(UsbDllFilePath, FileMode.Open, FileAccess.ReadWrite))) { //Force return true for BIND_InitDongle() bw.BaseStream.Seek(0x14D2B, SeekOrigin.Begin); bw.Write(new byte[] { 0x90, 0x90, }); //Force return true for BIND_IsDongleVerify() bw.BaseStream.Seek(0x14FE9, SeekOrigin.Begin); bw.Write(new byte[] { 0xB0, 0x01 }); //Force good BIND_InitUnisIo() bw.BaseStream.Seek(0x14DEA, SeekOrigin.Begin); bw.Write(new byte[] { 0xEB, 0x14 }); //Force return true for BIND_IsIoWorking() bw.BaseStream.Seek(0x15127, SeekOrigin.Begin); bw.Write(new byte[] { 0xEB, 0x1D }); //Force return -1 for BIND_DWInit() and Skipping 255 COM port loop opening to try to find a Card Reader bw.BaseStream.Seek(0xCC80, SeekOrigin.Begin); bw.Write(new byte[] { 0xE9, 0x63, 0x02, 0x00, 0x00 }); } } catch (Exception Ex) { MessageBox.Show("Error patching " + UsbDllFilePath + " : \n" + Ex.Message.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } MessageBox.Show("Patch Complete !", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information); } #endregion #region RPCS3 Tab private void Btn_Rpcs3_DeadStorm_Click(object sender, EventArgs e) { using (var fbd = new FolderBrowserDialog()) { fbd.Description = "Select \"DeadStorm Pirates\" rpcs3-gun.exe folder :"; DialogResult result = fbd.ShowDialog(); if (result == DialogResult.OK && fbd.SelectedPath != string.Empty) { string CacheFolder = fbd.SelectedPath + @"\cache\SCEEXE000\ppu-obiMX8TqMzUsChXLV1Ln5TAJegSZ-EBOOT.BIN\"; if (!Directory.Exists(CacheFolder)) { MessageBox.Show("Directory not found :\n " + CacheFolder + "\n\nPlease run the game once before patching the PPU-Cache", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } foreach (FileInfo file in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "\\RPCS3\\DeadStorm").GetFiles()) { Patch_RPCS3_PPU_CacheFile(CacheFolder, file.Name, file); } } } } private void Btn_Rpcs3_DarkEscape_Click(object sender, EventArgs e) { using (var fbd = new FolderBrowserDialog()) { fbd.Description = "Select \"Dark Escape\" rpcs3-gun.exe folder :"; DialogResult result = fbd.ShowDialog(); if (result == DialogResult.OK && fbd.SelectedPath != string.Empty) { string CacheFolder = fbd.SelectedPath + @"\cache\SCEEXE000\ppu-gfm17oJj1cUecjZQ8dVv46oQv2iW-EBOOT.BIN\"; if (!Directory.Exists(CacheFolder)) { MessageBox.Show("Directory not found :\n " + CacheFolder + "\n\nPlease run the game once before patching the PPU-Cache", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } foreach (FileInfo file in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "\\RPCS3\\DarkEscape").GetFiles()) { Patch_RPCS3_PPU_CacheFile(CacheFolder, file.Name, file); } } } } private void Btn_Rpcs3_SailorZombie_Click(object sender, EventArgs e) { using (var fbd = new FolderBrowserDialog()) { fbd.Description = "Select \"Sailor Zombie\" rpcs3-gun.exe folder :"; DialogResult result = fbd.ShowDialog(); if (result == DialogResult.OK && fbd.SelectedPath != string.Empty) { string CacheFolder = fbd.SelectedPath + @"\cache\SCEEXE000\ppu-se1PtZVS5iF9A6M5Y1vu0wNqiASu-EBOOT.BIN\"; if (!Directory.Exists(CacheFolder)) { MessageBox.Show("Directory not found :\n " + CacheFolder + "\n\nPlease run the game once before patching the PPU-Cache", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } foreach (FileInfo file in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "\\RPCS3\\SailorZombie").GetFiles()) { Patch_RPCS3_PPU_CacheFile(CacheFolder, file.Name, file); } } } } private void Btn_Rpcs3_RazingStorm_Click(object sender, EventArgs e) { } private void Txt_Rpcs3_Save_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void Patch_RPCS3_PPU_CacheFile(string CacheFolder, string CacheFileName, FileInfo SourceFile) { string CPU_Type = string.Empty; foreach (String sCacheFile in Directory.GetFiles(CacheFolder)) { String sCacheFileNameShort = Path.GetFileName(sCacheFile); if (sCacheFileNameShort.StartsWith(CacheFileName)) { try { SourceFile.CopyTo(sCacheFile, true); MessageBox.Show("Successfully replaced PPU-Cache file : " + sCacheFile, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show("Error writing PPU-Cache file :" + sCacheFile + "\n\n" + ex.Message.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } return; } } MessageBox.Show("PPU-Cache file not found :\n " + CacheFileName + "\n\nPlease run the game once before patching the PPU-Cache", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region Outputs tab private void Cbox_Outputs_CheckedChanged(object sender, EventArgs e) { Txt_OutputDelay.Enabled = Cbox_Outputs.Checked; Txt_OutputRecoilOn.Enabled = Cbox_Outputs.Checked; Txt_OutputRecoilOff.Enabled = Cbox_Outputs.Checked; Txt_OutputDamaged.Enabled = Cbox_Outputs.Checked; Cbox_WmOutputs.Enabled = Cbox_Outputs.Checked; Cbox_NetOutputs.Enabled = Cbox_Outputs.Checked; Configurator.GetInstance().OutputEnabled = Cbox_Outputs.Checked; } private void Cbox_WmOutputs_CheckedChanged(object sender, EventArgs e) { if (!Cbox_WmOutputs.Checked) { if (!Cbox_NetOutputs.Checked) Cbox_WmOutputs.Checked = true; } Configurator.GetInstance().Wm_OutputEnabled = Cbox_WmOutputs.Checked; } private void Cbox_NetOutputs_CheckedChanged(object sender, EventArgs e) { if (!Cbox_NetOutputs.Checked) { if (!Cbox_WmOutputs.Checked) Cbox_NetOutputs.Checked = true; } Configurator.GetInstance().Net_OutputEnabled = Cbox_NetOutputs.Checked; } private void Txt_OutputDelay_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().OutputPollingDelay = Convert.ToInt32(Txt_OutputDelay.Text); } catch { MessageBox.Show(Txt_OutputDelay.Text + " is not a valid delay. Please enter a non-decimal number"); Txt_OutputDelay.Text = Configurator.GetInstance().OutputPollingDelay.ToString(); } } private void Txt_OutputRecoilOn_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().OutputCustomRecoilOnDelay = Convert.ToInt32(Txt_OutputRecoilOn.Text); } catch { MessageBox.Show(Txt_OutputRecoilOn.Text + " is not a valid delay. Please enter a non-decimal number"); Txt_OutputRecoilOn.Text = Configurator.GetInstance().OutputCustomRecoilOnDelay.ToString(); } } private void Txt_OutputRecoilOff_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().OutputCustomRecoilOffDelay = Convert.ToInt32(Txt_OutputRecoilOff.Text); } catch { MessageBox.Show(Txt_OutputRecoilOff.Text + " is not a valid delay. Please enter a non-decimal number"); Txt_OutputRecoilOff.Text = Configurator.GetInstance().OutputCustomRecoilOffDelay.ToString(); } } private void Txt_OutputDamaged_TextChanged(object sender, EventArgs e) { try { Configurator.GetInstance().OutputCustomDamagedDelay = Convert.ToInt32(Txt_OutputDamaged.Text); } catch { MessageBox.Show(Txt_OutputDamaged.Text + " is not a valid delay. Please enter a non-decimal number"); Txt_OutputDamaged.Text = Configurator.GetInstance().OutputCustomDamagedDelay.ToString(); } } private void Btn_Save_Cfg_Click(object sender, EventArgs e) { if (Configurator.GetInstance().WriteConf(AppDomain.CurrentDomain.BaseDirectory + @"\" + CONF_FILENAME)) MessageBox.Show("Configuration saved !"); else MessageBox.Show("Impossible to save DemulShooter config file.", "DemulShooter", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region Keyboard Handling /// /// Low-level Keyboard hook. /// protected void ApplyKeyboardHook() { _KeyboardHookProc = new Win32API.HookProc(GuiKeyboardHookCallback); using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) _KeyboardHookID = Win32API.SetWindowsHookEx(Win32Define.WH_KEYBOARD_LL, _KeyboardHookProc, Win32API.GetModuleHandle(curModule.ModuleName), 0); if (_KeyboardHookID == IntPtr.Zero) { MessageBox.Show("Failed to register LowLevel Keyboard Hook.", "DemulShooter Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// Keyboard hook for the GUI part, to detect buttons for config /// /// /// /// /// private IntPtr GuiKeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { if ((UInt32)wParam == Win32Define.WM_KEYDOWN) { if (_This._Start_KeyRecord) { KBDLLHOOKSTRUCT s = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); //_SelectedTextBox.Text = s.scanCode.ToString(); _SelectedTextBox.Text = GetKeyStringFromVkCode(s.vkCode); if (_SelectedTextBox == TXT_P1_S) Configurator.GetInstance().DIK_Sha_P1_Start = s.scanCode; else if (_SelectedTextBox == TXT_P1_T) Configurator.GetInstance().DIK_Sha_P1_Trigger = s.scanCode; else if (_SelectedTextBox == TXT_P2_S) Configurator.GetInstance().DIK_Sha_P2_Start = s.scanCode; else if (_SelectedTextBox == TXT_P2_T) Configurator.GetInstance().DIK_Sha_P2_Trigger = s.scanCode; else if (_SelectedTextBox == TXT_EXIT) Configurator.GetInstance().DIK_Sha_Exit = s.scanCode; else if (_SelectedTextBox == TXT_TEST) Configurator.GetInstance().DIK_Sha_Test = s.scanCode; else if (_SelectedTextBox == TXT_SERVICE) Configurator.GetInstance().DIK_Sha_Service = s.scanCode; else if (_SelectedTextBox == TXT_CH_P1) Configurator.GetInstance().DIK_M2_Crosshair_P1 = s.scanCode; else if (_SelectedTextBox == TXT_CH_P2) Configurator.GetInstance().DIK_M2_Crosshair_P2 = s.scanCode; else if (_SelectedTextBox == TXT_CH_VIS) Configurator.GetInstance().DIK_M2_Crosshair_Visibility = s.scanCode; else if (_SelectedTextBox == TXT_GSOZ_PEDAL_1) Configurator.GetInstance().DIK_Gsoz_Pedal_P1 = s.scanCode; else if (_SelectedTextBox == TXT_GSOZ_PEDAL_2) Configurator.GetInstance().DIK_Gsoz_Pedal_P2 = s.scanCode; else if (_SelectedTextBox == Txt_Rpcs3_P1_Start) Configurator.GetInstance().DIK_Rpcs3_P1_Start = s.scanCode; else if (_SelectedTextBox == Txt_Rpcs3_P2_Start) Configurator.GetInstance().DIK_Rpcs3_P2_Start = s.scanCode; else if (_SelectedTextBox == Txt_Rpcs3_Service) Configurator.GetInstance().DIK_Rpcs3_Service = s.scanCode; else if (_SelectedTextBox == Txt_Rpcs3_Up) Configurator.GetInstance().DIK_Rpcs3_Up = s.scanCode; else if (_SelectedTextBox == Txt_Rpcs3_Down) Configurator.GetInstance().DIK_Rpcs3_Down = s.scanCode; else if (_SelectedTextBox == Txt_Rpcs3_Enter) Configurator.GetInstance().DIK_Rpcs3_Enter = s.scanCode; else if (_SelectedTextBox == Txt_Rpcs3_3D_Switch) Configurator.GetInstance().DIK_Rpcs3_3D_Switch = s.scanCode; else if (_SelectedTextBox == TXT_LE3_PEDAL_1) Configurator.GetInstance().DIK_Le3_Pedal_P1 = s.scanCode; else if (_SelectedTextBox == TXT_LE3_PEDAL_2) Configurator.GetInstance().DIK_Le3_Pedal_P2 = s.scanCode; else if (_SelectedTextBox == TXT_OPGHOST_ACTION_P1) Configurator.GetInstance().DIK_OpGhost_Action_P1 = s.scanCode; else if (_SelectedTextBox == TXT_OPGHOST_ACTION_P2) Configurator.GetInstance().DIK_OpGhost_Action_P2 = s.scanCode; else if (_SelectedTextBox == Txt_EAI_P1_Start) Configurator.GetInstance().DIK_Eai_P1_Start = s.scanCode; else if (_SelectedTextBox == Txt_EAI_P2_Start) Configurator.GetInstance().DIK_Eai_P2_Start = s.scanCode; else if (_SelectedTextBox == Txt_EAI_P1_Credits) Configurator.GetInstance().DIK_Eai_P1_Credits = s.scanCode; else if (_SelectedTextBox == Txt_EAI_P2_Credits) Configurator.GetInstance().DIK_Eai_P2_Credits = s.scanCode; else if (_SelectedTextBox == Txt_EAI_Settings) Configurator.GetInstance().DIK_Eai_Settings = s.scanCode; else if (_SelectedTextBox == Txt_EAI_Up) Configurator.GetInstance().DIK_Eai_MenuUp = s.scanCode; else if (_SelectedTextBox == Txt_EAI_Down) Configurator.GetInstance().DIK_Eai_MenuDown = s.scanCode; else if (_SelectedTextBox == Txt_EAI_Enter) Configurator.GetInstance().DIK_Eai_MenuEnter = s.scanCode; _SelectedTextBox = null; _Start_KeyRecord = false; return new IntPtr(1); } } } return Win32API.CallNextHookEx(_This._KeyboardHookID, nCode, wParam, lParam); } private String GetKeyStringFromScanCode(int ScanCode) { uint Vk = Win32API.MapVirtualKey((uint)ScanCode, VirtualKeyMapType.MAPVK_VSC_TO_VK); return GetKeyStringFromVkCode((int)Vk); } private String GetKeyStringFromVkCode(int vkCode) { KeysConverter kc = new KeysConverter(); return kc.ConvertToString((Keys)vkCode); } /// /// Enable a Textbox to be filled with a DIK key code /// public void TXT_DirectInput_MouseClick(object sender, MouseEventArgs e) { if (_SelectedTextBox != null && _SelectedTextBox != ((TextBox)sender)) { _SelectedTextBox.Text = _SelectedTextBoxTextBackup; } _SelectedTextBox = ((TextBox)sender); _SelectedTextBoxTextBackup = _SelectedTextBox.Text; _SelectedTextBox.Text = ""; _Start_KeyRecord = true; } #endregion #region WINDOW MESSAGE LOOP /*************** WM Boucle *********************/ protected const int WM_INPUT = 0x00FF; protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_INPUT: //read in new mouse values. ProcessRawInputMessage(m.LParam); break; } base.WndProc(ref m); } #endregion private void Tab_AnalogCalib_Click(object sender, EventArgs e) { } private bool CloneDirectory(string root, string dest) { try { foreach (var directory in Directory.GetDirectories(root)) { string dirName = Path.GetFileName(directory); if (!Directory.Exists(Path.Combine(dest, dirName))) { Directory.CreateDirectory(Path.Combine(dest, dirName)); } CloneDirectory(directory, Path.Combine(dest, dirName)); } foreach (var file in Directory.GetFiles(root)) { File.Copy(file, Path.Combine(dest, Path.GetFileName(file)), true); } } catch { return false; } return true; } } } ================================================ FILE: DemulShooter_GUI/Wnd_DemulShooterGui.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Some Act Labs Gun users have issues with their gun raw-input data not being well calibrated. You can use this tab to reajust the aim. For X offset, negative values move the aim to the left. For Y offset, negative values move the aim to the top. Merge Triggers : Both player weapons will be activated by the "Trigger" button from the selected player controller. Mostly for simple lightgun owner with one trigger. Separate Triggers : Left gun trigger will be activated by the "Trigger" button. Right gun trigger will be activated by "Action" button. Use this if you have mounted arcade guns with 2 separate triggers. Separate ACTION button : - Disabled : Action and Reload will be activated by the same mouse event (Right Button) - Enabled : Action will not be triggered by any mouse buttons, but need a keyboard key 115, 17 284, 17 39 AAABAAYAAAAAAAEAIABhJgAAZgAAAICAAAABACAAKAgBAMcmAABAQAAAAQAgAChCAADvLgEAMDAAAAEA IACoJQAAF3EBACAgAAABACAAqBAAAL+WAQAQEAAAAQAgAGgEAABnpwEAiVBORw0KGgoAAAANSUhEUgAA AQAAAAEACAYAAABccqhmAAAmKElEQVR42u2dd7weRbnHvxMSQm8C0rzBoDRBKSK6gNmJEJAiXKQGgggi SJMSkSKCF1ARqVGkBWmhCwhIkDZLyQIiCQii6AVDU7oUKTEhc/+YPdyAycl5Z/edZ/fd+X4+58MfnJl5 ns15f+/szFMUkZ4lSe1CwN7AdsDKwFCASWokgAVeAyYBZyljMml7I+FR0gZEukOS2rWA64BhH/5/hQB8 mPOBfZQx06Vtj4RjsLQBkepJUvtx4HZgiQ6G7YH7e/iatP2RcAySNiDSFX5GZx/+PnazWo+SNj4SjigA PUaS2hWBzUtMsb+0D5FwRAHoPTYoOX5DaQci4YgC0Ht8pOT4xa3W80g7EQlDFIDeo4p/03g71BKiAEQi LSYKQCTSYqIARCItJgpAJNJiogBEIi0mCkAk0mKiAEQiLSYKQCTSYqIARCItJgpAJNJiogBEIi0mCkAk 0mKiAEQiLSYKQCTSYqIARCItJgpAJNJiogBEIi0mCkAk0mKiAEQiLSYKQCTSYmJnoBqQpHYBQBc/qwHL AEOAV4AHgfPzTD0mbWcnWK1XBL4BfB5YGngPeBF4HLgTuE0Z87q0nW0nCoAASWoVsCqwGfBl4IsUjTtn QwocnKT2yDxTJ0rbPhCs1l8HzgLmnc3/HgUcAMywWt8L3AxMVMZMkba7jcTyz4EoPvRrADsA2wOreEyz XZ6pX81lnYOAU/v7nTk0B52VIcqYGT5+Wq03AO6i89fLp4CrgSuVMb/zWTvSOXEH0GWS1H4C2A33wff5 0M/K8Ulqr8kzZaX96ofj8DtbGgYcChxqtX4KuAq4UBnzqLRDvUwUgC6QpHZB4Ku4jrsjKpx6VdwZQS3P A6zWS+BeWcoyDBgLjLVa/x7XuvzSeGZQPVEAKiRJ7arAgcCuwMJdWmYVaioAwMpU/1r52eLnFKv1VcBp ypjJ0o72ClEASlK8248CDsId6nWboeWn6BpDujj3fMAYYIzV+h7gdOBaZcx70k43mSgAnoxI7eDpsCNw JLB6wKX/Lu17PzwXaJ0Ni5+pVuufAOOVMf+Wdr6JxECgDklSOyRJ7e7T3Tb8EsJ++N/FxQXUEmXMk8Cz AZdcETgTeMJqfYDVej7pZ9A0ogAMkCS1g5LU7gT8Gfgl8EkBMy7IM/WW9LOYC78QWHMF4AzgSav13rG9 +cCJAjAAktSOAO4HLgOGC5nxOO51o+6cDORCay+LC0B6xGr9FekH0QSiAPRDktrhSWp/DWS4k2gJpgEX ABvmmfqn9DOZG8qYabhD0dOAN4XMWA34tdU6s1qvKf1M6kw8BJwNSWqHAt8BjsKdPodkBjAJuA0XUfdA nql3pJ9JJyhj3gIOtlofBqyNC3X+Ei4mYv6ApowAJlutzwCOUcb8S/rZ1I0YCvwhktRq4GzCvuO/ClwP 3ADclmfqjRL2H4RgKHB/FId0GtgK2BpYruo1+uE54ABlzLUB16w9UQAKktQuBPwY2C/Qkq8DvwIuB7I8 U9Mr8uMgaioAs2K1VkCCC5HeEfhoN9ebhcuA/ZUxrwZar9ZEAQCS1G6Ee8/u9gHfTFz22wXADXmm3u2C LwfRAAGYleLUfhSwO7ANs88irJLngb2VMdeH8rGutPoMoAjmOQb3rt9NMXweOAc4N89UyHvyRlBE800E Jlqtl8QJwT7ASl1achncIeFZwMHKmMqFuCm09hYgSe3HpsMdwPfo3of/AWA0MCzP1DHxwz93lDEvK2N+ issr2Aq4vYvL7QPcb7VeVdpvKVopAElqNwUeAjbq0hI347Li1s8zdVmeqRim2iHKmJnKmBuVMRsD6wBX 4l6hqubTwINW69HSPkvQqleAInFnLO6wrxvidyPwgzxTv5f2tZcoqgXtaLVeBfg+sBPV/vstAEywWq8N HN6mBKPW7ACKunsTgJ90we87gS/kmdoqfvi7hzLmcWXMLsCauGvTqhkL3GS1Xlza11C0QgCS1C6Fe5fc ueKp/wJ8BdB5pu6T9rMtKGMeU8ZsjQswqjo5ahQwyWo9TNrPEPS8ABQluXJcddqqeAs4DFgzz9QNNS/R 1bMoY+4G1gP2Al6ucOrVgHuLV4KepqcFIEntusC9wCcqnPZ6YLU8UyfFwz15lDFWGXMerlLSBRVOvSxw l9V6Y2kfu0nPCkCS2g1w13xLVjTlK7iItW3yTD0j7V/kgyhjXlXGfB3YBHi6omkXAm60Wm8p7V+36EkB SFI7ErgFWKSiKW8APpVn6soGbPe7cVXWGJQxt+EOCS+oaMqhwDVW6+2lfesGPScASWpT4De4q52yTMPl BmydZ+oFad8GyCsVzNFoEVHGvFHsBnbC5VyUZQhwmdV6O2nfqqanBKDY9t9INSm8T+Ku9s5swLf+rDxe cvy7yphGC0AfypgrcHUcHq5gunmAS63WW0n7VSU9IwDFgd9NwIIVTHcr8Nk8U01sV/Uw5QpxVPX+XAuU Mf8LfAGXBViWIcBVVutNpP2qip4QgCS1K+E+/FW8848bAps3ofrO7CjSin9dYoqea8uljHlHGTMal/RV djfXdyawjrRfVdB4ASiCfG7GdaAtgwUOyTN14J2ZCpYK2yVOLTH2amnju4Uy5oe43gJlr28XwkUMflza p7I0WgCS1M6Hu5cve8//HjAmz1SZD05tyDM1GVccs1Mm4248ehZlzARc9ObbJaf6KC59eTFpn8rQWAEo EnvOpXyE3wxg+zxTE6R9qphv43LsB8pLwOheOQDsD2XMb3FdnMrWCFwFuKLJZcgbKwC4UNxdS87xHrBT nqmeqxNXRCluBXwXmNt5xkTgc8qYsjcIjaEII96C8juBUcAp0v54PwdpA3xIUrsxLtCnrP275Zm6WNqf bpOkdl5chdw1gUUmqZF931hPA6Y4KW8lVutRuKvjsn0Ndy1eLxpF4wQgSe3ywBRgqZJTHZZn6iRpf5pA UcDz48BHgHeAp5QxUjX/u+HfLsDFlPs8vI3bRf1R2p9OaJQAFDX8DK4xZBnOBPZvWIBPcIoy3t/Flc5a Ztb/hQs4ugdXC+FWZUxTIiXn5OsRwA9LTvM4sG7RF6ERNEoAktQeiyviWYabhsDWTbzqS1K7BrAGLsHp PdwWPu9GzILVehHca9b6A/l1XIm163Etu6uIvAuO1fpc4BslpxmvjCk7RzAaIwBJajfEfduUObh8CNgo z1RjOsQkqf0v3N31GNyp84eZjqt0dGieqcpq3VutLwF28Rz+58Kmi5QxjYkstFoPxh2Ilk0B/qoy5hpp fwZCIwSgaNrxB9x7qC9/xxXprH1l3iS1g3An+AcAIxnYv9NfgSTPVOnCGFbrT1NN/PxM3C7iTOA3Tbhi tFoviisgU6bt+6vA6k14LWrKNeCJlPvwvwN8pe4f/iS1CyapPRBXauw6XD+9gYr0J6nuOmq3iuYZhLtv vx543Gq9b3GuUFuUMa8DW+LiInxZAr9ArPD+ShswN4pefXeUnGb7PFO1DXFNUjs/Lu34MMrdbswAPpZn 6vky9litH6B73ZCfxxVm/UWdG3JYrTfE1ZEs06VotDKmiiSkrlHrHUAR6nt2yWmOqeuHf0RqByep3R+X enwS5a82B+Pu+8tSZQm1D7MMbqfyV6v1HlbrWv4NKmPuAb5VcprT6l5huJYPfxYOp1yX3l8Bx0s7MTuS 1Orp7lByHB+8YivLyhXMsXCAR7ACMB7XvvuLAdbrGGXM+cDpJaZYGteDorbUVgCKar5HlJjiEWD3PFO1 OnhKUrtsktorcK81n+rCEotK+9ghnwHutFpfWPQFrBtjKfcKupfVeiBXqSLUVgCAk/F//3od2LZu131J ancBHsW1xO4WTe32tBvwmNW6m8+mY4ouyTviXyhFAacX0ZS1o5YCkKR2E1zKpi9j8kzVJr49Se1SSWqv AS7BnRBHZs9SuOy6CcV1XC1QxrwMbI9/HYH1cXEctaN2ApCkdh7cgZgvJ+WZqk1OexHA9BDw39K2NIjR wENW6/WkDelDGfM73OuAL8fX8Qq0dgKAiz77jOfY+3Bln2pBktqxuNyF5aRtaSArAvdYrcuexFeGMmYc Lj7Dh48BB0r78GFqJQBF2upxnsP/BYwuauJJ+zFfcdB3Es19J68D8wJnWq3PsVqXTdetij1xUaU+HF6n VxuomQAAewD/5Tn2kDxTf5N2IEntkrgAklodZjWcvYDf1qH8ljLmVdzfqQ+LAwdL+zArtRGA4tvfd/t+ K3BeDXz4JO41JJG2pQfRuK69y0sbUpQUO99z+MF1ELI+aiMAuFPSFTzGvQt8Szq3P0ntasBdwEqSdvQ4 q+POBerwjMcCL3qMWwTYX9r4PmohAMXJ/+Gew3+cZ+oJYfvXBDKqjeiLzJ4VcYFDZSJES6OM+SeuWIoP 37ZaV9G6rjS1EABga/ziz5+l3JVhaYoiHYbyfQkiA2d5ILNaDxe240LgQY9xSwK7C9sO1EcADvEcd2ye qbJVXb1JUrsi8FtcrbxIWJYDbpM8E1DGWPx3AQfXIRFK3IAktZ8FNvAY+gRwkaDdS+MOH+MdvxwfB26W vFpTxtwO3O0x9BPA5lJ29yEuAPgfiJwodedf5O/fRHfTZiMDYw1crz7JeIsTPMeJHwaKCkCS2iVwPdw7 5UVcGWcpxgPrCq4f+SAjgTOkFi+uBR/xGDpKur+g9A5gDK7baqecnWdKpJpMktrvADtLrB3pl29ZrfcS XH+cxxiFC3ISQ0wAit5+e3oMnQmcI2TzSGpe4KHljBNs230pLg29U74m2VtQcgewNq5VVafcJFHcM0nt R3CvHdK7psicGQpcabVeKPTCRTOQSz2GLgdsEtrePiT/mH0be/qGYJZlPPHEvwmshN92vAp8/zbFagWI CEAR+edz+PcK8BsBe/fCBStFmsHuVuutQi+qjPk98JjH0G2s1guGthfkdgAbAct6jLusaHsdjCS1yyIc bRjx4myhpBuf26kFcI1ggiMlAL6pspcI2Ho6zSu0GXFfMBIHtpfieiV2ikj6eHABKLb/23oMfQL4XWBb N8fVgos0k2+Grshb9EK8x2PolyUOLyV2AAnwUY9xl4ZM+R2R2sHAqcGeSqQbSFXkneAxZj5gi8B2igiA 72GazxWLN9Phm1TTZCMiy/qED9y6Cte1uVOCF46VEIDNPMZMzjP151AGJqldGDgm3COJdJnjQtYULMqG 3ewxdJPQu5WgApCkdkH82i4H/fYHDiLm9/cSw/GLOi2Dz9/sEoWtwQi9AxhG5x2JLXB5KAMLkfp2yIcS CcLhgTMGr8dVqu6UoMlBoQXAJ9ghyzP1XEAb9yIW+OhFhuF6TgRBGfM2fj0EggYEhRaANzzG+JyoepGk dghwaLjHEQmMb+UpX3ziVnwSirwJLQBPAm918PuvA1cEtG8b/CoTR5rBp63WIwOudyswtYPft7jmscEI KgBFBZ9rOxjy08AdfvcO+TwiIgRrNaaMmQkc38EQUzQiDYbENeCxwEAKeT5IwBj8JLUr4SrLRHqbra3W IW94zmdgV4IzgCNDP4zgAlDU8N+W/k9IHwC2yDM1LaBpe9L5DUWkeQwhYGBQUTl4B9zrwJz4N7C7Mub+ 0A9DJBkoz9RvccVAzsHF+L+De9+/B9gX2CDP1AuBzYq9/NpD0Px7ZcybwKa4Ghi349La3wGewh0UrqOM CXbY/QHbJBatG0lq1wYmS9tREafnmTqozARW6xmAWJmqQAxXxog3k5UmlrdybCdtQIXMkDagIXxV2oA6 EAXAIVKMoUuEvDVpMltKG1AHWi8ARdrvatJ2VEjos5OmsoFkR6G60HoBmA5LAZJdZaqmiqzJ96SdCMBg XG2KVtN6AcAVYugVpgFVXCX9XdqRQLS+3kMUgN56Z55YUbdkn5bXTWRhaQOkab0A5Jl6CXhT2o6KOK2i ea6XdiQQL0obIE3rBaDgTmkDKuDyPFNV+XEl8A9phwKQSRsgTRQAh0ivwQqZQoWJTMqYd3FVkXqZa5Qx f5E2QpooAECeqRuAa6Tt8OQmYGSeKZ9aC3NEGXMlcLS0c13iYYS78taFKAD/zy7Aefg1dZDgYWDHPFNb 5Jl6rRsLKGOOx1WqnSrtbEW8DfwUSIrCna3nP3IBisYdq+EKFDZRICzwLvAqMLWoQTBgktSugisM8uni GQSrJjsXpgHP4QpG3J5n6o+hFi7aV28MpMAquE5JTcgVsLgCNE8AOTBRGdPxTqlo2LECsDgwL83LobG4 BKQ/KWM+EOPxviNFMcyjcO+SS0hbXBHTcanFVwPjq94mR3oTq/VQ3JfANsAGwMekbaqIV4CzgB8WNQud ABQNMG/Dr2R3U3gF+FaeqaukDYnUF6v1GOBE/JrXNoVHgY2VMS+oYsufA5+TtioQ++aZ+oW0EZH6YbUe B+wvbUcgcmCjQbgiBW358AOMS1K7rrQRkXphtd6f9nz4weVB7NQnAG1iHuAEaSMi9cFqvSBwnLQdAowZ hDvtbhujktQuI21EpDZsDCwmbYQAaw0CFpC2QgAFfEHaiEht6OXD7/5YaBDNu9Osil652omUp5fqQXSC amKgT1UMlTYgUhv+LW2AFG0WgHekDYjUhqD9+GqEHURvFcTohCpKZ0V6gz9JGyDEm4NoT/WXWXkD14Qk EgG4j94pCtMJDw4CLpC2QoCf55l6V9qISD1QxkzD9fBrG+cPwiXK3CFtSUB+TzuDPiL98z/AM9JGBOQW Zcy1g/JMWWB73Dao17ke2CTPVDwAjHyAoj7AKKAN7cImATvCB9OBhwAH4OKhPy5tYYW8gGvIOD7PVJt2 OhEPitz/g3Ah8qtI21MxTwDjgJ8rY2bAHIKAktQuByxJc68JLa6Axot5pgZc+SVJ7ca4PoFrAgtKOzEb ZgIv4W4w7saVAX8rxMJW69Vx+fHr4YKomhQ8Mw14EvfMrlTGvDxAnxfFpQUPpdkBczOBl5Qx/1HotclO VUaS2iWAK3Ax4U3iTVyBh+PyTHXlFNtqvTJwBq69dS/wNnCsMuYkaUPqQOsFIEmtwpWH/qK0LSV4Evhy nqlKq9xarb8MXEU9d0NlOUEZ8z1pI6Rp6ha/Sjaj2R9+gOHA7Ulql6pqQqv1WrhKyb344Qc4wmrda+/4 HRMFADaRNqAiVgB+XOF859JbfRM/zCDc7VeriQIAy0sbUCFjktR+pOwkVuuRwGelnQlA3AFIG1ADeikT bAgwsoJ5tpV2JBCt//tv/QOg9wI/VqtgjvWlnQhEW9qgz5EoAL2XFLRoBXP0UiBYf9wrbYA0UQCcAEyT NqJCqujYs7C0EwF4j3blwMyW1gtAnqm36Y324FXShLZfZblfGfOatBHStF4ACq6XNiASnF9LG1AHmhTP 3U2uxSVJtD4yskVcK7Go1XpZXBXiwbhmr//RsDMkYgJQdOf5Lq5DyUdxMdoP4gozTCjSlIOQZ+rvSWrv ATaSeh6RoExWxvw15IJW63WAk3EdlmflOav1kcDFypjgrelFXgGS1O4D3I+LxFoeJ0SLABq4GJiYpHah wGZNkHgWEREuDbmY1Xor3I1DOpv/vTxwIUJFaoILQJLakcCZ9H/QtClwWWDTrgBimbDeZzpwSajFimzK y4F55/KrR1mtg2ejSuwATmRg79pbJqkdE8qoPFOv4TLfIr3NjcqYFwKudzID7751ROiHEVQAktQOo7MY 82OKdN1QxLbhvc+ZoRayWq8KbNnBkBFW6/lDPozQO4A1O/z9lagmtn1A5Jm6F3gg6BOJhORRZcxtAdfr dAc7D7BiQPuCC4BPhFmw14CCnwZeLxKOU0ItZLVWwGiPoXM7K6iU0ALgU7Zq2yS1IbdFVwOVVtaJ1IKn CXj4h+s+vaLHuAHXsKyC0ALwhMeYhYGvhDIwz9RM4IRgTyQSihOVMdMDrrezx5i3cMFBwQgtAI/j14hx l8B2TgAeC7xmpHv8DVfhKAhW68HADh5D71PGzAz2VAgsAMW3660eQzerotJNB3a+BxwV7MFEus33A3/7 a2Bpj3G/DWgjIBMH8CuPMUPwU1Rv8kxdh6sWHGk2DxA+ytPn8A/8PhulkBCAG/GLuNtVwNaDcHnjkWZi gYNCxthbrefDr6TaFGXMk8GeTEFwAcgz9S9gosfQJEnt8MC2PozLEow0k/HKmDzwmpvj8lo6RSQKVaoe wJWe40LHBAAcDTwlsG6kHC8Ahwms63tgfbWArWICcD3wL49xuwUODe7bsewVcs1IJeytjPlnyAWt1ovR WehvH1NCpyf3ISIARRkun4IMwxHo4pNn6lYCxpBHSnORMkai4s9O+EXyhQxQ+gCSJcEu8hy3p5C9Y4mx AU3gCVyLewm+7jFmJi5dWARJAbgDF57ZKdsnqV08tLF5pt7BXUW+HXrtyICZBuysjOlKp+T+sFp/Gvic x9BblDFi/QnEBKAICvqlx9D5ENoF5Jn6I7C3xNqRAfFtZYxUNuc+nuPGC9kLyFcFPg+/e/b9k9SKlK7O M3UJcKrE2pF+OU8Zc7bEwlbrxYHdPIa+iHBFalEByDP1LHCDx9BhBI4M/BDfwQU0RepBBuwruP4++LVR P08ZI9qbUnoHAP6BNkclqZW6xXgPd+I7WWL9yAf4I7Bt4Fj/97FaLwAc7DF0JnCOhM2zIi4AeabuAP7g MfRT+KVcVmX3W8BmxNoBkjwDbBb6vv9D7Acs5THuWmWMeICZuAAU+FZqOT5J7VApo/NMvQRsAkyVsqHF /AMYqYx5VsoAq/USwJGew2txjlQXAbgU8PmHXBF3Py9Gnqmncemf4mreIp4HvqSM+V9hO34ALOYxLi9+ xKmFAOSZmg6c5Dn8yCS1ou2s80xNxXUVEgnnbBnPAl9UxvxJ0oii04/vweMPJboAzY5aCEDBuThl75QF AJHrn1nJM/UMTgTiwWD3+BOwgVTcfB9FxZ9z8fv8PATcJGn/rNRGAIpIux95Dt8kSa14gE6eqReAEcDN 0rb0IHcDGypjfKJHq+ZwYB3PscfW5dsfaiQABWfjdxYAcHKS2k9KO1BkD24JnCFtSw/xS2BjZUzQirmz o9j6H+M5fDI1a0VfKwHIMzUN+L7n8AWBy5PUBq2rPgc/3ssz9W1gd2K/wTJMBw5UxuwhHTADYLVeEHdg 7dtV+4g6fftDzQSg4ELgEc+x6xCw+cPcyDN1IfB5XDXkSGc8DYxQxtSpItMZwCqeY+/AryBuV6mdABRJ QoeWmGK/JLW+RRm74c/DwLoIJ300jKuAzyhj7pU2pA+r9e7AHr7DgbF1+/aHGgoAvF+Ao0xBh3OS1Hba h7Cb/ryVZ+obwFaAWOpnA3gFl867gzLmNWlj+rBar0W5xrHjlTFTpP2YHbUUgIKD8X9/XhC4LkntEtJO zEqeqRuB1YGzcLHgkf9nArC6MkasOMbssFp/BLgGl4buw+vA96T9mBO1FYA8U38Dji8xxXDgyhGp9T2w 6ZZfr+eZ+hbubOD+LixRu23mXHgUF9W3qzLmRWljZqW4778cKBNodoQy5gVpX+ZEbQWg4CeUK8P1pelw mrQTsyPP1AO4BpI741pXVYX4afkA+QcujXYtZcwd0sbMgROBjUuMf4AaZPz1R60FoAgR/gbltsv7JamV zBXvzz+bZ+py3MnyPviVSPswQZtLevAS8F1gJWXM2cqYWjZesVp/HTikxBQzgL3q6l8ftRYAgDxT91L+ au/0JLWbSvvSj4/T80ydDXwCFzvwaJnpKjCpG68RU4EDgWHKmJ8oY97pwhqVYLXeiPLh5ScoYx6W9mVu BK2x70uS2vlwUVSrlZjmDWCDPFNlPlwhfR6JSzb5Cq434kCYnGdq3bJrW61fwK+55X9MBdwG/By4IXTn W0/fhwO/A8o0o/0DsF4dgpfmRiMEACBJ7drAffjVXe/jaWD9PFM+SUdSfi+N6zazCy6eYE68DIzIM1W6 dLnV+mrgqyWm+AtwGXCBMmZquKdV2u/FcDuoMl8003Afft9gtqA0RgAAktQeApxccpoHcR+Ut6T98fB/ JVzjyVHAJ4H5cRmUtwMn5Zn6RxXrWK1TwHQ47A+4OPdrlTGNy4i0Wg8BfoMr8FKGg5Uxp0n7M1CaJgAK 94/05ZJT3QhsU9T2i8wGq/WPcFlvc+KvwCScUNwmWdu+Al8VLjbjmyWnmghs2YRXnT4aJQAARXDPg7hq QGU4N89U2X/wnsZqPQpX/HRZ3CvGo8AUYLIy5mVp+yr080jghJLTPA2so4x5RdqfTmicAAAkqV0X9+1T th7gCXmmahulVRVJalcAtsC92y4wSY1UwGu4Z3hD3a+quonVeg/K52lMx9Uq+J20P53SSAEASFK7Gy5z sCxH55kqE3FYW5LULgz8FNdJ6f1GKpPUyFl/7TFgdBOurKrGar0zrjFn2evwPZUx50v740Pt4wDmRJ6p i3CRgmU5Lknt0dL+VE2S2sWAu3Dvtf11UVoduMtqvba0zSGxWu8IXEz5z8DpTf3wU4Hz0hwBXFfBPP+T pNa3KGldGQ+sNcDfXQS4ymo9v7TRISi2/ZfRvzAOhFsQrkpdlkYLQFE7YDRQRd742CS1l9SholBZktR+ EXdd2Akr4Zpc9CxWa2W1PgInjmVffx8CtlPGzJD2qwyNFgB4v5jolsCfK5huF+CWJLVLSvtVEt/ch/2L K7Geo7jn/wXwwwqmewrYXKINedU0XgAA8ky9CmxKNck0I4AHktR+RtovH4quyZt7Dh8GrCftQ9VYrZfE bderqBz9Eq4dWSVBV9L0hADABzr0VJENtyJwb5LaPaX98mA1YOES47W0A1Vitf4cLo8krWC6f+KqE1ex 26wFPSMAAHmmngRG4nLNyzI/cF6S2glJaheR9q0DPlFyfE/cBlitB1mtDwbuAT5WwZRv4r75fRrZ1pae EgCAPFN/wan9MxVNORp4OEltKu3bACmbxVdWQMSxWi+LCxk/hYFnUvbHa7hv/sYF+syNnhMAeF8ENgKq ah65InBHktpxRXBNnfGtXdfHctIO+FKc8u+MC1nerKJpXwZ0L374oUcFACDP1FM4Eagqwk0B+wOPJan9 b2n/+qHsv+mi0g74YLX+L+AGXOOOqorBPovrTfCQtH/domcFAKDI+98IdwJcFSsA1ySpvTlJrW+TiDrT qGAgq/VQq/XhuGvgLSqc+lHgC8qY0vUV6kxPCwBAnqk3h7g/jKobc2wKPJKk9oweiBuYlUbEARTb/W2A P+KaylYpXBmwkTLGt09lY+h5AQC4M1MzisYchwBVZr4NAQ4AnkhS+/0GnA/0BFbrL+DyHK7FRTBWyTnA pnVqTNJNWiEAfeSZOhX3zV11zvYiwA+AqUlqj0pS28j36LpjtV7Pav0bXNmuDSuefgawrzJm7ybU8quK VgkAQJ6p24HPAr/vwvRL4JqZPJ2k9sQktctL+9t0iq3+CKv1zbhinb5Rjv3xPO6ar0z7r0bSOgEAyDM1 FUhw9QW7UQJ7EeAw4G9FINH60j43Dav1YKv1DrhErwy3c+sGt+Gak9wp7bMEtWqbFZKi6cjYJLW34wqL LNWFZYbgAolGJ6mdgnu/vCzP1OvS/tcVq/VyuAImewPd3EHNBI4BftTmikitFYA+8kxNTFK7BnAm5Uph z421cdlopySpvQ64eAjcememGp1OWgVW63lxW/uv425syubpz43Hga8pY7rRm7FRtF4AAPJMvQhsl6R2 R+BnQDev9ebH9QPceTq8lKT2Glz3WVPsSlqB1XoeXIzGjsD2lGvEMeBlceHBR9e5M1FIogDMQp6pK5LU GuAkYLcASy6F2+ruDbyepHYicBNwa5OalwyUouKQBrYufj4acPlHgX2UMZOkn0OdiALwIYrdwNeS1J6H a2m1ZqClF8WV4N4JIEnto8CduMq9E/NMvSb9bDqlaK/9GVxy1sbFf8vmKnTK28CxwGnKmNbssAZKFIA5 kGfq7hGpXWe6K5N1NGG2qLOyRvGzH/BmktoD8kxVUQW56xTv9EfhgqQWFzTlauBQZUwVhWJ6klZeAw6U IoLwdFy02Y8BqffGhYFfJqndRvqZzA2r9SDgcuD7yH347wM2UMZsHz/8/RMFYADkmXo9z9QRwMrAebhG EKFRwGlFya86szMglS35V2AHIFHGVNEmveeJAtABeaaezTO1F64x5y9wnWBDMgwXwFRn9hFY8y/AGGB1 ZcxVyphuBHf1JFEAPMgz9VSeqX2B4bhrpTcCLr+OtP9zonj3/3zAJR/DVXJeXRlzSdNLdEsQBaAEeab+ nmfqUFyNgANx30TdZjFpv/thScIcLE/EhQavoYy5tM2RfGWJtwAVkGfqTWBcktqf4UpR7Y2LaOvG831N 2t9+6GYW3ZvABOAMZcyfpB3tFaIAVEieKYv7dpqYpHZpYFdceOsaFS7zgLSf/fAK8CLlC5POyj24g9er lTFvSTvYa0QB6BJFQNEpuNj/dXGn01+lXAGLP1NNG7SuoIyxVuur8e9M1McTwJXARb1Ug7+ORAEIQJ6p B4EHge8mqV0bJwTb4pp4DJQZwH7FLqPO/Bi38+m0l8JU4ArcB39KPMkPQxSAwOSZmgJMAb6XpHYY7sxg U+BLzPlD8xzwzTxTd0jbPzeUMc9YrbfGReH1Fz35Hq6yz03FzyPxQx+eKACCFKXLzwbOHpHawdNdX77V gWWAobhWVH8A7mpSpqAyJrNarwx8DZfxtxwukOll3PZ+EnCLMuaf0ra2nUZUgI0MnCS1BwGn9vc7k9TI fudQxsS/i5YQ4wAikRYTBSASaTFRACKRFhMFIBJpMVEAIpEWEwUgEmkxUQAikRYTBSASaTFRACKRFhMF IBJpMVEAIpEWEwUgEmkxUQAikRYTBSASaTFRACKRFhMFIBJpMVEAIpEWEwUgEmkxUQAikRYTBSASaTFR AHqPsqW1Y2nuFhEFoPd4peT4l6UdiIQjCkDvcU/J8XdLOxAJRxSAHiPP1FTgxhJTjJP2IRKOKAC9yf74 vQqco4zJpI2PhCMKQA9StBwbCTzZwbAzgf2kbY+EJbaA6mGS1M4P7IXrRrwqrt9gX2swi9slTALOUsbU tu14pHv8H4LNi3DDgwePAAAAAElFTkSuQmCCKAAAAIAAAAAAAQAAAQAgAAAAAAAAAAEAEwsAABMLAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Rf9AOsv/QDr2oEGd+EFB//JBQf+2QUH/JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOjX/QDr6/0A6//9AOv+gQZ3/QUH//0FB//9B Qf/mQUH/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6n/9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf9mAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq3/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+g QZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgj/QDpF/0A6i/9AOrf/ QDq8/0A6i/9AOhMAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9B Qf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/xBBQf+cQUH/3UFB/99BQf+6QUH/dEFB/ypB Qf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Df9AOln/QDqt/0A69f9AOv//QDr//0A6//9AOv//QDr//0A6xf9AOgAAAAAAAAAAAAAAAAD/ QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/qUFB//9BQf//QUH//0FB//9BQf//QUH//0FB/9lBQf+EQUH/LAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoA/0A6Pv9AOqH/QDr0/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6KgAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9B Qf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/wVBQf/6QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/yEFB/2VBQf8KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoH/0A6Y/9AOtH/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDo8AAAAAAAAAAAA AAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/CEFB//5BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/+1B Qf+HQUH/FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoH/0A6bf9AOuP/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A69/9AOhAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/x0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/2QUH/jkFB/xYAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QD8B/0A6W/9AOt//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOvz/QDprAAAAAAAAAAAA AAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8uQUH/20FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/8UFB/3lBQf8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Lv9AOsP/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A65f9AOp3/QDpa/0A6GgAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8BQUH/KkFB/25B Qf+4QUH/+UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/9pB Qf9FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Bv9AOoH/QDr6/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDrn/0A6jv9AOjj/QDoBAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8RQUH/YkFB/8BBQf/+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+bQUH/DgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOiT/ QDrM/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/QDq3/0A6Tv9AOgQA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+g QZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/yZBQf+PQUH/8UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/eQUH/NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpU/0A68v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOvv/QDqf/0A6KQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8QQUH/e0FB/+1BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/5QUH/aQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QD8B/0A6g/9AOv7/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/QDqm/0A6IwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/DkFB/4VBQf/2QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/l0FB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Bv9AOqX/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDrN/0A6OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/fQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/yBBQf+yQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/tEFB/wsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgr/QDq3/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr3/0A6dP9AOgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tf9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf9tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/wBB Qf9YQUH/7UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/wkFB/w4AAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoJ/0A6vP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A61v9AOi0A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqf/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/0gA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8dQUH/xEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/xUFB/wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Bf9AOrX/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOq3/QDoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOmf/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf/3QUH/DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8FQUH/l0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/vEFB/wYA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgH/QDqf/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDqM/0A6AgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6EP9AOuv/QDr//0A6/6BBnf9BQf//QUH//0FB/5YA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8AQUH/d0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/pUFB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6ev9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6fgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6O/9AOuX/QDr/oEGd/0FB//9BQf+9QUH/DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/a0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOkn/QDr9/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOocAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6B/9AOj6gQZ1NQUH/N0FB/wEA AAAAAAAAAEFB/xJBQf9VQUH/R0FB/xcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/d0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/+QUH/SwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDod/0A67f9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDqi/0A6AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgn/QDpM/0A6cv9AOlj/ QDoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8QQUH/4UFB//9BQf//QUH//0FB/95B Qf+eQUH/VUFB/w0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8AQUH/lkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/tQUH/HQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Av9AOsD/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6v/9AOgYAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOjz/QDqg/0A68v9AOv//QDr//0A6//9AOrgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/2VBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/9kFB/6dBQf9CQUH/AQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8EQUH/uUFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+9QUH/AgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpw/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOtn/QDoSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Af9AOlP/QDrM/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOikAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/TQUH/WkFB/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8PQUH/1kFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6IP9AOvX/QDr//0A6//9AOv//QDr//0A6//9AOv//QDru/0A6JgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOkD/ QDrN/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6RgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/00FB/0YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8kQUH/7kFB//9BQf//QUH//0FB//9BQf//QUH//0FB//JB Qf8bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqy/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6/P9AOkcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOhP/QDqj/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6lBQf8WAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9IQUH//UFB//9BQf//QUH//0FB//9BQf//QUH//0FB/6gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Rv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDp7AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpF/0A65v9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+pBQf9LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9/QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/zwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDrR/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6uf9AOgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoB/0A6fP9AOv3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//5B Qf+DQUH/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/wNBQf/BQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/xEFB/wAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6WP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOu7/QDoaAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Bv9AOqT/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+qQUH/CAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/yBBQf/zQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgH/QDrU/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgj/QDq2/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf+8QUH/CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/3FBQf//QUH//0FB//9BQf//QUH//0FB//9BQf/EQUH/AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6TP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOs7/QDoCAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoF/0A6tf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+7QUH/BwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/BUFB/9lB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf86AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq8/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Af9AOqP/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+pQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/W0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6gA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6J/9AOv7/QDr//0A6//9AOv//QDr//0A6//9AOtX/QDoBAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp6/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8EQUH/4kFB//9BQf//QUH//0FB//9BQf//QUH/+kFB/xcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqK/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6awAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Qv9AOv3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//1BQf9IAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6A/9AOuX/QDr//0A6//9AOv//QDr//0A6//9AOvf/QDoQAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhH/QDrl/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/+hBQf8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/x5BQf/+QUH//0FB//9BQf//QUH//0FB//9BQf/QAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpC/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6n/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6UAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOpf/QDr//0A6//9AOv//QDr//0A6//9AOv//QDpMAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOjz/QDr+/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/0EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/ZkFB//9BQf//QUH//0FB//9BQf//QUH//0FB/3wAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A65v9AOv//QDr//0A6//9AOv// QDr//0A66/9AOgUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoB/0A6yf9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/zkFB/wEA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8SQUH/+kFB//9B Qf//QUH//0FB//9BQf//QUH/zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOh3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDqUAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOk7/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+zQUH//0FB//9BQf//QUH//0FB//9BQf/8QUH/CAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Kf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOjkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6yP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1lB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf8WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoI/0A68v9AOv//QDr//0A6//9AOv//QDrO/0A6AAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOjf/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf89AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/CkFB/+xBQf//QUH//0FB//9BQf//QUH/7kFB/wIA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpq/0A6/v9AOv// QDr//0A67f9AOjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6m/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/6EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/WkFB//5BQf//QUH//0FB//9BQf9yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDo3/0A6eP9AOmz/QDoXAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgf/QDrw/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/80FB/wkAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/PEFB/5dBQf+cQUH/UUFB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Sf9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/TwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqS/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOkcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/eUFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+XAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOsn/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6NQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9YQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6zf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOuX/QDoFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/xFB Qf/yQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/xwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDqD/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr3/0A6SAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1xBQf/8QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgf/QDoY/0A6GP9AOhj/ QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/ QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoPAAAAAAAAAAAAAAAAAAAAAP9AOgX/QDqA/0A61/9AOuj/ QDro/0A66P9AOuj/QDro/0A66P9AOuj/QDro/0A66P9AOuj/QDro/0A66P9AOuj/QDro/0A66P9AOuj/ QDro/0A66P9AOuf/QDrS/0A6lP9AOiYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/zhBQf+pQUH/3kFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hB Qf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+ZBQf/EQUH/YAAAAAAA AAAAAAAAAAAAAABBQf8EQUH/FkFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhB Qf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/wYA AAAAAAAAAP9AOgX/QDqP/0A69/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDrj/0A6ff9AOgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8KQUH/mUFB//VBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/9kFB/45BQf8E/0A6hf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6lgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/6FBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/4L/QDrn/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr8/0A6EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8SQUH//EFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/5v9AOvr/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDo4AAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/zhBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/5/0A6+f9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/PkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//n/QDrp/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrt/0A6BgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8XQUH/+0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/5/9AOov/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOmYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+AQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+I/0A6Bv9AOpn/ QDr7/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6/v9AOs3/QDpUAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9TQUH/v0FB//hBQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/6QUH/mEFB/wYAAAAAAAAAAP9AOgz/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/ QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/ QDog/0A6IP9AOh7/QDoLAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDop/0A6p/9AOtf/QDre/0A63v9AOt7/ QDre/0A63v9AOt7/QDre/0A63v9AOt7/QDre/0A63v9AOt7/QDre/0A63v9AOt7/QDre/0A63v9AOtP/ QDqm/0A6SgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/01B Qf+zQUH/2kFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BB Qf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/30FB/9FBQf+WQUH/JQAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/A0FB/xhBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBB Qf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/wwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6E/9AOuv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6pf9AOgQAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf90QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/zQUH/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpp/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6eQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/GkFB//lB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+jAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOo3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrp/0A6AwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9jQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/7oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6hv9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDorAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/31B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/lQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpW/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkMA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDou/0A6df9AOm3/QDocAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgz/QDr2/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/8kFB/wgA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/F0FB/2pB Qf92QUH/NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6WP9AOvz/QDr//0A6//9AOvL/QDo7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOqr/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/zFBQf/uQUH//0FB//9BQf/+QUH/YwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgH/QDrl/0A6//9AOv//QDr//0A6//9AOtv/ QDoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6RP9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/zsA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8BQUH/0EFB//9B Qf//QUH//0FB//9BQf/uQUH/BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6F/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A61P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/ztBQf//QUH//0FB//9BQf//QUH//0FB//9BQf8kAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoN/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6owAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDpd/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/1MA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/lUFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/xoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDrY/0A6//9AOv//QDr//0A6//9AOv//QDrz/0A6CgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgP/QDrU/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/NQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/wVBQf/rQUH//0FB//9BQf//QUH//0FB//9BQf/lQUH/AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOor/QDr//0A6//9AOv// QDr//0A6//9AOv//QDpYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOkr/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/0AA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/SkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Nv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOrIAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOq3/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+lQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/QgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A63f9AOv// QDr//0A6//9AOv//QDr//0A6+v9AOhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Gf9AOuv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/50FB/xMA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/DUFB//VB Qf//QUH//0FB//9BQf//QUH//0FB/+ZBQf8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqB/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6cwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6T/9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//1BQf9HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9kQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/jAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOiD/ QDr9/0A6//9AOv//QDr//0A6//9AOv//QDra/0A6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6h/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/fwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/81B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf8qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrX/QDr//0A6//9AOv//QDr//0A6//9AOv// QDpSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoC/0A6r/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/6hBQf8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9CQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/wQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Rv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOtH/QDoDAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoI/0A6v/9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+5QUH/BwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/AEFB/8NB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6z/9AOv//QDr//0A6//9AOv// QDr//0A6//9AOmYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoL/0A6vv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/uUFB/wkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9XQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/2UFB/wMA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpU/0A6//9AOv//QDr//0A6//9AOv//QDr//0A67v9AOhoAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoJ/0A6rf9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6lBQf8HAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/EkFB/+dB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDrP/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6t/9AOgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoC/0A6h/9AOv7/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//5BQf+BQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+pQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/2UFB/wQA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOkb/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6dgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Tv9AOuv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/pQUH/SQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/Z0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrP/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr7/0A6QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6F/9AOqz/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/qEFB/xYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/zVBQf/4QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/v0FB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Iv9AOvb/QDr//0A6//9AOv//QDr//0A6//9AOv// QDrq/0A6IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOkb/QDrU/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf97QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/0UFB/0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8ZQUH/40FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//pBQf8rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6df9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrS/0A6DQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/ QDpa/0A60v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOuf/QDoGAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/1ZBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/0UFB/1hB Qf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/CEFB/8hBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/ggAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoD/0A6xv9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDq0/0A6AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6Q/9AOqb/QDr1/0A6//9AOv// QDrm/0A6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/DEFB/+hBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/1QUH/pkFB/0FBQf8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/wFBQf+oQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/89BQf8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoh/0A68P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDqT/0A6AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgv/QDox/0A6Lf9AOgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/OEFB/9lBQf//QUH//0FB/9xBQf+cQUH/VEFB/wwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/hUFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/1QUH/KgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpS/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDp0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDoun0GeQUFB/zpBQf8JAAAAAAAAAAAAAAAAQUH/AEFB/xZB Qf8OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2dBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqH/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/ QDpoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDow/0A62f9AOv+g QZ3/QUH//0FB/+1BQf9MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9cQUH//UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+TAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/ QDqu/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/QDpzAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Df9AOuX/QDr//0A6/6BBnf9BQf//QUH//0FB//NBQf8UAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/Z0FB//1BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/uEFB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgn/QDrD/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDqU/0A6BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpn/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/AkFB/4hBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/8xBQf8NAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOg//QDrL/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrB/0A6GwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOqP/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/fgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/xZBQf+5QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/SQUH/EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhH/QDrI/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDrs/0A6Vv9AOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6t/9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9NQUH/50FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/z0FB/xUAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOg3/QDq6/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6sP9AOh8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8ZQUH/p0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/8JBQf8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgX/ QDqe/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A69v9AOoP/QDoOAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8LQUH/e0FB//JB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+oQUH/CAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgD/QDpu/0A6+/9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOuz/QDp7/0A6EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9B Qf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8MQUH/c0FB/+hBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/9QUH/eUFB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDo5/0A64P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDry/0A6kf9AOicAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8iQUH/ikFB/+5BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/5UFB/0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoP/0A6n/9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOsL/QDpl/0A6EwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9B Qf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/w9B Qf9fQUH/vEFB//5BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6hB Qf8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Sf9AOtz/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr7/0A6vP9AOnL/QDov/0A6AgAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8BQUH/K0FB/21BQf+3QUH/+UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+JBQf9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Cf9AOnz/QDry/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDrl/0A6PwAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/NkFB/+BBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//VBQf+DQUH/CwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhf/QDqQ/0A69v9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrj/0A6AwAAAAAA AAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf/XQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//hBQf+XQUH/GwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoY/0A6h/9AOu3/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDomAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/GEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//BBQf+NQUH/HAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Cv9AOmX/QDrH/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOh8A AAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8TQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/81B Qf9qQUH/DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoq/0A6gf9AOtb/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDrK/0A6AAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+g QZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+9QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/20FB/4VBQf8uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOif/QDpv/0A6tf9AOuD/QDrk/0A6rf9AOh4A AAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/xhBQf+nQUH/40FB/+JBQf+5QUH/c0FB/ysAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9B Qf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9B Qf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOrf/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6nv9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf9oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDo2/0A6+/9AOv//QDr/oEGd/0FB//9BQf//QUH/6EFB/xIAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpH/0A6zv9AOvig QZ36QUH/9UFB/7pBQf8pAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////4D///////////////////8Af//////////////// ///AH///////////////////wB///////////////////8Af///////////////////AH/////////// ////////wB///////////////////8Af///////////////////AH///////////////////wB////// /////////////8Af///////////////////AH///////////////////wB///////////////////8Af /////////////////gPAH8A///////////////ABwB/AD/////////////+AAcAfgAH////////////+ AAHAH4AAf///////////+AABwB/AAB///////////+AAA8AfwAAH///////////AAAfAH+AAA/////// ////AAA/wB/+AAD//////////gAB/8Af/8AAf/////////wAD//AH//wAD/////////wAD//wB///AAP ////////4AD//8Af//8AB////////8AB///AH///gAP///////+AB///wB///+AB////////AA///8Af ///wAP///////gAf///AP///+AB///////4Af///4D////4Af//////8AP////Bh////AD//////+AD/ //g/wB///wAf//////AB///gP8AD//+AD//////wA///AB/AAP//wA//////4Af//gAfwAB//+AH//// /+AP//gAH8AAH//wB//////AH//wAB/AAA//+AP/////gB//wAAfwAAD//gB/////4A//4AAH8AAAf/8 Af////8Af/8AAB/AAAD//gD/////AH/+AAAfwAAAf/4A/////wD//AAAH8AAAD//AP////4A//wAAB/A AAA//wB////+Af/4AAAfwAAAH/+Af////AH/8AAAH8AAAA//gH////wD//AAAB/AAAAP/8A////8A//g AAAfwAAAB//AP///+AP/wAAAH8AAAAP/wD////gH/8AAAB/AAAAD/+Af///4B//AAAAfwAAAA//gH/// +Af/gAAAH8AAAAH/4B////wP/4AAAB/AAAAB//A////+H/8AAAAfwAAAAP/4P///////AAAAH8AAAAD/ /////////wAAAB/AAAAA//////////8AAAAfwAAAAP//////////AAAAH8AAAAD//////////wAAAD/g AAAA/////8AAAA8AAAB/8AAAAeAAAAMAAAAB//////////+AAAAAAAAAAf//////////gAAAAAAAAAD/ /////////wAAAAAAAAAA//////////8AAAAAAAAAAP//////////AAAAAAAAAAD//////////wAAAAAA AAAB//////////+AAAAAAAAAA///////////wAAAAMAAAA+AAAD/8AAAAfAAAAP/////AAAAP+AAAAD/ /////////wAAAD/AAAAA//////////8AAAAfwAAAAP//////////AAAAH8AAAAD//////////wAAAB/A AAAA///////+H/8AAAAfwAAAAP/4f////A//gAAAH8AAAAH/8D////gH/4AAAB/AAAAB/+Af///4B/+A AAAfwAAAA//gH///+Af/wAAAH8AAAAP/4B////wD/8AAAB/AAAAD/8Af///8A//gAAAfwAAAB//AP/// /AP/8AAAH8AAAA//wD////wB//AAAB/AAAAP/4A////+Af/4AAAfwAAAH/+Af////gD//AAAH8AAAD// gH////8A//wAAB/AAAA//wD/////AH/+AAAfwAAAf/4A/////wB//wAAH8AAAP/+AP////+AP/+AAB/A AAH//AH/////gB//wAAfwAAD//wB/////8Af//AAH8AAD//4A//////gD//4AB/AAB//8AP/////4Af/ /gAfwAB//+AH//////AD//8AH8AA///AD//////wAf//wD/AA///gA//////+AD///h/4B///4Af//// //wA////8HH///8AP//////+AH///+A////+AH///////gA////AH////AB///////8AD///wB////AA ////////gAf//8Af///gAf///////8AB///AH///wAP////////gAP//wB///wAH////////8AA//8Af //wAD/////////gAD//AH//wAB/////////+AAP/wB//wAB//////////wAAf8Af/gAA///////////A AAfAH+AAA///////////4AADwB/AAAf///////////gAAcAfwAAf///////////+AAHAH4AAf/////// /////4ABwB+AAf/////////////wAcAfwA///////////////APAH8B/////////////////wB////// /////////////8Af///////////////////AH///////////////////wB///////////////////8Af ///////////////////AH///////////////////wB///////////////////8Af//////////////// ///AH///////////////////wB///////////////////8Af///////////////////AH/////////// ////////wB///////////////////+A//////////ygAAABAAAAAgAAAAAEAIAAAAAAAAEAAABMLAAAT CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOl3/QDrwcUHN+kFB/7BBQf8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrV/0A6/3FBzv9BQf//QUH/OQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A62/9AOv9xQc7/QUH//0FB/0AA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/ QDr/cUHO/0FB//9BQf9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDrb/0A6/3FBzv9BQf//QUH/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A62/9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9B Qf9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhr/QDpq/0A6tP9AOtz/QDqY/0A6AAAAAAD/ QDrb/0A6/3FBzv9BQf//QUH/QAAAAAAAAAAAAAAAAEFB/5VBQf/vQUH/y0FB/4FBQf8sAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoC/0A6Tf9AOrf/QDr8/0A6//9AOv// QDr//0A6//9AOhkAAAAA/0A62/9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAEFB/wNBQf/9QUH//0FB//9B Qf//QUH//0FB/8tBQf9fQUH/BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9APwD/QDpQ/0A61P9AOv// QDr//0A6//9AOv//QDr//0A6//9AOtf/QDoEAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9AAAAAAAAAAAAA AAAAQUH/tEFB//9BQf//QUH//0FB//9BQf//QUH//0FB/+BBQf9gQUH/AgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOiL/ QDq7/0A6//9AOv//QDr//0A6//9AOv//QDrd/0A6h/9AOj7/QDoHAAAAAAAAAAD/QDrb/0A6/3FBzv9B Qf//QUH/QAAAAAAAAAAAAAAAAEFB/wBBQf8mQUH/cUFB/8hBQf//QUH//0FB//9BQf//QUH//0FB/8dB Qf8qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOlv/QDry/0A6//9AOv//QDr//0A6/v9AOrH/QDpB/0A6AQAAAAAAAAAAAAAAAAAAAAAA AAAA/0A62/9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/LUFB/59B Qf/6QUH//0FB//9BQf//QUH/90FB/2YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Av9AOor/QDr//0A6//9AOv//QDr//0A6wP9AOjIAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9BQf8/AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/JUFB/7JBQf//QUH//0FB//9BQf//QUH/lEFB/wMAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOp//QDr//0A6//9AOv//QDr1/0A6Zv9AOgEA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrU/0A6/3FBzv9BQf//QUH/LQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8AQUH/WEFB//BBQf//QUH//0FB//9B Qf+lQUH/AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOpb/QDr//0A6//9AOv// QDri/0A6LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6mP9AOv9x Qc7/QUH/40FB/wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8nQUH/3UFB//9BQf//QUH//0FB/5pBQf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOnD/ QDr//0A6//9AOv//QDrh/0A6IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOg//QDqKckHMoUFB/zNBQf8EQUH/J0FB/wYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/xtBQf/dQUH//0FB//9BQf//QUH/cQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOjj/QDr6/0A6//9AOv//QDrv/0A6KgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOg//QDpn/0A6r/9AOoUAAAAAAAAAAAAAAAAAAAAAQUH/lUFB//9BQf/3QUH/vEFB/2pB Qf8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/J0FB/+1BQf//QUH//0FB//pB Qf83AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgj/QDrZ/0A6//9AOv//QDr7/0A6RAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6EP9AOoj/QDry/0A6//9AOv//QDr//0A6HAAAAAAAAAAAAAAAAEFB/75B Qf//QUH//0FB//9BQf//QUH/9EFB/4xBQf8SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9CQUH/+0FB//9BQf//QUH/10FB/wcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp+/0A6//9AOv//QDr//0A6cAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6T/9AOuj/QDr//0A6//9AOv//QDr//0A6//9AOiQA AAAAAAAAAAAAAABBQf+/QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/6kFB/1MAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/3FBQf//QUH//0FB//9BQf95AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoW/0A68/9AOv// QDr//0A6sP9AOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6iP9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDokAAAAAAAAAAAAAAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/i0FB/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8BQUH/tUFB//9BQf//QUH/8EFB/xIA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6iP9AOv//QDr//0A68/9AOhoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6nP9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+gQUH/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/x1B Qf/1QUH//0FB//9BQf9/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Cv9AOu7/QDr//0A6//9AOogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6h/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABB Qf+/QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/4oAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/kEFB//9BQf//QUH/6EFB/wYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOlz/QDr//0A6//9AOv3/QDofAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Tv9AOv7/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDokAAAAAAAAAAAAAAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/ydBQf//QUH//0FB//9BQf9RAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq2/0A6//9AOv// QDq8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6D/9AOuf/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+hBQf8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/yUFB//9B Qf//QUH/qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoH/0A6+f9AOv//QDr//0A6YQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOob/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/iQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/3BBQf//QUH//0FB//JBQf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6DP9AOvz/QDr//0A68/9AOg4AAAAAAAAAAAAAAAAAAAAAAAAAAP9AOg7/ QDrx/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDokAAAAAAAAAAAA AAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//JB Qf8PAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8ZQUH/+kFB//9BQf/7QUH/BgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpo/0A6uP9AOk0AAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpk/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/ZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2VBQf/MQUH/cQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+9QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/7kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOuX/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOvn/QDoOAAAAAAAAAAAAAAAAQUH/lkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/kAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoI/0A6DP9AOgz/QDoM/0A6DP9AOgz/QDoM/0A6DP9AOgz/QDoM/0A6DP9AOgz/QDoKAAAAAAAAAAD/ QDqB/0A67/9AOvP/QDrz/0A68/9AOvP/QDrz/0A68/9AOvP/QDrz/0A68/9AOtn/QDpZAAAAAAAAAAAA AAAAAAAAAEFB/xdBQf+3QUH/8UFB//NBQf/zQUH/80FB//NBQf/zQUH/80FB//NBQf/zQUH/80FB//NB Qf/qQUH/cgAAAABBQf8BQUH/C0FB/wxBQf8MQUH/DEFB/wxBQf8MQUH/DEFB/wxBQf8MQUH/DEFB/wxB Qf8MQUH/BwAAAAD/QDqG/0A6/f9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOtf/QDonAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8rQUH/40FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//1BQf+F/0A6+P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6kQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/kUFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/9/9AOvj/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOogAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/5RB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//j/ QDqK/0A6/v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOsj/ QDoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8gQUH/xEFB//1BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//5BQf+JAAAAAP9AOgv/QDoQ/0A6EP9AOhD/QDoQ/0A6EP9AOhD/QDoQ/0A6EP9AOhD/ QDoQ/0A6EP9AOgoAAAAAAAAAAP9AOkr/QDrf/0A67v9AOu7/QDru/0A67v9AOu7/QDru/0A67v9AOu7/ QDrs/0A6u/9AOioAAAAAAAAAAAAAAAAAAAAAQUH/HUFB/79BQf/uQUH/70FB/+9BQf/vQUH/70FB/+9B Qf/vQUH/70FB/+9BQf/vQUH/70FB/9lBQf9SAAAAAAAAAABBQf8HQUH/EEFB/xBBQf8QQUH/EEFB/xBB Qf8QQUH/EEFB/xBBQf8QQUH/EEFB/xBBQf8LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq9/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDrY/0A6AQAAAAAAAAAAAAAAAEFB/51BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/1wAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tv9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOhsAAAAAAAAAAAAAAABB Qf++QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/7gA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOmH/QDq4/0A6UgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOmv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDokAAAAAAAAAAAAAAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf9nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/TkFB/7dBQf9lAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgb/QDr5/0A6//9AOvb/ QDoTAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoR/0A69P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/yQUH/DwAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/D0FB//NB Qf//QUH/+0FB/woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoD/0A69f9AOv//QDr//0A6aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOo3/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/iAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/2FBQf//QUH//0FB//hBQf8HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOq//QDr//0A6//9AOsIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoS/0A66v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDokAAAAAAAAAAAA AAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/6EFB/xAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+7QUH//0FB//9BQf+1AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpY/0A6//9AOv//QDr+/0A6IgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOlX/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8cQUH//EFB//9BQf//QUH/XQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6CP9AOuz/ QDr//0A6//9AOosAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6jv9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/4oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/hEFB//9B Qf//QUH/70FB/woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDqF/0A6//9AOv//QDr0/0A6GgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/ QDqi/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDokAAAAAAAAAAAAAAAAQUH/v0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/59BQf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/FkFB//BBQf//QUH//0FB/4sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Ff9AOvP/QDr//0A6//9AOq//QDoAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Av9AOo3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6JAAAAAAA AAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/4pBQf8CAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/6hBQf//QUH//0FB//VBQf8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp+/0A6//9AOv// QDr//0A6bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6VP9AOur/QDr//0A6//9AOv// QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/6UFB/1IA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2VBQf//QUH//0FB//9BQf+EAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Cf9AOtr/QDr//0A6//9AOvr/QDpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoS/0A6jP9AOvT/QDr//0A6//9AOvn/QDoPAAAAAAAAAAAAAAAAQUH/tEFB//9BQf//QUH//0FB//9B Qf/zQUH/ikFB/xEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/zpBQf/4QUH//0FB//9B Qf/eQUH/CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDo6/0A6+/9AOv//QDr//0A67P9AOiYAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoR/0A6af9AOpf/QDpLAAAAAAAAAAAAAAAAAAAAAEFB/0tB Qf/2QUH/9kFB/7tBQf9qQUH/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/yFB Qf/pQUH//0FB//9BQf/9QUH/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOnb/QDr//0A6//9AOv// QDrc/0A6GgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgz/ QDqCcUHNnkFB/1EAAAAAQUH/BkFB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/xdBQf/YQUH//0FB//9BQf//QUH/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoB/0A6nv9AOv//QDr//0A6//9AOtz/QDomAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDqW/0A6/3FBzv9BQf/8QUH/HQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/yNBQf/YQUH//0FB//9BQf//QUH/pEFB/wEAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgT/QDqp/0A6//9AOv//QDr//0A68P9AOlf/QDoAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A61v9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1JBQf/tQUH//0FB//9BQf//QUH/rUFB/wUA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6A/9AOpf/QDr//0A6//9AOv// QDr//0A6sf9AOiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9B Qf9BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/IUFB/6xBQf//QUH//0FB//9B Qf//QUH/nEFB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoA/0A6aP9AOvf/QDr//0A6//9AOv//QDr6/0A6n/9AOi4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDrb/0A6/3FBzv9BQf//QUH/QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8rQUH/m0FB//lB Qf//QUH//0FB//9BQf/5QUH/bUFB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDos/0A6yf9AOv//QDr//0A6//9AOv//QDr//0A6yf9AOnL/ QDoo/0A6AQAAAAAAAAAA/0A62/9AOv9xQc7/QUH//0FB/0EAAAAAAAAAAAAAAABBQf8AQUH/JkFB/3BB Qf/GQUH//0FB//9BQf//QUH//0FB//9BQf/MQUH/LwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDph/0A64f9AOv// QDr//0A6//9AOv//QDr//0A6//9AOsL/QDoBAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9BAAAAAAAAAAAA AAAAQUH/u0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/+NBQf9lQUH/AwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgb/QDpg/0A6yv9AOv//QDr//0A6//9AOv//QDr//0A6EQAAAAD/QDrb/0A6/3FBzv9B Qf//QUH/QQAAAAAAAAAAQUH/C0FB//9BQf//QUH//0FB//9BQf//QUH/zUFB/2JBQf8HAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDor/0A6f/9AOsn/QDrw/0A6pf9AOgAA AAAA/0A62/9AOv9xQc7/QUH//0FB/0EAAAAAAAAAAAAAAABBQf+fQUH/8UFB/8pBQf+BQUH/LQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9BAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrb/0A6/3FBzv9BQf//QUH/QQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A62/9AOv9x Qc7/QUH//0FB/0EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrb/0A6/3FBzv9BQf//QUH/QQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A61f9AOv9xQc7/QUH//0FB/zoA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOl7/ QDrxcUHO+0FB/7JBQf8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA////+D/////////4P/////////g/////////+D/////////4P/////////g/ ////////+D////////wIOD//////4AgwB/////+ACDgB/////wAYOAD////+APg/gH////gH+D/gH/// 8A/4P/AP///gP/g//Af//+B/+A/+B///wP+Hgf8D//+B/gOAf4H//4P8A4A/wf//A/ADgA/A//8H4AOA B+D//g/gA4AH8H/+D8ADgAPwf/4fgAOAAfh//B+AA4AB+D/8HwADgAD4P/4/AAOAAPx///8AA4AA//// /wADgAD//4ADAAeAAIABAAD/////AAAAAP////8AAAAA/////wAAAAD/////AACAAwAHgADAAf//AAOA AP////8AA4AA///+PwADgAD8f/wfAAOAAPg//B+AA4AB+D/+H4ADgAH4f/4PwAOAA/B//g/gA4AH8H// B+ADgAfg//8D8AOAD+D//4P8A4A/wf//gf4DgH+B///A/4eB/wP//+B/+E/+B///4D/4P/wH///wD/g/ +A////gH+D/gH////AH4P4A/////ABg4AP////+ACDgB/////+AIMAf//////Ag4P///////+D////// ///4P/////////g/////////+D/////////4P/////////g/////////+D////8oAAAAMAAAAGAAAAAB ACAAAAAAAAAkAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Cf9AOsmU Qan7QUH/ngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6JP9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgX/QDpF/0A6jv9AOpj/QDoR/0A6Jf9AOv+U Qan/QUH/7wAAAAAAAAAAQUH/DUFB/6BBQf+fQUH/VkFB/wsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDob/0A6iP9AOuj/QDr//0A6//9AOv// QDpQ/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAQUH/PUFB//9BQf//QUH//0FB//FBQf+WQUH/JEFB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6DP9AOoX/QDr0/0A6//9AOv// QDr//0A62P9AOpL/QDoO/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAQUH/BkFB/31BQf/JQUH//EFB//9B Qf//QUH/+EFB/5BBQf8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoz/0A62f9AOv// QDr//0A69/9AOpr/QDoz/0A6AwAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAABB Qf8BQUH/JkFB/4tBQf/xQUH//0FB//9BQf/fQUH/OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOlb/ QDr2/0A6//9AOv3/QDql/0A6HAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7QAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8VQUH/mkFB//xBQf//QUH/+EFB/1xBQf8AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6W/9AOvr/QDr//0A68P9AOlT/QDoBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6FP9AOvqU Qan/QUH/zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/AEFB/0tBQf/tQUH//0FB//tB Qf9eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDo//0A6+f9AOv//QDru/0A6MwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6AP9AOm2VQai4QUH/QUFB/xJBQf8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8vQUH/7EFB//9BQf/5QUH/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOhf/QDrl/0A6//9AOvb/QDpAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Dv9AOmj/QDq7/0A6fgAAAAAAAAAAQUH/CUFB/+5BQf/6QUH/w0FB/2tBQf8PAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/PUFB//ZBQf//QUH/5UFB/xYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Af9AOqT/QDr//0A6/v9AOmIAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgP/QDpl/0A66P9AOv//QDr//0A62QAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf/pQUH/aEFB/wMAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2JBQf/+QUH//0FB/6FBQf8AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Of9AOvz/QDr//0A6nP9AOgEA AAAAAAAAAAAAAAAAAAAA/0A6Dv9AOrD/QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/7JBQf8PAAAAAAAAAAAAAAAAAAAAAEFB/wFBQf+fQUH//0FB//xB Qf80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tP9AOv// QDrp/0A6DgAAAAAAAAAAAAAAAAAAAAD/QDoN/0A6yP9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAA AAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/JQUH/DgAAAAAAAAAAAAAAAAAAAABB Qf8QQUH/7EFB//9BQf+uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoj/0A6/f9AOv//QDp8AAAAAAAAAAAAAAAAAAAAAP9AOgP/QDqv/0A6//9AOv//QDr//0A6//9AOv// QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/sUFB/wMA AAAAAAAAAAAAAAAAAAAAQUH/g0FB//9BQf/7QUH/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDp9/0A6//9AOvr/QDofAAAAAAAAAAAAAAAAAAAAAP9AOmT/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/2YAAAAAAAAAAAAAAAAAAAAAQUH/JUFB//xBQf//QUH/cwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrE/0A6//9AOrz/QDoAAAAAAAAAAAAAAAAA/0A6Df9AOuf/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+hBQf8OAAAAAAAAAAAAAAAAQUH/AUFB/8dBQf//QUH/uwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqZ/0A69v9AOksAAAAAAAAAAAAAAAAA AAAA/0A6Z/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf9pAAAAAAAAAAAAAAAAAAAAAEFB/1tB Qf/8QUH/mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoD/0A6DP9AOgAA AAAAAAAAAAAAAAAAAAAA/0A6vv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAA AAAAQUH/EEFB//5BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/AAAAAAAAAAAAA AAAAAAAAAEFB/wBBQf8WQUH/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A65v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6tQAAAAAAAAAAQUH/BUFB/9pBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6RP9AOoj/ QDqJ/0A6if9AOon/QDqJ/0A6if9AOon/QDqJ/0A6iP9AOnr/QDoX/0A6P/9AOnb/QDp3/0A6d/9AOnf/ QDp3/0A6d/9AOnf/QDpo/0A6EQAAAAAAAAAAAAAAAEFB/yBBQf9wQUH/d0FB/3dBQf93QUH/d0FB/3dB Qf93QUH/d0FB/3VBQf82QUH/HEFB/4BBQf+JQUH/iUFB/4lBQf+JQUH/iUFB/4lBQf+JQUH/iUFB/4hB Qf9E/0A68f9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDqiAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/okFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/x/0A68v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDqYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/oUFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/x/0A6R/9AOov/QDqM/0A6jP9AOoz/QDqM/0A6jP9AOoz/ QDqM/0A6i/9AOnX/QDoQ/0A6Jv9AOnD/QDpz/0A6c/9AOnP/QDpz/0A6c/9AOnP/QDpV/0A6BQAAAAAA AAAAAAAAAEFB/yVBQf9wQUH/dEFB/3RBQf90QUH/dEFB/3RBQf90QUH/dEFB/29BQf8mQUH/EUFB/3BB Qf+LQUH/jEFB/4xBQf+MQUH/jEFB/4xBQf+MQUH/jEFB/4tBQf9HAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6xP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6jQAAAAAAAAAAQUH/BkFB/99BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoC/0A6DP9AOgAAAAAAAAAAAAAAAAAAAAAA/0A6wP9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A61QAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/AAAAAAAAAAAAAAAAAAAAAAEFB/wBBQf8MQUH/AgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqR/0A69v9AOlAAAAAAAAAAAAAAAAAAAAAA/0A6bP9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9oAAAAAAAAAAAAAAAAAAAAAEFB/0xBQf/1QUH/lgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq+/0A6//9AOsH/QDoAAAAAAAAAAAAA AAAA/0A6D/9AOur/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/+hBQf8OAAAAAAAAAAAAAAAAQUH/AEFB/7xB Qf//QUH/wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp4/0A6//9AOvv/ QDoiAAAAAAAAAAAAAAAAAAAAAP9AOmn/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAA AAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/2YAAAAAAAAAAAAAAAAA AAAAQUH/HkFB//pBQf//QUH/fQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDog/0A6/f9AOv//QDp/AAAAAAAAAAAAAAAAAAAAAP9AOgP/QDqz/0A6//9AOv//QDr//0A6//9AOv// QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/sEFB/wMA AAAAAAAAAAAAAAAAAAAAQUH/ekFB//9BQf/9QUH/IwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6sv9AOv//QDrq/0A6DwAAAAAAAAAAAAAAAAAAAAD/QDoP/0A6y/9AOv// QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/JQUH/DgAAAAAAAAAAAAAAAAAAAABBQf8MQUH/5kFB//9BQf+3AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6OP9AOvz/QDr//0A6m/9AOgEAAAAAAAAAAAAAAAAA AAAA/0A6D/9AOrP/QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB/7FBQf8OAAAAAAAAAAAAAAAAAAAAAEFB/wBBQf+VQUH//0FB//1BQf88AAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Af9AOqX/QDr//0A6/f9AOl8A AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgP/QDpo/0A66f9AOv//QDr//0A61QAAAAAAAAAAQUH/D0FB//5B Qf//QUH//0FB//9BQf/pQUH/Z0FB/wMAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1pBQf/9QUH//0FB/6lB Qf8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhj/ QDrn/0A6//9AOvX/QDo8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6D/9AOmv/QDqu/0A6VwAAAAAA AAAAQUH/A0FB/8BBQf/6QUH/wkFB/2pBQf8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/OEFB//RB Qf//QUH/6UFB/xoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpD/0A6+v9AOv//QDrr/0A6LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6AP9AOmeUQam1QUH/XEFB/wFBQf8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8rQUH/6UFB//9BQf/7QUH/RgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Yf9AOvz/QDr//0A67P9AOkr/QDoAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Ff9AOvmUQan/QUH/5kFB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/AEFB/0dBQf/qQUH//0FB//xBQf9mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOl7/QDr4/0A6//9AOvz/ QDqZ/0A6FAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8TQUH/lkFB//tBQf//QUH/+UFB/2JBQf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDo7/0A64P9AOv//QDr//0A68f9AOoz/QDon/0A6AQAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEA AAAAAAAAAAAAAABBQf8BQUH/JUFB/4lBQf/wQUH//0FB//9BQf/iQUH/PgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Ef9AOpH/QDr4/0A6//9AOv//QDr8/0A6yv9AOoH/QDoJ/0A6Jf9AOv+U Qan/QUH/70FB/wEAAAAAQUH/CEFB/35BQf/JQUH//EFB//9BQf//QUH/+EFB/5RBQf8SAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgD/QDok/0A6lv9AOvH/QDr//0A6//9AOv// QDpI/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAQUH/Q0FB//9BQf//QUH//0FB//JBQf+YQUH/JkFB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgv/ QDpV/0A6nf9AOqX/QDoS/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAQUH/EEFB/6RBQf+fQUH/VkFB/wwA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+U Qan/QUH/70FB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6JP9AOv+UQan/QUH/70FB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Cf9AOsqUQan8QUH/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA///4f///AAD///h///8AAP//+H///wAA///4f///AAD///h///8AAP//AGD//wAA//wAYB//AAD/ 8ABgD/8AAP/gGHgH/wAA/4D4fwH/AAD/gfh/gf8AAP8H+B/g/wAA/g/DA/B/AAD8HwMA+D8AAPweAwB4 PwAA/DwDADw/AAD4eAMAHh8AAPh4AwAeHwAA+HADAA4fAAD48AMADx8AAPjwAwAPHwAA//ADAA//AAAA AAOAAAAAAAAP///wAAAAAA////AAAAAAAAOAAAAAAP/wAwAP/wAA+PADAA8fAAD48AMADx8AAPhwAwAO HwAA+HgDAB4fAAD4eAMAHh8AAPw8AwA8PwAA/B4DAHg/AAD8HwMA+D8AAP4PwwPwfwAA/wf4H+D/AAD/ gfg/gf8AAP+A+D8B/wAA/+AYOAf/AAD/8AAgD/8AAP/4ACAf/wAA//8AIP//AAD///g///8AAP//+D// /wAA///4P///AAD///g///8AAP//+H///wAAKAAAACAAAABAAAAAAQAgAAAAAAAAEAAAEwsAABMLAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6TLdAhfpBQf97AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpuuECE/0FB/58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOm64QIT/QUH/nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6If9AOmT/QDom/0A6brhAhP9BQf+fAAAAAEFB/yVBQf9uQUH/KwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QD8A/0A6Sf9AOsH/QDr+/0A6//9AOn3/QDpuuECE/0FB/58AAAAAQUH/bUFB//9B Qf//QUH/ykFB/1JBQf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6F/9AOrP/QDr//0A66/9AOof/QDox/0A6Av9AOm64QIT/QUH/nwAAAABB Qf8AQUH/JkFB/31BQf/mQUH//0FB/7pBQf8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOin/QDri/0A6/P9AOor/QDoNAAAAAAAAAAAAAAAA/0A6bLhAhP9B Qf+bAAAAAAAAAAAAAAAAAAAAAEFB/wlBQf+CQUH/+0FB/+RBQf8rAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoc/0A65f9AOvj/QDpMAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoqtkCGykFB/0dBQf8LAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9IQUH/9kFB/+ZBQf8cAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOsL/QDr+/0A6VwAAAAAAAAAAAAAAAP9AOgT/ QDpi/0A6xf9AOmgAAAAAQUH/VUFB//1BQf/JQUH/ZEFB/wQAAAAAAAAAAAAAAABBQf9WQUH//kFB/8JB Qf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpi/0A6//9AOogAAAAAAAAAAAAAAAD/ QDoi/0A6zf9AOv//QDr//0A6kgAAAABBQf9fQUH//0FB//9BQf//QUH/zkFB/yMAAAAAAAAAAAAAAABB Qf+JQUH//0FB/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOt3/QDre/0A6BgAAAAAA AAAA/0A6Iv9AOub/QDr//0A6//9AOv//QDqSAAAAAEFB/19BQf//QUH//0FB//9BQf//QUH/50FB/yMA AAAAAAAAAEFB/wdBQf/hQUH/2UFB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpE/0A6//9AOnYA AAAAAAAAAP9AOgT/QDrN/0A6//9AOv//QDr//0A6//9AOpIAAAAAQUH/X0FB//9BQf//QUH//0FB//9B Qf//QUH/zkFB/wQAAAAAAAAAAEFB/3xBQf//QUH/PgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOoL/ QDr8/0A6HAAAAAAAAAAA/0A6Yf9AOv//QDr//0A6//9AOv//QDr//0A6kgAAAABBQf9fQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/YwAAAAAAAAAAQUH/IkFB//5BQf99AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Gv9AOkEAAAAAAAAAAAAAAAD/QDrG/0A6//9AOv//QDr//0A6//9AOv//QDqRAAAAAEFB/19B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/IAAAAAAAAAAAAAAAAQUH/TEFB/xwAAAAAAAAAAAAAAAD/ QDoC/0A6Bv9AOgb/QDoG/0A6Bv9AOgb/QDoFAAAAAP9AOtX/QDr5/0A6+f9AOvn/QDr5/0A68/9AOlgA AAAAQUH/K0FB/+lBQf/5QUH/+UFB//lBQf/5QUH/+UFB/9BBQf8AQUH/BkFB/wZBQf8GQUH/BkFB/wZB Qf8GQUH/Av9AOt7/QDr//0A6//9AOv//QDr//0A6//9AOv//QDqkAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/6dBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/e/0A64P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOpoAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/nkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/9//QDoD/0A6CP9AOgj/QDoI/0A6CP9AOgj/QDoHAAAAAP9AOrn/ QDr3/0A69/9AOvf/QDr3/0A66f9AOkEAAAAAQUH/L0FB/+tBQf/3QUH/90FB//dBQf/3QUH/90FB/8AA AAAAQUH/BkFB/whBQf8IQUH/CEFB/whBQf8IQUH/AwAAAAAAAAAAAAAAAP9AOhj/QDpCAAAAAAAAAAAA AAAA/0A6yP9AOv//QDr//0A6//9AOv//QDr//0A6jwAAAABBQf9fQUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/xwAAAAAAAAAAAAAAAEFB/0FBQf8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6fv9AOv3/ QDofAAAAAAAAAAD/QDpl/0A6//9AOv//QDr//0A6//9AOv//QDqSAAAAAEFB/19BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf9iAAAAAAAAAABBQf8cQUH//EFB/4EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDpC/0A6//9AOngAAAAAAAAAAP9AOgX/QDrP/0A6//9AOv//QDr//0A6//9AOpIAAAAAQUH/X0FB//9B Qf//QUH//0FB//9BQf//QUH/zUFB/wQAAAAAAAAAAEFB/3VBQf//QUH/RQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgL/QDrc/0A63/9AOgcAAAAAAAAAAP9AOiT/QDro/0A6//9AOv//QDr//0A6kgAAAABB Qf9fQUH//0FB//9BQf//QUH//0FB/+dBQf8jAAAAAAAAAABBQf8FQUH/3EFB/95BQf8DAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOmL/QDr//0A6hwAAAAAAAAAAAAAAAP9AOiT/QDrP/0A6//9AOv// QDqRAAAAAEFB/19BQf//QUH//0FB//9BQf/OQUH/IwAAAAAAAAAAAAAAAEFB/4NBQf//QUH/ZQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOsT/QDr+/0A6VAAAAAAAAAAAAAAAAP9AOgT/ QDpk/0A6wP9AOlUAAAAAQUH/QEFB//pBQf/JQUH/ZEFB/wQAAAAAAAAAAAAAAABBQf9RQUH//UFB/8ZB Qf8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Hv9AOuf/QDr2/0A6RwAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6KLVAhsdBQf9aQUH/AgAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/REFB//VB Qf/oQUH/HwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6LP9AOuX/ QDr7/0A6gv9AOgkAAAAAAAAAAAAAAAD/QDpsuECE/0FB/6AAAAAAAAAAAAAAAAAAAAAAQUH/CEFB/39B Qf/7QUH/5kFB/y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Gv9AOrv/QDr//0A65v9AOn7/QDon/0A6AP9AOm64QIT/QUH/oAAAAABBQf8AQUH/JUFB/3xB Qf/lQUH//0FB/71BQf8bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Af9AOlL/QDrK/0A6//9AOv//QDp1/0A6brhAhP9BQf+gAAAAAEFB/3FB Qf//QUH//0FB/8tBQf9UQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoq/0A6bv9AOin/QDpuuECE/0FB/6AA AAAAQUH/KEFB/29BQf8rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOm64 QIT/QUH/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6brhAhP9BQf+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpNt0CF+0FB/3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//H////x////8f///4Ef//wBA//4AQH/8HHg/+Hw+H/DhBw/x wQOP4YEBh+MBAMfjAQDH5wEA5wEBAAAA//8AAP//AAEBAIDnAQDn4wEAx+MBAMfhgQGH8cEDj/DhBw/4 fD4f/Bx4P/4AQH//AED//+BH///8f////H////x//ygAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAAT CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy0BwrUFB/0cAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoI/0A6I81AbrZBQf9QQUH/JUFB/wsA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgb/QDp//0A6zP9AOmzNQG62QUH/UEFB/2RB Qf/LQUH/g0FB/wYAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgf/QDq6/0A6dP9AOgMAAAAAyUBymEFB/zsA AAAAQUH/AkFB/3FBQf+7QUH/BwAAAAAAAAAAAAAAAAAAAAD/QDqJ/0A6dwAAAAD/QDo9/0A6yf9AOj5B Qf+sQUH/y0FB/z4AAAAAQUH/d0FB/4gAAAAAAAAAAAAAAAD/QDoS/0A6zP9AOgL/QDo9/0A6+f9AOv// QDpJQUH/r0FB//9BQf/5QUH/PUFB/wJBQf/NQUH/EAAAAAAAAAAA/0A6J/9AOlYAAAAA/0A6yf9AOv// QDr//0A6SUFB/69BQf//QUH//0FB/8oAAAAAQUH/W0FB/yYAAAAA/0A6ef9AOoP/QDqD/0A6av9AOnT/ QDp9/0A6e/9AOhZBQf9FQUH/fUFB/31BQf9yQUH/a0FB/4NBQf+DQUH/ef9AOnr/QDqE/0A6hP9AOmj/ QDps/0A6e/9AOnj/QDoQQUH/RkFB/3xBQf98QUH/bkFB/2hBQf+EQUH/hEFB/3oAAAAA/0A6Jf9AOlcA AAAA/0A6y/9AOv//QDr//0A6SEFB/69BQf//QUH//0FB/8oAAAAAQUH/VkFB/ycAAAAAAAAAAP9AOhH/ QDrN/0A6Av9AOj7/QDr5/0A6//9AOklBQf+vQUH//0FB//lBQf89QUH/AUFB/8xBQf8SAAAAAAAAAAAA AAAA/0A6iv9AOnYAAAAA/0A6Pv9AOsj/QDo5QUH/pkFB/8tBQf89AAAAAEFB/3RBQf+LAAAAAAAAAAAA AAAAAAAAAP9AOgf/QDq7/0A6cf9AOgIAAAAAyUBzl0FB/z8AAAAAQUH/AkFB/3BBQf+8QUH/CAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6B/9AOoP/QDrL/0A6Z81AbrZBQf9QQUH/ZUFB/8tBQf+EQUH/BwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6C/9AOibNQG62QUH/UEFB/yZBQf8LAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy0BwrUFB/0cAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/n8AAPgfAADgBwAAwkMAAMgTAACAAQAAkAkAAAAAAAAAAAAAkAkAAIABAADI EwAAwkMAAOAHAAD4HwAA/n8AAA== ================================================ FILE: DemulShooter_GUI/app.config ================================================ ================================================ FILE: DsCore/Config/Configurator.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; using DsCore; using DsCore.Win32; namespace DsCore.Config { public sealed class Configurator { private static Configurator _Instance; public static Configurator GetInstance(){ if (_Instance == null) _Instance = new Configurator(); return _Instance; } //Maximum number of player allowed for DemulShooter to handle public const int MAX_PLAYERS = 4; //P1-P4 settings private PlayerSettings[] _PlayersSettings; public PlayerSettings[] PlayersSettings { get { return _PlayersSettings; } } //Timeout setting for Hooking procedure private int _HookTimeout = 0; public int HookTimeout { get { return _HookTimeout; } } //ActLbs offset private bool _Act_Labs_Offset_Enable = false; private bool _Act_Labs_Display_Crosshair = true; #region Accessors public bool Act_Labs_Offset_Enable { get { return _Act_Labs_Offset_Enable; } set { _Act_Labs_Offset_Enable = value; } } public bool Act_Labs_Display_Crosshair { get { return _Act_Labs_Display_Crosshair; } set { _Act_Labs_Display_Crosshair = value; } } #endregion //Silent Hill Data private const string SHA_CONF_FILEPATH = @"\bemani_config\sha_v01.cfg"; private HardwareScanCode _DIK_Sha_Exit; private HardwareScanCode _DIK_Sha_Test; private HardwareScanCode _DIK_Sha_Service; private HardwareScanCode _DIK_Sha_P1_Start; private HardwareScanCode _DIK_Sha_P1_Trigger; private HardwareScanCode _DIK_Sha_P2_Start; private HardwareScanCode _DIK_Sha_P2_Trigger; #region Accessors public HardwareScanCode DIK_Sha_Exit { get { return _DIK_Sha_Exit; } set { _DIK_Sha_Exit = value; } } public HardwareScanCode DIK_Sha_Test { get { return _DIK_Sha_Test; } set { _DIK_Sha_Test = value; } } public HardwareScanCode DIK_Sha_Service { get { return _DIK_Sha_Service; } set { _DIK_Sha_Service = value; } } public HardwareScanCode DIK_Sha_P1_Start { get { return _DIK_Sha_P1_Start; } set { _DIK_Sha_P1_Start = value; } } public HardwareScanCode DIK_Sha_P1_Trigger { get { return _DIK_Sha_P1_Trigger; } set { _DIK_Sha_P1_Trigger = value; } } public HardwareScanCode DIK_Sha_P2_Start { get { return _DIK_Sha_P2_Start; } set { _DIK_Sha_P2_Start = value; } } public HardwareScanCode DIK_Sha_P2_Trigger { get { return _DIK_Sha_P2_Trigger; } set { _DIK_Sha_P2_Trigger = value; } } #endregion //Model 2 Data private HardwareScanCode _DIK_M2_Crosshair_P1 = HardwareScanCode.DIK_7; private HardwareScanCode _DIK_M2_Crosshair_P2 = HardwareScanCode.DIK_8; private HardwareScanCode _DIK_M2_Crosshair_Visibility = HardwareScanCode.DIK_9; #region Accessors public HardwareScanCode DIK_M2_Crosshair_P1 { get { return _DIK_M2_Crosshair_P1; } set { _DIK_M2_Crosshair_P1 = value; } } public HardwareScanCode DIK_M2_Crosshair_P2 { get { return _DIK_M2_Crosshair_P2; } set { _DIK_M2_Crosshair_P2 = value; } } public HardwareScanCode DIK_M2_Crosshair_Visibility { get { return _DIK_M2_Crosshair_Visibility; } set { _DIK_M2_Crosshair_Visibility = value; } } #endregion //Dolphin Data private HardwareScanCode _DIK_Dolphin_P2_LClick = HardwareScanCode.DIK_S; private HardwareScanCode _DIK_Dolphin_P2_MClick = HardwareScanCode.DIK_D; private HardwareScanCode _DIK_Dolphin_P2_RClick = HardwareScanCode.DIK_F; #region Accessors public HardwareScanCode DIK_Dolphin_P2_LClick { get { return _DIK_Dolphin_P2_LClick; } set { _DIK_Dolphin_P2_LClick = value; } } public HardwareScanCode DIK_Dolphin_P2_MClick { get { return _DIK_Dolphin_P2_MClick; } set { _DIK_Dolphin_P2_MClick = value; } } public HardwareScanCode DIK_Dolphin_P2_RClick { get { return _DIK_Dolphin_P2_RClick; } set { _DIK_Dolphin_P2_RClick = value; } } #endregion //Elevator Action Invasion Keys private HardwareScanCode _DIK_Eai_Settings = HardwareScanCode.DIK_9; private HardwareScanCode _DIK_Eai_P1_Start = HardwareScanCode.DIK_1; private HardwareScanCode _DIK_Eai_P2_Start = HardwareScanCode.DIK_2; private HardwareScanCode _DIK_Eai_P1_Credits = HardwareScanCode.DIK_5; private HardwareScanCode _DIK_Eai_P2_Credits = HardwareScanCode.DIK_6; private HardwareScanCode _DIK_Eai_MenuUp = HardwareScanCode.DIK_NUMPAD8; private HardwareScanCode _DIK_Eai_MenuDown = HardwareScanCode.DIK_NUMPAD2; private HardwareScanCode _DIK_Eai_MenuEnter = HardwareScanCode.DIK_RETURN; #region Accessors public HardwareScanCode DIK_Eai_Settings { get { return _DIK_Eai_Settings; } set { _DIK_Eai_Settings = value; } } public HardwareScanCode DIK_Eai_P1_Start { get { return _DIK_Eai_P1_Start; } set { _DIK_Eai_P1_Start = value; } } public HardwareScanCode DIK_Eai_P2_Start { get { return _DIK_Eai_P2_Start; } set { _DIK_Eai_P2_Start = value; } } public HardwareScanCode DIK_Eai_P1_Credits { get { return _DIK_Eai_P1_Credits; } set { _DIK_Eai_P1_Credits = value; } } public HardwareScanCode DIK_Eai_P2_Credits { get { return _DIK_Eai_P2_Credits; } set { _DIK_Eai_P2_Credits = value; } } public HardwareScanCode DIK_Eai_MenuUp { get { return _DIK_Eai_MenuUp; } set { _DIK_Eai_MenuUp = value; } } public HardwareScanCode DIK_Eai_MenuDown { get { return _DIK_Eai_MenuDown; } set { _DIK_Eai_MenuDown = value; } } public HardwareScanCode DIK_Eai_MenuEnter { get { return _DIK_Eai_MenuEnter; } set { _DIK_Eai_MenuEnter = value; } } #endregion //Gundam Data private HardwareScanCode _DIK_Gsoz_Pedal_P1 = HardwareScanCode.DIK_G; private HardwareScanCode _DIK_Gsoz_Pedal_P2 = HardwareScanCode.DIK_H; private bool _Gsoz_Pedal_P1_Enabled = false; private bool _Gsoz_Pedal_P2_Enabled = false; #region Accessors public bool Gsoz_Pedal_P1_Enabled { get { return _Gsoz_Pedal_P1_Enabled; } set { _Gsoz_Pedal_P1_Enabled = value; } } public bool Gsoz_Pedal_P2_Enabled { get { return _Gsoz_Pedal_P2_Enabled; } set { _Gsoz_Pedal_P2_Enabled = value; } } public HardwareScanCode DIK_Gsoz_Pedal_P1 { get { return _DIK_Gsoz_Pedal_P1; } set { _DIK_Gsoz_Pedal_P1 = value; } } public HardwareScanCode DIK_Gsoz_Pedal_P2 { get { return _DIK_Gsoz_Pedal_P2; } set { _DIK_Gsoz_Pedal_P2 = value; } } #endregion //HeavyFire games need to get Path and cover sensibility settings private bool _HF_UseMiddleButtonAsGrenade = true; private int _HF_CoverSensibility = 3; private bool _HF_ReverseCover = false; #region Accessors public bool HF_UseMiddleButtonAsGrenade { get { return _HF_UseMiddleButtonAsGrenade; } set { _HF_UseMiddleButtonAsGrenade = value; } } public int HF_CoverSensibility { get { return _HF_CoverSensibility; } set { _HF_CoverSensibility = value; } } public bool HF_ReverseCover { get { return _HF_ReverseCover; } set { _HF_ReverseCover = value; } } #endregion //Lethal Enforcer 3 Data private HardwareScanCode _DIK_Le3_Pedal_P1 = HardwareScanCode.DIK_G; private HardwareScanCode _DIK_Le3_Pedal_P2 = HardwareScanCode.DIK_H; private bool _Le3_Pedal_P1_Enabled = false; private bool _Le3_Pedal_P2_Enabled = false; #region Accessors public bool Le3_Pedal_P1_Enabled { get { return _Le3_Pedal_P1_Enabled; } set { _Le3_Pedal_P1_Enabled = value; } } public bool Le3_Pedal_P2_Enabled { get { return _Le3_Pedal_P2_Enabled; } set { _Le3_Pedal_P2_Enabled = value; } } public HardwareScanCode DIK_Le3_Pedal_P1 { get { return _DIK_Le3_Pedal_P1; } set { _DIK_Le3_Pedal_P1 = value; } } public HardwareScanCode DIK_Le3_Pedal_P2 { get { return _DIK_Le3_Pedal_P2; } set { _DIK_Le3_Pedal_P2 = value; } } #endregion //Mission Impossible Arcade options private bool _MissionImpossible_MergeTriggers = true; #region Accessors public bool MissionImpossible_MergeTriggers { get { return _MissionImpossible_MergeTriggers; } set { _MissionImpossible_MergeTriggers = value; } } #endregion //RPCS3 Settings (only for System 357) private HardwareScanCode _DIK_Rpcs3_P1_Start = HardwareScanCode.DIK_1; private HardwareScanCode _DIK_Rpcs3_P2_Start = HardwareScanCode.DIK_2; private HardwareScanCode _DIK_Rpcs3_Service = HardwareScanCode.DIK_0; private HardwareScanCode _DIK_Rpcs3_Up = HardwareScanCode.DIK_NUMPAD8; //Scancodes for Up and Down arrows are the one of Numpad Arrows (bug in my side ??) private HardwareScanCode _DIK_Rpcs3_Down = HardwareScanCode.DIK_NUMPAD2; private HardwareScanCode _DIK_Rpcs3_Enter = HardwareScanCode.DIK_8; private HardwareScanCode _DIK_Rpcs3_3D_Switch = HardwareScanCode.DIK_SPACE; //Only for Dark Escape 4D #region Accessors public HardwareScanCode DIK_Rpcs3_P1_Start { get { return _DIK_Rpcs3_P1_Start; } set { _DIK_Rpcs3_P1_Start = value; } } public HardwareScanCode DIK_Rpcs3_P2_Start { get { return _DIK_Rpcs3_P2_Start; } set { _DIK_Rpcs3_P2_Start = value; } } public HardwareScanCode DIK_Rpcs3_Service { get { return _DIK_Rpcs3_Service; } set { _DIK_Rpcs3_Service = value; } } public HardwareScanCode DIK_Rpcs3_Up { get { return _DIK_Rpcs3_Up; } set { _DIK_Rpcs3_Up = value; } } public HardwareScanCode DIK_Rpcs3_Down { get { return _DIK_Rpcs3_Down; } set { _DIK_Rpcs3_Down = value; } } public HardwareScanCode DIK_Rpcs3_Enter { get { return _DIK_Rpcs3_Enter; } set { _DIK_Rpcs3_Enter = value; } } public HardwareScanCode DIK_Rpcs3_3D_Switch { get { return _DIK_Rpcs3_3D_Switch; } set { _DIK_Rpcs3_3D_Switch = value; } } #endregion //Specific setting for Operation Ghost CREDITS (the game is not using the E2PROM file at launch) //And Game Test options are available in gs2.ini config file private bool _OpGHost_EnableFreeplay = false; private int _OpGhost_CreditsToStart = 2; private int _OpGhost_CreditsToContinue = 1; private int _OpGhost_CoinsPerCredits = 2; //Other options private bool _OpGhost_SeparateButtons = false; private HardwareScanCode _DIK_OpGhost_Action_P1 = HardwareScanCode.DIK_G; private HardwareScanCode _DIK_OpGhost_Action_P2 = HardwareScanCode.DIK_H; #region Accessors public bool OpGhost_EnableFreeplay { get { return _OpGHost_EnableFreeplay; } set { _OpGHost_EnableFreeplay = value; } } public int OpGhost_CreditsToStart { get { return _OpGhost_CreditsToStart; } set { _OpGhost_CreditsToStart = value; } } public int OpGhost_CreditsToContinue { get { return _OpGhost_CreditsToContinue; } set { _OpGhost_CreditsToContinue = value; } } public int OpGhost_CoinsPerCredits { get { return _OpGhost_CoinsPerCredits; } set { _OpGhost_CoinsPerCredits = value; } } public bool OpGhost_SeparateButtons { get { return _OpGhost_SeparateButtons; } set { _OpGhost_SeparateButtons = value; } } public HardwareScanCode DIK_OpGhost_Action_P1 { get { return _DIK_OpGhost_Action_P1; } set { _DIK_OpGhost_Action_P1 = value; } } public HardwareScanCode DIK_OpGhost_Action_P2 { get { return _DIK_OpGhost_Action_P2; } set { _DIK_OpGhost_Action_P2 = value; } } #endregion //Transformer Shadow Rising Keys private HardwareScanCode _DIK_Tsr_Start_P1 = HardwareScanCode.DIK_1; private HardwareScanCode _DIK_Tsr_Start_P2 = HardwareScanCode.DIK_2; private HardwareScanCode _DIK_Tsr_LeverFront = HardwareScanCode.DIK_NUMPAD8; //DIK_UP doesn't work !! private HardwareScanCode _DIK_Tsr_LeverBack = HardwareScanCode.DIK_NUMPAD2; //DIK_DOWN doesn't work !! private HardwareScanCode _DIK_Tsr_Credits = HardwareScanCode.DIK_5; #region Accessors public HardwareScanCode DIK_Tsr_Start_P1 { get { return _DIK_Tsr_Start_P1; } set { _DIK_Tsr_Start_P1 = value; } } public HardwareScanCode DIK_Tsr_Start_P2 { get { return _DIK_Tsr_Start_P2; } set { _DIK_Tsr_Start_P2 = value; } } public HardwareScanCode DIK_Tsr_LeverFront { get { return _DIK_Tsr_LeverFront; } set { _DIK_Tsr_LeverFront = value; } } public HardwareScanCode DIK_Tsr_LeverBack { get { return _DIK_Tsr_LeverBack; } set { _DIK_Tsr_LeverBack = value; } } public HardwareScanCode DIK_Tsr_Credits { get { return _DIK_Tsr_Credits; } set { _DIK_Tsr_Credits = value; } } #endregion //Outputs settings private bool _OutputEnabled = false; private bool _Wm_OutputEnabled = true; private bool _Net_OutputEnabled = false; private int _OutputPollingDelay = 1; private int _OutputCustomDamagedDelay = 200; private int _OutputCustomRecoilOnDelay = 10; private int _OutputCustomRecoilOffDelay = 30; #region Accessors public bool OutputEnabled { get { return _OutputEnabled; } set {_OutputEnabled = value;} } public bool Wm_OutputEnabled { get { return _Wm_OutputEnabled; } set { _Wm_OutputEnabled = value; } } public bool Net_OutputEnabled { get { return _Net_OutputEnabled; } set { _Net_OutputEnabled = value; } } public int OutputPollingDelay { get { return _OutputPollingDelay; } set { _OutputPollingDelay = value; } } public int OutputCustomDamagedDelay { get { return _OutputCustomDamagedDelay; } set { _OutputCustomDamagedDelay = value; } } public int OutputCustomRecoilOnDelay { get { return _OutputCustomRecoilOnDelay; } set { _OutputCustomRecoilOnDelay = value; } } public int OutputCustomRecoilOffDelay { get { return _OutputCustomRecoilOffDelay; } set { _OutputCustomRecoilOffDelay = value; } } #endregion /// /// This class is used to acces/modify all players/games settings /// private Configurator() { _PlayersSettings = new PlayerSettings[MAX_PLAYERS]; for (int i = 0; i < _PlayersSettings.Length; i++) { _PlayersSettings[i] = new PlayerSettings(i + 1); } } /// /// Return a PlayerSettings class corresponding to the wanted player ID /// /// Wanted Player ID (from 1 to 4) /// public PlayerSettings GetPlayerSettings(int RequestedPlayerID) { foreach (PlayerSettings s in _PlayersSettings) { if (s.ID == RequestedPlayerID) return s; } return null; } /// /// Main application config file /// /// Path to DemulShooter config file public void ReadDsConfig(String ConfigFilePath) { try { Logger.WriteLog("Opening Configuration file : " + Path.GetFullPath(ConfigFilePath)); using (StreamReader sr = new StreamReader(ConfigFilePath)) { String line = sr.ReadLine(); String[] buffer; while (line != null) { if (!line.StartsWith(";")) { buffer = line.Split('='); if (buffer.Length == 2) { String StrKey = buffer[0].Trim(); String StrValue = buffer[1].Trim(); // There will never be more than 9 players (even more than 4) so we can assume that // removing only first 2 char is enough without verification if (StrKey.ToLower().StartsWith("p1")) { if (!GetPlayerSettings(1).ParseIniParameter(StrKey.ToLower().Substring(2), StrValue)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().StartsWith("p2")) { if (!GetPlayerSettings(2).ParseIniParameter(StrKey.ToLower().Substring(2), StrValue)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().StartsWith("p3")) { if (!GetPlayerSettings(3).ParseIniParameter(StrKey.ToLower().Substring(2), StrValue)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().StartsWith("p4")) { if (!GetPlayerSettings(4).ParseIniParameter(StrKey.ToLower().Substring(2), StrValue)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("act_labs_offset_enable")) { if (!bool.TryParse(StrValue, out _Act_Labs_Offset_Enable)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("act_labs_display_crosshair")) { if (!bool.TryParse(StrValue, out _Act_Labs_Display_Crosshair)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("m2_p1_ch")) { try { _DIK_M2_Crosshair_P1 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("m2_p2_ch")) { try { _DIK_M2_Crosshair_P2 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("m2_ch_vis")) { try { _DIK_M2_Crosshair_Visibility = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("gsoz_p1_pedal_enable")) { if (!bool.TryParse(StrValue, out _Gsoz_Pedal_P1_Enabled)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("gsoz_p2_pedal_enable")) { if (!bool.TryParse(StrValue, out _Gsoz_Pedal_P2_Enabled)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("gsoz_p1_pedal_key")) { try { _DIK_Gsoz_Pedal_P1 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("gsoz_p2_pedal_key")) { try { _DIK_Gsoz_Pedal_P2 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("dolphin_p2_lclick")) { try { _DIK_Dolphin_P2_LClick = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("dolphin_p2_mclick")) { try { _DIK_Dolphin_P2_MClick = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("dolphin_p2_rclick")) { try { _DIK_Dolphin_P2_RClick = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("hf_usemiddlebuttonasgrenade")) { if (!bool.TryParse(StrValue, out _HF_UseMiddleButtonAsGrenade)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("hf_coversensibility")) { if (!int.TryParse(StrValue, out _HF_CoverSensibility)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("hf_reversecover")) { if (!bool.TryParse(StrValue, out _HF_ReverseCover)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("opghost_enablefreeplay")) { if (!bool.TryParse(StrValue, out _OpGHost_EnableFreeplay)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("opghost_creditstostart")) { if (!int.TryParse(StrValue, out _OpGhost_CreditsToStart)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("opghost_creditstocontinue")) { if (!int.TryParse(StrValue, out _OpGhost_CreditsToContinue)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("opghost_coinsbycredits")) { if (!int.TryParse(StrValue, out _OpGhost_CoinsPerCredits)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("opghost_separatebuttons")) { if (!bool.TryParse(StrValue, out _OpGhost_SeparateButtons)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("opghost_p1_action_key")) { try { _DIK_OpGhost_Action_P1 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("opghost_p2_action_key")) { try { _DIK_OpGhost_Action_P2 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("rpcs3_p1_start")) { try { _DIK_Rpcs3_P1_Start = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("rpcs3_p2_start")) { try { _DIK_Rpcs3_P2_Start = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("rpcs3_service")) { try { _DIK_Rpcs3_Service = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("rpcs3_up")) { try { _DIK_Rpcs3_Up = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("rpcs3_down")) { try { _DIK_Rpcs3_Down = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("rpcs3_enter")) { try { _DIK_Rpcs3_Enter = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("rpcs3_3d")) { try { _DIK_Rpcs3_3D_Switch = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("le3_p1_pedal_enable")) { if (!bool.TryParse(StrValue, out _Le3_Pedal_P1_Enabled)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("le3_p2_pedal_enable")) { if (!bool.TryParse(StrValue, out _Le3_Pedal_P2_Enabled)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("le3_p1_pedal_key")) { try { _DIK_Le3_Pedal_P1 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("le3_p2_pedal_key")) { try { _DIK_Le3_Pedal_P2 = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_p1_start_key")) { try { _DIK_Eai_P1_Start = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_p2_start_key")) { try { _DIK_Eai_P2_Start = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_p1_credits_key")) { try { _DIK_Eai_P1_Credits = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_p2_credits_key")) { try { _DIK_Eai_P2_Credits = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_settings_key")) { try { _DIK_Eai_Settings = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_menu_up_key")) { try { _DIK_Eai_MenuUp = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_menu_down_key")) { try { _DIK_Eai_MenuDown = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("eai_menu_enter_key")) { try { _DIK_Eai_MenuEnter = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } else if (StrKey.ToLower().Equals("mia_merge_triggers")) { if (!bool.TryParse(StrValue, out _MissionImpossible_MergeTriggers)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("outputenabled")) { if (!bool.TryParse(StrValue, out _OutputEnabled)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("wm_outputsenabled")) { if (!bool.TryParse(StrValue, out _Wm_OutputEnabled)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("net_outputsenabled")) { if (!bool.TryParse(StrValue, out _Net_OutputEnabled)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("outputpollingdelay")) { if (!int.TryParse(StrValue, out _OutputPollingDelay)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("outputcustomdamageddelay")) { if (!int.TryParse(StrValue, out _OutputCustomDamagedDelay)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("outputcustomrecoilondelay")) { if (!int.TryParse(StrValue, out _OutputCustomRecoilOnDelay)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("outputcustomrecoiloffdelay")) { if (!int.TryParse(StrValue, out _OutputCustomRecoilOffDelay)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } else if (StrKey.ToLower().Equals("hooktimeout")) { if (!int.TryParse(StrValue, out _HookTimeout)) Logger.WriteLog("Error parsing " + StrKey + " value in INI file : " + StrValue + " is not valid"); } } } line = sr.ReadLine(); } sr.Close(); Logger.WriteLog("Configuration file succesfuly loaded"); } } catch (Exception ex) { Logger.WriteLog("Error reading " + ConfigFilePath + " : " + ex.Message); } } /// /// Read Silent Hill the Arcade Key mapping /// public void Read_Sha_Conf() { String appData = Environment.GetEnvironmentVariable("appdata").ToString(); ; if (File.Exists(appData + SHA_CONF_FILEPATH)) { byte[] fileBytes = File.ReadAllBytes(appData + @"\bemani_config\sha_v01.cfg"); int Offset = 0x622; int n = (int)fileBytes[0]; for (int i = 0; i < n; i++) { int j = Offset + (i * 4); switch (fileBytes[j + 1]) { case 0x01: { _DIK_Sha_Test = (HardwareScanCode)fileBytes[j]; } break; case 0x02: { _DIK_Sha_Service = (HardwareScanCode)fileBytes[j]; } break; case 0x10: { _DIK_Sha_P1_Start = (HardwareScanCode)fileBytes[j]; } break; case 0x11: { _DIK_Sha_P1_Trigger = (HardwareScanCode)fileBytes[j]; } break; case 0x20: { _DIK_Sha_P2_Start = (HardwareScanCode)fileBytes[j]; } break; case 0x21: { _DIK_Sha_P2_Trigger = (HardwareScanCode)fileBytes[j]; } break; case 0xFF: { _DIK_Sha_Exit = (HardwareScanCode)fileBytes[j]; } break; } } } else { //MessageBox.Show("Silent Hill the Arcade : " + appData + @"\bemani_config\sha_v01.cfg not found", "DemulShooter Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// Write Conf file /// public bool WriteConf(String ConfigFilePath) { try { using (StreamWriter sr = new StreamWriter(ConfigFilePath, false)) { foreach (PlayerSettings PlayerData in _PlayersSettings) { sr.WriteLine(";Player" + PlayerData.ID + " Device configuration"); sr.WriteLine("P" + PlayerData.ID + "Mode = " + PlayerData.Mode); sr.WriteLine("P" + PlayerData.ID + "DeviceName = " + PlayerData.DeviceName); if (PlayerData.RIController != null) { if (PlayerData.RIController.DeviceType == RawInput.RawInputDeviceType.RIM_TYPEHID) { sr.WriteLine("P" + PlayerData.ID + "HidAxisX = 0x" + PlayerData.RIController.Selected_AxisX.ToString("x2")); sr.WriteLine("P" + PlayerData.ID + "InvertAxisX = " + PlayerData.InvertAxis_X); sr.WriteLine("P" + PlayerData.ID + "HidAxisY = 0x" + PlayerData.RIController.Selected_AxisY.ToString("x2")); sr.WriteLine("P" + PlayerData.ID + "InvertAxisY = " + PlayerData.InvertAxis_Y); sr.WriteLine("P" + PlayerData.ID + "HidBtnOnscreenTrigger = " + PlayerData.RIController.Selected_OnScreenTriggerButton.ToString()); sr.WriteLine("P" + PlayerData.ID + "HidBtnAction = " + PlayerData.RIController.Selected_ActionButton.ToString()); sr.WriteLine("P" + PlayerData.ID + "HidBtnOffscreenTrigger = " + PlayerData.RIController.Selected_OffScreenTriggerButton.ToString()); } } sr.WriteLine(""); } sr.WriteLine(";VirtualGun Button keys for users who don't have more than a trigger with Aimtrak"); foreach (PlayerSettings PlayerData in _PlayersSettings) { sr.WriteLine("P" + PlayerData.ID + "VirtualMouseButtons_Enable = " + PlayerData.isVirtualMouseButtonsEnabled.ToString()); sr.WriteLine("P" + PlayerData.ID + "VirtualMouseButtonLeft_Key = " + PlayerData.DIK_VirtualMouseButton_Left.ToString()); sr.WriteLine("P" + PlayerData.ID + "VirtualMouseButtonMiddle_Key = " + PlayerData.DIK_VirtualMouseButton_Middle.ToString()); sr.WriteLine("P" + PlayerData.ID + "VirtualMouseButtonRight_Key = " + PlayerData.DIK_VirtualMouseButton_Right.ToString()); } sr.WriteLine(""); sr.WriteLine(";Model2 emulator keyboard keys to change in-game crosshairs"); sr.WriteLine("M2_P1_CH = " + _DIK_M2_Crosshair_P1.ToString()); sr.WriteLine("M2_P2_CH = " + _DIK_M2_Crosshair_P2.ToString()); sr.WriteLine("M2_CH_VIS = " + _DIK_M2_Crosshair_Visibility.ToString()); sr.WriteLine(""); sr.WriteLine(";Enable Pedal-Mode for TTX Gundam Zeon, and set Keys"); sr.WriteLine("GSOZ_P1_PEDAL_ENABLE = " + _Gsoz_Pedal_P1_Enabled.ToString()); sr.WriteLine("GSOZ_P1_PEDAL_KEY = " + _DIK_Gsoz_Pedal_P1.ToString()); sr.WriteLine("GSOZ_P2_PEDAL_ENABLE = " + _Gsoz_Pedal_P2_Enabled.ToString()); sr.WriteLine("GSOZ_P2_PEDAL_KEY = " + _DIK_Gsoz_Pedal_P2.ToString()); sr.WriteLine(""); sr.WriteLine(";Dolphin Keyboard keys configuration for P2 buttons"); sr.WriteLine("DOLPHIN_P2_LCLICK = " + _DIK_Dolphin_P2_LClick.ToString()); sr.WriteLine("DOLPHIN_P2_MCLICK = " + _DIK_Dolphin_P2_MClick.ToString()); sr.WriteLine("DOLPHIN_P2_RCLICK = " + _DIK_Dolphin_P2_RClick.ToString()); sr.WriteLine(""); sr.WriteLine(";Offset for devices lacking calibration (Act Labs gun, etc...)"); sr.WriteLine("Act_Labs_Offset_Enable = " + _Act_Labs_Offset_Enable.ToString()); sr.WriteLine("Act_Labs_Display_Crosshair = " + _Act_Labs_Display_Crosshair.ToString()); foreach (PlayerSettings PlayerData in _PlayersSettings) { sr.WriteLine("P" + PlayerData.ID + "Act_Labs_Offset_X = " + PlayerData.Act_Labs_Offset_X.ToString()); sr.WriteLine("P" + PlayerData.ID + "Act_Labs_Offset_Y = " + PlayerData.Act_Labs_Offset_Y.ToString()); } sr.WriteLine(""); sr.WriteLine(";Manual calibration for Analog devices"); foreach (PlayerSettings PlayerData in _PlayersSettings) { sr.WriteLine("P" + PlayerData.ID + "Analog_Calibration_Override = " + PlayerData.AnalogAxisRangeOverride.ToString()); sr.WriteLine("P" + PlayerData.ID + "Analog_Manual_Xmin = " + PlayerData.AnalogManual_Xmin.ToString()); sr.WriteLine("P" + PlayerData.ID + "Analog_Manual_Xmax = " + PlayerData.AnalogManual_Xmax.ToString()); sr.WriteLine("P" + PlayerData.ID + "Analog_Manual_Ymin = " + PlayerData.AnalogManual_Ymin.ToString()); sr.WriteLine("P" + PlayerData.ID + "Analog_Manual_Ymax = " + PlayerData.AnalogManual_Ymax.ToString()); } sr.WriteLine(""); sr.WriteLine(";Heavy Fire series settings"); sr.WriteLine("HF_UseMiddleButtonAsGrenade = " + _HF_UseMiddleButtonAsGrenade); sr.WriteLine("HF_CoverSensibility = " + _HF_CoverSensibility.ToString()); sr.WriteLine("HF_ReverseCover = " + _HF_ReverseCover.ToString()); sr.WriteLine(""); sr.WriteLine(";Operation G.H.O.S.T credits settings"); sr.WriteLine("OpGhost_EnableFreeplay = " + _OpGHost_EnableFreeplay.ToString()); sr.WriteLine("OpGhost_CreditsToStart = " + _OpGhost_CreditsToStart.ToString()); sr.WriteLine("OpGhost_CreditsToContinue = " + _OpGhost_CreditsToContinue.ToString()); sr.WriteLine("OpGhost_CoinsByCredits = " + _OpGhost_CoinsPerCredits.ToString()); sr.WriteLine("OpGhost_SeparateButtons = " + _OpGhost_SeparateButtons.ToString()); sr.WriteLine("OpGhost_P1_ACTION_KEY = " + _DIK_OpGhost_Action_P1.ToString()); sr.WriteLine("OpGhost_P2_ACTION_KEY = " + _DIK_OpGhost_Action_P2.ToString()); sr.WriteLine(""); sr.WriteLine(";RPCS3 Keys (System 357 only)"); sr.WriteLine("RPCS3_P1_START = " + _DIK_Rpcs3_P1_Start.ToString()); sr.WriteLine("RPCS3_P2_START = " + _DIK_Rpcs3_P2_Start.ToString()); sr.WriteLine("RPCS3_SERVICE = " + _DIK_Rpcs3_Service.ToString()); sr.WriteLine("RPCS3_UP = " + _DIK_Rpcs3_Up.ToString()); sr.WriteLine("RPCS3_DOWN = " + _DIK_Rpcs3_Down.ToString()); sr.WriteLine("RPCS3_ENTER = " + _DIK_Rpcs3_Enter.ToString()); sr.WriteLine("RPCS3_3D = " + _DIK_Rpcs3_3D_Switch.ToString()); sr.WriteLine(""); sr.WriteLine(";Enable Pedal-Mode for Lethal Enforcers 3, and set Keys"); sr.WriteLine("LE3_P1_PEDAL_ENABLE = " + _Le3_Pedal_P1_Enabled.ToString()); sr.WriteLine("LE3_P1_PEDAL_KEY = " + _DIK_Le3_Pedal_P1.ToString()); sr.WriteLine("LE3_P2_PEDAL_ENABLE = " + _Le3_Pedal_P2_Enabled.ToString()); sr.WriteLine("LE3_P2_PEDAL_KEY = " + _DIK_Le3_Pedal_P2.ToString()); sr.WriteLine(""); sr.WriteLine(";Elevator Action Invasion Keys"); sr.WriteLine("EAI_P1_START_KEY = " + _DIK_Eai_P1_Start.ToString()); sr.WriteLine("EAI_P2_START_KEY = " + _DIK_Eai_P2_Start.ToString()); sr.WriteLine("EAI_P1_CREDITS_KEY = " + _DIK_Eai_P1_Credits.ToString()); sr.WriteLine("EAI_P2_CREDITS_KEY = " + _DIK_Eai_P2_Credits.ToString()); sr.WriteLine("EAI_SETTINGS_KEY = " + _DIK_Eai_Settings.ToString()); sr.WriteLine("EAI_MENU_UP_KEY = " + _DIK_Eai_MenuUp.ToString()); sr.WriteLine("EAI_MENU_DOWN_KEY = " + _DIK_Eai_MenuDown.ToString()); sr.WriteLine("EAI_MENU_ENTER_KEY = " + _DIK_Eai_MenuEnter.ToString()); sr.WriteLine(""); sr.WriteLine(";Mission Impossible Arcade options"); sr.WriteLine("MIA_MERGE_TRIGGERS = " + _MissionImpossible_MergeTriggers.ToString()); sr.WriteLine(""); sr.WriteLine(";Output Settings"); sr.WriteLine("OutputEnabled = " + _OutputEnabled.ToString()); sr.WriteLine("WM_OutputsEnabled = " + _Wm_OutputEnabled.ToString()); sr.WriteLine("Net_OutputsEnabled = " + _Net_OutputEnabled.ToString()); sr.WriteLine("OutputPollingDelay = " + _OutputPollingDelay.ToString()); sr.WriteLine("OutputCustomDamagedDelay = " + _OutputCustomDamagedDelay.ToString()); sr.WriteLine("OutputCustomRecoilOnDelay = " + _OutputCustomRecoilOnDelay.ToString()); sr.WriteLine("OutputCustomRecoilOffDelay = " + _OutputCustomRecoilOffDelay.ToString()); sr.WriteLine(""); sr.WriteLine(";DemulShooter Hooking procedure Timeout value"); sr.WriteLine("HookTimeout = " + _HookTimeout.ToString()); sr.Close(); } return true; } catch { return false; } } /// /// Write Silent Hill the Arcade key mapping /// public bool Write_Sha_Config() { try { String appData = Environment.GetEnvironmentVariable("appdata").ToString(); if (!Directory.Exists(appData + @"\bemani_config")) { Directory.CreateDirectory(appData + @"\bemani_config"); } using (FileStream s = new FileStream(appData + SHA_CONF_FILEPATH, FileMode.Create)) { using (BinaryWriter w = new BinaryWriter(s)) { byte n = 0; for (int i = 0; i < 0x620; i++) { w.Write((byte)0x00); } if (_DIK_Sha_Exit != 0) { w.Write((short)0); w.Write((byte)_DIK_Sha_Exit); w.Write((byte)0xFF); n++; } if (_DIK_Sha_Test != 0) { w.Write((short)0); w.Write((byte)_DIK_Sha_Test); w.Write((byte)0x01); n++; } if (_DIK_Sha_Service != 0) { w.Write((short)0); w.Write((byte)_DIK_Sha_Service); w.Write((byte)0x02); n++; } if (_DIK_Sha_P1_Start != 0) { w.Write((short)0); w.Write((byte)_DIK_Sha_P1_Start); w.Write((byte)0x10); n++; } if (_DIK_Sha_P1_Trigger != 0) { w.Write((short)0); w.Write((byte)_DIK_Sha_P1_Trigger); w.Write((byte)0x11); n++; } if (_DIK_Sha_P2_Start != 0) { w.Write((short)0); w.Write((byte)_DIK_Sha_P2_Start); w.Write((byte)0x20); n++; } if (_DIK_Sha_P2_Trigger != 0) { w.Write((short)0); w.Write((byte)_DIK_Sha_P2_Trigger); w.Write((byte)0x21); n++; } w.Seek(0, SeekOrigin.Begin); w.Write(n); } } return true; } catch { return false; } } } } ================================================ FILE: DsCore/Config/PlayerSettings.cs ================================================ using System; using System.Windows.Forms; using DsCore.RawInput; using DsCore.Win32; using DsCore.XInput; namespace DsCore.Config { /// /// This class will store all Player-relative settings. /// This will be filled at start by parsing the config.ini file, and at runtime by adding a reference to the desired /// selected controller (if available and plugged) /// public class PlayerSettings { public const string PLAYER_MODE_RAWINPUT = "RAWINPUT"; public const string PLAYER_MODE_XINPUT = "XINPUT"; // General Data private int _ID = 0; private string _Mode = PLAYER_MODE_RAWINPUT; private string _DeviceName = string.Empty; #region Accessors public int ID { get { return _ID; } } public string Mode { get { return _Mode; } } public string DeviceName { get { return _DeviceName; } } #endregion // RawInput Data private RawInputController _RIController; private byte _HidAxis_X = 0x30; private byte _HidAxis_Y = 0x31; private bool _InvertAxis_X = false; private bool _InvertAxis_Y = false; private int _HidButton_OnScreenTrigger = 1; private int _HidButton_OffScreenTrigger = 2; private int _HidButton_Action = 3; #region Accessors public RawInputController RIController { get { return _RIController; } set { _RIController = value; if (value != null) { _DeviceName = _RIController.DeviceName; _RIController.Selected_AxisX = _HidAxis_X; _RIController.Selected_AxisY = _HidAxis_Y; RIController.Selected_ActionButton = _HidButton_Action; RIController.Selected_OnScreenTriggerButton = _HidButton_OnScreenTrigger; RIController.Selected_OffScreenTriggerButton = _HidButton_OffScreenTrigger; } else _DeviceName = String.Empty; } } public byte HidAxisX { get { return _HidAxis_X; } } public byte HidAxisY { get { return _HidAxis_Y; } } public bool InvertAxis_X { get { return _InvertAxis_X; } set { _InvertAxis_X = value; } } public bool InvertAxis_Y { get { return _InvertAxis_Y; } set { _InvertAxis_Y = value; } } public int HidButton_OnScreenTrigger { get { return _HidButton_OnScreenTrigger; } } public int HidButton_OffScreenTrigger { get { return _HidButton_OffScreenTrigger; } } public int HidButton_Action { get { return _HidButton_Action; } } #endregion // Virtual Middle click private bool _VirtualMouseButtonsEnabled = false; private HardwareScanCode _DIK_VirtualMouseButton_Left; private HardwareScanCode _DIK_VirtualMouseButton_Middle; private HardwareScanCode _DIK_VirtualMouseButton_Right; #region Accessors public bool isVirtualMouseButtonsEnabled { get { return _VirtualMouseButtonsEnabled; } set {_VirtualMouseButtonsEnabled = value;} } public HardwareScanCode DIK_VirtualMouseButton_Left { get { return _DIK_VirtualMouseButton_Left; } set { _DIK_VirtualMouseButton_Left = value; } } public HardwareScanCode DIK_VirtualMouseButton_Middle { get { return _DIK_VirtualMouseButton_Middle; } set { _DIK_VirtualMouseButton_Middle = value; } } public HardwareScanCode DIK_VirtualMouseButton_Right { get { return _DIK_VirtualMouseButton_Right; } set { _DIK_VirtualMouseButton_Right = value; } } #endregion // XInput data private int _GamepadID = -1; private XinputButtonFlags _Gamepad_LeftClick = XinputButtonFlags.XINPUT_GAMEPAD_B; private XinputButtonFlags _Gamepad_MiddleClick = XinputButtonFlags.XINPUT_GAMEPAD_X; private XinputButtonFlags _Gamepad_RightClick = XinputButtonFlags.XINPUT_GAMEPAD_A; private String _Gamepad_Stick = "L"; private int _Gamepad_VibrationEnabled = 0; private int _Gamepad_VibrationLength = 50; private int _Gamepad_VibrationStrength = 0; #region Accessors public int GamepadID { get { return _GamepadID; } } #endregion // Act Lab calibration offset data private int _Act_Labs_OffsetX = 0; private int _Act_Labs_OffsetY = 0; #region Accessors public int Act_Labs_Offset_X { get { return _Act_Labs_OffsetX; } set { _Act_Labs_OffsetX = value; } } public int Act_Labs_Offset_Y { get { return _Act_Labs_OffsetY; } set { _Act_Labs_OffsetY = value; } } #endregion //Analog Gun axis calibration private bool _AnalogAxisRangeOverride = false; private int _AnalogManual_Xmin = 0; private int _AnalogManual_Xmax = 0; private int _AnalogManual_Ymin = 0; private int _AnalogManual_Ymax = 0; #region Accessors public bool AnalogAxisRangeOverride { get { return _AnalogAxisRangeOverride; } set { _AnalogAxisRangeOverride = value; } } public int AnalogManual_Xmin { get { return _AnalogManual_Xmin; } set { _AnalogManual_Xmin = value; } } public int AnalogManual_Xmax { get { return _AnalogManual_Xmax; } set { _AnalogManual_Xmax = value; } } public int AnalogManual_Ymin { get { return _AnalogManual_Ymin; } set { _AnalogManual_Ymin = value; } } public int AnalogManual_Ymax { get { return _AnalogManual_Ymax; } set { _AnalogManual_Ymax = value; } } #endregion /// /// Constructors, Setting default values. /// This will be of use if no .INI file is found at launch /// /// public PlayerSettings(int PlayerID) { _ID = PlayerID; if (_ID == 1) { _DIK_VirtualMouseButton_Left = HardwareScanCode.DIK_T; _DIK_VirtualMouseButton_Middle = HardwareScanCode.DIK_C; _DIK_VirtualMouseButton_Right = HardwareScanCode.DIK_F; } else if (_ID == 2) { _DIK_VirtualMouseButton_Left = HardwareScanCode.DIK_Y; _DIK_VirtualMouseButton_Middle = HardwareScanCode.DIK_V; _DIK_VirtualMouseButton_Right = HardwareScanCode.DIK_G; } else if (_ID == 3) { _DIK_VirtualMouseButton_Left = HardwareScanCode.DIK_U; _DIK_VirtualMouseButton_Middle = HardwareScanCode.DIK_B; _DIK_VirtualMouseButton_Right = HardwareScanCode.DIK_H; } else if (_ID == 4) { _DIK_VirtualMouseButton_Left = HardwareScanCode.DIK_I; _DIK_VirtualMouseButton_Middle = HardwareScanCode.DIK_N; _DIK_VirtualMouseButton_Right = HardwareScanCode.DIK_J; } } /// /// Reading and parsing existing confil file /// public bool ParseIniParameter(string StrKey, string StrValue) { if (StrKey.Equals("mode")) { _Mode = StrValue.ToUpper(); } else if (StrKey.Equals("devicename")) { _DeviceName = StrValue; } else if (StrKey.Equals("hidaxisx")) { try { _HidAxis_X = Convert.ToByte(StrValue, 16); } catch { return false; } } else if (StrKey.Equals("hidaxisy")) { try { _HidAxis_Y = Convert.ToByte(StrValue, 16); } catch { return false; } } else if (StrKey.Equals("invertaxisx")) { if (!bool.TryParse(StrValue, out _InvertAxis_X)) return false; } else if (StrKey.Equals("invertaxisy")) { if (!bool.TryParse(StrValue, out _InvertAxis_Y)) return false; } else if (StrKey.Equals("hidbtnonscreentrigger")) { if (!int.TryParse(StrValue, out _HidButton_OnScreenTrigger)) return false; } else if (StrKey.Equals("hidbtnoffscreentrigger")) { if (!int.TryParse(StrValue, out _HidButton_OffScreenTrigger)) return false; } else if (StrKey.Equals("hidbtnaction")) { if (!int.TryParse(StrValue, out _HidButton_Action)) return false; } else if (StrKey.Equals("gamepadleftclick")) { try { _Gamepad_LeftClick = (XinputButtonFlags)Enum.Parse(typeof(XinputButtonFlags), StrValue); } catch { return false; } } else if (StrKey.Equals("gamepadmiddleclick")) { try { _Gamepad_MiddleClick = (XinputButtonFlags)Enum.Parse(typeof(XinputButtonFlags), StrValue); } catch { return false; } } else if (StrKey.Equals("gamepadrightclick")) { try { _Gamepad_RightClick = (XinputButtonFlags)Enum.Parse(typeof(XinputButtonFlags), StrValue); } catch { return false; } } else if (StrKey.Equals("gamepadstick")) { _Gamepad_Stick = StrValue.ToUpper(); } else if (StrKey.Equals("gamepadvibrationenabled")) { if (!int.TryParse(StrValue, out _Gamepad_VibrationEnabled)) return false; } else if (StrKey.Equals("gamepadvibrationlength")) { if (!int.TryParse(StrValue, out _Gamepad_VibrationLength)) return false; } else if (StrKey.Equals("gamepadvibrationstrength")) { if (!int.TryParse(StrValue, out _Gamepad_VibrationStrength)) return false; } else if (StrKey.Equals("act_labs_offset_x")) { if (!int.TryParse(StrValue, out _Act_Labs_OffsetX)) return false; } else if (StrKey.Equals("act_labs_offset_y")) { if (!int.TryParse(StrValue, out _Act_Labs_OffsetY)) return false; } else if (StrKey.Equals("analog_calibration_override")) { if (!bool.TryParse(StrValue, out _AnalogAxisRangeOverride)) return false; ; } else if (StrKey.Equals("analog_manual_xmin")) { if (!int.TryParse(StrValue, out _AnalogManual_Xmin)) return false; } else if (StrKey.Equals("analog_manual_xmax")) { if (!int.TryParse(StrValue, out _AnalogManual_Xmax)) return false; } else if (StrKey.Equals("analog_manual_ymin")) { if (!int.TryParse(StrValue, out _AnalogManual_Ymin)) return false; } else if (StrKey.Equals("analog_manual_ymax")) { if (!int.TryParse(StrValue, out _AnalogManual_Ymax)) return false; } else if (StrKey.Equals("virtualmousebuttons_enable")) { if (!bool.TryParse(StrValue, out _VirtualMouseButtonsEnabled)) return false; ; } else if (StrKey.Equals("virtualmousebuttonleft_key")) { try { _DIK_VirtualMouseButton_Left = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { return false; } } else if (StrKey.Equals("virtualmousebuttonmiddle_key")) { try { _DIK_VirtualMouseButton_Middle = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { return false; } } else if (StrKey.Equals("virtualmousebuttonright_key")) { try { _DIK_VirtualMouseButton_Right = (HardwareScanCode)Enum.Parse(typeof(HardwareScanCode), StrValue); } catch { return false; } } return true; } } } ================================================ FILE: DsCore/DsCore.csproj ================================================  Debug x86 8.0.30703 2.0 {BD88CC4D-2944-43F8-85C3-A274A45487AF} Library Properties DsCore DsCore v4.8 512 x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 false x86 pdbonly true bin\Release\ TRACE prompt 4 false true bin\Debug\ TRACE;DEBUG;DEBUG_ASYNCGAMEOUTPUTS, DEBUG_DSTCPDATA full AnyCPU prompt true false false bin\Release\ TRACE true pdbonly AnyCPU prompt true true false ================================================ FILE: DsCore/INIFile.cs ================================================ using System.IO; using System.Runtime.InteropServices; using System.Text; namespace DsCore { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: DsCore/IPC/DsTcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; namespace DsCore.IPC { [StructLayout(LayoutKind.Sequential)] public class DsTcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public DsTcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); Logger.WriteLog(this.GetType().ToString() + " : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { Logger.WriteLog("OutputData.Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { #if DEBUG_DSTCPDATA string s = string.Empty; for (int i = 0; i < ReceivedData.Length; i++) { s += "0x" + ReceivedData[i].ToString("X2") + " "; } Logger.WriteLog("DsTcpData.Update(): Received byte array " + s); #endif using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); #if DEBUG_DSTCPDATA Logger.WriteLog("DsTcpData.Update(): " + fi.Name + "[" + i + "] = " + a.GetValue(i).ToString()); #endif } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); #if DEBUG_DSTCPDATA Logger.WriteLog("DsTcpData.Update(): " + fi.Name + " = " + fi.GetValue(this).ToString()); #endif } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } private void PrivateDebug(string s) { #if DEBUG_DSTCPDATA #endif } } } ================================================ FILE: DsCore/IPC/DsTcp_Client.cs ================================================ using System; using System.Net.Sockets; using System.Threading; namespace DsCore.IPC { public class DsTcp_Client { public static readonly int DS_TCP_CLIENT_PORT = 33610; bool TerminateListenerThread = false; bool ListenerThreadActive = false; private TcpClient _TcpClient; public TcpClient Client { get { return _TcpClient; } } private NetworkStream _ClientNetworkStream; private string _ServerIpAddress; private int _ServerTcpPort; /// /// Constructor /// /// IP Adress of the server to connect to /// TCP Port the server is listening at public DsTcp_Client(string Address, int Port) { _ServerIpAddress = Address; _ServerTcpPort = Port; _TcpClient = new TcpClient(); } public void Connect() { try { StartListenerThread(); } catch (Exception ex) { Logger.WriteLog("Ds_TcpClient.Connect(): Error trying to connect to " + _ServerIpAddress + ":" + _ServerTcpPort + "=>" + ex.Message); } } public void Disconnect() { //Stop the packet listener thread. TerminateListenerThread = true; ListenerThreadActive = false; //Handle the disconnection in a separate thread. Thread DThread = new Thread(DisconnectThread); DThread.IsBackground = true; DThread.Start(); _ClientNetworkStream = null; } private void DisconnectThread() { //Wait just a little moment so the packet listener thread can terminate. System.Threading.Thread.Sleep(25); try { _TcpClient.GetStream().Close(5000); } catch { } try { _TcpClient.Close(); } catch { } } public bool IsConnected() { return _TcpClient != null && _TcpClient.Connected; } /// /// Send message to server using socket connection. /// public void SendMessage(byte[] DataToSend) { try { if (_TcpClient == null) { Logger.WriteLog("DspTcp_Client.SendMessage() => TcpClient is NULL, skipping."); return; } if (_TcpClient.Connected) { // Get a stream object for writing. NetworkStream stream = _TcpClient.GetStream(); if (stream.CanWrite) { stream.Write(DataToSend, 0, DataToSend.Length); Logger.WriteLog("DspTcp_Client.SendMessage() => Succesfully sent data (size=" + DataToSend.Length + " bytes) to TCP server"); } } } catch (Exception Ex) { Logger.WriteLog("DspTcp_Client.SendMessage() => Socket exception: " + Ex.Message.ToString()); } } /// /// Starts the thread that listens for packets (automatically started by Connect()). /// /// private void StartListenerThread() { if (ListenerThreadActive == true) { Logger.WriteLog("Ds_TcpClient.StartListenerThread(): Error -> Listener already running !!"); return; } Thread ListenerThread = new Thread(Listener); ListenerThread.IsBackground = true; ListenerThread.Start(); ListenerThreadActive = true; } private void Listener() { Logger.WriteLog("Successfully Started DsTcp_Client Listener() Thread"); while (true) { try { _TcpClient.Connect(_ServerIpAddress, _ServerTcpPort); Logger.WriteLog("Ds_TcpClient.Listener(): Successfully connected to " + _ServerIpAddress + ":" + _ServerTcpPort); _ClientNetworkStream = _TcpClient.GetStream(); OnConnected(this, new EventArgs()); int PacketLengthInfo = -1; int BytesRead = 0; byte[] InputBuffer = new byte[0]; while (_TcpClient != null && IsConnected() == true && TerminateListenerThread == false) { while (_ClientNetworkStream != null && _ClientNetworkStream.DataAvailable == true) { //If Lenght Information is not existing, we need first to get it in order to knwow the full message length //For that, we need to get the first 4 bytes of the frame if (PacketLengthInfo < 0) { if (BytesRead == 0) InputBuffer = new byte[4]; BytesRead += _ClientNetworkStream.Read(InputBuffer, BytesRead, (InputBuffer.Length - BytesRead)); //Getting the Length of the pakcet to follow using the first 4 bytes read if (BytesRead == 4) { PacketLengthInfo = BitConverter.ToInt32(InputBuffer, 0); BytesRead = 0; Array.Clear(InputBuffer, 0, InputBuffer.Length); } } //If the Packet lenght is known, we can know read data correctly and safely else { if (BytesRead == 0) InputBuffer = new byte[PacketLengthInfo]; BytesRead += _ClientNetworkStream.Read(InputBuffer, BytesRead, (InputBuffer.Length - BytesRead)); if (BytesRead >= PacketLengthInfo) //Check so that we've received the whole packet. { OnPacketReceived(this, new PacketReceivedEventArgs(new DsTcp_TcpPacket(InputBuffer))); //Reset BytesRead = 0; PacketLengthInfo = -1; Array.Clear(InputBuffer, 0, InputBuffer.Length); } } } System.Threading.Thread.Sleep(1); } } catch (SocketException Ex) { Logger.WriteLog("Ds_TcpClient.Listener(): Error trying to connect to " + _ServerIpAddress + ":" + _ServerTcpPort); Logger.WriteLog(Ex.Message.ToString()); } } } #region Custom Event /* OnConnected custom event*/ public delegate void TcpConnectedEventHandler(object sender, EventArgs e); public event TcpConnectedEventHandler TcpConnected; private void OnConnected(object sender, EventArgs e) { if (TcpConnected != null) TcpConnected(sender, e); } /*OnPacketReceived custom event */ public delegate void PacketReceivedEventHandler(object sender, PacketReceivedEventArgs e); public event PacketReceivedEventHandler PacketReceived; private void OnPacketReceived(object sender, PacketReceivedEventArgs e) { if (PacketReceived != null) PacketReceived(sender, e); } public class PacketReceivedEventArgs : EventArgs { private DsTcp_TcpPacket _TcpPacket; public DsTcp_TcpPacket Packet { get { return _TcpPacket; } } public PacketReceivedEventArgs(DsTcp_TcpPacket Packet) { _TcpPacket = Packet; } } #endregion } } ================================================ FILE: DsCore/IPC/DsTcp_TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace DsCore.IPC { public class DsTcp_TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets sent from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public DsTcp_TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public DsTcp_TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } public DsTcp_TcpPacket(byte[] PacketAndHeader) { _Header = (PacketHeader)PacketAndHeader[0]; _Payload = new byte[PacketAndHeader.Length - 1]; Array.Copy(PacketAndHeader, 1, _Payload, 0, _Payload.Length); _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } } } ================================================ FILE: DsCore/IPC/MMFH_HotdRemakeArcade.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.IPC { public class MMFH_HotdRemakeArcade : MappedMemoryFileHelper { private const String MAPPED_FILE_NAME = "DemulShooter_MMF_Hotdra"; private const String MUTEX_NAME = "DemulShooter_Mutex_Hotdra"; private const long MAPPED_FILE_CAPACITY = 2048; //Shared Memory Payload between DemulSHooter and the game, structured as followed: // Demulshooter --> Game : //Byte[0]-Byte[3] : P1_X [0-WindowWidth] //Byte[4]-Byte[7] : P1_Y [0-WindowHeight] //Byte[8]-Byte[11] : P2_X [0-WindowWidth] //Byte[12]-Byte[15] : P2_Y [0-WindowHeight] //Byte[16] : P1_Trigger [0-1-2] //Byte[17] : P2_Trigger [0-1-2] //Byte[18] : P1_Reload [0-1] //Byte[19] : P2_Reload [0-1] // Game -> DemulShooter : //Byte[20] : P1_StartLmp [0-1] //Byte[21] : P2_StartLmp [0-1] //Byte[22] : P1_Life [int] //Byte[23] : P2_Life [int] //Byte[24] : P1_Ammo [int] //Byte[25] : P2_Ammo [int] //Byte[26] : P1_Recoil [0-1] //Byte[27] : P2_Recoil [0-1] //Byte[28] : P1_Damaged [int] //Byte[29] : P2_Damaged [int] //Byte[30] : Credits [0-FF] private Byte[] _bPayload; #region Payload Indexes public const int PAYLOAD_LENGTH = 31; public const int PAYLOAD_INPUT_LENGTH = 20; public const int PAYLOAD_OUTPUTS_LENGTH = 11; public enum Payload_Inputs_Index { P1_AxisX = 0, P1_AxisY = 4, P2_AxisX = 8, P2_AxisY = 12, P1_Trigger = 16, P2_Trigger, P1_Reload, P2_Reload } public enum Payload_Outputs_Index { P1_StartLmp = PAYLOAD_INPUT_LENGTH, P2_StartLmp, P1_Life, P2_Life, P1_Ammo, P2_Ammo, P1_Recoil, P2_Recoil, P1_Damaged, P2_Damaged, Credits } #endregion public Byte[] Payload { get { return _bPayload; } set { _bPayload = value; } } public MMFH_HotdRemakeArcade() : base(MAPPED_FILE_NAME, MUTEX_NAME, MAPPED_FILE_CAPACITY) { _bPayload = new Byte[PAYLOAD_LENGTH]; } /// /// Reading data /// /// data /// Initial address /// Number /// public int ReadAll() { if (PAYLOAD_LENGTH > _MemSize) return 2; // Beyond data area if (_bInit) { if (_Mutex != null) _Mutex.WaitOne(); Marshal.Copy(_pwData, _bPayload, 0, (int)PAYLOAD_LENGTH); if (_Mutex != null) _Mutex.ReleaseMutex(); } else { return 1; // Shared memory not initialized } return 0; // Read successfully } /// /// Write all Payload in one hit /// /// public int Writeall() { return WriteByteArray(_bPayload, 0, PAYLOAD_LENGTH); } //Writing the input part of the shared memory public int WriteInputs() { return WriteByteArray(_bPayload, 0, PAYLOAD_INPUT_LENGTH); } } } ================================================ FILE: DsCore/IPC/MMF_DataStruct.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace DsCore.IPC { public class MMF_DataStruct { public UInt32 RawValue_X; public UInt32 RawValue_Y; public Int32 ComputedValue_X; public Int32 ComputedValue_Y; public UInt32 ComputedButtonEvent; public MMF_DataStruct() { RawValue_X = 0; RawValue_Y = 0; ComputedValue_X = 0; ComputedValue_Y = 0; ComputedButtonEvent = 0; } public void UpdateRawValues(UInt32 NewX, UInt32 NewY) { RawValue_X = NewX; RawValue_Y = NewY; } public void UpdateComputedValues(Int32 NewX, Int32 NewY, bool[] NewButtons) { ComputedValue_X = NewX; ComputedValue_Y = NewY; //Temporary set up for buttons : //Buttons bool array on a 32bits variable (for fix length dtruct to send) //Better to send directly the boolean array and add a byte header to get the variable length data ComputedButtonEvent = 0; for (int i = 0; i < NewButtons.Length; i++) { if (NewButtons[i]) ComputedButtonEvent += (uint)(1 << i); if (i >= 31) break; } } } } ================================================ FILE: DsCore/IPC/MappedMemoryFileHelper.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Threading; using DsCore.Win32; using System.Runtime.InteropServices; namespace DsCore.IPC { public class MappedMemoryFileHelper { protected IntPtr _hSharedMemoryFile = IntPtr.Zero; protected String _sMmfName = String.Empty; protected bool _bMmfAlreadyExist = false; protected long _MemSize = 0; protected Mutex _Mutex; protected String _sMutexName = String.Empty; protected bool _IsMutexCreated; protected IntPtr _pwData = IntPtr.Zero; protected bool _bInit = false; public bool IsOpened { get { return _bInit; } } public bool IsMutexCreated { get { return _IsMutexCreated; } } public MappedMemoryFileHelper(string MMF_Name, string Mutex_Name, long lngSize) { _sMmfName = MMF_Name; _sMutexName = Mutex_Name; if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000; _MemSize = lngSize; } ~MappedMemoryFileHelper() { MMFClose(); } /// /// Initialize shared memory /// /// Shared memory name /// Shared memory size /// public int MMFOpen() { if (_sMmfName.Length > 0) { // Create a memory share (INVALID_HANDLE_VALUE) _hSharedMemoryFile = Win32API.CreateFileMapping(Win32Define.INVALID_HANDLE_VALUE, IntPtr.Zero, PageProtection.ReadWrite, 0, (uint)_MemSize, _sMmfName); if (_hSharedMemoryFile == IntPtr.Zero) { _bMmfAlreadyExist = false; _bInit = false; return 2; // Failed to create shared body } else { if (Marshal.GetLastWin32Error() == Win32Define.ERROR_ALREADY_EXISTS) // Already Created { _bMmfAlreadyExist = true; } else { _bMmfAlreadyExist = false; // New creation } } //--------------------------------------- // Create memory maps //FileMapAccessType.AllAccess gives Memory protected ERROR !!! _pwData = Win32API.MapViewOfFile(_hSharedMemoryFile, FileMapAccessType.Write, 0, 0, (uint)_MemSize); if (_pwData == IntPtr.Zero) { _bInit = false; Win32API.CloseHandle(_hSharedMemoryFile); return 3; // Failed to create memory map } else { if (_bMmfAlreadyExist == false) { // initialization } } //---------------------------------------- if (_sMutexName.Length > 0) { _Mutex = new Mutex(false, _sMutexName, out _IsMutexCreated); //false to not take the mutex at creation !! if (_Mutex == null) return 4; //Mutex Error } else { return 5; //Mutex Parameter Error } } else { return 1; // Parameter error } _bInit = true; return 0; // Create success } /// /// Writing data /// /// data /// Initial address /// Number /// public int WriteByteArray(byte[] bytData, int lngAddr, int lngSize) { if (lngAddr + lngSize > _MemSize) return 2; // Beyond data area if (_bInit) { try { if (_Mutex != null) _Mutex.WaitOne(); Marshal.Copy(bytData, lngAddr, _pwData, lngSize); if (_Mutex != null) _Mutex.ReleaseMutex(); } catch { return 2; } } else { return 1; // Shared memory not initialized } return 0; // Write a successful } /// /// Turn off shared memory /// public void MMFClose() { if (_bInit) { Win32API.UnmapViewOfFile(_pwData); Win32API.CloseHandle(_hSharedMemoryFile); } } } } ================================================ FILE: DsCore/IPC/MappedMemoryFile_Helper_old.cs ================================================ using System; using DsCore.Win32; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Threading; namespace DsCore.IPC { public class MemoryMappedFileHelper_Old { private IntPtr _hSharedMemoryFile = IntPtr.Zero; private IntPtr _pwData = IntPtr.Zero; private bool _bAlreadyExist = false; private bool _bInit = false; private long _MemSize = 0; private String _sName = String.Empty; private Mutex _Mutex; private bool _IsMutexCreated; private MMF_DataStruct[] _mmfData; //Total length of data exported : //For each Player : 5 * 4 Bits fields (UInt32 / Int32) //4 Players = 80 Bytes private const int DATA_LENGTH_TOTAL = 80; #region Accessors public bool IsNewCreation { get { return !_bAlreadyExist; } } public bool IsAlreadyExisting { get { return _bAlreadyExist; } } public bool IsAvailable { get { return _bInit; } } public String MemoryFileName { get { return _sName; } } #endregion public MemoryMappedFileHelper_Old(String strMutexName) { _mmfData = new MMF_DataStruct[4]; for (int i = 0; i < 4; i++) { _mmfData[i] = new MMF_DataStruct(); } if (strMutexName.Length > 0) _Mutex = new Mutex(false, strMutexName, out _IsMutexCreated); //false to not take the mutex at creation !! } ~MemoryMappedFileHelper_Old() { MMFClose(); } /// /// Initialize shared memory /// /// Shared memory name /// Shared memory size /// public int MMFInit(string strName, long lngSize) { _sName = strName; if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000; _MemSize = lngSize; if (strName.Length > 0) { // Create a memory share (INVALID_HANDLE_VALUE) _hSharedMemoryFile = Win32API.CreateFileMapping(Win32Define.INVALID_HANDLE_VALUE, IntPtr.Zero, PageProtection.ReadWrite, 0, (uint)lngSize, strName); if (_hSharedMemoryFile == IntPtr.Zero) { _bAlreadyExist = false; _bInit = false; Logger.WriteLog("CreateFileMapping() error for mapped file : '" + strName + "'"); return 2; // Failed to create shared body } else { if (Marshal.GetLastWin32Error() == Win32Define.ERROR_ALREADY_EXISTS) // Already Created { _bAlreadyExist = true; Logger.WriteLog("Successfully opened already existing mapped file '" + strName + "'"); } else { _bAlreadyExist = false; // New creation Logger.WriteLog("Successfully created new mapped file '" + strName + "'"); } } //--------------------------------------- // Create memory maps //FileMapAccessType.AllAccess gives Memory protected ERROR !!! _pwData = Win32API.MapViewOfFile(_hSharedMemoryFile, FileMapAccessType.Write, 0, 0, (uint)lngSize); if (_pwData == IntPtr.Zero) { _bInit = false; Win32API.CloseHandle(_hSharedMemoryFile); Logger.WriteLog("Failed to create memory map '" + strName + "'"); return 3; // Failed to create memory map } else { _bInit = true; if (_bAlreadyExist == false) { // initialization } } //---------------------------------------- } else { return 1; // Parameter error } Logger.WriteLog("Memory map '" + strName + "' succesfully initialized"); return 0; // Create success } /// /// Turn off shared memory /// public void MMFClose() { if (_bInit) { Win32API.UnmapViewOfFile(_pwData); Win32API.CloseHandle(_hSharedMemoryFile); Logger.WriteLog("Memory map '" + _sName + "' succesfully closed"); } } /// /// Write Full 4-Players data to mapped memory file /// /// public int WriteData() { int wIndex = 0; byte[] bData = new byte[DATA_LENGTH_TOTAL]; for (int i = 0; i < _mmfData.Length; i++) { Array.Copy(BitConverter.GetBytes(_mmfData[i].RawValue_X), 0, bData, wIndex, 4); wIndex += 4; Array.Copy(BitConverter.GetBytes(_mmfData[i].RawValue_Y), 0, bData, wIndex, 4); wIndex += 4; Array.Copy(BitConverter.GetBytes(_mmfData[i].ComputedValue_X), 0, bData, wIndex, 4); wIndex += 4; Array.Copy(BitConverter.GetBytes(_mmfData[i].ComputedValue_Y), 0, bData, wIndex, 4); wIndex += 4; Array.Copy(BitConverter.GetBytes(_mmfData[i].ComputedButtonEvent), 0, bData, wIndex, 4); wIndex += 4; } return WriteByteArray(bData, 0, DATA_LENGTH_TOTAL); } /// /// Writing data /// /// data /// Initial address /// Number /// private int WriteByteArray(byte[] bytData, int lngAddr, int lngSize) { if (lngAddr + lngSize > _MemSize) return 2; // Beyond data area if (_bInit) { try { if (_Mutex != null) _Mutex.WaitOne(); Marshal.Copy(bytData, lngAddr, _pwData, lngSize); } catch (Exception ex) { Logger.WriteLog("MappedMemoryFile_Helper.WriteByteArray() error : " + ex.Message.ToString()); } finally { if (_Mutex != null) _Mutex.ReleaseMutex(); } } else { return 1; // Shared memory not initialized } return 0; // Write a successful } /// /// Reading data /// /// data /// Initial address /// Number /// private int ReadByteArray(ref byte[] bytData, int lngAddr, int lngSize) { if (lngAddr + lngSize > _MemSize) return 2; // Beyond data area if (_bInit) { Marshal.Copy(_pwData, bytData, lngAddr, lngSize); } else { return 1; // Shared memory not initialized } return 0; // Read successfully } public void UpdateRawPlayerData(int PlayerID, UInt32 NewX, UInt32 NewY) { if (_mmfData != null) _mmfData[PlayerID -1].UpdateRawValues(NewX, NewY); } public void UpdateComputedPlayerData(int PlayerID, Int32 NewX, Int32 NewY, bool[] NewButtons) { if (_mmfData != null) _mmfData[PlayerID -1].UpdateComputedValues(NewX, NewY, NewButtons); } } } ================================================ FILE: DsCore/Logger.cs ================================================ using System; using System.IO; using System.Diagnostics; namespace DsCore { public static class Logger { private static string LogFilename = "debug.txt"; public static bool IsEnabled { get; set; } public static bool IsTraceEnabled { get; set; } public static void InitLogFileName() { LogFilename = "Debug_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".txt"; } /// /// Writing to Log only if verbose arg given in cmdline /// public static void WriteLog(String Data) { if (IsTraceEnabled) { Trace.WriteLine(Data); } else if (IsEnabled) { try { using (StreamWriter sr = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + @"\" + LogFilename, true)) { sr.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffffff") + " : " + Data); sr.Close(); } } catch { } } } } } ================================================ FILE: DsCore/MameOutput/AsyncGameOutput.cs ================================================ using System; namespace DsCore.MameOutput { public class AsyncGameOutput : GameOutput { private Mmt _AsyncResetTimer; private UInt32 _AsyncResetTimerOnInterval = 50; private UInt32 _AsyncResetTimerOffInterval = 50; private bool _IsTimerRunning = false; private int _OffValue = 0; public override int OutputValue { get { { return _OutputValue; } } set { if (!_IsTimerRunning && value != _OffValue) { if (value != _OffValue) { _IsTimerRunning = true; _OutputValue = value; _AsyncResetTimer.Period = _AsyncResetTimerOnInterval; _AsyncResetTimer.Start(); #if DEBUG_ASYNCGAMEOUTPUTS Logger.WriteLog("[Tick=" + Environment.TickCount + "] Starting " + this._Name + " OutputTimer with value = " + value); #endif } } } } public AsyncGameOutput(OutputId Id, int AsyncResetTimerOnInterval, int AsyncResetTimerOffInterval, int RestValue) : base(Id) { _OffValue = RestValue; _AsyncResetTimerOnInterval = (UInt32)AsyncResetTimerOnInterval; _AsyncResetTimerOffInterval = (UInt32)AsyncResetTimerOffInterval; _AsyncResetTimer = new Mmt(); _AsyncResetTimer.Period = (UInt32)AsyncResetTimerOnInterval; _AsyncResetTimer.Stop(); _AsyncResetTimer.Tick += AsyncResetTimer_Elapsed; } private void AsyncResetTimer_Elapsed(Object sender, EventArgs e) { if (_OutputValue != _OffValue) { _AsyncResetTimer.Period = _AsyncResetTimerOffInterval; _OutputValue = _OffValue; #if DEBUG_ASYNCGAMEOUTPUTS Logger.WriteLog("[Tick=" + Environment.TickCount + "] Forcing " + this._Name + " OutputTimer with value = " + _OffValue); #endif //MameOutputHelper.Instance().SendValue(this._Id, this._OutputValue); } else { #if DEBUG_ASYNCGAMEOUTPUTS Logger.WriteLog("[Tick=" + Environment.TickCount + "] Stopping " + this._Name + " OutputTimer with value = " + _OffValue); #endif _AsyncResetTimer.Stop(); _IsTimerRunning = false; } } } } ================================================ FILE: DsCore/MameOutput/BlinkGameOutput.cs ================================================ using System; using System.Timers; namespace DsCore.MameOutput { public class BlinkGameOutput : GameOutput { private Timer _BlinkTimer; private bool _IsBlinking = false; public override int OutputValue { get { { return _OutputValue; } } set { if (value == 0) { _BlinkTimer.Stop(); _OutputValue = value; _IsBlinking = false; } else if (value == 1) { _BlinkTimer.Stop(); _OutputValue = value; _IsBlinking = false; } else { if (!_IsBlinking) { _OutputValue = 1; _BlinkTimer.Start(); _IsBlinking = true; } } } } public BlinkGameOutput(OutputId Id, int BlinkTimerInterval): base(Id) { _BlinkTimer = new Timer(); _BlinkTimer.Interval = BlinkTimerInterval; _BlinkTimer.Enabled = true; _BlinkTimer.Elapsed += new ElapsedEventHandler(BlinkTimer_Elapsed); } private void BlinkTimer_Elapsed(Object sender, EventArgs e) { if (_OutputValue == 1) _OutputValue = 0; else _OutputValue = 1; } } } ================================================ FILE: DsCore/MameOutput/GameOutput.cs ================================================ using System; namespace DsCore.MameOutput { /// /// A specific Output from a game to send to MameHooker /// public class GameOutput { protected String _Name; protected UInt32 _Id; protected int _OutputValue; public String Name { get { return _Name; } } public UInt32 Id { get { return _Id; } } public virtual int OutputValue { get { return _OutputValue; } set { _OutputValue = value;} } public GameOutput(OutputId Id) { _Name = Id.ToString(); _Id = (UInt32)Id; _OutputValue = 0; } public GameOutput(GameOutput Output) { _Name = Output.Name; _Id = Output.Id; _OutputValue = Output.OutputValue; } } } ================================================ FILE: DsCore/MameOutput/MMT.cs ================================================ using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; namespace DsCore.MameOutput { /// /// Defines constants for the multimedia Timer's event types. /// public enum TimerMode { /// /// Timer event occurs once. /// OneShot, /// /// Timer event occurs periodically. /// Periodic }; /// /// Represents information about the multimedia Timer's capabilities. /// [StructLayout(LayoutKind.Sequential)] public struct TimeCaps { /// /// Minimum supported period in milliseconds. /// public UInt32 wPeriodMin; /// /// Maximum supported period in milliseconds. /// public UInt32 wPeriodMax; } /// /// Represents the Windows multimedia timer. /// public sealed class Mmt { #region Timer Members #region Delegates // Represents the method that is called by Windows when a timer event occurs. private delegate void TimeProc(int id, int msg, int user, int param1, int param2); // Represents methods that raise events. private delegate void EventRaiser(EventArgs e); #endregion #region Win32 Multimedia Timer Functions // Gets timer capabilities. [DllImport("winmm.dll")] private static extern UInt32 timeGetDevCaps(ref TimeCaps ptc, int cbtc); // Creates and starts the timer. [DllImport("winmm.dll")] private static extern UInt32 timeSetEvent(UInt32 uDelay, UInt32 uResolution, TimeProc lpTimeProc, UIntPtr dwUser, UInt32 fuEvent); // Stops and destroys the timer. [DllImport("winmm.dll")] private static extern UInt32 timeKillEvent(UInt32 uTimerID); // Indicates that the operation was successful. private const int TIMERR_NOERROR = 0; #endregion #region Fields // Timer identifier. private UInt32 _TimerID; // Timer mode. private volatile TimerMode _Mode; // Period between timer events in milliseconds. private volatile UInt32 _Period; // Timer resolution in milliseconds. private volatile UInt32 _Resolution; // Called by Windows when a timer periodic event occurs. private TimeProc timeProcPeriodic; // Called by Windows when a timer one shot event occurs. private TimeProc timeProcOneShot; // Represents the method that raises the Tick event. private EventRaiser tickRaiser; // Indicates whether or not the timer is running. private bool _IsRunning = false; // Multimedia timer capabilities. private static TimeCaps caps; #endregion #region Events /// /// Occurs when the Timer has started; /// public event EventHandler Started; /// /// Occurs when the Timer has stopped; /// public event EventHandler Stopped; /// /// Occurs when the time period has elapsed. /// public event EventHandler Tick; #endregion #region Construction /// /// Initialize class. /// static Mmt() { // Get multimedia timer capabilities. timeGetDevCaps(ref caps, Marshal.SizeOf(caps)); } /// /// Initializes a new instance of the Timer class. /// public Mmt() { Initialize(); } ~Mmt() { if (IsRunning) { // Stop and destroy timer. timeKillEvent(_TimerID); } } // Initialize timer with default values. private void Initialize() { this._Mode = TimerMode.Periodic; this._Period = Capabilities.wPeriodMin; this._Resolution = 1; _IsRunning = false; timeProcPeriodic = new TimeProc(TimerPeriodicEventCallback); timeProcOneShot = new TimeProc(TimerOneShotEventCallback); tickRaiser = new EventRaiser(OnTick); } #endregion #region Methods public void Start() { if (IsRunning) return; if (Mode == TimerMode.Periodic) _TimerID = timeSetEvent(Period, Resolution, timeProcPeriodic, UIntPtr.Zero, (UInt32)Mode); else _TimerID = timeSetEvent(Period, Resolution, timeProcOneShot, UIntPtr.Zero, (UInt32)Mode); if (_TimerID != 0) _IsRunning = true; else throw new TimerStartException("Unable to start multimedia Timer."); } public void Stop() { if (!_IsRunning) return; UInt32 result = timeKillEvent(_TimerID); Debug.Assert(result == TIMERR_NOERROR); _IsRunning = false; OnStopped(EventArgs.Empty); } #region Callbacks // Callback method called by the Win32 multimedia timer when a timer // periodic event occurs. private void TimerPeriodicEventCallback(int id, int msg, int user, int param1, int param2) { OnTick(EventArgs.Empty); } // Callback method called by the Win32 multimedia timer when a timer // one shot event occurs. private void TimerOneShotEventCallback(int id, int msg, int user, int param1, int param2) { OnTick(EventArgs.Empty); Stop(); } #endregion #region Event Raiser Methods // Raises the Started event. private void OnStarted(EventArgs e) { EventHandler handler = Started; if (handler != null) { handler(this, e); } } // Raises the Stopped event. private void OnStopped(EventArgs e) { EventHandler handler = Stopped; if (handler != null) { handler(this, e); } } // Raises the Tick event. private void OnTick(EventArgs e) { EventHandler handler = Tick; if (handler != null) { handler(this, e); } } #endregion #endregion /// /// Gets or sets the time between Tick events. /// public UInt32 Period { get { return _Period; } set { if (value < Capabilities.wPeriodMin || value > Capabilities.wPeriodMax) { throw new ArgumentOutOfRangeException("Period", value, "Multimedia Timer period out of range."); } _Period = value; if (IsRunning) { Stop(); Start(); } } } /// /// Gets or sets the timer resolution. /// /// /// The resolution is in milliseconds. The resolution increases /// with smaller values; a resolution of 0 indicates periodic events /// should occur with the greatest possible accuracy. To reduce system /// overhead, however, you should use the maximum value appropriate /// for your application. /// public UInt32 Resolution { get { return _Resolution; } set { if (value < 0) { throw new ArgumentOutOfRangeException("Resolution", value, "Multimedia timer resolution out of range."); } _Resolution = value; if (IsRunning) { Stop(); Start(); } } } /// /// Gets the timer mode. /// public TimerMode Mode { get { return _Mode; } set { _Mode = value; if (IsRunning) { Stop(); Start(); } } } /// /// Gets a value indicating whether the Timer is running. /// public bool IsRunning { get { return _IsRunning; } } /// /// Gets the timer capabilities. /// public static TimeCaps Capabilities { get { return caps; } } #endregion } /// /// The exception that is thrown when a timer fails to start. /// public class TimerStartException : ApplicationException { /// /// Initializes a new instance of the TimerStartException class. /// /// /// The error message that explains the reason for the exception. /// public TimerStartException(string message) : base(message) { } } } ================================================ FILE: DsCore/MameOutput/Network/Net_OutputClient.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; namespace DsCore.MameOutput { public class Net_OutputClient { private TcpClient _TcpClient; private NetworkStream _Stream; public TcpClient Handler { get { return _TcpClient; } } public NetworkStream Stream { get { return _Stream; } } //This flag will tell if a client needs to be send the full list of outputs (usually at connection) //To ensure list of outputs don't arrive before MameStart private bool _ReadyToGetOutputs = false; public bool ReadyToGetOutputs { get { return _ReadyToGetOutputs; } set { _ReadyToGetOutputs = value; } } //First outputs are client specific : need to send all of them at first when a client is connected //Then it will be filtered by change private bool _FirstOutputs = true; public bool FirstOutputs { get { return _FirstOutputs; } set { _FirstOutputs = value; } } public Net_OutputClient(TcpClient Handler) { _TcpClient = Handler; _Stream = _TcpClient.GetStream(); /*----------- GetStream() will return the same instance if it is already existing, so it can be reused anywhere in the code without memory leak --------------*/ } } } ================================================ FILE: DsCore/MameOutput/Network/Net_OutputHelper.cs ================================================ using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace DsCore.MameOutput { public class Net_OutputHelper { private const string MAME_START_NAME = "mame_start"; private const string MAME_STOP_NAME = "mame_stop"; private const string MAME_START_EMPTY = "___empty"; private Thread _TcpListenerThread; private TcpListener _TcpListener; private const int TCP_PORT = 8000; private List _ConnectedTcpClients; private List _OutputsBefore; private string _RomName = string.Empty; private bool _IsGameHooked = false; private bool _ReadyToSendValues = false; public bool ReadyToSendValues { get { return _ReadyToSendValues; } } public Net_OutputHelper(string RomName) { _RomName = RomName; _ConnectedTcpClients = new List(); try { // Create listener on localhost port 8000. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), TCP_PORT); } catch (Exception Ex) { Logger.WriteLog("Net_OutputHelper() => Error creating the Listener : " + Ex.Message.ToString()); } } public void Start() { _TcpListenerThread = new Thread(new ThreadStart(ListenerThread)); _TcpListenerThread.Start(); } public void Stop() { try { foreach (Net_OutputClient Client in _ConnectedTcpClients) { SendStopMessage(Client); if (Client.Handler.Connected && Client.Stream != null) { Client.Stream.Close(); Client.Handler.Close(); } } } catch (Exception Ex) { Logger.WriteLog("Net_OutputHelper.Stop() => Exception: " + Ex); } finally { _TcpListener.Stop(); } } /// /// Separate thread with infinite loop to get new TcpClients and store them for later use /// private void ListenerThread() { try { _TcpListener.Start(); Logger.WriteLog("Net_OutputHelper.TcpClientThreadLoop() => TCP Server is listening on Port " + TCP_PORT); while (true) //Thread is blocked while waiting for a connection, can't be stopped by a flag { Logger.WriteLog("Net_OutputHelper.TcpClientThreadLoop() => TCP Server is waiting for a client " + TCP_PORT); TcpClient client = _TcpListener.AcceptTcpClient(); Net_OutputClient NetClient = new Net_OutputClient(client); Logger.WriteLog("Net_OutputHelper.TcpClientThreadLoop() => New client connected : IP=" + client.Client.RemoteEndPoint.ToString()); _ConnectedTcpClients.Add(NetClient); //Sending the MameStart message with corresponding information /************* CLosing the NetworkStrem close the TcpClient !!!!! *************/ string mStart = string.Empty; if (!_IsGameHooked) { SendValue(NetClient, MAME_START_NAME, MAME_START_EMPTY); } else { SendStartMessage(NetClient); } byte[] data = Encoding.ASCII.GetBytes(mStart); NetClient.Stream.Write(data, 0, data.Length); } } catch (Exception Ex) { Logger.WriteLog("Net_OutputHelper.TcpClientThreadLoop() => ListenerThreadError " + Ex.Message.ToString()); } } /// /// As opposed to WindowMessage outputs, where MameHooker has to be run _before_ DemulsHooter, /// Network cliant can be connected at everytime. So it may miss the 'GameHooked' event occuring before it's connection. /// We need that info to send a mame start '___empty' or '[GameRom]' accorting to it when connection is initiated /// /// public void SetGameHookedState(bool HookedState) { _IsGameHooked = HookedState; } /// /// Send updated values to all registered clients /// A small "filtering" method was added, to send output message to MameHooker only on changed state of a value /// /// List of values to send public void BroadcastValues(List Outputs) { if (_OutputsBefore == null) { //Cloning the output list without references to the GameOutput object _OutputsBefore = Outputs.ConvertAll(x => new GameOutput(x)); } foreach (Net_OutputClient Client in _ConnectedTcpClients) { if (Client.ReadyToGetOutputs) { if (Client.FirstOutputs) { for (int i = 0; i < Outputs.Count; i++) { SendValue(Client, Outputs[i].Name, Outputs[i].OutputValue.ToString()); } Client.FirstOutputs = false; } else { for (int i = 0; i < Outputs.Count; i++) { if (Outputs[i].OutputValue != _OutputsBefore[i].OutputValue) { SendValue(Client, Outputs[i].Name, Outputs[i].OutputValue.ToString()); } } } } } _OutputsBefore = Outputs.ConvertAll(x => new GameOutput(x)); } /// /// Send a 'mamestart = [RomName'] to all connected clients /// /// public void BroadcatStartMessage() { foreach (Net_OutputClient Client in _ConnectedTcpClients) { SendStartMessage(Client); } } /// /// Send a 'mamestart = ['RomName'] to a connected client /// /// public void SendStartMessage(Net_OutputClient Client) { SendValue(Client, MAME_STOP_NAME, "1"); SendValue(Client, MAME_START_NAME, _RomName); Client.ReadyToGetOutputs = true; } /// /// Send a 'mamestop = 1" message to a connected client /// /// public void SendStopMessage(Net_OutputClient Client) { SendValue(Client, MAME_STOP_NAME, "1"); SendValue(Client, MAME_START_NAME, MAME_START_EMPTY); Client.ReadyToGetOutputs = false; } /// /// Send a specific updated value to all registered clients /// /// /// public void SendValue(Net_OutputClient Client, String Name, string Value) { try { if (Client.Stream != null && Client.Stream.CanWrite) { byte[] Payload = Encoding.ASCII.GetBytes(Name + " = " + Value.ToString() + "\r"); Client.Stream.Write(Payload, 0, Payload.Length); } } catch (Exception Ex) { Logger.WriteLog("Net_OutputHelper.SendValue() => Error sending to " + Client.Handler.Client.RemoteEndPoint.ToString() + " : " + Ex.Message.ToString()); } } } } ================================================ FILE: DsCore/MameOutput/OutputId.cs ================================================  namespace DsCore.MameOutput { public enum OutputId : uint { P1_LmpStart = 1, P2_LmpStart, P3_LmpStart, P4_LmpStart, P1_LmpPanel, P2_LmpPanel, LmpPanel2, Lmp2D3D, //LGI 3D button 2D/3D LmpRoom, //Let's Go Jungle LmpCoin, //Let's Go Jungle LmpPanel, LmpBillboard, LmpSpeaker, //Time Crisis 5 LmpDinoHead, //\ LmpLogo, // LmpDinoEyes, // LmpRoof, // LmpMarquee, // LmpDash, // LmpFoliage, //--Jurassic Park (Rawh Trill) LmpSeat_R, // LmpSeat_G, // LmpSeat_B, // LmpBenchLogo, // LmpSeatBase, // LmpEstop, // LmpCompressor, /// LmpRoof_R, //\ LmpRoof_G, // LmpRoof_B, // LmpLgMarquee, // LmpSqMarquee, // LmpWalker_R, // Walking Dead (Raw Thrill) LmpWalker_G, // LmpWalker_B, // LmpWalkerEyes, // LmpWalkerCeiling, /// LmpPosts, Lmp1, //\ Lmp2, // Lmp3, // Lmp4, // 2 Spicy, HOD EX Lmp5, // Lmp6, /// LmpLeft, LmpSide_R, //\ LmpSide_G, // Elevator Action Death Parade LmpSide_B, /// LmpRight, LmpRear_R, LmpRear_G, LmpRear_B, LmpLBtn, //\ LmpMBtn, // Music Gun Gun 2 Buttons < o > LmpRBtn, /// LmpUpBtn, //\ LmpDownBtn, // Elevator Action Death Parade LmpCloseBtn, /// LmpCannonBtn, //\ LmpCannon_R, // Block King Ball Shooter LmpCannon_G, // LmpCannon_B, /// P1_LmpGun_R, //\ P1_LmpGun_G, // P1_LmpGun_B, // P2_LmpGun_R, // P2_LmpGun_G, // P2_LmpGun_B, // Luigi Mansion Arcade P1_LmpWindow_R, // P1_LmpWindow_G, // P1_LmpWindow_B, // P2_LmpWindow_R, // P2_LmpWindow_G, // P2_LmpWindow_B, /// P1_LmpGun, P2_LmpGun, P3_LmpGun, P4_LmpGun, P1_LmpHolder, P2_LmpHolder, P3_LmpHolder, P4_LmpHolder, P1_LmpBreak, //\ P2_LmpBreak, /// Transformers P1_Lmp_R, P1_Lmp_G, P1_Lmp_B, P2_Lmp_R, P2_Lmp_G, P2_Lmp_B, P1_LmpCard_R, P1_LmpCard_G, P2_LmpCard_R, P2_LmpCard_G, P1_LedAmmo1, //\ P1_LedAmmo2, // Aliens Extermination, Ammo digits P2_LedAmmo1, // P2_LedAmmo2, /// P1_LmpGunGrenadeBtn, //\ P2_LmpGunGrenadeBtn, // P1_LmpGunMolding, // P2_LmpGunMolding, // Aliens Armageddon LmpMarqueeBacklight, // LmpMarqueeUplight, // LmpUpperCtrlPanel, // LmpLowerCtrlPanel, /// P1_GunRecoil, P2_GunRecoil, P3_GunRecoil, P4_GunRecoil, P1_GunMotor, P2_GunMotor, P3_GunMotor, P4_GunMotor, P1_AirFront, P2_AirFront, P1_AirRear, P2_AirRear, Blower_Level, P1_Fan, P2_Fan, VibrationSeat, DoorB, DoorA, ElevatorLedsStatus, LmpStop, LmpAction, LmpReset, LmpSpot, LmpFloor, LmpError, LmpSafety, P1_LedKills1, P1_LedKills2, P2_LedKills1, P2_LedKills2, P1_Whip_R, P1_Whip_G, P1_Whip_B, P2_Whip_R, P2_Whip_G, P2_Whip_B, P1_LmpHead, P2_LmpHead, P1_LmpFoot, P2_LmpFoot, P1_LmpFront, P2_LmpFront, P3_LmpFront, P4_LmpFront, LmpLever, Lmp_Downlight, Lmp_DirectHit, Lmp_PoliceBar, Lmp_GreenTestLight, Lmp_RedLight, Lmp_WhiteStrobe, P1_LmpGunTip, P1_LmpGunBack, P2_LmpGunTip, P2_LmpGunBack, BallAgitator_State, BallAgitator_Direction, P1_BallShooter, P2_BallShooter, Lmp_Horn_R, Lmp_Horn_G, Lmp_Horn_B, Lmp_LeftBulletMark, Lmp_RightBulletMark, Lmp_W, Lmp_A, Lmp_N, Lmp_T, Lmp_E, Lmp_D, Lmp_LeftReload, Lmp_RightReload, Lmp_Payout, TicketDrive, TicketMeter, P1_LmpBonusWeapon, P2_LmpBonusWeapon, P3_LmpBonusWeapon, P4_LmpBonusWeapon, BonusWeaponLamp, SeatVibrationLamp, WaterFallLamp, WaterLevelLamp, P1_Lmp_SeatPuck, P1_Lmp_SeatMarquee, P1_Lmp_SeatSpeaker_R, P1_Lmp_SeatSpeaker_O, P1_Lmp_SeatSpeaker_B, P2_Lmp_SeatPuck, P2_Lmp_SeatMarquee, P2_Lmp_SeatSpeaker_R, P2_Lmp_SeatSpeaker_O, P2_Lmp_SeatSpeaker_B, Lmp_TMolding_R, Lmp_TMolding_G, Lmp_TMolding_B, Lmp_SeatDownLight, P1_WaterFire, P2_WaterFire, P3_WaterFire, P4_WaterFire, WaterPump, P1_BigGun, P2_BigGun, P3_BigGun, P4_BigGun, P1_TicketFeeder, P2_TicketFeeder, P3_TicketFeeder, P4_TicketFeeder, P1_ChairShake, P2_ChairShake, P3_ChairShake, P4_ChairShake, SmokeSwitch, P1_Shaker, //\ P2_Shaker, // Mech Tornado P3_Shaker, // P4_Shaker, /// BonusDisplay, //Robin Hood CoinBlocker, //\ MotorUnit_1, // Gashaaaan 2 MotorUnit_2, /// P1_WaterRotate, //\ P2_WaterRotate, /// Hero Fire arcade P1_GunType, P2_GunType, Neuralizer, //MIB Lmp_Disinfection, Lmp_P1_P2_OutOfTickets, Lmp_P3_P4_OutOfTickets, Lmp_Marquee_1, Lmp_Marquee_2, P1_InnerWater, P2_InnerWater, P3_InnerWater, P4_InnerWater, P1_OuterWater, P2_OuterWater, P3_OuterWater, P4_OuterWater, Credits = 1000, P1_CtmLmpStart, P2_CtmLmpStart, P3_CtmLmpStart, P4_CtmLmpStart, P1_Ammo, P2_Ammo, P3_Ammo, P4_Ammo, P1_Clip, P2_Clip, P3_Clip, P4_Clip, P1_CtmRecoil, P2_CtmRecoil, P3_CtmRecoil, P4_CtmRecoil, P1_HealthBar, P2_HealthBar, P3_HealthBar, P4_HealthBar, P1_Life, P2_Life, P3_Life, P4_Life, P1_Damaged, P2_Damaged, P3_Damaged, P4_Damaged, P1_Credits, P2_Credits, P3_Credits, P4_Credits, MameOrientation = 12345, MamePause } } ================================================ FILE: DsCore/MameOutput/SyncBlinkingGameOutput.cs ================================================ using System; using System.Timers; namespace DsCore.MameOutput { /// /// This kind of output is used to get synchronised blinking outputs between players /// Examples: Start buttons in pc game not supporting genuine outputs system /// The synchronised-to-be outputs are created at the same time, and immediately blinking. /// At the same time, we can set a fixed value. /// Then, according to the what we want to read, we ask for the fixed value or the blinking one /// public class SyncBlinkingGameOutput : GameOutput { private Timer _SyncBlinkingTimer; private int _BlinkingValue = 0; private bool _EnableBlinking = true; public override int OutputValue { get { if (_EnableBlinking) return _BlinkingValue; else return _OutputValue; } set { if (value == 0) { _EnableBlinking = false; _OutputValue = value; } else if (value == 1) { _EnableBlinking = false; _OutputValue = value; } else if (value == -1) { _EnableBlinking = true; } } } public SyncBlinkingGameOutput(OutputId Id, int BlinkingTimerInterval): base(Id) { _SyncBlinkingTimer = new Timer(); _SyncBlinkingTimer.Interval = BlinkingTimerInterval; _SyncBlinkingTimer.Enabled = true; _SyncBlinkingTimer.Elapsed += new ElapsedEventHandler(BlinkingTimer_Elapsed); _SyncBlinkingTimer.Start(); } private void BlinkingTimer_Elapsed(Object sender, EventArgs e) { if (_BlinkingValue == 0) _BlinkingValue = 1; else if (_BlinkingValue == 1) _BlinkingValue = 0; } } } ================================================ FILE: DsCore/MameOutput/WindowMessage/Wm_OutputClient.cs ================================================ using System; namespace DsCore.MameOutput { /// /// Structure containing information about a specific client registered to get Mame output /// public struct Wm_OutputClient { /// /// Client ID /// public UInt32 Id; /// /// Client hWnd /// public IntPtr hWnd; }; } ================================================ FILE: DsCore/MameOutput/WindowMessage/Wm_OutputDataStruct.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.MameOutput { /// /// Structure used to transfer String ID data to MameHooker /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public struct Wm_OutputDataStruct { /// /// Requested ID /// public UInt32 Id; // The string is included inline in the data. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)] public string lpStr; } } ================================================ FILE: DsCore/MameOutput/WindowMessage/Wm_OutputHelper.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using DsCore.Win32; using System.Timers; namespace DsCore.MameOutput { public class Wm_OutputHelper { private static Wm_OutputHelper _Instance = null; private IntPtr _hWnd = IntPtr.Zero; private List _RegisteredClients; private IntPtr HWND_BROADCAST = (IntPtr)0xFFFF; private const uint COPYDATA_MESSAGE_ID_STRING = 1; private const String MAME_START_STRING = "MAMEOutputStart"; private const String MAME_STOP_STRING = "MAMEOutputStop"; private const String MAME_UPDATE_STRING = "MAMEOutputUpdateState"; private const String MAME_REGISTER_STRING = "MAMEOutputRegister"; private const String MAME_UNREGISTER_STRING = "MAMEOutputUnregister"; private const String MAME_GETID_STRING = "MAMEOutputGetIDString"; private uint _Mame_OnStartMsg = 0; private uint _Mame_OnStopMsg = 0; private uint _Mame_UpdateStateMsg = 0; private uint _Mame_RegisterClientMsg = 0; private uint _Mame_UnregisterClientMsg = 0; private uint _Mame_GetIdStringMsg = 0; #region Accessors public uint MameOutput_RegisterClient { get { return _Mame_RegisterClientMsg; } } public uint MameOutput_UnregisterClient { get { return _Mame_UnregisterClientMsg; } } public uint MameOutput_GetIdString { get { return _Mame_GetIdStringMsg; } } #endregion private List _OutputsBefore; private bool _FirstOutputs = true; //After a start session, MAME is asking for the name (ID == 0) private bool _RomNameSent = false; public bool RomNameSent { get { return _RomNameSent; } set { _RomNameSent = value; } } public Wm_OutputHelper(IntPtr MainWindowHandle) { if (_Instance == null) { _hWnd = MainWindowHandle; _RegisteredClients = new List(); _Mame_OnStartMsg = RegisterMameOutputMessage(MAME_START_STRING); _Mame_OnStopMsg = RegisterMameOutputMessage(MAME_STOP_STRING); _Mame_UpdateStateMsg = RegisterMameOutputMessage(MAME_UPDATE_STRING); _Mame_RegisterClientMsg = RegisterMameOutputMessage(MAME_REGISTER_STRING); _Mame_UnregisterClientMsg = RegisterMameOutputMessage(MAME_UNREGISTER_STRING); _Mame_GetIdStringMsg = RegisterMameOutputMessage(MAME_GETID_STRING); _Instance = this; } } public static Wm_OutputHelper Instance() { return _Instance; } /// /// Broadcast a "MAMEOutput_Start" message to inform all potentials clients /// public void Start() { //_FirstOutputs = true; _RomNameSent = false; Win32API.PostMessage(HWND_BROADCAST, _Mame_OnStartMsg, _hWnd, IntPtr.Zero); } /// /// Broadcast a "MAMEOutput_Stop" message to inform all potentials clients /// public void Stop() { Win32API.PostMessage(HWND_BROADCAST, _Mame_OnStopMsg, _hWnd, IntPtr.Zero); } /// /// Add (or update if already exixting) a client to the OutputClient list /// /// Client Handle /// Client Id public int RegisterClient(IntPtr hWnd, UInt32 Id) { for (int i = 0; i < _RegisteredClients.Count; i++) { if (_RegisteredClients[i].Id == Id) { //C# can't modify directly a field of struct in a List<>, that's why we are forced to use a temporary local variable Wm_OutputClient c = _RegisteredClients[i]; c.hWnd = hWnd; _RegisteredClients[i] = c; Logger.WriteLog("Successfully updated following MameOutput Client : Id=" + Id.ToString() + ", hWnd=0x" + hWnd.ToString("X8")); return 1; } } Wm_OutputClient NewClient = new Wm_OutputClient(); NewClient.hWnd = hWnd; NewClient.Id = Id; _RegisteredClients.Add(NewClient); Logger.WriteLog("Successfully registered following MameOutput Client : Id=" + Id.ToString() + ", hWnd=0x" + hWnd.ToString("X8")); return 0; } /// /// Remove any MameOutput client with the corresponding ID /// /// Client Handle /// Client ID public void UnregisterClient(IntPtr hWnd, UInt32 Id) { for (int i = _RegisteredClients.Count - 1; i >= 0; i--) { if (_RegisteredClients[i].Id == Id) { _RegisteredClients.RemoveAt(i); Logger.WriteLog("Successfully unregistered following MameOutput Client : Id=" + Id.ToString() + ", hWnd=0x" + hWnd.ToString("X8")); } } } /// /// Reply to a Client String/Id request /// MameHooker is sending a Request with Id=0 to get the rom name /// Other Id are OutputId string identification /// /// client hwnd /// Requested Id public void SendIdString(IntPtr hWnd, String lpStr, UInt32 Id) { Logger.WriteLog(System.Reflection.MethodBase.GetCurrentMethod().Name + "(): Id= " + Id + ", lpStr = " + lpStr); Wm_OutputDataStruct data = new Wm_OutputDataStruct(); data.Id = Id; data.lpStr = lpStr; IntPtr buffer = IntPtrAlloc(data); CopyDataStruct copyData = new CopyDataStruct(); copyData.dwData = new IntPtr(COPYDATA_MESSAGE_ID_STRING); copyData.lpData = buffer; copyData.cbData = Marshal.SizeOf(data); IntPtr copyDataBuff = IntPtrAlloc(copyData); Win32API.SendMessage(hWnd, Win32Define.WM_COPYDATA, _hWnd, copyDataBuff); IntPtrFree(ref copyDataBuff); IntPtrFree(ref buffer); } /*public void SendIdStringV2(IntPtr hWnd, String lpStr, UInt32 Id) { byte[] data = System.Text.Encoding.ASCII.GetBytes(lpStr); int length = data.Length; IntPtr ptr = Marshal.AllocHGlobal(IntPtr.Size * 3 + length); Marshal.WriteIntPtr(ptr, 0, IntPtr.Zero); Marshal.WriteIntPtr(ptr, IntPtr.Size, (IntPtr)length); IntPtr dataPtr = new IntPtr(ptr.ToInt64() + IntPtr.Size * 3); Marshal.WriteIntPtr(ptr, IntPtr.Size * 2, dataPtr); Marshal.Copy(data, 0, dataPtr, length); IntPtr result = Win32API.SendMessage(hWnd, Win32Define.WM_COPYDATA, _hWnd, ptr); Marshal.FreeHGlobal(ptr); }*/ /// /// Send updated values to all registered clients /// A small "filtering" method was added, to send output message to MameHooker only on changed state of a value /// /// List of values to send public void SendValues(List Outputs) { if (_FirstOutputs) { _FirstOutputs = false; Logger.WriteLog("Wm_OutputHelper.SendValues() => Sending Initial values"); //For MameHooker compatibility : Sending orientation once Outputs.Insert(0, new GameOutput(OutputId.MameOrientation)); Outputs[0].OutputValue = 0; Outputs.Insert(0, new GameOutput(OutputId.MamePause)); Outputs[0].OutputValue = 0; //Cloning the output list without references to the GameOutput object _OutputsBefore = Outputs.ConvertAll(x => new GameOutput(x)); for (int i = 0; i < Outputs.Count; i++) { //System.Threading.Thread.Sleep(100); SendValue(Outputs[i].Id, Outputs[i].OutputValue); Logger.WriteLog("MAME Output sent : " + Outputs[i].Name + " [Value=" + Outputs[i].OutputValue.ToString() + "]"); //MAME Hooker will not get all values if we do not delay the send a little !!!! } } else { for (int i = 0; i < Outputs.Count; i++) { try { if (_OutputsBefore != null) { //DEBUG only : //Logger.WriteLog(Outputs[i].Name + " : Before=" + _OutputsBefore[i].OutputValue + ", Current=" + Outputs[i].OutputValue); if (Outputs[i].OutputValue != _OutputsBefore[i].OutputValue) { SendValue(Outputs[i].Id, Outputs[i].OutputValue); //DEBUG only : Logger.WriteLog("MAME Output sent : " + Outputs[i].Name + " [Value=" + Outputs[i].OutputValue.ToString() + "]"); _OutputsBefore[i].OutputValue = Outputs[i].OutputValue; } } } //Rare unhandled exception when the Game.Hook() output send is overlapping the Threaded loop while _OutputBefore not yet created ?? catch (Exception Ex) { Logger.WriteLog("Wm_OutputHelper.SendValues() => Error trying to send Output: " + Outputs[i].Name + " [Value=" + Outputs[i].OutputValue.ToString() + "]"); Logger.WriteLog(Ex.Message.ToString()); } } } } /// /// Send a specific updated value to all registered clients /// /// /// public void SendValue(uint Id, int Value) { foreach (Wm_OutputClient c in _RegisteredClients) { //DEBUG ONLY: //Logger.WriteLog("Sending WameOutputValue : ID=" + Id.ToString() + ", Value=" + Value.ToString()); Win32API.PostMessage(c.hWnd, _Mame_UpdateStateMsg, new IntPtr(Id), new IntPtr(Value)); } } /// /// Registering specifid Windows Messages used by MAME (and so MameHooker) for inter-process communication /// private uint RegisterMameOutputMessage(String lpString) { uint id = Win32API.RegisterWindowMessage(lpString); if (id == 0) Logger.WriteLog("Error registering the following MameHooker message : " + lpString); return id; } /// /// Allocate a pointer to an arbitrary structure on the global heap. /// /// /// /// public static IntPtr IntPtrAlloc(T param) { IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param)); Marshal.StructureToPtr(param, retval, false); return retval; } /// /// Free a pointer to an arbitrary structure from the global heap. /// /// Pointer to a previously allocated memory public static void IntPtrFree(ref IntPtr preAllocated) { if (IntPtr.Zero == preAllocated) Logger.WriteLog("MameHookerHelper->SendIdString() error : Impossible to free unallocated Pointer"); Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero; } } } ================================================ FILE: DsCore/Memory/Codecave.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; using DsCore.Win32; namespace DsCore.Memory { public class Codecave { private Process _Process; private IntPtr _ProcessHandle; private IntPtr _ModuleBaseAddress = IntPtr.Zero; private UInt32 _Cave_Address = 0; private UInt32 _CaveOffset = 0; public UInt32 CaveAddress { get { return _Cave_Address; } } public UInt32 CaveOffset { get { return _CaveOffset; } } public Codecave(Process p, IntPtr BaseAddress) { _Process = p; _ModuleBaseAddress = BaseAddress; } /// /// Trying to access the process /// /// True if success, otherwise False public bool Open() { _ProcessHandle = _Process.Handle; if (_ProcessHandle != IntPtr.Zero) return true; else return false; } /// /// Reserves a region of memory within the virtual address space of a specified process. /// The function initializes the memory it allocates to zero. /// /// The size of the region of memory to allocate, in bytes. /// True is success, otherwise False public bool Alloc(UInt32 Size) { //Allocation mémoire _Cave_Address = (UInt32)Win32API.VirtualAllocEx(_ProcessHandle, IntPtr.Zero, Size, MemoryAllocType.MEM_COMMIT, MemoryPageProtect.PAGE_EXECUTE_READWRITE); if (_Cave_Address != 0) return true; else return false; } //call Address public bool Write_call(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 5; List Buffer = new List(); Buffer.Add(0xE8); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //cmp eax,[Value] public bool Write_cmp(int Value) { List Buffer = new List(); Buffer.Add(0x81); Buffer.Add(0xF9); Buffer.AddRange(BitConverter.GetBytes(Value)); return Write_Bytes(Buffer.ToArray()); } //je [Address] public bool Write_je(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 6; List Buffer = new List(); Buffer.Add(0x0F); Buffer.Add(0x84); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //jb [Address] public bool Write_jb(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 6; List Buffer = new List(); Buffer.Add(0x0F); Buffer.Add(0x82); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //jng [Address] public bool Write_jng(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 6; List Buffer = new List(); Buffer.Add(0x0F); Buffer.Add(0x8E); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //jnl [Address] public bool Write_jnl(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 6; List Buffer = new List(); Buffer.Add(0x0F); Buffer.Add(0x8D); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //jng [Address] public bool Write_jg(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 6; List Buffer = new List(); Buffer.Add(0x0F); Buffer.Add(0x8F); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //jng [Address] public bool Write_ja(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 6; List Buffer = new List(); Buffer.Add(0x0F); Buffer.Add(0x87); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //jnl [Address] public bool Write_jl(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 6; List Buffer = new List(); Buffer.Add(0x0F); Buffer.Add(0x8C); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //jmp [Address] public bool Write_jmp(UInt32 Address) { UInt32 JmpAddress = Address - (_Cave_Address + _CaveOffset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(JmpAddress)); return Write_Bytes(Buffer.ToArray()); } //nop public bool Write_nop(int Amount = 1) { List Buffer = new List(); for (int i = 0; i < Amount; i++) { Buffer.Add(0x90); } return Write_Bytes(Buffer.ToArray()); } /// /// Write bytes in memory, read from a string like "00 00 00 00" /// /// String formated series of bytes to write /// True if success, otherwise False public bool Write_StrBytes(String StrBuffer) { String[] sBytes = StrBuffer.Split(' '); List Buffer = new List(); foreach (String hex in sBytes) { Buffer.Add((byte)Convert.ToInt32(hex, 16)); } return Write_Bytes(Buffer.ToArray()); } /// /// Write bytes in memory, read from an array of bytes /// /// Array of bytes to write /// True if success, otherwise False public bool Write_Bytes(Byte[] Buffer) { UInt32 BytesWritten = 0; if (Win32API.WriteProcessMemory(_ProcessHandle, _Cave_Address + _CaveOffset, Buffer, (UInt32)Buffer.Length, ref BytesWritten)) { _CaveOffset += BytesWritten; return true; } else { return false; } } /// /// Create both Jump-To and Jump-Back instruction and inject the codecave in memory /// /// InjectionStruct with needed offset to inject the codecave to the good paert of the game code /// Name that will be used to log the codecave creation public void InjectToOffset(InjectionStruct iStruct, string sCodeCaveName, bool bCreateButNotInject = false) { InjectToOffset(_ModuleBaseAddress, iStruct, sCodeCaveName, bCreateButNotInject); } /// /// Create both Jump-To and Jump-Back instruction and inject the codecave in memory /// /// InjectionStruct with needed offset to inject the codecave to the good paert of the game code /// Name that will be used to log the codecave creation public void InjectToOffset(IntPtr BaseAddress, InjectionStruct iStruct, string sCodeCaveName, bool bCreateButNotInject = false) { //Jump back Write_jmp((UInt32)BaseAddress + iStruct.InjectionReturnOffset); Logger.WriteLog("Adding " + sCodeCaveName + " CodeCave at : 0x" + _Cave_Address.ToString("X8")); //Code injection UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = _Cave_Address - ((UInt32)BaseAddress + iStruct.InjectionOffset) - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); for (int i = 0; i < iStruct.NeededNops; i++) { Buffer.Add(0x90); } //For "crash" debug purpose, sometimes I need to create the codecave to examine it, without making the game jump to it. if (!bCreateButNotInject) Win32API.WriteProcessMemory(_ProcessHandle, (UInt32)BaseAddress + iStruct.InjectionOffset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } /// /// Create both Jump-To and Jump-Back instruction and inject the codecave in memory /// /// InjectionStruct with needed offset to inject the codecave to the good paert of the game code /// Name that will be used to log the codecave creation public void InjectToAddress(InjectionStruct iStruct, string sCodeCaveName, bool bCreateButNotInject = false) { //Jump back Write_jmp(iStruct.InjectionReturnOffset); Logger.WriteLog("Adding " + sCodeCaveName + " CodeCave at : 0x" + _Cave_Address.ToString("X8")); //Code injection UInt32 bytesWritten = 0; UInt32 jumpTo = 0; jumpTo = _Cave_Address - iStruct.InjectionOffset - 5; List Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes(jumpTo)); for (int i = 0; i < iStruct.NeededNops; i++) { Buffer.Add(0x90); } //For "crash" debug purpose, sometimes I need to create the codecave to examine it, without making the game jump to it. if (!bCreateButNotInject) Win32API.WriteProcessMemory(_ProcessHandle, iStruct.InjectionOffset, Buffer.ToArray(), (UInt32)Buffer.Count, ref bytesWritten); } } } ================================================ FILE: DsCore/Memory/InjectionStruct.cs ================================================ using System; using System.Globalization; namespace DsCore.Memory { public class InjectionStruct { private UInt32 _InjectionOffset; private UInt32 _Length; public UInt32 InjectionOffset { get {return _InjectionOffset;} } public UInt32 InjectionReturnOffset { get { return _InjectionOffset + _Length; } } public UInt32 Length { get { return _Length; } } public int NeededNops { get { return (int)(_Length - 5); } } public InjectionStruct(UInt32 Offset, UInt32 Length) { _InjectionOffset = Offset; _Length = Length; } public InjectionStruct(String OffsetAndLength) { _InjectionOffset = 0; _Length = 0; if (OffsetAndLength != null) { try { _Length = UInt32.Parse((OffsetAndLength.Split('|'))[1]); _InjectionOffset = UInt32.Parse((OffsetAndLength.Split('|'))[0].Substring(2).Trim(), NumberStyles.HexNumber); } catch { Logger.WriteLog("Impossible to load InjectionStruct from following String : " + OffsetAndLength); } } } } } ================================================ FILE: DsCore/Memory/NopStruct.cs ================================================ using System; using System.Globalization; namespace DsCore.Memory { /// /// Defines how many NOP to write at a given Memory offset /// public struct NopStruct { public UInt32 MemoryOffset; public UInt32 Length; public NopStruct(UInt32 Offset, UInt32 NopLength) { MemoryOffset = Offset; Length = NopLength; } public NopStruct(String OffsetAndNumber) { MemoryOffset = 0; Length = 0; if (OffsetAndNumber != null) { try { Length = UInt32.Parse((OffsetAndNumber.Split('|'))[1]); MemoryOffset = UInt32.Parse((OffsetAndNumber.Split('|'))[0].Substring(2).Trim(), NumberStyles.HexNumber); } catch { Logger.WriteLog("Impossible to load NopStruct from following String : " + OffsetAndNumber); } } } } } ================================================ FILE: DsCore/MemoryX64/Codecave.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using DsCore.Win32; namespace DsCore.MemoryX64 { public class Codecave { private Process _Process; private IntPtr _ProcessHandle; private IntPtr _ModuleBaseAddress = IntPtr.Zero; private UInt64 _Cave_Address = 0; private UInt64 _CaveOffset = 0; public UInt64 CaveAddress { get { return _Cave_Address; } } public UInt64 CaveOffset { get { return _CaveOffset; } } public Codecave(Process p, IntPtr BaseAddress) { _Process = p; _ModuleBaseAddress = BaseAddress; } /// /// Trying to access the process /// /// True if success, otherwise False public bool Open() { _ProcessHandle = _Process.Handle; if (_ProcessHandle != IntPtr.Zero) return true; else return false; } /// /// Reserves a region of memory within the virtual address space of a specified process. /// The function initializes the memory it allocates to zero. /// /// The size of the region of memory to allocate, in bytes. /// True is success, otherwise False public bool Alloc(UInt32 Size) { //Allocation mémoire _Cave_Address = (UInt64)Win32API.VirtualAllocEx(_ProcessHandle, IntPtr.Zero, Size, MemoryAllocType.MEM_COMMIT, MemoryPageProtect.PAGE_EXECUTE_READWRITE); if (_Cave_Address != 0) return true; else return false; } /// /// To call an absolute X64 Address, using the following sequence : /// call RIP+2 /// JMP 08 (to jump over the RIP+2 address and get back to code) /// /// /// public bool Write_call_absolute(UInt64 AbsoluteAddress) { List Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x15); Buffer.Add(0x02); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0xEB); Buffer.Add(0x08); Buffer.AddRange(BitConverter.GetBytes(AbsoluteAddress)); return Write_Bytes(Buffer.ToArray()); } //jmp [Address] public bool Write_jmp(UInt64 AbsoluteAddress) { List Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(AbsoluteAddress)); return Write_Bytes(Buffer.ToArray()); } //nop public bool Write_nop(int Amount = 1) { List Buffer = new List(); for (int i = 0; i < Amount; i++) { Buffer.Add(0x90); } return Write_Bytes(Buffer.ToArray()); } /// /// Write bytes in memory, read from a string like "00 00 00 00" /// /// String formated series of bytes to write /// True if success, otherwise False public bool Write_StrBytes(String StrBuffer) { String[] sBytes = StrBuffer.Split(' '); List Buffer = new List(); foreach (String hex in sBytes) { Buffer.Add((byte)Convert.ToInt32(hex, 16)); } return Write_Bytes(Buffer.ToArray()); } /// /// Write bytes in memory, read from an array of bytes /// /// Array of bytes to write /// True if success, otherwise False public bool Write_Bytes(Byte[] Buffer) { UIntPtr BytesWritten = UIntPtr.Zero; if (Win32API.WriteProcessMemoryX64(_ProcessHandle, (IntPtr)(_Cave_Address + _CaveOffset), Buffer, (UIntPtr)Buffer.Length, out BytesWritten)) { _CaveOffset += (UInt64)BytesWritten; return true; } else { return false; } } /// /// Create both Jump-To and Jump-Back instruction and inject the codecave in memory /// /// InjectionStruct with needed offset to inject the codecave to the good paert of the game code /// Name that will be used to log the codecave creation public void InjectToOffset(InjectionStruct iStruct, string sCodeCaveName, bool bCreateButNotInject = false) { //Jump back Write_jmp((UInt64)_ModuleBaseAddress + iStruct.InjectionReturnOffset); Logger.WriteLog("Adding " + sCodeCaveName + " CodeCave at : 0x" + _Cave_Address.ToString("X16")); //Code injection List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(_Cave_Address)); for (int i = 0; i < iStruct.NeededNops; i++) { Buffer.Add(0x90); } //For "crash" debug purpose, sometimes I need to create the codecave to examine it, without making the game jump to it. if (!bCreateButNotInject) Win32API.WriteProcessMemoryX64(_ProcessHandle, (IntPtr)((UInt64)_ModuleBaseAddress + iStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Create both Jump-To and Jump-Back instruction and inject the codecave in memory /// /// InjectionStruct with needed offset to inject the codecave to the good paert of the game code /// Name that will be used to log the codecave creation public void InjectToAddress(InjectionStruct iStruct, string sCodeCaveName, bool bCreateButNotInject = false) { //Jump back Write_jmp(iStruct.InjectionReturnOffset); Logger.WriteLog("Adding " + sCodeCaveName + " CodeCave at : 0x" + _Cave_Address.ToString("X16")); //Code injection List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(_Cave_Address)); for (int i = 0; i < iStruct.NeededNops; i++) { Buffer.Add(0x90); } //For "crash" debug purpose, sometimes I need to create the codecave to examine it, without making the game jump to it. if (!bCreateButNotInject) Win32API.WriteProcessMemoryX64(_ProcessHandle, (IntPtr)iStruct.InjectionOffset, Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } /// /// Asolute jump Codecave will need 14 bytes to be written. /// When space is limited, we can use a Trampoline address to use a short 5-bytes long short jump to some close free area /// In which we will be able to write the 14-bytes long JMP /// /// /// /// /// public void InjectToOffset_WithTrampoline(InjectionStruct iStruct, UInt64 TrampolineOffset, string sCodeCaveName, bool bCreateButNotInject = false) { //Jump back Write_jmp((UInt64)_ModuleBaseAddress + iStruct.InjectionReturnOffset); Logger.WriteLog("Adding " + sCodeCaveName + " CodeCave at : 0x" + _Cave_Address.ToString("X16")); //Code injection //1st Step : writing the long jump in the Trampoline List Buffer = new List(); UIntPtr bytesWritten = UIntPtr.Zero; Buffer = new List(); Buffer.Add(0xFF); Buffer.Add(0x25); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.Add(0x00); Buffer.AddRange(BitConverter.GetBytes(_Cave_Address)); //For "crash" debug purpose, sometimes I need to create the codecave to examine it, without making the game jump to it. if (!bCreateButNotInject) Win32API.WriteProcessMemoryX64(_ProcessHandle, (IntPtr)((UInt64)_ModuleBaseAddress + TrampolineOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); //2nd step : writing the short jump Buffer = new List(); Buffer.Add(0xE9); Buffer.AddRange(BitConverter.GetBytes((UInt32)(TrampolineOffset - iStruct.InjectionOffset - 5))); //InjectionStruct inner NeededNops is based on 14-bytes length absolute jump injection //Using a custom one for 5-bytes length short JMP uint NeededNops = iStruct.Length - 5; for (int i = 0; i < NeededNops; i++) { Buffer.Add(0x90); } if (!bCreateButNotInject) Win32API.WriteProcessMemoryX64(_ProcessHandle, (IntPtr)((UInt64)_ModuleBaseAddress + iStruct.InjectionOffset), Buffer.ToArray(), (UIntPtr)Buffer.Count, out bytesWritten); } } } ================================================ FILE: DsCore/MemoryX64/InjectionStruct.cs ================================================ using System; using System.Globalization; namespace DsCore.MemoryX64 { public class InjectionStruct { private UInt64 _InjectionOffset; private UInt32 _Length; public UInt64 InjectionOffset { get { return _InjectionOffset; } } public UInt64 InjectionReturnOffset { get { return _InjectionOffset + _Length; } } public UInt32 Length { get { return _Length; } } public int NeededNops { get { return (int)(_Length - 14); } } public InjectionStruct(UInt64 Offset, UInt32 Length) { _InjectionOffset = Offset; _Length = Length; } public InjectionStruct(String OffsetAndLength) { _InjectionOffset = 0; _Length = 0; if (OffsetAndLength != null) { try { _Length = UInt32.Parse((OffsetAndLength.Split('|'))[1]); _InjectionOffset = UInt64.Parse((OffsetAndLength.Split('|'))[0].Substring(2).Trim(), NumberStyles.HexNumber); } catch { Logger.WriteLog("Impossible to load InjectionStruct from following String : " + OffsetAndLength); } } } } } ================================================ FILE: DsCore/MemoryX64/NopStruct.cs ================================================ using System; using DsCore; using System.Globalization; namespace DsCore.MemoryX64 { /// /// Defines how many NOP to write at a given Memory offset /// public struct NopStruct { public UInt64 MemoryOffset; public UInt32 Length; public NopStruct(UInt64 Offset, UInt32 NopLength) { MemoryOffset = Offset; Length = NopLength; } public NopStruct(String OffsetAndNumber) { MemoryOffset = 0; Length = 0; if (OffsetAndNumber != null) { try { Length = UInt32.Parse((OffsetAndNumber.Split('|'))[1]); MemoryOffset = UInt64.Parse((OffsetAndNumber.Split('|'))[0].Substring(3).Trim(), NumberStyles.HexNumber); } catch { Logger.WriteLog("Impossible to load NopStruct from following String : " + OffsetAndNumber); } } } } } ================================================ FILE: DsCore/RawInput/Hid/HidPButtonCaps.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// The HIDP_BUTTON_CAPS structure contains information about the capability of a /// HID control button usage (or a set of buttons associated with a usage range). /// [StructLayout(LayoutKind.Explicit)] public struct HidPButtonCaps { /// /// Specifies the usage page for a usage or usage range. /// [FieldOffset(0)] public ushort UsagePage; /// /// Specifies the report ID of the HID report that contains the usage or usage range. /// [FieldOffset(2)] public byte ReportID; /// /// Indicates, if TRUE, that a button has a set of aliased usages. /// Otherwise, if IsAlias is FALSE, the button has only one usage. /// [FieldOffset(3), MarshalAs(UnmanagedType.U1)] public bool IsAlias; /// /// Contains the data fields (one or two bytes) associated with an input, output, or feature main item. /// [FieldOffset(4)] public ushort BitField; /// /// Specifies the index of the link collection in a top-level collection's link collection array that contains the usage or usage range. /// If LinkCollection is zero, the usage or usage range is contained in the top-level collection. /// [FieldOffset(6)] public ushort LinkCollection; /// /// Specifies the usage of the link collection that contains the usage or usage range. /// If LinkCollection is zero, LinkUsage specifies the usage of the top-level collection. /// [FieldOffset(8)] public ushort LinkUsage; /// /// Specifies the usage page of the link collection that contains the usage or usage range. /// If LinkCollection is zero, LinkUsagePage specifies the usage page of the top-level collection. /// [FieldOffset(10)] public ushort LinkUsagePage; /// /// Specifies, if TRUE, that the structure describes a usage range. /// Otherwise, if IsRange is FALSE, the structure describes a single usage. /// [FieldOffset(12), MarshalAs(UnmanagedType.U1)] public bool IsRange; /// /// Specifies, if TRUE, that the usage or usage range has a set of string descriptors. /// Otherwise, if IsStringRange is FALSE, the usage or usage range has zero or one string descriptor. /// [FieldOffset(13), MarshalAs(UnmanagedType.U1)] public bool IsStringRange; /// /// Specifies, if TRUE, that the usage or usage range has a set of designators. /// Otherwise, if IsDesignatorRange is FALSE, the usage or usage range has zero or one designator. /// [FieldOffset(14), MarshalAs(UnmanagedType.U1)] public bool IsDesignatorRange; /// /// Specifies, if TRUE, that the button usage or usage range provides absolute data. /// Otherwise, if IsAbsolute is FALSE, the button data is the change in state from the previous value. /// [FieldOffset(15), MarshalAs(UnmanagedType.U1)] public bool IsAbsolute; /// /// Reserved for internal system use. /// [FieldOffset(16), MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public uint[] Reserved; /// /// Range or NotRange are UNION /// [FieldOffset(16 + 10 * 4)] public ButtonCapsRange Range; [FieldOffset(16 + 10 * 4)] public ButtonCapsNotRange NotRange; public override string ToString() { if (IsRange) { return string.Format("UsagePage: {0}, LinkCollection: {1}, LinkUsage: {2}, LinkUsagePage: {3}, IsAbsolute: {4}, IsRange: {5}, Range->UsageMin: 0x{6:X2}, Range->UsageMax: 0x{7:X2}", UsagePage, LinkCollection, LinkUsage, LinkUsagePage, IsAbsolute, IsRange, Range.UsageMin, Range.UsageMax); } else { return string.Format("UsagePage: {0}, LinkCollection: {1}, LinkUsage: {2}, LinkUsagePage: {3}, IsAbsolute: {4}, IsRange: {5}, NotRange->Usage: 0x{6:X2}", UsagePage, LinkCollection, LinkUsage, LinkUsagePage, IsAbsolute, IsRange, NotRange.Usage); } } } [StructLayout(LayoutKind.Sequential)] public struct ButtonCapsRange { /// /// Indicates the inclusive lower bound of usage range whose inclusive upper bound is specified by Range.UsageMax. /// public ushort UsageMin; /// /// Indicates the inclusive upper bound of a usage range whose inclusive lower bound is indicated by Range.UsageMin. /// public ushort UsageMax; /// /// Indicates the inclusive lower bound of a range of string descriptors /// (specified by string minimum and string maximum items) whose inclusive upper bound is indicated by Range.StringMax. /// public ushort StringMin; /// /// Indicates the inclusive upper bound of a range of string descriptors /// (specified by string minimum and string maximum items) whose inclusive lower bound is indicated by Range.StringMin. /// public ushort StringMax; /// /// Indicates the inclusive lower bound of a range of designators /// (specified by designator minimum and designator maximum items) whose inclusive lower bound is indicated by Range.DesignatorMax. /// public ushort DesignatorMin; /// /// Indicates the inclusive upper bound of a range of designators /// (specified by designator minimum and designator maximum items) whose inclusive lower bound is indicated by Range.DesignatorMin. /// public ushort DesignatorMax; /// /// Indicates the inclusive lower bound of a sequential range of data indices that correspond, one-to-one and in the same order, /// to the usages specified by the usage range Range.UsageMin to Range.UsageMax. /// public ushort DataIndexMin; /// ///Indicates the inclusive upper bound of a sequential range of data indices that correspond, one-to-one and in the same order, ///to the usages specified by the usage range Range.UsageMin to Range.UsageMax. /// public ushort DataIndexMax; } [StructLayout(LayoutKind.Sequential)] public struct ButtonCapsNotRange { /// /// Indicates a usage ID. /// public ushort Usage; /// /// Reserved for internal system use. /// public ushort Reserved1; /// /// Indicates a string descriptor ID for the usage specified by NotRange.Usage. /// public ushort StringIndex; /// /// Reserved for internal system use. /// public ushort Reserved2; /// /// Indicates a designator ID for the usage specified by NotRange.Usage. /// public ushort DesignatorIndex; /// /// Reserved for internal system use. /// public ushort Reserved3; /// /// Indicates the data index of the usage specified by NotRange.Usage. /// public ushort DataIndex; /// /// Reserved for internal system use. /// public ushort Reserved4; } } ================================================ FILE: DsCore/RawInput/Hid/HidPCaps.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// The HIDP_CAPS structure contains information about a top-level collection's capability. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct HidPCaps { /// /// Specifies a top-level collection's usage ID. /// public ushort Usage; /// /// Specifies the top-level collection's usage page. /// public ushort UsagePage; /// /// Specifies the maximum size, in bytes, of all the input reports /// (including the report ID, if report IDs are used, which is prepended to the report data). /// public ushort InputReportByteLength; /// /// Specifies the maximum size, in bytes, of all the output reports /// (including the report ID, if report IDs are used, which is prepended to the report data). /// public ushort OutputReportByteLength; /// /// Specifies the maximum length, in bytes, of all the feature reports /// (including the report ID, if report IDs are used, which is prepended to the report data). /// public ushort FeatureReportByteLength; /// /// Reserved for internal system use. /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] public ushort[] Reserved; /// /// Specifies the number of HIDP_LINK_COLLECTION_NODE structures that are returned for this /// top-level collection by HidP_GetLinkCollectionNodes. /// public ushort NumberLinkCollectionNodes; /// /// Specifies the number of input HIDP_BUTTON_CAPS structures that HidP_GetButtonCaps returns. /// public ushort NumberInputButtonCaps; /// /// Specifies the number of input HIDP_VALUE_CAPS structures that HidP_GetValueCaps returns. /// public ushort NumberInputValueCaps; /// /// Specifies the number of data indices assigned to buttons and values in all input reports. /// public ushort NumberInputDataIndices; /// /// Specifies the number of output HIDP_BUTTON_CAPS structures that HidP_GetButtonCaps returns. /// public ushort NumberOutputButtonCaps; /// /// Specifies the number of output HIDP_VALUE_CAPS structures that HidP_GetValueCaps returns. /// public ushort NumberOutputValueCaps; /// /// Specifies the number of data indices assigned to buttons and values in all output reports. /// public ushort NumberOutputDataIndices; /// /// Specifies the total number of feature HIDP_BUTTONS_CAPS structures that HidP_GetButtonCaps returns. /// public ushort NumberFeatureButtonCaps; /// /// Specifies the total number of feature HIDP_VALUE_CAPS structures that HidP_GetValueCaps returns. /// public ushort NumberFeatureValueCaps; /// /// Specifies the number of data indices assigned to buttons and values in all feature reports. /// public ushort NumberFeatureDataIndices; public override string ToString() { return string.Format("Usage: {0}, UsagePage: {1}, InputReportByteLength: {2}, OutputReportByteLength: {3}, FeatureReportByteLength: {4}, NumberInputButtonCaps: {5}, NumberInputValueCaps: {6}, NumberOutputButtonCaps: {7}, NumberOutputValueCaps: {8}, NumberOutputDataIndices: {9}", Usage, UsagePage, InputReportByteLength, OutputReportByteLength, FeatureReportByteLength, NumberInputButtonCaps, NumberInputValueCaps, NumberOutputButtonCaps, NumberOutputValueCaps, NumberOutputDataIndices); } } } ================================================ FILE: DsCore/RawInput/Hid/HidPReportType.cs ================================================  namespace DsCore.RawInput { /// /// The HIDP_REPORT_TYPE enumeration type is used to specify a HID report type. /// public enum HidPReportType { /// /// Indicates an input report. /// Input = 0, /// /// Indicates an output report. /// Output = 1, /// /// Indicates a feature report. /// Feature = 2, } } ================================================ FILE: DsCore/RawInput/Hid/HidPValueCaps.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// The HIDP_VALUE_CAPS structure contains information that describes the capability of a set of HID control values /// (either a single usage or a usage range). /// [StructLayout(LayoutKind.Explicit)] public struct HidPValueCaps { /// /// Specifies the usage page of the usage or usage range. /// [FieldOffset(0)] public ushort UsagePage; /// /// Specifies the report ID of the HID report that contains the usage or usage range. /// [FieldOffset(2), MarshalAs(UnmanagedType.U1)] public byte ReportID; /// /// Indicates, if TRUE, that the usage is member of a set of aliased usages. /// Otherwise, if IsAlias is FALSE, the value has only one usage. /// [FieldOffset(3), MarshalAs(UnmanagedType.U1)] public bool IsAlias; /// /// Contains the data fields (one or two bytes) associated with an input, output, or feature main item. /// [FieldOffset(4)] public ushort BitField; /// /// Specifies the index of the link collection in a top-level collection's link collection array /// that contains the usage or usage range. /// If LinkCollection is zero, the usage or usage range is contained in the top-level collection. /// [FieldOffset(6)] public ushort LinkCollection; /// /// Specifies the usage of the link collection that contains the usage or usage range. /// If LinkCollection is zero, LinkUsage specifies the usage of the top-level collection. /// [FieldOffset(8)] public ushort LinkUsage; /// /// Specifies the usage page of the link collection that contains the usage or usage range. /// If LinkCollection is zero, LinkUsagePage specifies the usage page of the top-level collection. /// [FieldOffset(10)] public ushort LinkUsagePage; /// /// Specifies, if TRUE, that the structure describes a usage range. /// Otherwise, if IsRange is FALSE, the structure describes a single usage. /// [FieldOffset(12), MarshalAs(UnmanagedType.U1)] public bool IsRange; /// /// Specifies, if TRUE, that the usage or usage range has a set of string descriptors. /// Otherwise, if IsStringRange is FALSE, the usage or usage range has zero or one string descriptor. /// [FieldOffset(13), MarshalAs(UnmanagedType.U1)] public bool IsStringRange; /// /// Specifies, if TRUE, that the usage or usage range has a set of designators. /// Otherwise, if IsDesignatorRange is FALSE, the usage or usage range has zero or one designator. /// [FieldOffset(14), MarshalAs(UnmanagedType.U1)] public bool IsDesignatorRange; /// /// Specifies, if TRUE, that the usage or usage range provides absolute data. /// Otherwise, if IsAbsolute is FALSE, the value is the change in state from the previous value. /// [FieldOffset(15), MarshalAs(UnmanagedType.U1)] public bool IsAbsolute; /// /// Specifies, if TRUE, that the usage supports a NULL value, which indicates that the data is not valid and should be ignored. /// Otherwise, if HasNull is FALSE, the usage does not have a NULL value. /// [FieldOffset(16), MarshalAs(UnmanagedType.U1)] public bool HasNull; /// /// Reserved for internal system use. /// [FieldOffset(17), MarshalAs(UnmanagedType.U1)] public byte Reserved; /// /// Specifies the size, in bits, of a usage's data field in a report. /// If ReportCount is greater than one, each usage has a separate data field of this size. /// [FieldOffset(18)] public ushort BitSize; /// /// Specifies the number of usages that this structure describes. /// [FieldOffset(20)] public ushort ReportCount; /// // Flattened Array Reserved2 : Reserved for internal system use. /// [FieldOffset(22)] public ushort Reserved2a; [FieldOffset(24)] public ushort Reserved2b; [FieldOffset(26)] public ushort Reserved2c; [FieldOffset(28)] public ushort Reserved2d; [FieldOffset(30)] public ushort Reserved2e; /// /// Specifies the usage's exponent, as described by the USB HID standard. /// [FieldOffset(32)] public uint UnitsExp; /// /// Specifies the usage's units, as described by the USB HID Standard. /// [FieldOffset(36)] public uint Units; /// /// Specifies a usage's signed lower bound. /// [FieldOffset(40)] public int LogicalMin; /// /// Specifies a usage's signed upper bound. /// [FieldOffset(44)] public int LogicalMax; /// /// Specifies a usage's signed lower bound after scaling is applied to the logical minimum value. /// [FieldOffset(48)] public int PhysicalMin; /// /// Specifies a usage's signed ûpper bound after scaling is applied to the logical maximum value. /// [FieldOffset(52)] public int PhysicalMax; /// /// Range or NotRange UNION /// [FieldOffset(56)] public ValueCapsRange Range; [FieldOffset(56)] public ValuenCapsNotRange NotRange; public override string ToString() { if (IsRange) { return string.Format("IsAbsolute: {0}, IsRange: {1}, LogicallMin: 0x{2:X8}, LogicalMax: 0x{3:X8}, ReportCount: {4}, Range->UsageMin: 0x{5:X2}, Range->UsageMax: 0x{6:X2}", IsAbsolute, IsRange, LogicalMin, LogicalMax, ReportCount, Range.UsageMin, Range.UsageMax); } else { return string.Format("IsAbsolute: {0}, IsRange: {1}, LogicallMin: 0x{2:X8}, LogicalMax: 0x{3:X8}, ReportCount: {4}, NotRange->Usage: 0x{5:X2}", IsAbsolute, IsRange, LogicalMin, LogicalMax, ReportCount, NotRange.Usage); } } } [StructLayout(LayoutKind.Sequential)] public struct ValueCapsRange { /// /// Indicates the inclusive lower bound of usage range whose inclusive upper bound is specified by Range.UsageMax. /// public ushort UsageMin; /// /// Indicates the inclusive upper bound of a usage range whose inclusive lower bound is indicated by Range.UsageMin. /// public ushort UsageMax; /// /// Indicates the inclusive lower bound of a range of string descriptors (specified by string minimum and string maximum items) /// whose inclusive upper bound is indicated by Range.StringMax. /// public ushort StringMin; /// /// Indicates the inclusive upper bound of a range of string descriptors (specified by string minimum and string maximum items) ///whose inclusive lower bound is indicated by Range.StringMin. /// public ushort StringMax; /// /// Indicates the inclusive lower bound of a range of (specified by designator minimum and designator maximum items) ///whose inclusive lower bound is indicated by Range.DesignatorMax. /// public ushort DesignatorMin; /// /// Indicates the inclusive upper bound of a range of designators (specified by designator minimum and designator maximum items) /// whose inclusive lower bound is indicated by Range.DesignatorMin. /// public ushort DesignatorMax; /// /// Indicates the inclusive lower bound of a sequential range of data indices that correspond, one-to-one and in the same order, /// to the usages specified by the usage range Range.UsageMin to Range.UsageMax. /// public ushort DataIndexMin; /// /// Indicates the inclusive upper bound of a sequential range of data indices that correspond, one-to-one and in the same order, /// to the usages specified by the usage range Range.UsageMin to Range.UsageMax. /// public ushort DataIndexMax; } [StructLayout(LayoutKind.Sequential)] public struct ValuenCapsNotRange { /// /// Reserved for internal system use. /// public ushort Reserved1; /// /// Indicates a usage ID. /// public ushort Usage; /// /// Indicates a string descriptor ID for the usage specified by NotRange.Usage. /// public ushort StringIndex; /// /// Reserved for internal system use. /// public ushort Reserved2; /// /// Indicates a designator ID for the usage specified by NotRange.Usage. /// public ushort DesignatorIndex; /// /// Reserved for internal system use. /// public ushort Reserved3; /// /// Indicates the data index of the usage specified by NotRange.Usage. /// public ushort DataIndex; /// /// Reserved for internal system use. /// public ushort Reserved4; } } ================================================ FILE: DsCore/RawInput/Hid/HidUsage.cs ================================================  namespace DsCore.RawInput { /// /// Top level collection usage for the raw input device. /// public enum HidUsage : ushort { /// Unknown usage. Undefined = 0x00, /// Pointer Pointer = 0x01, /// Mouse Mouse = 0x02, /// Joystick Joystick = 0x04, /// Game Pad Gamepad = 0x05, /// Keyboard Keyboard = 0x06, /// Keypad Keypad = 0x07, /// Muilt-axis Controller SystemControl = 0x80, /// Tablet PC controls Tablet = 0x80, /// Consumer Consumer = 0x0C } } ================================================ FILE: DsCore/RawInput/Hid/HidUsageAndPage.cs ================================================ using System; namespace DsCore.RawInput { /// /// UsagePages /// public enum HID_USAGE_PAGE : ushort { GENERIC = 0x01, SIMULATION = 0x02, VR = 0x03, SPORT = 0x04, GAME = 0x05, KEYBOARD = 0x07, LED = 0x08, BUTTON = 0x09, ORDINAL = 0x0A, TELEPHONY = 0x0B, CONSUMER = 0x0C, DIGITIZER = 0x0D, UNICODE = 0x10, ALPHANUMERIC = 0x14 } /// /// Usages Generic Desktop Page (0x01) /// public enum HID_USAGE_GENERIC : ushort { POINTER = 0x01, MOUSE = 0x02, JOYSTICK = 0x04, GAMEPAD = 0x05, KEYBOARD = 0x06, KEYPAD = 0x07, MULTIAXIS = 0x08, X = 0x30, Y = 0x31, Z = 0x32, RX = 0x33, RY = 0x34, RZ = 0x35, SLIDER = 0x36, DIAL = 0x37, WHEEL = 0x38, HATSWITCH = 0x39, COUNTED_BUFFER = 0x3A, BYTE_COUNT = 0x3B, MOTION_WAKEUP = 0x3C, START = 0x3D, SELECT = 0x3E, VX = 0x40, VY = 0x41, VZ = 0x42, VBRX = 0x43, VBRY = 0x44, VBRZ = 0x45, VNO = 0x46, FEATURE_NOTIFICATION = 0x47, SYSTEM_CTL = 0x80, SYSCTL_POWER = 0x81, SYSCTL_SLEEP = 0x82, SYSCTL_WAKE = 0x83, SYSCTL_CONTEXT_MENU = 0x84, SYSCTL_MAIN_MENU = 0x85, SYSCTL_APP_MENU = 0x86, SYSCTL_HELP_MENU = 0x87, SYSCTL_MENU_EXIT = 0x88, SYSCTL_MENU_SELECT = 0x89, SYSCTL_MENU_RIGHT = 0x8A, SYSCTL_MENU_LEFT = 0x8B, SYSCTL_MENU_UP = 0x8C, SYSCTL_MENU_DOWN = 0x8D, SYSCTL_COLD_RESTART = 0x8E, SYSCTL_WARM_RESTART = 0x8F, SYSCTL_DPAD_UP = 0x90, SYSCTL_DPAD_DOWN = 0x91, SYSCTL_DPAD_RIGHT = 0x92, SYSCTL_DPAD_LEFT = 0x93, SYSCTL_DOCK = 0xA0, SYSCTL_UNDOCK = 0xA1, SYSCTL_SETUP = 0xA2, SYSCTL_BREAK = 0xA3, SYSCTL_DEBUGGER_BREAK = 0xA4, SYSCTL_APP_BREAK = 0xA5, SYSCTL_APP_DEBUGGER_BREAK = 0xA6, SYSCTL_SYSTEM_SPEAKER_MUTE = 0xA7, SYSCTL_SYSTEM_HIBERNATE = 0xA8, SYSCTL_DISPLAY_INVERT = 0xB0, SYSCTL_DISPLAY_INTERNAL = 0xB1, SYSCTL_DISPLAY_EXTERNAL = 0xB2, SYSCTL_DISPLAY_BOTH = 0xB3, SYSCTL_DISPLAY_DUAL = 0xB4, SYSCTL_DISPLAY_TOGGLE_INT_EXT = 0xB5, SYSCTL_DISPLAY_SWAP = 0xB6, SYSCTL_DISPLAY_LCD_AUTOSCALE = 0xB7 } /// /// Usages from Simulation Controls Page (0x02) /// public enum HID_USAGE_SIMULATION : ushort { RUDDER = 0xBA, THROTTLE = 0xBB } // // Virtual Reality Controls Page (0x03) // // // Sport Controls Page (0x04) // // // Game Controls Page (0x05) // // // Keyboard/Keypad Page (0x07) // /// /// LED page (0x08) /// public enum HID_USAGE_LED : ushort { UNDEFINED = 0x00, NUM_LOCK = 0x01, CAPS_LOCK = 0x02, SCROLL_LOCK = 0x03, COMPOSE = 0x04, KANA = 0x05, POWER = 0x06, SHIFT = 0x07, DO_NOT_DISTURB = 0x08, MUTE = 0x09, TONE_ENABLE = 0x0A, HIGH_CUT_FILTER = 0x0B, LOW_CUT_FILTER = 0x0C, EQUALIZER_ENABLE = 0x0D, SOUND_FIELD_ON = 0x0E, SURROUND_FIELD_ON = 0x0F, REPEAT = 0x10, STEREO = 0x11, SAMPLING_RATE_DETECT = 0x12, SPINNING = 0x13, CAV = 0x14, CLV = 0x15, RECORDING_FORMAT_DET = 0x16, OFF_HOOK = 0x17, RING = 0x18, MESSAGE_WAITING = 0x19, DATA_MODE = 0x1A, BATTERY_OPERATION = 0x1B, BATTERY_OK = 0x1C, BATTERY_LOW = 0x1D, SPEAKER = 0x1E, HEAD_SET = 0x1F, HOLD = 0x20, MICROPHONE = 0x21, COVERAGE = 0x22, NIGHT_MODE = 0x23, SEND_CALLS = 0x24, CALL_PICKUP = 0x25, CONFERENCE = 0x26, STAND_BY = 0x27, CAMERA_ON = 0x28, CAMERA_OFF = 0x29, ON_LINE = 0x2A, OFF_LINE = 0x2B, BUSY = 0x2C, READY = 0x2D, PAPER_OUT = 0x2E, PAPER_JAM = 0x2F, REMOTE = 0x30, FORWARD = 0x31, REVERSE = 0x32, STOP = 0x33, REWIND = 0x34, FAST_FORWARD = 0x35, PLAY = 0x36, PAUSE = 0x37, RECORD = 0x38, ERROR = 0x39, SELECTED_INDICATOR = 0x3A, IN_USE_INDICATOR = 0x3B, MULTI_MODE_INDICATOR = 0x3C, INDICATOR_ON = 0x3D, INDICATOR_FLASH = 0x3E, INDICATOR_SLOW_BLINK = 0x3F, INDICATOR_FAST_BLINK = 0x40, INDICATOR_OFF = 0x41, FLASH_ON_TIME = 0x42, SLOW_BLINK_ON_TIME = 0x43, SLOW_BLINK_OFF_TIME = 0x44, FAST_BLINK_ON_TIME = 0x45, FAST_BLINK_OFF_TIME = 0x46, INDICATOR_COLOR = 0x47, RED = 0x48, GREEN = 0x49, AMBER = 0x4A, GENERIC_INDICATOR = 0x4B, SYSTEM_SUSPEND = 0x4C, EXTERNAL_POWER = 0x4D } // // Button Page (0x09) // // There is no need to label these usages. // // // Ordinal Page (0x0A) // // There is no need to label these usages. // // // Telephony Device Page (0x0B) // } ================================================ FILE: DsCore/RawInput/Hid/HidUsagePage.cs ================================================  namespace DsCore.RawInput { /// /// Top level collection Usage page for the raw input device. /// public enum HidUsagePage : ushort { /// Unknown usage page. UNDEFINED = 0x00, /// Generic desktop controls. GENERIC = 0x01, /// Simulation controls. SIMULATION = 0x02, /// Virtual reality controls. VR = 0x03, /// Sports controls. SPORT = 0x04, /// Games controls. GAME = 0x05, /// Keyboard controls. KEYBOARD = 0x07 } } ================================================ FILE: DsCore/RawInput/Hid/NtStatus.cs ================================================  namespace DsCore.RawInput { /// /// NTSTATUS /// public enum NtStatus : uint { Success = 0x00110000, Null = 0x80110001, InvalidPreparsedData = 0xC0110001, InvalidReportType = 0xC0110002, InvalidReportLength = 0xC0110003, UsageNotFound = 0xC0110004, ValueOutOfRange = 0xC0110005, BadLogPhyValues = 0xC0110006, BufferTooSmall = 0xC0110007, InternalError = 0xC0110008, I8042TransUnknown = 0xC0110009, IncompatibleReportId = 0xC011000A, NotValueArray = 0xC011000B, IsValueArray = 0xC011000C, DataIndexNotFound = 0xC011000D, DataIndexOutOfRange = 0xC011000E, ButtonNotPressed = 0xC011000F, ReportDoesNotExist = 0xC0110010, NotImplemented = 0xC0110020, } } ================================================ FILE: DsCore/RawInput/RawInputController.cs ================================================ using System; using System.Collections.Generic; using System.Text; using DsCore.RawInput; using DsCore.Win32; using System.Runtime.InteropServices; namespace DsCore.RawInput { public class RawInputController { private IntPtr _hDevice; private RawInputDeviceType _dwType; private String _DeviceName; private RawInputDeviceInfo _DeviceInfo; private string _ManufacturerName; private String _ProductName; #region Accessors public IntPtr DeviceHandle { get { return _hDevice; } } public RawInputDeviceType DeviceType { get { return _dwType; } } public String DeviceName { get { return _DeviceName; } } public HidUsage DHidUsage { get { if (_dwType == RawInputDeviceType.RIM_TYPEHID) return (HidUsage)_Caps.Usage; else if (_dwType == RawInputDeviceType.RIM_TYPEMOUSE) return HidUsage.Mouse; else return HidUsage.Keyboard; } } public String PID { get { if (_dwType == RawInputDeviceType.RIM_TYPEHID) { return "0x" + _DeviceInfo.hid.dwProductId.ToString("X4"); } else if (_DeviceName.Contains("PID_")) { return "0x" + _DeviceName.Substring(_DeviceName.IndexOf("PID_") + 4, 4); } else return ""; } } public String VID { get { if (_dwType == RawInputDeviceType.RIM_TYPEHID) { return "0x" + _DeviceInfo.hid.dwVendorId.ToString("X4"); } else if (_DeviceName.Contains("VID_")) { return "0x" + _DeviceName.Substring(_DeviceName.IndexOf("VID_") + 4, 4); } else return ""; } } public String ManufacturerName { get { return _ManufacturerName; } } public String ProductName { get { return _ProductName; } } #endregion //Common struct used for any type of devices private RawInputHeader _RawInputHeader; private IntPtr _pPreparsedData; //If the controller is HID device, we will use the following struct to parse WM_INPUT: private RawHid _RawHidData; private int _Hid_NumberofButtons; private bool[] _Hid_Buttons; private int _Hid_NumberofAxis; private List _Hid_Axis_List; private Int32 _Hid_Axis_X_Max; private Int32 _Hid_Axis_X_Min; private Int32 _Hid_Axis_Y_Max; private Int32 _Hid_Axis_Y_Min; #region Accessors public int NumberOfButtons { get { int b = 0; if (_dwType == RawInputDeviceType.RIM_TYPEHID) b = _Hid_NumberofButtons; else if (_dwType == RawInputDeviceType.RIM_TYPEMOUSE) { b = _DeviceInfo.mouse.dwNumberOfButtons; } else if (_dwType == RawInputDeviceType.RIM_TYPEKEYBOARD) b = _DeviceInfo.keyboard.dwNumberOfKeysTotal; return b; } } public bool[] Hid_Buttons { get { return _Hid_Buttons; } } public int NumberOfAxis { get { return _Hid_NumberofAxis; } } public List AxisList { get { return _Hid_Axis_List; } } public Int32 Axis_X_Min { get { return _Hid_Axis_X_Min; } } public Int32 Axis_X_Max { get { return _Hid_Axis_X_Max; } } public Int32 Axis_Y_Min { get { return _Hid_Axis_Y_Min; } } public Int32 Axis_Y_Max { get { return _Hid_Axis_Y_Max; } } #endregion //If the controller is Mouse device, we will use the following struct private RawInputDataMouse _RawInputDataMouse; //Adding some field to know if the device is "relative" (i.e normal mouse) or "Absolute" (i.e lightgun) public bool IsRelativeCoordinates { get { if (DeviceType == RawInputDeviceType.RIM_TYPEMOUSE) return _RawInputDataMouse.data.usFlags == 0 ? true : false; else return false; } } //HidP variables private HidPCaps _Caps; private HidPButtonCaps[] _pButtonCaps; private HidPValueCaps[] _pValueCaps; private HidPValueCaps[] _pOutputValueCaps; #region Accessors public HidPCaps HID_Capabilities { get { return _Caps; } } public HidPButtonCaps[] HID_ButtonCapabilitiesArray { get { return _pButtonCaps; } } public HidPValueCaps[] HID_ValueCapabilitiesArray { get { return _pValueCaps; } } public HidPValueCaps[] HID_OutputValueCapabilitiesArray { get { return _pOutputValueCaps; } } #endregion //Our Controller data to be used for each player public struct ControllerComputedData { public POINT Axis; public RawInputcontrollerButtonEvent Buttons; } private ControllerComputedData _ControllerData; private ushort _Selected_HidAxisX = 0x30; private ushort _Selected_HidAxisY = 0x31; #region Accessor public ushort Selected_AxisX { get { return _Selected_HidAxisX; } set { _Selected_HidAxisX = value; } } public ushort Selected_AxisY { get { return _Selected_HidAxisY; } set { _Selected_HidAxisY = value; } } public Int32 Computed_X { get { return _ControllerData.Axis.X; } set { _ControllerData.Axis.X = value; } } public Int32 Computed_Y { get { return _ControllerData.Axis.Y; } set { _ControllerData.Axis.Y = value; } } public RawInputcontrollerButtonEvent Computed_Buttons { get { return _ControllerData.Buttons; } set { _ControllerData.Buttons = value; } } #endregion //At start, DemulShooter was created for Aimtraks : // - Aimtrak Trigger action was set to LeftButton (i.e Trigger) // - Aimtrak OutOfScreen Trigger action was set to RightButton (i.e Reload) // - Aimtrak "Another button" if available was set to MiddleButton (i.e Action) //With these 3 actions we could use RawInputMouseButtonState fully, but with Joypad and HID it's a little bit different : //There can be a lot of buttons so the user has to choose it's buttons for these 3 actions. //Default values are for MouseType device, and they will be changed by the HID device if needed private int _Button_OnScreenTrigger_Index = 0; private int _Button_OffScreenTrigger_Index = 1; private int _Button_Action_Index = 2; #region Accessors //Buttons index are 0-based for code handling, but 1-based for config file and easy user choice //That's why we are making this little change here : public int Selected_OnScreenTriggerButton { get { return _Button_OnScreenTrigger_Index + 1; } set { _Button_OnScreenTrigger_Index = value - 1; } } public int Selected_ActionButton { get { return _Button_Action_Index + 1; } set { _Button_Action_Index = value - 1; } } public int Selected_OffScreenTriggerButton { get { return _Button_OffScreenTrigger_Index + 1; } set { _Button_OffScreenTrigger_Index = value - 1; } } #endregion /// /// Constructor, fill as much Hidp_Struct as we can at the creation /// /// A handle to the raw input device. /// The type of the raw input device /// Caller class so that we can send data back for WriteLogDebug public RawInputController(IntPtr DeviceHandle, RawInputDeviceType DeviceType) { _hDevice = DeviceHandle; _dwType = DeviceType; _DeviceName = String.Empty; _ManufacturerName = String.Empty; _ProductName = String.Empty; _pPreparsedData = IntPtr.Zero; _Hid_NumberofButtons = 0; _Hid_Axis_List = new List(); GetDeviceName(); GetDeviceInfo(); //DeviceName is also the path to acces the device with OpenFile GetManufacturerAndProductString(_DeviceName); if (_dwType == RawInputDeviceType.RIM_TYPEHID) { _pPreparsedData = GetPreparsedData(); if (_pPreparsedData != IntPtr.Zero) { if (!GetCapabilities(_pPreparsedData, out _Caps)) { Logger.WriteLog("Error: Impossible to get Capabilities for device " + _DeviceName); return; } if (!GetButtonCapabilities(_pPreparsedData, _Caps, out _pButtonCaps)) { Logger.WriteLog("Error: Impossible to get Button Capabilities for device " + _DeviceName); return; } _Hid_NumberofButtons = GetNumberOfHidDeviceButtons(_pPreparsedData, _pButtonCaps); _Hid_Buttons = new bool[_Hid_NumberofButtons]; if (!GetValueCapabilities(_pPreparsedData, _Caps, out _pValueCaps)) { Logger.WriteLog("Error: Impossible to get Value Capabilities for device " + _DeviceName); return; } if (!GetOutputValueCapabilities(_pPreparsedData, _Caps, out _pOutputValueCaps)) { Logger.WriteLog("Error: Impossible to get Output Value Capabilities for device " + _DeviceName); //return; } //Number of absolute axis for (int i = 0; i < _pValueCaps.Length; i++) { if (_pValueCaps[i].IsAbsolute) { _Hid_NumberofAxis++; _Hid_Axis_List.Add(_pValueCaps[i].Range.UsageMin); } } } } else if (_dwType == RawInputDeviceType.RIM_TYPEMOUSE) { //A mouse only have 1 set of axis : X,Y _Hid_Buttons = new bool[_DeviceInfo.mouse.dwNumberOfButtons]; } } /// /// Getting WM_INPUT data => RawInputHeader to compare the hDevice handle and check if this specific device is the good one /// /// LParam parameter from WindowLoop during WM_INPUT message /// public Boolean isSourceOfRawInputMessage(IntPtr LParam) { if (GetRawInputHeader(LParam)) { if (_RawInputHeader.hDevice == _hDevice) return true; else return false; } else { //Error return false; } } /// /// Getting RawInputHEader, RawInputData and getting Axis and Buttons information /// If the controller is type of Mouse, Axis values and Buttons are directly available in the struct /// If the controller is HID, we need to work a little bit more with hid.dll to determine axis and buttons values /// /// LParam parameter from WindowLoop during WM_INPUT message public void ProcessRawInputData(IntPtr LParam) { if (!GetRawInputHeader(LParam)) { Logger.WriteLog("ProcessRawInputData error: Impossible to get RawInputHeader for device " + _DeviceName); return; } if (!GetRawInputData(LParam)) { Logger.WriteLog("ProcessRawInputData error: Impossible to get RawInputData for device " + _DeviceName); return; } //If this controller is HID device, we need to use Hid.dll calls //to determine buttons and axis messages in the variable RawData array if (_dwType == RawInputDeviceType.RIM_TYPEHID) { try { //Updating buttons ushort[] usage = new ushort[128]; uint usageLength = (uint)_Hid_NumberofButtons; if (Win32API.HidP_GetUsages(HidPReportType.Input, _pButtonCaps[0].UsagePage, 0, usage, ref usageLength, _pPreparsedData, _RawHidData.bRawData, (uint)_RawHidData.dwSizeHid) != NtStatus.Success) { Logger.WriteLog("ProcessRawInputData error: Impossible to get Usages for device " + _DeviceName); } bool[] bButtonStates = new bool[128]; string strButtons = string.Empty; for (int i = 0; i < usageLength; i++) { bButtonStates[usage[i] - _pButtonCaps[0].Range.UsageMin] = true; } _ControllerData.Buttons = 0; for (int i = 0; i < _Hid_Buttons.Length; i++) { if (i == _Button_OnScreenTrigger_Index) { if (!_Hid_Buttons[i] && bButtonStates[i]) _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OnScreenTriggerDown; else if (_Hid_Buttons[i] && !bButtonStates[i]) _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OnScreenTriggerUp; } if (i == _Button_Action_Index) { if (!_Hid_Buttons[i] && bButtonStates[i]) _ControllerData.Buttons |= RawInputcontrollerButtonEvent.ActionDown; else if (_Hid_Buttons[i] && !bButtonStates[i]) _ControllerData.Buttons |= RawInputcontrollerButtonEvent.ActionUp; } if (i == _Button_OffScreenTrigger_Index) { if (!_Hid_Buttons[i] && bButtonStates[i]) _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OffScreenTriggerDown; else if (_Hid_Buttons[i] && !bButtonStates[i]) _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OffScreenTriggerUp; } _Hid_Buttons[i] = bButtonStates[i]; } //Updating Values for (int i = 0; i < _pValueCaps.Length; i++) { Int32 value; if (!_pValueCaps[i].IsRange) { if (Win32API.HidP_GetUsageValue(HidPReportType.Input, _pValueCaps[i].UsagePage, 0, _pValueCaps[i].NotRange.Usage, out value, _pPreparsedData, _RawHidData.bRawData, (uint)_RawHidData.dwSizeHid) != NtStatus.Success) { Logger.WriteLog("ProcessRawInputData error: Impossible to get UsageValue for device " + _DeviceName); return; } if (_pValueCaps[i].NotRange.Usage == _Selected_HidAxisX) { _Hid_Axis_X_Min = _pValueCaps[i].LogicalMin; _Hid_Axis_X_Max = Correct_Axis_Max(_pValueCaps[i].LogicalMax); _ControllerData.Axis.X = CorrectNegative_Value(value, _Hid_Axis_X_Min); } if (_pValueCaps[i].NotRange.Usage == _Selected_HidAxisY) { _Hid_Axis_Y_Min = _pValueCaps[i].LogicalMin; _Hid_Axis_Y_Max = Correct_Axis_Max(_pValueCaps[i].LogicalMax); _ControllerData.Axis.Y = CorrectNegative_Value(value, _Hid_Axis_Y_Min); } } else { if (Win32API.HidP_GetUsageValue(HidPReportType.Input, _pValueCaps[i].UsagePage, 0, _pValueCaps[i].Range.UsageMin, out value, _pPreparsedData, _RawHidData.bRawData, (uint)_RawHidData.dwSizeHid) != NtStatus.Success) { Logger.WriteLog("ProcessRawInputData error: Impossible to get UsageValue for device " + _DeviceName); return; } if (_pValueCaps[i].Range.UsageMin == _Selected_HidAxisX) { _Hid_Axis_X_Min = _pValueCaps[i].LogicalMin; _Hid_Axis_X_Max = Correct_Axis_Max(_pValueCaps[i].LogicalMax); _ControllerData.Axis.X = CorrectNegative_Value(value, _Hid_Axis_X_Min); } if (_pValueCaps[i].Range.UsageMax == _Selected_HidAxisY) { _Hid_Axis_Y_Min = _pValueCaps[i].LogicalMin; _Hid_Axis_Y_Max = Correct_Axis_Max(_pValueCaps[i].LogicalMax); _ControllerData.Axis.Y = CorrectNegative_Value(value, _Hid_Axis_Y_Min); } } } } catch (Exception Ex) { Logger.WriteLog("ProcessRawInputData error: " + Ex.Message.ToString()); } } //If this controller is a mouse device, we can directly access //axis and buttons info from the RawInputData struct else if (_dwType == RawInputDeviceType.RIM_TYPEMOUSE) { _ControllerData.Axis.X = _RawInputDataMouse.data.lLastX; _ControllerData.Axis.Y = _RawInputDataMouse.data.lLastY; _ControllerData.Buttons = 0; //This is what was used before with AIMTRAK and other lighguns so we assume we can keep it ! _Hid_Axis_X_Max = 0x0000FFFF; _Hid_Axis_X_Min = 0; _Hid_Axis_Y_Max = 0x0000FFFF; _Hid_Axis_Y_Min = 0; //For mouse device, buttons event have fixed index: // LeftCLick = OnScreenTrigger // MiddleClick = Action // RightClick = OffScreenTrigger // We're just changing the state of _HidButtons[i] for diplay purposes in the GUI if (_RawInputDataMouse.data.usButtonFlags == RawMouseButtonFlags.RI_MOUSE_LEFT_BUTTON_DOWN) { _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OnScreenTriggerDown; Set_Hid_Button_Value(0, true); } else if (_RawInputDataMouse.data.usButtonFlags == RawMouseButtonFlags.RI_MOUSE_MIDDLE_BUTTON_DOWN) { _ControllerData.Buttons |= RawInputcontrollerButtonEvent.ActionDown; Set_Hid_Button_Value(1, true); } else if (_RawInputDataMouse.data.usButtonFlags == RawMouseButtonFlags.RI_MOUSE_RIGHT_BUTTON_DOWN) { _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OffScreenTriggerDown; Set_Hid_Button_Value(2, true); } else if (_RawInputDataMouse.data.usButtonFlags == RawMouseButtonFlags.RI_MOUSE_LEFT_BUTTON_UP) { _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OnScreenTriggerUp; Set_Hid_Button_Value(0, false); } else if (_RawInputDataMouse.data.usButtonFlags == RawMouseButtonFlags.RI_MOUSE_MIDDLE_BUTTON_UP) { _ControllerData.Buttons |= RawInputcontrollerButtonEvent.ActionUp; Set_Hid_Button_Value(1, false); } else if (_RawInputDataMouse.data.usButtonFlags == RawMouseButtonFlags.RI_MOUSE_RIGHT_BUTTON_UP) { _ControllerData.Buttons |= RawInputcontrollerButtonEvent.OffScreenTriggerUp; Set_Hid_Button_Value(2, false); } } } /// /// Github issue #30 fix (https://github.com/argonlefou/DemulShooter/issues/30) /// Old ActLabs gun driver only outputs support for 2 mouse buttons. /// By default, RightClick is hardcoded on the 3rd value of _Hid_Buttons array, but the array length will only be 2 (= error) /// Hence this verification /// private void Set_Hid_Button_Value(int ButtonNumber, bool ButtonState) { if (_Hid_Buttons.Length > ButtonNumber) _Hid_Buttons[ButtonNumber] = ButtonState; } /// /// Special case for Sony Dual SHock 3 or WiiMotes with XInput driver : /// The driver is returning 0xFFFFFFFF as LogicalMax (which is -1 for a signed long) /// whereas real data are between 0x00000000 and 0x0000FFFF /// /// private int Correct_Axis_Max(int Max_Value) { if (Max_Value == -1) //-1 = 0xFFFFFFFF return 0x0000FFFF; else return Max_Value; } /// /// Trying to resolve an issue for APAC or device with negative Xmin [-2048;2048] /// GetUsageValue here is returning a 16bits signed values (i.e 0x0000F800 for -2048) instead of a full 32bits (i.e 0xFFFFF800) /// As a consequence, the Xmin being 0xFFFFF800, the value is not good for later calculation. /// Not sure if it's my code which is faulty and I do not have a device like this to test myself. /// The following workaround will simply add missing 0xFFFF0000 bytes when it's needed /// /// private int CorrectNegative_Value(int Value, int MinValue) { if (MinValue < 0) { if ((Int16)Value < 0) return (int)((uint)Value | 0xFFFF0000); } return Value; } /// /// Retrieve the DeviceName from a raw input device /// This name is also the filename used to open a File Handle with this device /// private void GetDeviceName() { uint pcbSize = 0; Win32API.GetRawInputDeviceInfo(_hDevice, RawInputUiCommand.RIDI_DEVICENAME, IntPtr.Zero, ref pcbSize); if (pcbSize <= 0) return; IntPtr pData = Marshal.AllocHGlobal((int)pcbSize); Win32API.GetRawInputDeviceInfo(_hDevice, RawInputUiCommand.RIDI_DEVICENAME, pData, ref pcbSize); _DeviceName = Marshal.PtrToStringAnsi(pData); Marshal.FreeHGlobal(pData); } /// /// Retrive RawInputDeviceInformation structure for the device /// private void GetDeviceInfo() { uint pcbSize = 0; Win32API.GetRawInputDeviceInfo(_hDevice, RawInputUiCommand.RIDI_DEVICEINFO, IntPtr.Zero, ref pcbSize); if (pcbSize <= 0) return; IntPtr pData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(RawInputDeviceInfo))); Win32API.GetRawInputDeviceInfo(_hDevice, RawInputUiCommand.RIDI_DEVICEINFO, pData, ref pcbSize); _DeviceInfo = (RawInputDeviceInfo)Marshal.PtrToStructure(pData, typeof(RawInputDeviceInfo)); Marshal.FreeHGlobal(pData); } /// /// Read and store Manufacturer string and Product string from the device /// These values are read by opening a File Handle with the device /// private void GetManufacturerAndProductString(String DevicePath) { IntPtr DeviceFileHandle = Win32API.CreateFile(DevicePath, Win32API.DesiredAccess.None, Win32API.ShareMode.Read | Win32API.ShareMode.Write, IntPtr.Zero, Win32API.CreateDisposition.OpenExisting, 0, IntPtr.Zero); if (DeviceFileHandle != new IntPtr(-1)) { byte[] buf = new byte[256]; if (!Win32API.HidD_GetManufacturerString(DeviceFileHandle, buf, (uint)buf.Length)) { //Error _ManufacturerName = "[Unknown Manufacturer]"; } else { _ManufacturerName = Encoding.Unicode.GetString(buf, 0, buf.Length); if (_ManufacturerName.Contains("\0")) _ManufacturerName = _ManufacturerName.Substring(0, _ManufacturerName.IndexOf("\0")); } if (!Win32API.HidD_GetProductString(DeviceFileHandle, buf, (uint)buf.Length)) { //Error _ProductName = "[Unknown Product]"; } else { _ProductName = Encoding.Unicode.GetString(buf, 0, buf.Length); if (_ProductName.Contains("\0")) { _ProductName = _ProductName.Substring(0, _ProductName.IndexOf("\0")); } } Win32API.CloseHandle(DeviceFileHandle); } } /// /// Retrieve a top-level collection's HIDP_CAPS structure for this device. /// /// Pointer to a top-level collection's preparsed data. /// Pointer to a caller-allocated buffer that the routine uses to return a collection's HIDP_CAPS structure /// TRUE is success, otherwise FALSE private bool GetCapabilities(IntPtr pPreparsedData, out HidPCaps Caps) { if (Win32API.HidP_GetCaps(pPreparsedData, out Caps) != NtStatus.Success) { return false; } return true; } /// /// Retrieve a button capability array that describes all the HID control buttons in a top-level collection for a specified type of HID report. /// /// Pointer to a top-level collection's preparsed data. /// Pointer to a caller-allocated buffer that the routine uses to return a collection's HIDP_CAPS structure /// Pointer to a caller-allocated buffer that the routine uses to return a button capability array for the specified report type. /// TRUE is success, otherwise FALSE private bool GetButtonCapabilities(IntPtr pPreparsedData, HidPCaps Caps, out HidPButtonCaps[] pButtonCaps) { ushort capsLength = Caps.NumberInputButtonCaps; pButtonCaps = new HidPButtonCaps[capsLength]; if (Win32API.HidP_GetButtonCaps(HidPReportType.Input, pButtonCaps, ref capsLength, pPreparsedData) != NtStatus.Success) { return false; } return true; } /// /// Retrieve a value capability array that describes all the HID control buttons in a top-level collection for a specified type of HID report. /// /// Pointer to a top-level collection's preparsed data. /// Pointer to a caller-allocated buffer that the routine uses to return a collection's HIDP_CAPS structure /// Pointer to a caller-allocated buffer that the routine uses to return a value capability array for the specified report type. /// TRUE is success, otherwise FALSE private bool GetValueCapabilities(IntPtr pPreparsedData, HidPCaps Caps, out HidPValueCaps[] pValueCaps) { ushort capsLength = Caps.NumberInputValueCaps; pValueCaps = new HidPValueCaps[capsLength]; if (Win32API.HidP_GetValueCaps(HidPReportType.Input, pValueCaps, ref capsLength, pPreparsedData) != NtStatus.Success) { return false; } return true; } private bool GetOutputValueCapabilities(IntPtr pPreparsedData, HidPCaps Caps, out HidPValueCaps[] pValueCaps) { ushort capsLength = Caps.NumberOutputValueCaps; pValueCaps = new HidPValueCaps[capsLength]; if (Win32API.HidP_GetValueCaps(HidPReportType.Output, pValueCaps, ref capsLength, pPreparsedData) != NtStatus.Success) { return false; } return true; } /// /// Retrieve the pointer to the previously parsed data for a HID device /// /// A pointer to the pPreparsedData struct is succes, otherwise IntPtr.Zero private IntPtr GetPreparsedData() { uint result = 0; uint bufferSize = 0; result = Win32API.GetRawInputDeviceInfo(_hDevice, RawInputUiCommand.RID_PREPARSEDDATA, IntPtr.Zero, ref bufferSize); if (result != 0) { return IntPtr.Zero; } IntPtr pPreparsedData = Marshal.AllocHGlobal((int)bufferSize); result = Win32API.GetRawInputDeviceInfo(_hDevice, RawInputUiCommand.RID_PREPARSEDDATA, pPreparsedData, ref bufferSize); if (result == 0) { Marshal.FreeHGlobal(pPreparsedData); return IntPtr.Zero; } return pPreparsedData; } /// /// Count the number of available buttons for this specific HID device /// Only ised when this Controller is of type HID (not MOUSE or KEYBOARD) /// /// Pointer to a previously parsed data structure /// Number of available input buttons for this HID device private int GetNumberOfHidDeviceButtons(IntPtr pPreparsedData, HidPButtonCaps[] pButtonCaps) { if (_pPreparsedData != IntPtr.Zero) { int nButtons = pButtonCaps[0].Range.UsageMax - pButtonCaps[0].Range.UsageMin + 1; return nButtons; } return 0; } /// /// Retrieve the RawInputHeader struct from a WM_INPUT LParam /// /// LParam parameter from WindowLoop during WM_INPUT message /// TRUE if success, otherwise, FALSE private bool GetRawInputHeader(IntPtr LParam) { uint HeaderSize = (uint)Marshal.SizeOf(typeof(RawInputHeader)); if (Win32API.GetRawInputData(LParam, RawInputUiCommand.RID_HEADER, out _RawInputHeader, ref HeaderSize, HeaderSize) != HeaderSize) { //Error return false; } return true; } /// /// Retrieving the raw input data struct when a WM_INPUT message has arrived /// For Mouse and keyboard we can directly fill the RawInput struct /// For HID we need to get RawHeader and RawHid separatly because RawHid is not constant size /// /// LParam parameter from WindowLoop during WM_INPUT message /// private bool GetRawInputData(IntPtr LParam) { uint HeaderSize = (uint)Marshal.SizeOf(typeof(RawInputHeader)); uint dwSize = 0; uint size = (uint)_RawInputHeader.dwSize; Win32API.GetRawInputData(LParam, RawInputUiCommand.RID_INPUT, IntPtr.Zero, ref dwSize, (uint)Marshal.SizeOf(typeof(RawInputHeader))); uint result; if (_dwType == RawInputDeviceType.RIM_TYPEHID) { IntPtr p = Marshal.AllocHGlobal((int)size); result = Win32API.GetRawInputData(LParam, RawInputUiCommand.RID_INPUT, p, ref size, HeaderSize); if (result != 0xFFFF && result == size) { IntPtr RawHidDataPtr = IntPtr.Add(p, (int)HeaderSize); _RawHidData = RawHid.FromIntPtr(RawHidDataPtr); Marshal.FreeHGlobal(p); return true; } } else if (_dwType == RawInputDeviceType.RIM_TYPEMOUSE) { result = Win32API.GetRawInputData(LParam, RawInputUiCommand.RID_INPUT, out _RawInputDataMouse, ref size, (uint)Marshal.SizeOf(typeof(RawInputHeader))); if (result != 0xFFFF && result == size) { return true; } else return false; } else if (_dwType == RawInputDeviceType.RIM_TYPEKEYBOARD) { //TODO return false; } return false; } public void Set_SONY_DS4_Output(byte RightMotor, byte LeftMotor, byte RedLed, byte GreenLed, byte BlueLed) { IntPtr DeviceFileHandle = Win32API.CreateFile(_DeviceName, Win32API.DesiredAccess.Read | Win32API.DesiredAccess.Write, Win32API.ShareMode.Read | Win32API.ShareMode.Write, IntPtr.Zero, Win32API.CreateDisposition.OpenExisting, 0, IntPtr.Zero); if (DeviceFileHandle != new IntPtr(-1)) { byte[] buf = new byte[32]; buf[0] = 0x05; buf[1] = 0xFF; buf[4] = RightMotor; // 0-255 buf[5] = LeftMotor; // 0-255 buf[6] = RedLed; // 0-255 buf[7] = GreenLed; // 0-255 buf[8] = BlueLed; // 0-255 uint bytes_written; bool res = Win32API.WriteFile(DeviceFileHandle, buf, (uint)buf.Length, out bytes_written, IntPtr.Zero); Win32API.CloseHandle(DeviceFileHandle); } } public void Set_SONY_PS3_Output(byte RightMotor, byte LeftMotor, byte RedLed, byte GreenLed, byte BlueLed) { IntPtr DeviceFileHandle = Win32API.CreateFile(_DeviceName, Win32API.DesiredAccess.Read | Win32API.DesiredAccess.Write, Win32API.ShareMode.Read | Win32API.ShareMode.Write, IntPtr.Zero, Win32API.CreateDisposition.OpenExisting, 0, IntPtr.Zero); if (DeviceFileHandle != new IntPtr(-1)) { byte[] command = new byte[37]; command = new byte[] {0x52, 0x01, 0x00, 0xfe, RightMotor, 0xfe, LeftMotor, 0x00, 0x00, 0x00, 0x00, RedLed, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint bytes_written; bool res = Win32API.WriteFile(DeviceFileHandle, command, (uint)command.Length, out bytes_written, IntPtr.Zero); Win32API.CloseHandle(DeviceFileHandle); } } } [Flags] public enum RawInputcontrollerButtonEvent : int { OnScreenTriggerDown = 0x00000001, OnScreenTriggerUp = 0x00000002, OffScreenTriggerDown = 0x00000010, OffScreenTriggerUp = 0x00000020, ActionDown = 0x000000100, ActionUp = 0x00000200, } } ================================================ FILE: DsCore/RawInput/RawInputHelper.cs ================================================ using System; using System.Runtime.InteropServices; using DsCore.Win32; using System.Collections.Generic; namespace DsCore.RawInput { public static class RawInputHelper { /// /// Enumerates all available RawInput Devices and returned them as an Array /// /// Array of RawInputController public static RawInputController[] GetRawInputDevices() { return GetRawInputDevices(new RawInputDeviceType[] { RawInputDeviceType.RIM_TYPEHID, RawInputDeviceType.RIM_TYPEKEYBOARD, RawInputDeviceType.RIM_TYPEMOUSE }); } /// /// Enumerates available RawInput Devices and returned selected Types as an Array /// /// Filter to select devices to keep based on their Type. Others won't be returned in the Array /// Array of RawInputController from desired Type(s) public static RawInputController[] GetRawInputDevices(RawInputDeviceType[] AccecptedDeviceTypes) { uint deviceCount = 0; var dwSize = (Marshal.SizeOf(typeof(RawInputDeviceList))); List Result = new List(); if (Win32API.GetRawInputDeviceList(IntPtr.Zero, ref deviceCount, (uint)dwSize) == 0) { IntPtr pRawInputDeviceList = Marshal.AllocHGlobal((int)(dwSize * deviceCount)); Win32API.GetRawInputDeviceList(pRawInputDeviceList, ref deviceCount, (uint)dwSize); for (int i = 0; i < deviceCount; i++) { // On Window 8 64bit when compiling against .Net > 3.5 using .ToInt32 you will generate an arithmetic overflow. Leave as it is for 32bit/64bit applications RawInputDeviceList rid = (RawInputDeviceList)Marshal.PtrToStructure(new IntPtr((pRawInputDeviceList.ToInt64() + (dwSize * i))), typeof(RawInputDeviceList)); RawInputController controller = new RawInputController(rid.hDevice, rid.dwType); foreach (RawInputDeviceType Type in AccecptedDeviceTypes) { if (controller.DeviceType == Type) { Result.Add(controller); break; } } } Marshal.FreeHGlobal(pRawInputDeviceList); } return Result.ToArray(); } } } ================================================ FILE: DsCore/RawInput/User32/RawHid.cs ================================================ using System; using System.Drawing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// Describes the format of the raw input from a Human Interface Device (HID). /// public struct RawHid { /// /// The size, in bytes, of each HID input in bRawData. /// public int dwSizeHid; /// /// The number of HID inputs in bRawData. /// public int dwCount; /// /// The raw input data, as an array of bytes. /// public byte[] bRawData; /// /// The bRawData array is variable in lenght, and using GetDeviceInfo we can get a pointer to some RawInput structure /// containing the RawInputHeader + RawHID structures. /// This functions will parse bytes from the pointer to fill a struct with a fixed size byte array as bRawData /// /// Pointer to RAWINPUT struct obtained with a call GetRawInputDeviceInfo /// RawHid struct filled with data public static RawHid FromIntPtr(IntPtr Ptr) { RawHid result = new RawHid(); //Bytes 0-3 = dwSizeHid //Bytes 4-7 = dwCount byte[] buffer = new byte[8]; Marshal.Copy(Ptr, buffer, 0, 8); result.dwSizeHid = BitConverter.ToInt32(buffer, 0); result.dwCount = BitConverter.ToInt32(buffer, 4); //Creating a fixed size byte array result.bRawData = new byte[result.dwCount * result.dwSizeHid]; try { Marshal.Copy(IntPtr.Add(Ptr, 8), result.bRawData, 0, result.bRawData.Length); return result; } catch { return result; } } public override string ToString() { return string.Format("dwCount : {0}, dwSize : {1}, rawData: {2}", dwCount, dwSizeHid, BitConverter.ToString(bRawData).Replace("-", " ")); } } } ================================================ FILE: DsCore/RawInput/User32/RawInputDataKeyboard.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.RawInput { [StructLayout(LayoutKind.Sequential)] public struct RawInputDataKeyboard { public RawInputHeader header; // 64 bit header size is 24 32 bit the header size is 16 public RawKeyboard data; // Creating the rest in a struct allows the header size to align correctly for 32 or 64 bit } /// /// Contains information about the state of the keyboard /// [StructLayout(LayoutKind.Sequential)] public struct RawKeyboard { /// /// The Scan code from the key depression. /// public ushort Makecode; /// /// Flags for scan code information. /// public RawKeyboardFlags Flags; /// /// Reserved, must be Zero /// public ushort Reserved; /// /// Windows message compatible virtual-key code. /// public ushort VKey; /// /// The corresponding window message, for example WM_KEYDOWN, WM_SYSKEYDOWN. /// public uint Message; /// /// The device-specific additional information for the event. /// public uint ExtraInformation; public override string ToString() { return string.Format("Makecode: {0}, Makecode(hex) : {0:X}, Flags: {1}, Reserved: {2}, VKeyName: {3}, Message: {4}, ExtraInformation {5}", Makecode, Flags, Reserved, VKey, Message, ExtraInformation); } } [Flags] public enum RawKeyboardFlags : ushort { /// /// The key is down. /// RI_KEY_MAKE, /// /// The key is up. /// RI_KEY_BREAK, /// /// The scan code has E0 prefix. /// RI_KEY_E0, /// /// The scan code has E1 prefix. /// RI_KEY_E1 = 4, } } ================================================ FILE: DsCore/RawInput/User32/RawInputDataMouse.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.RawInput { [StructLayout(LayoutKind.Sequential)] public struct RawInputDataMouse { public RawInputHeader header; // 64 bit header size is 24 32 bit the header size is 16 public RawMouse data; // Creating the rest in a struct allows the header size to align correctly for 32 or 64 bit } /// /// Contains information about the state of the mouse /// [StructLayout(LayoutKind.Explicit)] public struct RawMouse { /// /// The mouse state. /// [FieldOffset(0)] public RawMouseFlags usFlags; /// /// The transition state of the mouse button. /// [FieldOffset(4)] public uint ulButtons; /// /// The transition state of the mouse button. /// [FieldOffset(4)] public RawMouseButtonFlags usButtonFlags; /// /// If usButtonFlag is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta. /// [FieldOffset(6)] public ushort usButtonData; /// /// The raw state of the mouse buttons. /// [FieldOffset(8)] public uint ulRawButtons; /// /// The motion in the X direction. This is signed relative motion or absolute motion, depending on the value of usFlags. /// [FieldOffset(12)] public int lLastX; /// /// The motion in the Y direction. This is signed relative motion or absolute motion, depending on the value of usFlags. /// [FieldOffset(16)] public int lLastY; /// /// The device-specific additional information for the event. /// /// Mouse movement data is relative to the last mouse position. /// MoveRelative = 0, /// /// Mouse movement data is based on absolute position. /// MoveAbsolute = 1, /// /// Mouse coordinates are mapped to the virtual desktop (for a multiple monitor system). /// VirtualDesktop = 2, /// /// Mouse attributes changed; application needs to query the mouse attributes. /// AttributesChanged = 4, } [Flags] public enum RawMouseButtonFlags : ushort { /// /// Nothing. /// RI_MOUSE_NO_BUTTONS, /// /// Left button changed to down. /// RI_MOUSE_LEFT_BUTTON_DOWN = 0x0001, /// /// Left button changed to up. /// RI_MOUSE_LEFT_BUTTON_UP = 0x0002, /// /// Right button changed to down. /// RI_MOUSE_RIGHT_BUTTON_DOWN = 0x0004, /// /// Right button changed to up. /// RI_MOUSE_RIGHT_BUTTON_UP = 0x0008, /// /// Middle button changed to down. /// RI_MOUSE_MIDDLE_BUTTON_DOWN = 0x0010, /// /// Middle button changed to up. /// RI_MOUSE_MIDDLE_BUTTON_UP = 0x0020, /// /// XBUTTON1 changed to down. /// RI_MOUSE_BUTTON_4_DOWN = 0x0040, /// /// XBUTTON1 changed to up. /// RI_MOUSE_BUTTON_4_UP = 0x0080, /// /// XBUTTON2 changed to down. /// RI_MOUSE_BUTTON_5_DOWN = 0x0100, /// /// XBUTTON2 changed to up. /// RI_MOUSE_BUTTON_5_UP = 0x0200, /// /// Raw input comes from a mouse wheel. The wheel delta is stored in us ButtonData. /// RI_MOUSE_WHEEL = 0x0400, /// /// Raw input comes from a mouse horizontal wheel. The wheel delta is stored in us ButtonData. /// RI_MOUSE_HORIZONTAL_WHEEL = 0x0800, } } ================================================ FILE: DsCore/RawInput/User32/RawInputDevice.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// Device infirmation for the raw input device. /// [StructLayout(LayoutKind.Sequential)] public struct RawInputDevice { /// /// Top level collection Usage page for the raw input device. /// public HidUsagePage UsagePage; /// /// Top level collection Usage for the raw input device. /// public HidUsage Usage; /// /// Mode flag that specifies how to interpret the information provided by usUsagePage and usUsage. /// public RawInputDeviceFlags dwFlags; /// /// A handle to the target window. If NULL it follows the keyboard focus. /// public IntPtr hwndTarget; public override string ToString() { return string.Format("UsagePage/Page: {0}/{1}, flags: {2}, target: {3}", UsagePage, Usage, dwFlags, hwndTarget); } } } ================================================ FILE: DsCore/RawInput/User32/RawInputDeviceFlag.cs ================================================ using System; namespace DsCore.RawInput { /// /// Mode flag that sepcifies how to interpret the information provided by usUsagePage and usUsage. /// [Flags] public enum RawInputDeviceFlags { /// No flags. RIDEV_NONE = 0, /// If set, this removes the top level collection from the inclusion list. This tells the operating system to stop reading from a device which matches the top level collection. RIDEV_REMOVE = 0x00000001, /// If set, this specifies the top level collections to exclude when reading a complete usage page. This flag only affects a TLC whose usage page is already specified with PageOnly. EXCLUDE = 0x00000010, /// If set, this specifies all devices whose top level collection is from the specified UsagePage. Note that Usage must be zero. To exclude a particular top level collection, use Exclude. RIDEV_PAGEONLY = 0x00000020, /// If set, this prevents any devices specified by UsagePage or Usage from generating legacy messages. This is only for the mouse and keyboard. RIDEV_NOLEGACY = 0x00000030, /// If set, this enables the caller to receive the input even when the caller is not in the foreground. Note that WindowHandle must be specified. RIDEV_INPUTSINK = 0x00000100, /// If set, the mouse button click does not activate the other window. RIDEV_CAPTUREMOUSE = 0x00000200, /// If set, the application-defined keyboard device hotkeys are not handled. However, the system hotkeys; for example, ALT+TAB and CTRL+ALT+DEL, are still handled. By default, all keyboard hotkeys are handled. NoHotKeys can be specified even if NoLegacy is not specified and WindowHandle is NULL. RIDEV_NOHOTKEYS = 0x00000200, /// If set, application keys are handled. NoLegacy must be specified. Keyboard only. RIDEV_APPKEYS = 0x00000400, /// If set, this enables the caller to receive input in the background only if the foreground application /// does not process it. In other words, if the foreground application is not registered for raw input, /// then the background application that is registered will receive the input. /// RIDEV_EXINPUTSINK = 0x00001000, /// /// If set, this enables the caller to receive WM_INPUT_DEVICE_CHANGE notifications for device arrival and removal. /// RIDEV_DEVNOTIFY = 0x00002000 } } ================================================ FILE: DsCore/RawInput/User32/RawInputDeviceInfo.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// Defines the raw input data coming from any device. /// [StructLayout(LayoutKind.Explicit)] public struct RawInputDeviceInfo { /// /// The size, in bytes, of the RawInputDeviceInfo structure. /// [FieldOffset(0)] public int cbSize; /// /// The type of raw input data. this member can be one of the following values : RIM_TYPEHID, RIM_TYPEKEYBOARD or RIM_TYPEMOUSE /// [FieldOffset(4)] public RawInputDeviceType dwType; /// /// The HID device is defined by the structure RawInputMouseInfo. /// [FieldOffset(8)] public RawInputMouseInfo mouse; /// /// The HID device is defined by the structure RawInputKeyboardInfo. /// [FieldOffset(8)] public RawInputKeyboardInfo keyboard; /// /// The HID device is defined by the structure RawInputHidInfo. /// [FieldOffset(8)] public RawInputHidInfo hid; public override string ToString() { string str = string.Format("cbSize: {0}, dwType: {1}", cbSize, dwType); switch (dwType) { case RawInputDeviceType.RIM_TYPEMOUSE: { str = str + string.Format(", dwId: {0}, dwNumberOfButtons: {1}, dwSampleRate: {2}, fHasHorizontalWheel: {3}", mouse.dwId, mouse.dwNumberOfButtons, mouse.dwSampleRate, mouse.fHasHorizontalWheel); }break; case RawInputDeviceType.RIM_TYPEHID: { str = str + string.Format(", dwVendorId: 0x{0:X4}, dwProductId: 0x{1:X4}, dwVersionNumber: {2}, usUsagePage: {3}, usUsage: {4}", hid.dwProductId, hid.dwVendorId, hid.dwVersionNumber, hid.usUsagePage, hid.usUsage); } break; } return str; } } /// /// Defines the raw input data coming from the specified mouse. /// [StructLayout(LayoutKind.Sequential)] public struct RawInputMouseInfo { /// /// The identifier of the mouse device. /// public int dwId; /// /// The number of buttons for the mouse. /// public int dwNumberOfButtons; /// /// the number of data points per second. This information may not be applicable for every mouse device. /// public int dwSampleRate; /// /// TRUE if the mouse has a wheel for horizontal scrolling; otherwise FALSE. /// [MarshalAs(UnmanagedType.Bool)] public bool fHasHorizontalWheel; } /// /// Defines the raw input data coming from the specified keyboard. /// [StructLayout(LayoutKind.Sequential)] public struct RawInputKeyboardInfo { /// /// The type of the keyboard. /// public int dwType; /// /// The subtype of the keyboard. /// public int dwSubType; /// /// The scan code mode. /// public int dwKeyboardMode; /// /// The number of function keys on the keyboard. /// public int dwNumberOfFunctionKeys; /// /// The number of LED indicators on the keyboard. /// public int dwNumberOfIndicators; /// /// The total number of keys on the keyboard. /// public int dwNumberOfKeysTotal; } /// /// Defines the raw input data coming from the specified Human Interface Device (HID). /// [StructLayout(LayoutKind.Sequential)] public struct RawInputHidInfo { /// /// The vendor identifier for the HID. /// public int dwVendorId; /// /// The product idendifier for the HID. /// public int dwProductId; /// /// the version number for the HID. /// public int dwVersionNumber; /// /// The top-level collection Usage Page for the device. /// public ushort usUsagePage; /// /// The top-level collection Usage for the device. /// public ushort usUsage; } } ================================================ FILE: DsCore/RawInput/User32/RawInputDeviceList.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// Contains information about a raw input device. /// [StructLayout(LayoutKind.Sequential)] public struct RawInputDeviceList { /// /// A handle to the raw input device. /// public IntPtr hDevice; /// /// The type of device. this can be one of the following values : RIM_TYPEHID, RIM_TYPEKEYBOARD or RIM_TYPEMOUSE. /// public RawInputDeviceType dwType; } } ================================================ FILE: DsCore/RawInput/User32/RawInputDeviceType.cs ================================================  namespace DsCore.RawInput { /// /// Type of RawInput device. /// public enum RawInputDeviceType { /// /// The device is a mouse. /// RIM_TYPEMOUSE, /// /// The device is a keyboard. /// RIM_TYPEKEYBOARD, /// /// The device is an HID that is not a keyboard and not a mouse. /// RIM_TYPEHID } } ================================================ FILE: DsCore/RawInput/User32/RawInputHeader.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.RawInput { /// /// Contains the header information that is part of raw input data. /// [StructLayout(LayoutKind.Sequential)] public struct RawInputHeader { /// /// The type of raw input. It can be one of the following values : RIM_TYPEHID, RIM_TYPEKEYBOARD or RIM_TYPEMOUSE. /// public RawInputDeviceType dwType; /// /// The size, in bytes, of the entire input packet of data. This includes RAWINPUT plus possible extra input reports in the RAWHID variable lenght array. /// public int dwSize; /// /// A handle to the device generating the raw input data. /// public IntPtr hDevice; /// /// The value passed in the wParam parameter of the WM_INPUt message /// public IntPtr wParam; public override string ToString() { return string.Format("dwType : {0}, DeviceHandle : {1}, dwSize: {2}, WParam: {3}", dwType, hDevice, dwSize, wParam); } } } ================================================ FILE: DsCore/RawInput/User32/RawInputUiCommand.cs ================================================  namespace DsCore.RawInput { /// /// Specifies what data will be returned in pData. /// public enum RawInputUiCommand : uint { /// /// /// RID_INPUT = 0x10000003, /// /// /// RID_HEADER = 0x10000005, /// /// pData points to a string that contains the device name. /// For this uiCommand only, the value in pcbSize is the character count (not the byte count). /// RIDI_DEVICENAME = 0x20000007, /// /// pData points to an RawInputDeviceInfo struct. /// RIDI_DEVICEINFO = 0x2000000b, /// /// pData points to the previously parsed data. /// RID_PREPARSEDDATA = 0x20000005 } } ================================================ FILE: DsCore/Win32/CopyDataStruct.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.Win32 { [StructLayout(LayoutKind.Sequential)] class CopyDataStruct { public IntPtr dwData; public int cbData; public IntPtr lpData; } } ================================================ FILE: DsCore/Win32/FileMapAccessType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace DsCore.Win32 { [Flags] public enum FileMapAccessType : uint { Copy = 0x01, Write = 0x02, Read = 0x04, AllAccess = 0x08, Execute = 0x20, } } ================================================ FILE: DsCore/Win32/HardwareScanCode.cs ================================================  namespace DsCore.Win32 { public enum HardwareScanCode : byte { DIK_ESCAPE = 0x01, DIK_1 = 0x02, DIK_2 = 0x03, DIK_3 = 0x04, DIK_4 = 0x05, DIK_5 = 0x06, DIK_6 = 0x07, DIK_7 = 0x08, DIK_8 = 0x09, DIK_9 = 0x0A, DIK_0 = 0x0B, DIK_MINUS = 0x0C, DIK_EQUALS = 0x0D, DIK_BACK = 0x0E, DIK_TAB = 0x0F, DIK_Q = 0x10, DIK_W = 0x11, DIK_E = 0x12, DIK_R = 0x13, DIK_T = 0x14, DIK_Y = 0x15, DIK_U = 0x16, DIK_I = 0x17, DIK_O = 0x18, DIK_P = 0x19, DIK_LBRACKET = 0x1A, DIK_RBRACKET = 0x1B, DIK_RETURN = 0x1C, DIK_LCONTROL = 0x1D, DIK_A = 0x1E, DIK_S = 0x1F, DIK_D = 0x20, DIK_F = 0x21, DIK_G = 0x22, DIK_H = 0x23, DIK_J = 0x24, DIK_K = 0x25, DIK_L = 0x26, DIK_SEMICOLON = 0x27, DIK_APOSTROPHE = 0x28, DIK_GRAVE = 0x29, DIK_LSHIFT = 0x2A, DIK_BACKSLASH = 0x2B, DIK_Z = 0x2C, DIK_X = 0x2D, DIK_C = 0x2E, DIK_V = 0x2F, DIK_B = 0x30, DIK_N = 0x31, DIK_M = 0x32, DIK_COMMA = 0x33, DIK_PERIOD = 0x34, DIK_SLASH = 0x35, DIK_RSHIFT = 0x36, DIK_MULTIPLY = 0x37, DIK_LMENU = 0x38, DIK_SPACE = 0x39, DIK_CAPITAL = 0x3A, DIK_F1 = 0x3B, DIK_F2 = 0x3C, DIK_F3 = 0x3D, DIK_F4 = 0x3E, DIK_F5 = 0x3F, DIK_F6 = 0x40, DIK_F7 = 0x41, DIK_F8 = 0x42, DIK_F9 = 0x43, DIK_F10 = 0x44, DIK_NUMLOCK = 0x45, DIK_SCROLL = 0x46, DIK_NUMPAD7 = 0x47, DIK_NUMPAD8 = 0x48, DIK_NUMPAD9 = 0x49, DIK_SUBTRACT = 0x4A, DIK_NUMPAD4 = 0x4B, DIK_NUMPAD5 = 0x4C, DIK_NUMPAD6 = 0x4D, DIK_ADD = 0x4E, DIK_NUMPAD1 = 0x4F, DIK_NUMPAD2 = 0x50, DIK_NUMPAD3 = 0x51, DIK_NUMPAD0 = 0x52, DIK_DECIMAL = 0x53, DIK_F11 = 0x57, DIK_F12 = 0x58, DIK_F13 = 0x64, DIK_F14 = 0x65, DIK_F15 = 0x66, DIK_KANA = 0x70, DIK_CONVERT = 0x79, DIK_NOCONVERT = 0x7B, DIK_YEN = 0x7D, DIK_NUMPADEQUALS = 0x8D, DIK_CIRCUMFLEX = 0x90, DIK_AT = 0x91, DIK_COLON = 0x92, DIK_UNDERLINE = 0x93, DIK_KANJI = 0x94, DIK_STOP = 0x95, DIK_AX = 0x96, DIK_UNLABELED = 0x97, DIK_NUMPADENTER = 0x9C, DIK_RCONTROL = 0x9D, DIK_NUMPADCOMMA = 0xB3, DIK_DIVIDE = 0xB5, DIK_SYSRQ = 0xB7, DIK_RMENU = 0xB8, DIK_HOME = 0xC7, DIK_UP = 0xC8, DIK_PRIOR = 0xC9, DIK_LEFT = 0xCB, DIK_RIGHT = 0xCD, DIK_END = 0xCF, DIK_DOWN = 0xD0, DIK_NEXT = 0xD1, DIK_INSERT = 0xD2, DIK_DELETE = 0xD3, DIK_LWIN = 0xDB, DIK_RWIN = 0xDC, DIK_APPS = 0xDD, } } ================================================ FILE: DsCore/Win32/Input.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.Win32 { /// /// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. /// https://docs.microsoft.com/fr-fr/windows/win32/api/winuser/ns-winuser-input /// [StructLayout(LayoutKind.Explicit)] public struct INPUT { [FieldOffset(0)] public InputType type; [FieldOffset(4)] public MOUSEINPUT mi; [FieldOffset(4)] public KEYBDINPUT ki; [FieldOffset(4)] public HARDWAREINPUT hi; } /// /// The type of the input event. This member can be one of the following values. /// public enum InputType : int { INPUT_MOUSE = 0, INPUT_KEYBOARD = 1, INPUT_HARDWARE = 2 } /// /// Contains information about a simulated mouse event. /// https://docs.microsoft.com/fr-fr/windows/win32/api/winuser/ns-winuser-mouseinput /// [StructLayout(LayoutKind.Sequential)] public struct MOUSEINPUT { public int dx; public int dy; public int mouseData; public int dwFlags; public int time; public IntPtr dwExtraInfo; } /// /// Contains information about a simulated keyboard event. /// https://docs.microsoft.com/fr-fr/windows/win32/api/winuser/ns-winuser-keybdinput /// [StructLayout(LayoutKind.Sequential)] public struct KEYBDINPUT { public short wVk; public HardwareScanCode wScan; public KeybdInputFlags dwFlags; public int time; public IntPtr dwExtraInfo; } /// /// Contains information about a simulated message generated by an input device other than a keyboard or mouse. /// https://docs.microsoft.com/fr-fr/windows/win32/api/winuser/ns-winuser-hardwareinput /// [StructLayout(LayoutKind.Sequential)] public struct HARDWAREINPUT { public int uMsg; public short wParamL; public short wParamH; } [Flags] public enum KeybdInputFlags : uint { KEYEVENTF_EXTENDEDKEY = 0x0001, KEYEVENTF_KEYUP = 0x0002, KEYEVENTF_UNICODE = 0x0004, KEYEVENTF_SCANCODE = 0x0008, } } ================================================ FILE: DsCore/Win32/KbdLlHookStruct.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.Win32 { /// /// Contains information about a low-level keyboard input event. /// [StructLayout(LayoutKind.Sequential)] public struct KBDLLHOOKSTRUCT { /// /// A virtual-key code. The code must be a value in the range 1 to 254. /// public int vkCode; /// /// A hardware scan code for the key. /// public HardwareScanCode scanCode; /// /// The extended-key flag, event-injected flags, context code, and transition-state flag. /// public int flags; /// /// The time stamp for this message, equivalent to what GetMessageTime would return for this message. /// public int time; /// /// Additional information associated with the message. /// public UIntPtr dwExtraInfo; public override string ToString() { return string.Format("vkCode=0x{0:X}, scanCode=0x{1:X}, flags={2}, time={3}, dwextrainfo={4:X}", vkCode, scanCode, flags, time, dwExtraInfo); } } } ================================================ FILE: DsCore/Win32/MemoryAllocType.cs ================================================ using System; namespace DsCore.Win32 { [Flags] public enum MemoryAllocType { MEM_COMMIT = 0x1000, MEM_RESERVE = 0x2000, MEM_RESET = 0x8000, MEM_RESET_UNDO = 0x1000000, } } ================================================ FILE: DsCore/Win32/MemoryFreeType.cs ================================================ using System; namespace DsCore.Win32 { [Flags] public enum MemoryFreeType { MEM_COALESCE_PLACEHOLDERS = 0x00000001, MEM_PRESERVE_PLACEHOLDER = 0x00000002, MEM_DECOMMIT = 0x4000, MEM_RELEASE = 0x8000, } } ================================================ FILE: DsCore/Win32/MemoryPageProtect.cs ================================================ using System; namespace DsCore.Win32 { [Flags] public enum MemoryPageProtect { PAGE_EXECUTE = 0x10, PAGE_EXECUTE_READ = 0x20, PAGE_EXECUTE_READWRITE = 0x40, PAGE_EXECUTE_WRITECOPY = 0x80, PAGE_NOACCESS = 0x01, PAGE_READONLY = 0x02, PAGE_READWRITE = 0x04, PAGE_WRITECOPY = 0x08, PAGE_TARGETS_INVALID = 0x40000000, PAGE_TARGETS_NO_UPDATE = 0x40000000 } } ================================================ FILE: DsCore/Win32/MsLlHookStruct.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.Win32 { /// /// Contains information about a low-level mouse input event. /// [StructLayout(LayoutKind.Sequential)] public struct MSLLHOOKSTRUCT { /// /// The x- and y-coordinates of the cursor, in per-monitor-aware screen coordinates. /// public POINT pt; /// /// Complementary Data /// public int mouseData; /// /// The event-injected flags. /// public int flags; /// /// The time stamp for this message. /// public int time; /// /// Additional information associated with the message. /// public UIntPtr dwExtraInfo; } } ================================================ FILE: DsCore/Win32/PageProtection.cs ================================================ using System; namespace DsCore.Win32 { [Flags] public enum PageProtection : uint { NoAccess = 0x01, Readonly = 0x02, ReadWrite = 0x04, WriteCopy = 0x08, Execute = 0x10, ExecuteRead = 0x20, ExecuteReadWrite = 0x40, ExecuteWriteCopy = 0x80, Guard = 0x100, NoCache = 0x200, WriteCombine = 0x400, } } ================================================ FILE: DsCore/Win32/Point.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.Win32 { /// /// The POINT structure defines the x- and y-coordinates of a point. /// https://docs.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point /// [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; public POINT(int x, int y) { this.X = x; this.Y = y; } } } ================================================ FILE: DsCore/Win32/QueryUserNotificationState.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace DsCore.Win32 { public enum QUERY_USER_NOTIFICATION_STATE { QUNS_NOT_PRESENT = 1, QUNS_BUSY = 2, QUNS_RUNNING_D3D_FULL_SCREEN = 3, QUNS_PRESENTATION_MODE = 4, QUNS_ACCEPTS_NOTIFICATIONS = 5, QUNS_QUIET_TIME = 6, QUNS_APP = 7 }; } ================================================ FILE: DsCore/Win32/Rect.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace DsCore.Win32 { /// /// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. /// public struct Rect { /// /// Specifies the x-coordinate of the upper-left corner of the rectangle. /// public int Left { get; set; } /// /// Specifies the y-coordinate of the upper-left corner of the rectangle. /// public int Top { get; set; } /// /// Specifies the x-coordinate of the lower-right corner of the rectangle. /// public int Right { get; set; } /// /// Specifies the y-coordinate of the lower-right corner of the rectangle. /// public int Bottom { get; set; } } } ================================================ FILE: DsCore/Win32/SystemMetricsIndex.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace DsCore.Win32 { /// /// The system metric or configuration setting to be retrieved by GetsystemMetrics(). /// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics /// public enum SystemMetricsIndex : int { SM_ARRANGE = 56, SM_CLEANBOOT = 67, SM_CMONITORS = 80, SM_CMOUSEBUTTONS = 43, SM_CONVERTIBLESLATEMODE = 0x2003, SM_CXBORDER = 5, SM_CXCURSOR = 13, SM_CXDLGFRAME = 7, SM_CXDOUBLECLK = 36, SM_CXDRAG = 68, SM_CXEDGE = 45, SM_CXFIXEDFRAME = 7, SM_CXFOCUSBORDER = 83, SM_CXFRAME = 32, SM_CXFULLSCREEN = 16, SM_CXHSCROLL = 21, SM_CXHTHUMB = 10, SM_CXICON = 11, SM_CXICONSPACING = 38, SM_CXMAXIMIZED = 61, SM_CXMAXTRACK = 59, SM_CXMENUCHECK = 71, SM_CXMENUSIZE = 54, SM_CXMIN = 28, SM_CXMINIMIZED = 57, SM_CXMINSPACING = 47, SM_CXMINTRACK = 34, SM_CXPADDEDBORDER = 92, SM_CXSCREEN = 0, SM_CXSIZE = 30, SM_CXSIZEFRAME = 32, SM_CXSMICON = 49, SM_CXSMSIZE = 52, SM_CXVIRTUALSCREEN = 78, SM_CXVSCROLL = 2, SM_CYBORDER = 6, SM_CYCAPTION = 4, SM_CYCURSOR = 14, SM_CYDLGFRAME = 8, SM_CYDOUBLECLK = 37, SM_CYDRAG = 69, SM_CYEDGE = 46, SM_CYFIXEDFRAME = 8, SM_CYFOCUSBORDER = 84, SM_CYFRAME = 33, SM_CYFULLSCREEN = 17, SM_CYHSCROLL = 3, SM_CYICON = 12, SM_CYICONSPACING = 39, SM_CYKANJIWINDOW = 18, SM_CYMAXIMIZED = 62, SM_CYMAXTRACK = 60, SM_CYMENU = 15, SM_CYMENUCHECK = 72, SM_CYMENUSIZE = 55, SM_CYMIN = 29, SM_CYMINIMIZED = 58, SM_CYMINSPACING = 48, SM_CYMINTRACK = 35, SM_CYSCREEN = 1, SM_CYSIZE = 31, SM_CYSIZEFRAME = 33, SM_CYSMCAPTION = 51, SM_CYSMICON = 50, SM_CYSMSIZE = 53, SM_CYVIRTUALSCREEN = 79, SM_CYVSCROLL = 20, SM_CYVTHUMB = 9, SM_DBCSENABLED = 42, SM_DEBUG = 22, SM_DIGITIZER = 94, SM_IMMENABLED = 82, SM_MAXIMUMTOUCHES = 95, SM_MEDIACENTER = 87, SM_MENUDROPALIGNMENT = 40, SM_MIDEASTENABLED = 74, SM_MOUSEPRESENT = 19, SM_MOUSEHORIZONTALWHEELPRESENT = 91, SM_MOUSEWHEELPRESENT = 75, SM_NETWORK = 63, SM_PENWINDOWS = 41, SM_REMOTECONTROL = 0x2001, SM_REMOTESESSION = 0x1000, SM_SAMEDISPLAYFORMAT = 81, SM_SECURE = 44, SM_SERVERR2 = 89, SM_SHOWSOUNDS = 70, SM_SHUTTINGDOWN = 0x2000, SM_SLOWMACHINE = 73, SM_STARTER = 88, SM_SWAPBUTTON = 23, SM_SYSTEMDOCKED = 0x2004, SM_TABLETPC = 86, SM_XVIRTUALSCREEN = 76, SM_YVIRTUALSCREEN = 77 } } ================================================ FILE: DsCore/Win32/VirtualKeyCode.cs ================================================  namespace DsCore.Win32 { public enum VirtualKeyCode : byte { VK_LBUTTON = 0x01, VK_RBUTTON = 0x02, VK_CANCEL = 0x03, VK_MBUTTON = 0x04, VK_XBUTTON1 = 0x05, VK_XBUTTON2 = 0x06, VK_UNDEFINED_1 = 0x07, VK_BACK = 0x08, VK_TAB = 0x09, VK_RESERVED_1 = 0x0A, VK_RESERVED_2 = 0x0B, VK_CLEAR = 0x0C, VK_RETURN = 0x0D, VK_UNDEFINED_2 = 0x0E, VK_UNDEFINED_3 = 0x0F, VK_SHIFT = 0x10, VK_CONTROL = 0x11, VK_MENU = 0x12, VK_PAUSE = 0x13, VK_CAPITAL = 0x14, VK_KANA = 0x15, VK_HANGUEL = 0x15, VK_HANGUL = 0x15, VK_IME_ON = 0x16, VK_JUNJA = 0x17, VK_FINAL = 0x18, VK_HANJA = 0x19, VK_KANJI = 0x19, VK_IME_OFF = 0x1A, VK_ESCAPE = 0x1B, VK_CONVERT = 0x1C, VK_NONCONVERT = 0x1D, VK_ACCEPT = 0x1E, VK_MODECHANGE = 0x1F, VK_SPACE = 0x20, VK_PRIOR = 0x21, VK_NEXT = 0x22, VK_END = 0x23, VK_HOME = 0x24, VK_LEFT = 0x25, VK_UP = 0x26, VK_RIGHT = 0x27, VK_DOWN = 0x28, VK_SELECT = 0x29, VK_PRINT = 0x2A, VK_EXECUTE = 0x2B, VK_SNAPSHOT = 0x2C, VK_INSERT = 0x2D, VK_DELETE = 0x2E, VK_HELP = 0x2F, VK_0 = 0x30, VK_1 = 0x31, VK_2 = 0x32, VK_3 = 0x33, VK_4 = 0x34, VK_5 = 0x35, VK_6 = 0x36, VK_7 = 0x37, VK_8 = 0x38, VK_9 = 0x39, VK_UNDEFINED_4 = 0x3A, VK_UNDEFINED_5 = 0x3B, VK_UNDEFINED_6 = 0x3C, VK_UNDEFINED_7 = 0x3D, VK_UNDEFINED_8 = 0x3E, VK_UNDEFINED_9 = 0x3F, VK_UNDEFINED_10 = 0x40, VK_A = 0x41, VK_B = 0x42, VK_C = 0x43, VK_D = 0x44, VK_E = 0x45, VK_F = 0x46, VK_G = 0x47, VK_H = 0x48, VK_I = 0x49, VK_J = 0x4A, VK_K = 0x4B, VK_L = 0x4C, VK_M = 0x4D, VK_N = 0x4E, VK_O = 0x4F, VK_P = 0x50, VK_Q = 0x51, VK_R = 0x52, VK_S = 0x53, VK_T = 0x54, VK_U = 0x55, VK_V = 0x56, VK_W = 0x57, VK_X = 0x58, VK_Y = 0x59, VK_Z = 0x5A, VK_LWIN = 0x5B, VK_RWIN = 0x5C, VK_APPS = 0x5D, VK_RESERVED_3 = 0x5E, VK_SLEEP = 0x5F, VK_NUMPAD0 = 0x60, VK_NUMPAD1 = 0x61, VK_NUMPAD2 = 0x62, VK_NUMPAD3 = 0x63, VK_NUMPAD4 = 0x64, VK_NUMPAD5 = 0x65, VK_NUMPAD6 = 0x66, VK_NUMPAD7 = 0x67, VK_NUMPAD8 = 0x68, VK_NUMPAD9 = 0x69, VK_MULTIPLY = 0x6A, VK_ADD = 0x6B, VK_SEPARATOR = 0x6C, VK_SUBSTRACT = 0x6D, VK_DECIMAL = 0x6E, VK_DIVIDE = 0x6F, VK_F1 = 0x70, VK_F2 = 0x71, VK_F3 = 0x72, VK_F4 = 0x73, VK_F5 = 0x74, VK_F6 = 0x75, VK_F7 = 0x76, VK_F8 = 0x77, VK_F9 = 0x78, VK_F10 = 0x79, VK_F11 = 0x7A, VK_F12 = 0x7B, VK_F13 = 0x7C, VK_F14 = 0x7D, VK_F15 = 0x7E, VK_F16 = 0x7F, VK_F17 = 0x80, VK_F18 = 0x81, VK_F19 = 0x82, VK_F20 = 0x83, VK_F21 = 0x84, VK_F22 = 0x85, VK_F23 = 0x86, VK_F24 = 0x87, VK_UNASSIGNED_1 = 0x88, VK_UNASSIGNED_2 = 0x89, VK_UNASSIGNED_3 = 0x8A, VK_UNASSIGNED_4 = 0x8B, VK_UNASSIGNED_5 = 0x8C, VK_UNASSIGNED_6 = 0x8D, VK_UNASSIGNED_7 = 0x8E, VK_UNASSIGNED_8 = 0x8F, VK_NUMLOCK = 0x90, VK_SCROLL = 0x91, VK_OEM_SPECIFIC_1 = 0x92, VK_OEM_SPECIFIC_2 = 0x93, VK_OEM_SPECIFIC_3 = 0x94, VK_OEM_SPECIFIC_4 = 0x95, VK_OEM_SPECIFIC_5 = 0x96, VK_UNASSIGNED_9 = 0x97, VK_UNASSIGNED_10 = 0x98, VK_UNASSIGNED_11 = 0x99, VK_UNASSIGNED_12 = 0x9A, VK_UNASSIGNED_13 = 0x9B, VK_UNASSIGNED_14 = 0x9C, VK_UNASSIGNED_15 = 0x9D, VK_UNASSIGNED_16 = 0x9E, VK_UNASSIGNED_17 = 0x9F, VK_LSHIFT = 0xA0, VK_RSHIFT = 0xA1, VK_LCONTROL = 0xA2, VK_RCONTROL = 0xA3, VK_LMENU = 0xA4, VK_RMENU = 0xA5, VK_BROWSER_BACK = 0xA6, VK_BROWSER_FORWARD = 0xA7, VK_BROWSER_REFRESH = 0xA8, VK_BROWSER_STOP = 0xA9, VK_BROWSER_SEARCH = 0xAA, VK_BROWSER_FAVORITES = 0xAB, VK_BROWSER_HOME = 0xAC, VK_VOLUME_MUTE = 0xAD, VK_VOLUME_DOWN = 0xAE, VK_VOLUME_UP = 0xAF, VK_MEDIA_NEXT_TRACK = 0xB0, VK_MEDIA_PREV_TRACK = 0xB1, VK_MEDIA_STOP = 0xB2, VK_MEDIA_PLAY_PAUSE = 0xB3, VK_LAUNCH_MAIL = 0xB4, VK_LAUNCH_MEDIA_SELECT = 0xB5, VK_LAUNCH_APP1 = 0xB6, VK_LAUNCH_APP2 = 0xB7, VK_RESERVED_4 = 0xB8, VK_RESERVED_5 = 0xB9, VK_OEM_1 = 0xBA, VK_OEM_PLUS = 0xBB, VK_OEM_COMMA = 0xBC, VK_OEM_MINUS = 0xBD, VK_OEM_PERIOD = 0xBE, VK_OEM_2 = 0xBF, VK_OEM_3 = 0xC0, VK_RESERVED_6 = 0xC1, VK_RESERVED_7 = 0xC3, VK_RESERVED_8 = 0xC3, VK_RESERVED_9 = 0xC4, VK_RESERVED_10 = 0xC5, VK_RESERVED_11 = 0xC6, VK_RESERVED_12 = 0xC7, VK_RESERVED_13 = 0xC8, VK_RESERVED_14 = 0xC9, VK_RESERVED_15 = 0xCA, VK_RESERVED_16 = 0xCB, VK_RESERVED_17 = 0xCC, VK_RESERVED_18 = 0xCD, VK_RESERVED_19 = 0xCE, VK_RESERVED_20 = 0xCF, VK_RESERVED_21 = 0xD0, VK_RESERVED_22 = 0xD1, VK_RESERVED_23 = 0xD2, VK_RESERVED_24 = 0xD3, VK_RESERVED_25 = 0xD4, VK_RESERVED_26 = 0xD5, VK_RESERVED_27 = 0xD6, VK_RESERVED_28 = 0xD7, VK_UNASSIGNED_18 = 0xD8, VK_UNASSIGNED_19= 0xD9, VK_UNASSIGNED_20 = 0xDA, VK_OEM_4 = 0xDB, VK_OEM_5 = 0xDC, VK_OEM_6 = 0xDD, VK_OEM_7 = 0xDE, VK_OEM_8 = 0xDF, VK_RESERVED_29 = 0xE0, VK_OEM_SPECIFIC_6 = 0xE1, VK_OEM_102 = 0xE2, VK_OEM_SPECIFIC_7 = 0xE3, VK_OEM_SPECIFIC_8 = 0xE4, VK_PROCESSKEY = 0xE5, VK_OEM_SPECIFIC_9 = 0xE6, VK_PACKET = 0xE7, VK_UNASSIGNED_21 = 0xE8, VK_OEM_SPECIFIC_10 = 0xE9, VK_OEM_SPECIFIC_11 = 0xEA, VK_OEM_SPECIFIC_12 = 0xEB, VK_OEM_SPECIFIC_13 = 0xEC, VK_OEM_SPECIFIC_14 = 0xED, VK_OEM_SPECIFIC_15 = 0xEE, VK_OEM_SPECIFIC_16 = 0xEF, VK_OEM_SPECIFIC_17 = 0xF0, VK_OEM_SPECIFIC_18 = 0xF1, VK_OEM_SPECIFIC_19 = 0xF2, VK_OEM_SPECIFIC_20 = 0xF3, VK_OEM_SPECIFIC_21 = 0xF4, VK_OEM_SPECIFIC_22 = 0xF5, VK_ATTN = 0xF6, VK_CRSEL = 0xF7, VK_EXSEL = 0xF8, VK_EREOF = 0xF9, VK_PLAY = 0xFA, VK_ZOOM = 0xFB, VK_NONAME = 0xFC, VK_PA1 = 0xFD, VK_OEM_CLEAR = 0xFE, } } ================================================ FILE: DsCore/Win32/VirtualKeyMapType.cs ================================================ namespace DsCore.Win32 { /// /// Used for MapVirtualKey() or MapVirtualKeyEx() /// The translation to be performed. The value of this parameter depends on the value of the uCode parameter. /// public enum VirtualKeyMapType { /// /// uCode is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the function returns 0. /// MAPVK_VK_TO_VSC = 0x00, /// /// uCode is a scan code and is translated into a virtual-key code that does not distinguish between left- and right-hand keys. If there is no translation, the function returns 0. /// MAPVK_VSC_TO_VK = 0x01, /// /// uCode is a virtual-key code and is translated into an unshifted character value in the low-order word of the return value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, the function returns 0. /// MAPVK_VK_TO_CHAR = 0x02, /// /// uCode is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand keys. If there is no translation, the function returns 0. /// MAPVK_VSC_TO_VK_EX = 0x03, /// /// The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If the scan code is an extended scan code, the high byte of the uCode value can contain either 0xe0 or 0xe1 to specify the extended scan code. If there is no translation, the function returns 0. /// MAPVK_VK_TO_VSC_EX = 0x04, } } ================================================ FILE: DsCore/Win32/Win32Api.cs ================================================ using System; using System.Runtime.InteropServices; using System.Text; using DsCore.RawInput; namespace DsCore.Win32 { public static class Win32API { #region hid.dll [DllImport("hid", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool HidD_GetManufacturerString(IntPtr HidDeviceObject, [Out] byte[] Buffer, uint BufferLength); [DllImport("hid", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool HidD_GetProductString(IntPtr HidDeviceObject, [Out] byte[] Buffer, uint BufferLength); [DllImport("hid.dll", SetLastError = true)] public static extern NtStatus HidP_GetCaps(IntPtr pPreparsedData, out HidPCaps Capabilities); [DllImport("hid.dll", SetLastError = true)] public static extern NtStatus HidP_GetButtonCaps(HidPReportType ReportType, [Out] HidPButtonCaps[] ButtonCaps, ref ushort ButtonCapsLength, IntPtr pPreparsedData); [DllImport("hid.dll", CharSet = CharSet.Auto)] public static extern NtStatus HidP_GetValueCaps(HidPReportType ReportType, [Out] HidPValueCaps[] Values, ref ushort ValueCapsLength, IntPtr pPreparsedData); [DllImport("hid")] public static extern NtStatus HidP_GetUsages(HidPReportType ReportType, ushort UsagePage, ushort LinkCollection, [Out] ushort[] UsageList, ref uint UsageLength, IntPtr pPreparsedData, byte[] Report, uint ReportLength); [DllImport("hid")] //public static extern NtStatus HidP_GetUsageValue(HidPReportType ReportType, ushort UsagePage, ushort LinkCollection, ushort Usage, out long UsageValue, IntPtr pPreparsedData, byte[] Report, uint ReportLength); public static extern NtStatus HidP_GetUsageValue(HidPReportType ReportType, ushort UsagePage, ushort LinkCollection, ushort Usage, out int UsageValue, IntPtr pPreparsedData, byte[] Report, uint ReportLength); [DllImport("hid")] public static extern NtStatus HidP_GetScaledUsageValue(HidPReportType ReportType, ushort UsagePage, ushort LinkCollection, ushort Usage, out int UsageValue, IntPtr pPreparsedData, byte[] Report, uint ReportLength); #endregion #region user32.dll #region GDI [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] public static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase); [DllImport("user32.dll", SetLastError = true)] public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); #endregion #region RawInput API [DllImport("user32.dll", SetLastError = true)] public static extern uint GetRawInputData(IntPtr hRawInput, RawInputUiCommand uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetRawInputData(IntPtr hRawInput, RawInputUiCommand uiCommand, out RawInputHeader pData, ref uint pcbSize, uint cbSizeHeader); [DllImport("User32.dll", SetLastError = true)] public static extern uint GetRawInputData(IntPtr hRawInput, RawInputUiCommand uiCommand, out RawInputDataMouse pData, ref uint pcbSize, uint cbSizeHeader); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetRawInputDeviceInfo(IntPtr hDevice, RawInputUiCommand uiCommand, IntPtr pData, ref uint dataSize); [DllImport("user32", SetLastError = true)] public static extern uint GetRawInputDeviceInfo(IntPtr hDevice, RawInputUiCommand uiCommand, out RawInputDeviceInfo pData, uint pcbSize); [DllImport("User32.dll", SetLastError = true)] public static extern uint GetRawInputDeviceList(IntPtr pRawInputDeviceList, ref uint NumberDevices, uint Size); [DllImport("User32.dll", SetLastError = true)] public static extern bool RegisterRawInputDevices(RawInputDevice[] pRawInputDevice, uint NumberDevices, uint Size); #endregion #region Low Level Hook API [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId); /** ScanCode <-> VK_CODE mapping **/ [DllImport("user32.dll", SetLastError = true)] public static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl); [DllImport("user32.dll", SetLastError = true)] public static extern uint MapVirtualKey(uint uCode, VirtualKeyMapType uMapType); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId); public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnhookWindowsHookEx(IntPtr hhk); #endregion #region Screen API [DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx")] public static extern IntPtr CreateWindowEx( int dwExStyle, UInt16 regResult, //string lpClassName, string lpWindowName, UInt32 dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); [DllImport("user32.dll")] public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern bool EnableWindow(IntPtr hWnd, bool enable); [DllImport("user32.dll")] public static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam); public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string className, string windowTitle); [DllImport("user32.dll", SetLastError = true)] public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll", SetLastError = true)] public static extern bool GetClientRect(IntPtr hWnd, ref Rect rectangle); [DllImport("user32.dll")] public static extern bool GetCursorPos(out POINT point); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetDesktopWindow(); [DllImport("gdi32.dll")] public static extern int GetDeviceCaps(IntPtr hdc, int nIndex); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetForegroundWindow(); [DllImport("kernel32.dll")] public static extern uint GetLastError(); [DllImport("user32.dll")] public static extern int GetSystemMetrics(SystemMetricsIndex smIndex); [DllImport("user32.dll", SetLastError = true)] public static extern bool GetWindowRect(IntPtr hWnd, ref Rect rectangle); [DllImport("user32.dll", SetLastError = true)] public static extern int GetWindowText(IntPtr Hwnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out UInt32 lpdwProcessId); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName); [DllImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassEx")] public static extern UInt16 RegisterClassEx([In] ref WNDCLASSEX lpWndClass); [DllImport("user32.dll")] public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint); [DllImport("user32.dll")] public static extern IntPtr SetCapture(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool UpdateWindow(IntPtr hWnd); [DllImport("shell32.dll")] public static extern int SHQueryUserNotificationState(out QUERY_USER_NOTIFICATION_STATE pquns); #endregion #region Windows Messages [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern uint RegisterWindowMessage(string lpString); [return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); #endregion /** VirtualKey Sending **/ /** Used for Operation Ghost which have hardcoded Numpad Keys that are not working with DIK if no keyboard plugged **/ [DllImport("user32.dll", SetLastError = true)] public static extern void keybd_event(VirtualKeyCode bVk, byte bScan, KeybdInputFlags dwFlags, int dwExtraInfo); /** DIRECTINPUT SendKey **/ [DllImport("user32.dll", SetLastError = true)] public static extern UInt32 SendInput(UInt32 nInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] pInputs, Int32 cbSize); [DllImport("user32.dll")] public static extern int ShowCursor(bool bShow); [DllImport("USER32.dll")] public static extern short GetKeyState(VirtualKeyCode nVirtKey); #endregion #region kernel32.dll #region I/O API [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr CreateFile(string lpFileName, DesiredAccess dwDesiredAccess, ShareMode dwShareMode, IntPtr lpSecurityAttributes, CreateDisposition dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped); [Flags] public enum DesiredAccess : uint { None, Write = 0x40000000, Read = 0x80000000 } [Flags] public enum ShareMode : uint { None, Read = 0x00000001, Write = 0x00000002, Delete = 0x00000004 } public enum CreateDisposition : uint { CreateNew = 1, CreateAlways, OpenExisting, OpenAlways, TruncateExisting } #endregion #region Memory API [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool ReadProcessMemory(IntPtr hProcess, UInt32 lpBaseAddress, byte[] lpBuffer, UInt32 dwSize, ref UInt32 lpNumberOfBytesRead); [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "ReadProcessMemory")] [return: MarshalAs(UnmanagedType.Bool)] public static extern Boolean ReadProcessMemoryX64([In] IntPtr hProcess, [In] IntPtr lpBaseAddress, [Out] Byte[] lpBuffer, [In] UIntPtr nSize, [Out] out UIntPtr lpNumberOfBytesRead); [DllImport("kernel32.dll")] public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, UInt32 dwSize, MemoryAllocType flAllocationType, MemoryPageProtect flProtect); [DllImport("kernel32.dll")] public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, UInt32 dwSize, MemoryFreeType dwFreeType); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool WriteProcessMemory(IntPtr hProcess, UInt32 lpBaseAddress, byte[] lpBuffer, UInt32 dwSize, ref UInt32 lpNumberOfBytesWritten); [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "WriteProcessMemory")] [return: MarshalAs(UnmanagedType.Bool)] public static extern Boolean WriteProcessMemoryX64([In] IntPtr hProcess, [In] IntPtr lpBaseAddress, [Out] Byte[] lpBuffer, [In] UIntPtr nSize, [Out] out UIntPtr lpNumberOfBytesWritten); #endregion #region Mapped Memory File [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, PageProtection flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr OpenFileMapping(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, FileMapAccessType dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress); #endregion [DllImport("kernel32.dll", SetLastError = true)] public static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize); [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)] public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName); [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); #endregion #region winmm.dll [DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")] public static extern uint MM_BeginPeriod(uint uMilliseconds); [DllImport("winmm.dll", EntryPoint = "timeEndPeriod")] public static extern uint MM_EndPeriod(uint uMilliseconds); #endregion } } ================================================ FILE: DsCore/Win32/Win32Define.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace DsCore.Win32 { public static class Win32Define { //Mouse and Keyboard Hook public const int WH_MOUSE_LL = 14; public const int WH_KEYBOARD_LL = 13; //Windows messages public const UInt32 WM_QUIT = 0x0012; public const UInt32 WM_COPYDATA = 0x004A; public const UInt32 WM_INPUT = 0x00FF; public const UInt32 WM_KEYDOWN = 0x0100; public const UInt32 WM_KEYUP = 0x0101; public const UInt32 WM_SYSKEYDOWN = 0x0104; public const UInt32 WM_MOUSEMOVE = 0x0200; public const UInt32 WM_LBUTTONDOWN = 0x0201; public const UInt32 WM_LBUTTONUP = 0x0202; public const UInt32 WM_RBUTTONDOWN = 0x0204; public const UInt32 WM_RBUTTONUP = 0x0205; public const UInt32 WM_MBUTTONDOWN = 0x0207; public const UInt32 WM_MBUTTONUP = 0x0208; public const UInt32 WM_MOUSEWHEEL = 0x020A; public const UInt32 WM_USB_DEVICECHANGE = 0x0219; public const UInt32 WM_APP = 0x8000; //Mapped Memory File public const UInt32 ERROR_ALREADY_EXISTS = 183; public const Int32 INVALID_HANDLE_VALUE = -1; public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000; public const UInt32 SECTION_QUERY = 0x0001; public const UInt32 SECTION_MAP_WRITE = 0x0002; public const UInt32 SECTION_MAP_READ = 0x0004; public const UInt32 SECTION_MAP_EXECUTE = 0x0008; public const UInt32 SECTION_EXTEND_SIZE = 0x0010; public const UInt32 SECTION_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE); public const UInt32 FILE_MAP_ALL_ACCESS = SECTION_ALL_ACCESS; } } ================================================ FILE: DsCore/Win32/WndClassEx.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.Win32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct WNDCLASSEX { [MarshalAs(UnmanagedType.U4)] public int cbSize; [MarshalAs(UnmanagedType.U4)] public int style; public IntPtr lpfnWndProc; public int cbClsExtra; public int cbWndExtra; public IntPtr hInstance; public IntPtr hIcon; public IntPtr hCursor; public IntPtr hbrBackground; public string lpszMenuName; public string lpszClassName; public IntPtr hIconSm; } } ================================================ FILE: DsCore/XInput/XInputBatteryInformation.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.XInput { /// /// Contains information on battery type and charge state. /// [StructLayout(LayoutKind.Explicit)] public struct XInputBatteryInformation { /// /// The type of battery. BatteryType will be one of the following values. /// [MarshalAs(UnmanagedType.I1)] [FieldOffset(0)] public BatteryTypes BatteryType; /// /// The charge state of the battery. This value is only valid for wireless devices with a known battery type. /// [MarshalAs(UnmanagedType.I1)] [FieldOffset(1)] public BatteryLevel BatteryLevel; } /// /// Flags for battery status level /// public enum BatteryTypes : byte { /// This device is not connected BATTERY_TYPE_DISCONNECTED = 0x00, /// Wired device, no battery BATTERY_TYPE_WIRED = 0x01, /// Alkaline battery source BATTERY_TYPE_ALKALINE = 0x02, /// Nickel Metal Hydride battery source BATTERY_TYPE_NIMH = 0x03, /// Cannot determine the battery type BATTERY_TYPE_UNKNOWN = 0xFF, }; /// /// These are only valid for wireless, connected devices, with known battery types /// The amount of use time remaining depends on the type of device. /// public enum BatteryLevel : byte { BATTERY_LEVEL_EMPTY = 0x00, BATTERY_LEVEL_LOW = 0x01, BATTERY_LEVEL_MEDIUM = 0x02, BATTERY_LEVEL_FULL = 0x03 }; } ================================================ FILE: DsCore/XInput/XInputCapabilities.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.XInput { /// /// Describes the capabilities of a connected controller. The XInputGetCapabilities function returns XINPUT_CAPABILITIES. /// [StructLayout(LayoutKind.Explicit)] public struct XInputCapabilities { /// /// Controller type. /// [MarshalAs(UnmanagedType.I1)] [FieldOffset(0)] XInputCapabilitiesType Type; /// /// Subtype of the game controller. See XINPUT and Controller Subtypes for a list of allowed subtypes. /// [MarshalAs(UnmanagedType.I1)] [FieldOffset(1)] public byte SubType; /// /// Features of the controller. /// [MarshalAs(UnmanagedType.I2)] [FieldOffset(2)] public CapabilityFlags Flags; /// /// XINPUT_GAMEPAD structure that describes available controller features and control resolutions. /// [FieldOffset(4)] public XInputGamepad Gamepad; /// /// XINPUT_VIBRATION structure that describes available vibration functionality and resolutions. /// [FieldOffset(16)] public XInputVibration Vibration; } /// /// Flags for XINPUT_CAPABILITIES public enum XInputCapabilitiesType : byte { /// /// The device is a game controller. /// XINPUT_DEVTYPE_GAMEPAD = 0x01 }; public enum CapabilityFlags : short { /// /// Device has an integrated voice device. /// XINPUT_CAPS_VOICE_SUPPORTED = 0x0004, /// /// Device supports force feedback functionality. /// Note that these force-feedback features beyond rumble are not currently supported through XINPUT on Windows. /// XINPUT_CAPS_FFB_SUPPORTED = 0x0001, /// /// Device is wireless. /// XINPUT_CAPS_WIRELESS = 0x0002, /// /// Device supports plug-in modules. /// Note that plug-in modules like the text input device (TID) are not supported currently through XINPUT on Windows. /// XINPUT_CAPS_PMD_SUPPORTED = 0x0008, /// /// Device lacks menu navigation buttons (START, BACK, DPAD). /// XINPUT_CAPS_NO_NAVIGATION = 0x0010, }; } ================================================ FILE: DsCore/XInput/XInputDeviceSubtype.cs ================================================ using System; namespace DsCore.XInput { [Flags] public enum XInputDeviceSubtype { XINPUT_DEVSUBTYPE_UNKNOWN = 0x00, XINPUT_DEVSUBTYPE_WHEEL = 0x02, XINPUT_DEVSUBTYPE_ARCADE_STICK = 0x03, XINPUT_DEVSUBTYPE_FLIGHT_STICK = 0x04, XINPUT_DEVSUBTYPE_DANCE_PAD = 0x05, XINPUT_DEVSUBTYPE_GUITAR = 0x06, XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE = 0x07, XINPUT_DEVSUBTYPE_DRUM_KIT = 0x08, XINPUT_DEVSUBTYPE_GUITAR_BASS = 0x0B, XINPUT_DEVSUBTYPE_ARCADE_PAD = 0x13 }; } ================================================ FILE: DsCore/XInput/XInputGamepad.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.XInput { /// /// Describes the current state of the Xbox 360 Controller. /// [StructLayout(LayoutKind.Explicit)] public struct XInputGamepad { /// /// Bitmask of the device digital buttons, as follows. A set bit indicates that the corresponding button is pressed. /// [MarshalAs(UnmanagedType.I2)] [FieldOffset(0)] public XinputButtonFlags wButtons; /// /// The current value of the left trigger analog control. The value is between 0 and 255. /// [MarshalAs(UnmanagedType.I1)] [FieldOffset(2)] public byte bLeftTrigger; /// /// The current value of the right trigger analog control. The value is between 0 and 255. /// [MarshalAs(UnmanagedType.I1)] [FieldOffset(3)] public byte bRightTrigger; /// /// Left thumbstick x-axis value. Each of the thumbstick axis members is a signed value between /// -32768 and 32767 describing the position of the thumbstick. /// A value of 0 is centered. /// Negative values signify down or to the left. /// Positive values signify up or to the right. /// The constants XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE or XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE /// can be used as a positive and negative value to filter a thumbstick input. /// [MarshalAs(UnmanagedType.I2)] [FieldOffset(4)] public short sThumbLX; /// /// Left thumbstick y-axis value. The value is between -32768 and 32767. /// [MarshalAs(UnmanagedType.I2)] [FieldOffset(6)] public short sThumbLY; /// /// Right thumbstick x-axis value. The value is between -32768 and 32767. /// [MarshalAs(UnmanagedType.I2)] [FieldOffset(8)] public short sThumbRX; /// /// Right thumbstick y-axis value. The value is between -32768 and 32767. /// [MarshalAs(UnmanagedType.I2)] [FieldOffset(10)] public short sThumbRY; } } ================================================ FILE: DsCore/XInput/XInputHandler.cs ================================================ using System; using System.Runtime.InteropServices; namespace DsCore.XInput { /// /// This class is used to handle various XInput devices plugged on the computer. /// We can read axis and buttons values, and even set rumble force and speed. /// public class XInputHandler { // // Gamepad thresholds // public const int XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE = 7849; public const int XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE = 8689; public const int XINPUT_GAMEPAD_TRIGGER_THRESHOLD = 30; // //Various stuff // public const int MAX_CONTROLLER_COUNT = 4; public const int FIRST_CONTROLLER_INDEX = 0; public const int ERROR_DEVICE_NOT_CONNECTED = 0x48f; public const int ERROR_SUCCES = 0; /// /// Retrieves the capabilities and features of a connected controller. /// /// /// Index of the user's controller. Can be a value in the range 0–3. /// /// /// Input flags that identify the controller type. /// If this value is 0, then the capabilities of all controllers connected to the system are returned. /// Currently, only one value is supported: XINPUT_FLAG_GAMEPAD /// /// /// Pointer to an XINPUT_CAPABILITIES structure that receives the controller capabilities. /// /// /// If the function succeeds, the return value is ERROR_SUCCESS. /// If the controller is not connected, the return value is ERROR_DEVICE_NOT_CONNECTED. /// If the function fails, the return value is an error code defined in WinError.h. /// The function does not use SetLastError to set the calling thread's last-error code. /// [DllImport("xinput9_1_0.dll")] public static extern int XInputGetCapabilities ( int dwUserIndex, int dwFlags, ref XInputCapabilities pCapabilities ); public const int XINPUT_FLAG_GAMEPAD = 0x00000001; /// /// Retrieves the current state of the specified controller. /// /// Index of the user's controller. Can be a value from 0 to 3. /// Pointer to an XINPUT_STATE structure that receives the current state of the controller. /// [DllImport("xinput9_1_0.dll")] public static extern int XInputGetState ( int dwUserIndex, ref XInputState pState ); /// /// Sends data to a connected controller. This function is used to activate the vibration function of a controller. /// /// Index of the user's controller. Can be a value from 0 to 3. /// Pointer to an XINPUT_VIBRATION structure containing the vibration information to send to the controller. /// [DllImport("xinput9_1_0.dll")] public static extern int XInputSetState ( int dwUserIndex, ref XInputVibration pVibration ); } } ================================================ FILE: DsCore/XInput/XInputState.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.XInput { /// /// Represents the state of a controller. /// [StructLayout(LayoutKind.Explicit)] public struct XInputState { /// /// State packet number. The packet number indicates whether there have been any changes in the state of the controller. /// If the dwPacketNumber member is the same in sequentially returned XINPUT_STATE structures, /// the controller state has not changed. /// [FieldOffset(0)] public int dwPacketNumber; /// /// XINPUT_GAMEPAD structure containing the current state of an Xbox 360 Controller. /// [FieldOffset(4)] public XInputGamepad Gamepad; } } ================================================ FILE: DsCore/XInput/XInputVibration.cs ================================================ using System.Runtime.InteropServices; namespace DsCore.XInput { /// /// Specifies motor speed levels for the vibration function of a controller. /// The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor. /// The two motors are not the same, and they create different vibration effects. /// [StructLayout(LayoutKind.Sequential)] public struct XInputVibration { /// /// Speed of the left motor. Valid values are in the range 0 to 65,535. /// Zero signifies no motor use /// 65 535 signifies 100 percent motor use. /// [MarshalAs(UnmanagedType.I2)] public ushort LeftMotorSpeed; /// /// Speed of the right motor. Valid values are in the range 0 to 65,535. /// Zero signifies no motor use; /// 65 535 signifies 100 percent motor use. /// [MarshalAs(UnmanagedType.I2)] public ushort RightMotorSpeed; } } ================================================ FILE: DsCore/XInput/XinputButtonFlags.cs ================================================ using System; namespace DsCore.XInput { [Flags] public enum XinputButtonFlags : ushort { XINPUT_GAMEPAD_DPAD_UP = 0x0001, XINPUT_GAMEPAD_DPAD_DOWN = 0x0002, XINPUT_GAMEPAD_DPAD_LEFT = 0x0004, XINPUT_GAMEPAD_DPAD_RIGHT = 0x0008, XINPUT_GAMEPAD_START = 0x0010, XINPUT_GAMEPAD_BACK = 0x0020, XINPUT_GAMEPAD_LEFT_THUMB = 0x0040, XINPUT_GAMEPAD_RIGHT_THUMB = 0x0080, XINPUT_GAMEPAD_LEFT_SHOULDER = 0x0100, XINPUT_GAMEPAD_RIGHT_SHOULDER = 0x0200, XINPUT_GAMEPAD_A = 0x1000, XINPUT_GAMEPAD_B = 0x2000, XINPUT_GAMEPAD_X = 0x4000, XINPUT_GAMEPAD_Y = 0x8000, }; } ================================================ FILE: DsCore/XOutput/XOutput.cs ================================================ using System; using System.Globalization; using System.Runtime.InteropServices; namespace DsCore { /// /// This class is used to create a virtual XInput device so that a game can believe that a Gamepad is plugged /// We can then simulate Axis and Buttons values to control in-game actions. /// This is meant to be used with BlueEstate and FarCry but both are still Work-In-Progress for many reasons.... /// public class XOutput { #region Struct [StructLayout(LayoutKind.Sequential)] private struct SP_DEVICE_INTERFACE_DATA { public int cbSize; public Guid InterfaceClassGuid; public int Flags; public IntPtr Reserved; } public enum MessageType { Plugin = 0x2A4000, Report = 0x2A400C, Unplug = 0x2A4004, IsDevPlug = 0x2AE404, EmptySlots = 0x2AE408 } #endregion #region Constant private const int INVALID_HANDLE_VALUE = -1; private const uint FILE_ATTRIBUTE_NORMAL = 0x80; private const uint FILE_FLAG_OVERLAPPED = 0x40000000; private const uint FILE_SHARE_READ = 1; private const uint FILE_SHARE_WRITE = 2; private const uint GENERIC_READ = 0x80000000; private const uint GENERIC_WRITE = 0x40000000; private const uint OPEN_EXISTING = 3; private const int DIGCF_PRESENT = 0x0002; private const int DIGCF_DEVICEINTERFACE = 0x0010; //Gamepad private const int FEEDBACK_BUFFER_LENGTH = 9; public const int MAX_NUMBER_XBOX_CTRLS = 4; public const short AXIS_MAX = 32767; public const short AXIS_MIN = -32768; #endregion #region Win32 PInvoke [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, UIntPtr hTemplateFile); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, byte[] lpInBuffer, uint nInBufferSize, byte[] lpOutBuffer, uint nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped); [DllImport("setupapi.dll", SetLastError = true)] private static extern int SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); [DllImport("setupapi.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, IntPtr hwndParent, int flags); [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, ref SP_DEVICE_INTERFACE_DATA deviceInfoData); #endregion private class Gamepad_state { private byte[] _StateBuffer; public byte[] Buffer { get { return _StateBuffer; } set { _StateBuffer = Buffer; } } public Gamepad_state() { _StateBuffer = new byte[18]; } } private Gamepad_state[] _Gamepads; private const string SCP_BUS_CLASS_GUID = "{F679F562-3164-42CE-A4DB-E7DDBE723909}"; private IntPtr _hBus; private int _Win32Error; public int Win32ErrorNo { get { return _Win32Error; } } public XOutput() { _hBus = new IntPtr(INVALID_HANDLE_VALUE); _Gamepads = new Gamepad_state[4]; } /// /// Look for an installed ScpVBus on the system /// /// /// True if Bus is fount /// False if not public bool isVBusExists() { string Path = ""; int n = GetVXbusPath(ref Path); if (n > 0) return true; else return false; } /// /// Plug a virtual Controller on one of the user port. /// Port are from 1 to 4, and are not the same as Player ID of /// the gamepad (LED Number) /// /// Virtual port on which the GamePad should be plugged /// /// True if succes. /// False if failed or port already used public bool PlugIn(int UserIndex) { bool OUT = false; if (UserIndex < 1 || UserIndex > 4) return OUT; if (_hBus == new IntPtr(INVALID_HANDLE_VALUE)) _hBus = GetVXbusHandle(); if (_hBus == new IntPtr(INVALID_HANDLE_VALUE)) return false; int Transfered = 0; byte[] buffer = new byte[16]; buffer[0] = 0x10; buffer[4] = (byte)((UserIndex >> 0) & 0xFF); buffer[5] = (byte)((UserIndex >> 8) & 0xFF); buffer[6] = (byte)((UserIndex >> 16) & 0xFF); buffer[8] = (byte)((UserIndex >> 24) & 0xFF); if (DeviceIoControl(_hBus, (int)MessageType.Plugin, buffer, 16, null, 0, ref Transfered, IntPtr.Zero)) { _Gamepads[UserIndex - 1] = new Gamepad_state(); OUT = true; } _Win32Error = 0; if (!OUT) _Win32Error = Marshal.GetLastWin32Error(); return OUT; } /// /// Unplug a virtual Gamepad from a virtual Port /// /// Virtual Port targetted for unplug /// If true, can unplug non-owned gamepad /// /// True if succes. /// Else if failed. /// public bool Unplug(int UserIndex, bool Force = false) { bool b = UnplugOpt(UserIndex, Force); if (b) _Gamepads[UserIndex - 1] = null; return b; } public bool UnplugAll(bool Force = false) { bool b = UnplugOpt(0, true); if (b) { for (int i = 0; i < MAX_NUMBER_XBOX_CTRLS; i++) _Gamepads[i] = null; } return b; } public bool GetLedNumber(int UserIndex, byte[] Led) { bool rep = XOutputSetGetState(UserIndex, _Gamepads[UserIndex - 1], null, null, null, Led); if (rep) Led[0]++; return rep; } public bool SetDPad_Up(int UserIndex) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 0; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetDPad_Down(int UserIndex) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 1; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetDPad_Left(int UserIndex) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 2; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetDPad_Right(int UserIndex) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 3; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetDPad_Off(int UserIndex) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 0; _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 1; _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 2; _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 3; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_Start(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 4; else _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 4; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_Back(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 5; else _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 5; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_L3(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 6; else _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 6; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_R3(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[0] |= 1 << 7; else _Gamepads[UserIndex - 1].Buffer[0] &= 0 << 7; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_L1(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[1] |= 1 << 0; else _Gamepads[UserIndex - 1].Buffer[1] &= 0 << 0; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_R1(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[1] |= 1 << 1; else _Gamepads[UserIndex - 1].Buffer[1] &= 0 << 1; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_A(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[1] |= 1 << 4; else _Gamepads[UserIndex - 1].Buffer[1] &= 0 << 4; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_B(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[1] |= 1 << 5; else _Gamepads[UserIndex - 1].Buffer[1] &= 0 << 5; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_X(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[1] |= 1 << 6; else _Gamepads[UserIndex - 1].Buffer[1] &= 0 << 6; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_Y(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[1] |= 1 << 7; else _Gamepads[UserIndex - 1].Buffer[1] &= 0 << 7; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_Guide(int UserIndex, bool Pressed) { if (_Gamepads[UserIndex - 1] != null) { if (Pressed) _Gamepads[UserIndex - 1].Buffer[1] |= 1 << 2; else _Gamepads[UserIndex - 1].Buffer[1] &= 0 << 2; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetLAxis_X(int UserIndex, short Value) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[4] = (byte)(Value & 0xFF); _Gamepads[UserIndex - 1].Buffer[5] = (byte)((Value >> 8) & 0xFF); return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetLAxis_Y(int UserIndex, short Value) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[6] = (byte)(Value & 0xFF); _Gamepads[UserIndex - 1].Buffer[7] = (byte)((Value >> 8) & 0xFF); return true; } else return false; } public bool SetRAxis_X(int UserIndex, short Value) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[8] = (byte)(Value & 0xFF); _Gamepads[UserIndex - 1].Buffer[9] = (byte)((Value >> 8) & 0xFF); return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetRAxis_Y(int UserIndex, short Value) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[10] = (byte)(Value & 0xFF); _Gamepads[UserIndex - 1].Buffer[11] = (byte)((Value >> 8) & 0xFF); return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_L2(int UserIndex, byte Value) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[2] = Value; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool SetButton_R2(int UserIndex, byte Value) { if (_Gamepads[UserIndex - 1] != null) { _Gamepads[UserIndex - 1].Buffer[3] = Value; return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } else return false; } public bool UpdateXoutputState(int UserIndex) { return XOutputSetState(UserIndex, _Gamepads[UserIndex - 1]); } private int GetVXbusPath(ref String Path) { IntPtr detailDataBuffer = IntPtr.Zero; IntPtr deviceInfoSet = IntPtr.Zero; SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); SP_DEVICE_INTERFACE_DATA Da = new SP_DEVICE_INTERFACE_DATA(); DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData); Da.cbSize = Marshal.SizeOf(DeviceInterfaceData); Guid deviceClassGuid = new Guid(SCP_BUS_CLASS_GUID); int memberIndex = 0; int requiredSize = 0; deviceInfoSet = SetupDiGetClassDevs(ref deviceClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref deviceClassGuid, memberIndex, ref DeviceInterfaceData)) { //get required target buffer size SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref requiredSize, ref Da); //Allocate target buffer detailDataBuffer = Marshal.AllocHGlobal(requiredSize); if (detailDataBuffer == IntPtr.Zero) return -1; Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); //Get detail Buffer if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, requiredSize, ref requiredSize, ref Da)) { SetupDiDestroyDeviceInfoList(deviceInfoSet); Marshal.FreeHGlobal(detailDataBuffer); return -1; } IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt64() + 4); Path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(CultureInfo.InvariantCulture); //CleanUp SetupDiDestroyDeviceInfoList(deviceInfoSet); Marshal.FreeHGlobal(detailDataBuffer); } else return -1; return requiredSize; } private IntPtr GetVXbusHandle() { string Path = ""; int n = GetVXbusPath(ref Path); if (n < 1) return new IntPtr(INVALID_HANDLE_VALUE); //bus found, open it and get handle _hBus = CreateFile(Path, (GENERIC_WRITE | GENERIC_READ), FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, UIntPtr.Zero); return _hBus; } private bool UnplugOpt(int UserIndex, bool Force) { bool OUT = false; if (UserIndex < 0 || UserIndex > 4) return OUT; if (_hBus == new IntPtr(INVALID_HANDLE_VALUE)) _hBus = GetVXbusHandle(); if (_hBus == new IntPtr(INVALID_HANDLE_VALUE)) return OUT; int Transfered = 0; //BUSENUM_UNPLUG_HARDWARE struct equivalent byte[] buf = new byte[16]; //Size of struct buf[0] = 0x10; //SerialNo buf[4] = (byte)((UserIndex) & 0xFF); buf[5] = (byte)((UserIndex >> 8) & 0xFF); buf[6] = (byte)((UserIndex >> 16) & 0xFF); buf[7] = (byte)((UserIndex >> 24) & 0xFF); //Flag if (Force) buf[8] = 1; if (DeviceIoControl(_hBus, (int)MessageType.Unplug, buf, 16, null, 0, ref Transfered, IntPtr.Zero)) { OUT = true; } _Win32Error = 0; if (!OUT) _Win32Error = Marshal.GetLastWin32Error(); return OUT; } private bool XOutputSetState(int UserIndex, Gamepad_state Gamepad) { bool OUT = false; if (UserIndex < 1 || UserIndex > 4) return OUT; if (_hBus == new IntPtr(INVALID_HANDLE_VALUE)) _hBus = GetVXbusHandle(); if (_hBus == new IntPtr(INVALID_HANDLE_VALUE)) return false; int Transfered = 0; byte[] buffer = new byte[28]; buffer[0] = 0x1C; // encode user index buffer[4] = (byte)((UserIndex >> 0) & 0xFF); buffer[5] = (byte)((UserIndex >> 8) & 0xFF); buffer[6] = (byte)((UserIndex >> 16) & 0xFF); buffer[7] = (byte)((UserIndex >> 24) & 0xFF); buffer[9] = 0x14; Array.Copy(Gamepad.Buffer, 0, buffer, 10, Gamepad.Buffer.Length); // vibration and LED info end up here byte[] output = new byte[FEEDBACK_BUFFER_LENGTH]; // send report to bus, receive vibration and LED status if (!DeviceIoControl(_hBus, (int)MessageType.Report, buffer, (uint)buffer.Length, output, FEEDBACK_BUFFER_LENGTH, ref Transfered, IntPtr.Zero)) { _Win32Error = 0; return false; } return true; } private bool XOutputSetGetState(int UserIndex, Gamepad_state Gamepad, byte[] bVibrate, byte[] bLargeMotor, byte[] bSmallMotor, byte[] bLed) { bool OUT = false; if (UserIndex < 1 || UserIndex > 4) return OUT; if (_Gamepads[UserIndex - 1] == null) return OUT; int Transfered = 0; byte[] Buffer = new byte[28]; Buffer[0] = 0x1C; // encode user index Buffer[4] = (byte)((UserIndex >> 0) & 0xFF); Buffer[5] = (byte)((UserIndex >> 8) & 0xFF); Buffer[6] = (byte)((UserIndex >> 16) & 0xFF); Buffer[7] = (byte)((UserIndex >> 24) & 0xFF); Buffer[9] = 0x14; // concat gamepad info to buffer Array.Copy(Gamepad.Buffer, 0, Buffer, 10, Gamepad.Buffer.Length); // vibration and LED info end up here byte[] Output = new byte[FEEDBACK_BUFFER_LENGTH]; // send report to bus, receive vibration and LED status if (!DeviceIoControl(_hBus, (int)MessageType.Report, Buffer, (uint)Buffer.Length, Output, FEEDBACK_BUFFER_LENGTH, ref Transfered, IntPtr.Zero)) { return false; } // cache feedback /*if (bVibrate != null) { *bVibrate = (output[1] == 0x08) ? 0x01 : 0x00; } if (bLargeMotor != null) { *bLargeMotor = output[3]; } if (bSmallMotor != null) { *bSmallMotor = output[4]; } if (bLed != null) { *bLed = output[8]; }*/ return true; } } } ================================================ FILE: DsDiag/DsDiag.csproj ================================================  Debug x86 8.0.30703 2.0 {A42BEED3-14C0-4034-ABD9-75C3D70B02A2} WinExe Properties DsDiag DsDiag v4.8 512 x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 false x86 pdbonly true bin\Release\ TRACE prompt 4 false false DemulShooter_Icon.ico UserControl Ds_Diag_Button.cs Form Ds_Diag.cs Ds_Diag.cs Ds_Diag_Button.cs ResXFileCodeGenerator Resources.Designer.cs Designer True Resources.resx True SettingsSingleFileGenerator Settings.Designer.cs True Settings.settings True {BD88CC4D-2944-43F8-85C3-A274A45487AF} DsCore ================================================ FILE: DsDiag/Ds_Diag.Designer.cs ================================================ namespace DsDiag { partial class DsDiag { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur Windows Form /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DsDiag)); this.Cbo_Dev = new System.Windows.Forms.ComboBox(); this.label1 = new System.Windows.Forms.Label(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.Lbl_OnClient = new System.Windows.Forms.Label(); this.label10 = new System.Windows.Forms.Label(); this.Lbl_ClientSize = new System.Windows.Forms.Label(); this.label8 = new System.Windows.Forms.Label(); this.Lbl_ScreenSize = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.Lbl_OnScreen = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.Lbl_RawInput = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.Pbox_Target = new System.Windows.Forms.PictureBox(); this.groupBox2 = new System.Windows.Forms.GroupBox(); this.Lbl_AxisYMax = new System.Windows.Forms.Label(); this.Lbl_AxisYMin = new System.Windows.Forms.Label(); this.lbl14 = new System.Windows.Forms.Label(); this.lbl13 = new System.Windows.Forms.Label(); this.Lbl_AxisXMax = new System.Windows.Forms.Label(); this.Lbl_AxisXMin = new System.Windows.Forms.Label(); this.label7 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.Cbo_AxisY = new System.Windows.Forms.ComboBox(); this.Cbo_AxisX = new System.Windows.Forms.ComboBox(); this.Lbl_AxisX_Txt = new System.Windows.Forms.Label(); this.FlowPanelButtons = new System.Windows.Forms.FlowLayoutPanel(); this.Lbl_Nbr_Buttons = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.Lbl_AxisY_Txt = new System.Windows.Forms.Label(); this.Lbl_Product = new System.Windows.Forms.Label(); this.label24 = new System.Windows.Forms.Label(); this.Lbl_Nbr_Axis = new System.Windows.Forms.Label(); this.Lbl_Nbr_Axis_Txt = new System.Windows.Forms.Label(); this.Lbl_Manufacturer = new System.Windows.Forms.Label(); this.label17 = new System.Windows.Forms.Label(); this.Lbl_dwType = new System.Windows.Forms.Label(); this.label19 = new System.Windows.Forms.Label(); this.Lbl_VID = new System.Windows.Forms.Label(); this.label21 = new System.Windows.Forms.Label(); this.Lbl_PID = new System.Windows.Forms.Label(); this.label23 = new System.Windows.Forms.Label(); this.Txt_Log = new System.Windows.Forms.RichTextBox(); this.Btn_Export = new System.Windows.Forms.Button(); this.Tmr_RefreshGui = new System.Windows.Forms.Timer(this.components); this.groupBox1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.Pbox_Target)).BeginInit(); this.groupBox2.SuspendLayout(); this.SuspendLayout(); // // Cbo_Dev // this.Cbo_Dev.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.Cbo_Dev.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbo_Dev.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Cbo_Dev.FormattingEnabled = true; this.Cbo_Dev.Location = new System.Drawing.Point(17, 36); this.Cbo_Dev.Margin = new System.Windows.Forms.Padding(5); this.Cbo_Dev.Name = "Cbo_Dev"; this.Cbo_Dev.Size = new System.Drawing.Size(630, 28); this.Cbo_Dev.TabIndex = 21; this.Cbo_Dev.SelectionChangeCommitted += new System.EventHandler(this.Cbo_Dev_SelectionChangeCommitted); // // label1 // this.label1.AutoSize = true; this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(16, 11); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(124, 20); this.label1.TabIndex = 22; this.label1.Text = "Select a device :"; // // groupBox1 // this.groupBox1.Controls.Add(this.Lbl_OnClient); this.groupBox1.Controls.Add(this.label10); this.groupBox1.Controls.Add(this.Lbl_ClientSize); this.groupBox1.Controls.Add(this.label8); this.groupBox1.Controls.Add(this.Lbl_ScreenSize); this.groupBox1.Controls.Add(this.label6); this.groupBox1.Controls.Add(this.Lbl_OnScreen); this.groupBox1.Controls.Add(this.label5); this.groupBox1.Controls.Add(this.Lbl_RawInput); this.groupBox1.Controls.Add(this.label2); this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.groupBox1.ForeColor = System.Drawing.Color.White; this.groupBox1.Location = new System.Drawing.Point(20, 81); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(371, 168); this.groupBox1.TabIndex = 23; this.groupBox1.TabStop = false; this.groupBox1.Text = "Device Data [x, y]:"; // // Lbl_OnClient // this.Lbl_OnClient.AutoSize = true; this.Lbl_OnClient.Location = new System.Drawing.Point(107, 137); this.Lbl_OnClient.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_OnClient.Name = "Lbl_OnClient"; this.Lbl_OnClient.Size = new System.Drawing.Size(14, 20); this.Lbl_OnClient.TabIndex = 33; this.Lbl_OnClient.Text = "-"; // // label10 // this.label10.AutoSize = true; this.label10.Location = new System.Drawing.Point(7, 137); this.label10.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label10.Name = "label10"; this.label10.Size = new System.Drawing.Size(78, 20); this.label10.TabIndex = 32; this.label10.Text = "OnClient :"; // // Lbl_ClientSize // this.Lbl_ClientSize.AutoSize = true; this.Lbl_ClientSize.Location = new System.Drawing.Point(107, 49); this.Lbl_ClientSize.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_ClientSize.Name = "Lbl_ClientSize"; this.Lbl_ClientSize.Size = new System.Drawing.Size(79, 20); this.Lbl_ClientSize.TabIndex = 31; this.Lbl_ClientSize.Text = "1024x768"; // // label8 // this.label8.AutoSize = true; this.label8.Location = new System.Drawing.Point(7, 49); this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(92, 20); this.label8.TabIndex = 30; this.label8.Text = "Client Size :"; // // Lbl_ScreenSize // this.Lbl_ScreenSize.AutoSize = true; this.Lbl_ScreenSize.Location = new System.Drawing.Point(107, 29); this.Lbl_ScreenSize.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_ScreenSize.Name = "Lbl_ScreenSize"; this.Lbl_ScreenSize.Size = new System.Drawing.Size(79, 20); this.Lbl_ScreenSize.TabIndex = 29; this.Lbl_ScreenSize.Text = "1024x768"; // // label6 // this.label6.AutoSize = true; this.label6.Location = new System.Drawing.Point(7, 29); this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(103, 20); this.label6.TabIndex = 28; this.label6.Text = "Screen Size :"; // // Lbl_OnScreen // this.Lbl_OnScreen.AutoSize = true; this.Lbl_OnScreen.Location = new System.Drawing.Point(107, 117); this.Lbl_OnScreen.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_OnScreen.Name = "Lbl_OnScreen"; this.Lbl_OnScreen.Size = new System.Drawing.Size(14, 20); this.Lbl_OnScreen.TabIndex = 27; this.Lbl_OnScreen.Text = "-"; // // label5 // this.label5.AutoSize = true; this.label5.Location = new System.Drawing.Point(7, 117); this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(89, 20); this.label5.TabIndex = 26; this.label5.Text = "OnScreen :"; // // Lbl_RawInput // this.Lbl_RawInput.AutoSize = true; this.Lbl_RawInput.Location = new System.Drawing.Point(107, 97); this.Lbl_RawInput.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_RawInput.Name = "Lbl_RawInput"; this.Lbl_RawInput.Size = new System.Drawing.Size(14, 20); this.Lbl_RawInput.TabIndex = 25; this.Lbl_RawInput.Text = "-"; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(7, 97); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(86, 20); this.label2.TabIndex = 24; this.label2.Text = "RawInput :"; // // Pbox_Target // this.Pbox_Target.BackColor = System.Drawing.Color.Transparent; this.Pbox_Target.BackgroundImage = global::DsDiag.Properties.Resources.sharps1; this.Pbox_Target.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; this.Pbox_Target.Location = new System.Drawing.Point(750, 81); this.Pbox_Target.Name = "Pbox_Target"; this.Pbox_Target.Size = new System.Drawing.Size(80, 80); this.Pbox_Target.TabIndex = 24; this.Pbox_Target.TabStop = false; // // groupBox2 // this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.groupBox2.BackColor = System.Drawing.Color.Black; this.groupBox2.Controls.Add(this.Lbl_AxisYMax); this.groupBox2.Controls.Add(this.Lbl_AxisYMin); this.groupBox2.Controls.Add(this.lbl14); this.groupBox2.Controls.Add(this.lbl13); this.groupBox2.Controls.Add(this.Lbl_AxisXMax); this.groupBox2.Controls.Add(this.Lbl_AxisXMin); this.groupBox2.Controls.Add(this.label7); this.groupBox2.Controls.Add(this.label4); this.groupBox2.Controls.Add(this.Cbo_AxisY); this.groupBox2.Controls.Add(this.Cbo_AxisX); this.groupBox2.Controls.Add(this.Lbl_AxisX_Txt); this.groupBox2.Controls.Add(this.FlowPanelButtons); this.groupBox2.Controls.Add(this.Lbl_Nbr_Buttons); this.groupBox2.Controls.Add(this.label3); this.groupBox2.Controls.Add(this.Lbl_AxisY_Txt); this.groupBox2.Controls.Add(this.Lbl_Product); this.groupBox2.Controls.Add(this.label24); this.groupBox2.Controls.Add(this.Lbl_Nbr_Axis); this.groupBox2.Controls.Add(this.Lbl_Nbr_Axis_Txt); this.groupBox2.Controls.Add(this.Lbl_Manufacturer); this.groupBox2.Controls.Add(this.label17); this.groupBox2.Controls.Add(this.Lbl_dwType); this.groupBox2.Controls.Add(this.label19); this.groupBox2.Controls.Add(this.Lbl_VID); this.groupBox2.Controls.Add(this.label21); this.groupBox2.Controls.Add(this.Lbl_PID); this.groupBox2.Controls.Add(this.label23); this.groupBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.groupBox2.ForeColor = System.Drawing.Color.White; this.groupBox2.Location = new System.Drawing.Point(17, 310); this.groupBox2.Name = "groupBox2"; this.groupBox2.Size = new System.Drawing.Size(455, 357); this.groupBox2.TabIndex = 41; this.groupBox2.TabStop = false; this.groupBox2.Text = "Device Informations:"; // // Lbl_AxisYMax // this.Lbl_AxisYMax.Anchor = System.Windows.Forms.AnchorStyles.Right; this.Lbl_AxisYMax.AutoSize = true; this.Lbl_AxisYMax.Location = new System.Drawing.Point(276, 211); this.Lbl_AxisYMax.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_AxisYMax.Name = "Lbl_AxisYMax"; this.Lbl_AxisYMax.Size = new System.Drawing.Size(14, 20); this.Lbl_AxisYMax.TabIndex = 57; this.Lbl_AxisYMax.Text = "-"; // // Lbl_AxisYMin // this.Lbl_AxisYMin.Anchor = System.Windows.Forms.AnchorStyles.Right; this.Lbl_AxisYMin.AutoSize = true; this.Lbl_AxisYMin.Location = new System.Drawing.Point(276, 191); this.Lbl_AxisYMin.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_AxisYMin.Name = "Lbl_AxisYMin"; this.Lbl_AxisYMin.Size = new System.Drawing.Size(14, 20); this.Lbl_AxisYMin.TabIndex = 56; this.Lbl_AxisYMin.Text = "-"; // // lbl14 // this.lbl14.Anchor = System.Windows.Forms.AnchorStyles.Right; this.lbl14.AutoSize = true; this.lbl14.Location = new System.Drawing.Point(233, 211); this.lbl14.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbl14.Name = "lbl14"; this.lbl14.Size = new System.Drawing.Size(42, 20); this.lbl14.TabIndex = 55; this.lbl14.Text = "Max:"; // // lbl13 // this.lbl13.Anchor = System.Windows.Forms.AnchorStyles.Right; this.lbl13.AutoSize = true; this.lbl13.Location = new System.Drawing.Point(233, 191); this.lbl13.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbl13.Name = "lbl13"; this.lbl13.Size = new System.Drawing.Size(38, 20); this.lbl13.TabIndex = 54; this.lbl13.Text = "Min:"; // // Lbl_AxisXMax // this.Lbl_AxisXMax.AutoSize = true; this.Lbl_AxisXMax.Location = new System.Drawing.Point(53, 211); this.Lbl_AxisXMax.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_AxisXMax.Name = "Lbl_AxisXMax"; this.Lbl_AxisXMax.Size = new System.Drawing.Size(14, 20); this.Lbl_AxisXMax.TabIndex = 53; this.Lbl_AxisXMax.Text = "-"; // // Lbl_AxisXMin // this.Lbl_AxisXMin.AutoSize = true; this.Lbl_AxisXMin.Location = new System.Drawing.Point(53, 191); this.Lbl_AxisXMin.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_AxisXMin.Name = "Lbl_AxisXMin"; this.Lbl_AxisXMin.Size = new System.Drawing.Size(14, 20); this.Lbl_AxisXMin.TabIndex = 52; this.Lbl_AxisXMin.Text = "-"; // // label7 // this.label7.AutoSize = true; this.label7.Location = new System.Drawing.Point(10, 211); this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(42, 20); this.label7.TabIndex = 51; this.label7.Text = "Max:"; // // label4 // this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(10, 191); this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(38, 20); this.label4.TabIndex = 50; this.label4.Text = "Min:"; // // Cbo_AxisY // this.Cbo_AxisY.Anchor = System.Windows.Forms.AnchorStyles.Right; this.Cbo_AxisY.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbo_AxisY.FormattingEnabled = true; this.Cbo_AxisY.Location = new System.Drawing.Point(297, 152); this.Cbo_AxisY.Name = "Cbo_AxisY"; this.Cbo_AxisY.Size = new System.Drawing.Size(71, 28); this.Cbo_AxisY.TabIndex = 49; this.Cbo_AxisY.SelectionChangeCommitted += new System.EventHandler(this.Cbo_AxisY_SelectionChangeCommitted); // // Cbo_AxisX // this.Cbo_AxisX.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.Cbo_AxisX.FormattingEnabled = true; this.Cbo_AxisX.Location = new System.Drawing.Point(74, 152); this.Cbo_AxisX.Name = "Cbo_AxisX"; this.Cbo_AxisX.Size = new System.Drawing.Size(71, 28); this.Cbo_AxisX.TabIndex = 48; this.Cbo_AxisX.SelectionChangeCommitted += new System.EventHandler(this.Cbo_AxisX_SelectionChangeCommitted); // // Lbl_AxisX_Txt // this.Lbl_AxisX_Txt.AutoSize = true; this.Lbl_AxisX_Txt.Location = new System.Drawing.Point(10, 155); this.Lbl_AxisX_Txt.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_AxisX_Txt.Name = "Lbl_AxisX_Txt"; this.Lbl_AxisX_Txt.Size = new System.Drawing.Size(57, 20); this.Lbl_AxisX_Txt.TabIndex = 47; this.Lbl_AxisX_Txt.Text = "Axis X:"; // // FlowPanelButtons // this.FlowPanelButtons.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.FlowPanelButtons.Location = new System.Drawing.Point(11, 244); this.FlowPanelButtons.Name = "FlowPanelButtons"; this.FlowPanelButtons.Size = new System.Drawing.Size(438, 107); this.FlowPanelButtons.TabIndex = 46; // // Lbl_Nbr_Buttons // this.Lbl_Nbr_Buttons.Anchor = System.Windows.Forms.AnchorStyles.Right; this.Lbl_Nbr_Buttons.AutoSize = true; this.Lbl_Nbr_Buttons.Location = new System.Drawing.Point(347, 97); this.Lbl_Nbr_Buttons.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_Nbr_Buttons.Name = "Lbl_Nbr_Buttons"; this.Lbl_Nbr_Buttons.Size = new System.Drawing.Size(14, 20); this.Lbl_Nbr_Buttons.TabIndex = 45; this.Lbl_Nbr_Buttons.Text = "-"; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Right; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(192, 97); this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(147, 20); this.label3.TabIndex = 44; this.label3.Text = "Number of Buttons:"; // // Lbl_AxisY_Txt // this.Lbl_AxisY_Txt.Anchor = System.Windows.Forms.AnchorStyles.Right; this.Lbl_AxisY_Txt.AutoSize = true; this.Lbl_AxisY_Txt.Location = new System.Drawing.Point(233, 155); this.Lbl_AxisY_Txt.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_AxisY_Txt.Name = "Lbl_AxisY_Txt"; this.Lbl_AxisY_Txt.Size = new System.Drawing.Size(57, 20); this.Lbl_AxisY_Txt.TabIndex = 43; this.Lbl_AxisY_Txt.Text = "Axis Y:"; // // Lbl_Product // this.Lbl_Product.AutoSize = true; this.Lbl_Product.Location = new System.Drawing.Point(79, 69); this.Lbl_Product.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_Product.Name = "Lbl_Product"; this.Lbl_Product.Size = new System.Drawing.Size(14, 20); this.Lbl_Product.TabIndex = 42; this.Lbl_Product.Text = "-"; // // label24 // this.label24.AutoSize = true; this.label24.Location = new System.Drawing.Point(7, 69); this.label24.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label24.Name = "label24"; this.label24.Size = new System.Drawing.Size(68, 20); this.label24.TabIndex = 41; this.label24.Text = "Product:"; // // Lbl_Nbr_Axis // this.Lbl_Nbr_Axis.Anchor = System.Windows.Forms.AnchorStyles.Right; this.Lbl_Nbr_Axis.AutoSize = true; this.Lbl_Nbr_Axis.Location = new System.Drawing.Point(347, 117); this.Lbl_Nbr_Axis.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_Nbr_Axis.Name = "Lbl_Nbr_Axis"; this.Lbl_Nbr_Axis.Size = new System.Drawing.Size(14, 20); this.Lbl_Nbr_Axis.TabIndex = 33; this.Lbl_Nbr_Axis.Text = "-"; // // Lbl_Nbr_Axis_Txt // this.Lbl_Nbr_Axis_Txt.Anchor = System.Windows.Forms.AnchorStyles.Right; this.Lbl_Nbr_Axis_Txt.AutoSize = true; this.Lbl_Nbr_Axis_Txt.Location = new System.Drawing.Point(192, 117); this.Lbl_Nbr_Axis_Txt.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_Nbr_Axis_Txt.Name = "Lbl_Nbr_Axis_Txt"; this.Lbl_Nbr_Axis_Txt.Size = new System.Drawing.Size(120, 20); this.Lbl_Nbr_Axis_Txt.TabIndex = 32; this.Lbl_Nbr_Axis_Txt.Text = "Number of Axis:"; // // Lbl_Manufacturer // this.Lbl_Manufacturer.AutoSize = true; this.Lbl_Manufacturer.Location = new System.Drawing.Point(114, 49); this.Lbl_Manufacturer.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_Manufacturer.Name = "Lbl_Manufacturer"; this.Lbl_Manufacturer.Size = new System.Drawing.Size(14, 20); this.Lbl_Manufacturer.TabIndex = 31; this.Lbl_Manufacturer.Text = "-"; // // label17 // this.label17.AutoSize = true; this.label17.Location = new System.Drawing.Point(7, 49); this.label17.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label17.Name = "label17"; this.label17.Size = new System.Drawing.Size(108, 20); this.label17.TabIndex = 30; this.label17.Text = "Manufacturer:"; // // Lbl_dwType // this.Lbl_dwType.AutoSize = true; this.Lbl_dwType.Location = new System.Drawing.Point(107, 29); this.Lbl_dwType.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_dwType.Name = "Lbl_dwType"; this.Lbl_dwType.Size = new System.Drawing.Size(14, 20); this.Lbl_dwType.TabIndex = 29; this.Lbl_dwType.Text = "-"; // // label19 // this.label19.AutoSize = true; this.label19.Location = new System.Drawing.Point(7, 29); this.label19.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label19.Name = "label19"; this.label19.Size = new System.Drawing.Size(99, 20); this.label19.TabIndex = 28; this.label19.Text = "DeviceType :"; // // Lbl_VID // this.Lbl_VID.AutoSize = true; this.Lbl_VID.Location = new System.Drawing.Point(56, 117); this.Lbl_VID.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_VID.Name = "Lbl_VID"; this.Lbl_VID.Size = new System.Drawing.Size(14, 20); this.Lbl_VID.TabIndex = 27; this.Lbl_VID.Text = "-"; // // label21 // this.label21.AutoSize = true; this.label21.Location = new System.Drawing.Point(7, 117); this.label21.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label21.Name = "label21"; this.label21.Size = new System.Drawing.Size(41, 20); this.label21.TabIndex = 26; this.label21.Text = "VID:"; // // Lbl_PID // this.Lbl_PID.AutoSize = true; this.Lbl_PID.Location = new System.Drawing.Point(56, 97); this.Lbl_PID.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.Lbl_PID.Name = "Lbl_PID"; this.Lbl_PID.Size = new System.Drawing.Size(14, 20); this.Lbl_PID.TabIndex = 25; this.Lbl_PID.Text = "-"; // // label23 // this.label23.AutoSize = true; this.label23.Location = new System.Drawing.Point(7, 97); this.label23.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label23.Name = "label23"; this.label23.Size = new System.Drawing.Size(40, 20); this.label23.TabIndex = 24; this.label23.Text = "PID:"; // // Txt_Log // this.Txt_Log.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.Txt_Log.Location = new System.Drawing.Point(647, 292); this.Txt_Log.Name = "Txt_Log"; this.Txt_Log.Size = new System.Drawing.Size(183, 375); this.Txt_Log.TabIndex = 42; this.Txt_Log.Text = ""; this.Txt_Log.Visible = false; // // Btn_Export // this.Btn_Export.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.Btn_Export.Location = new System.Drawing.Point(655, 35); this.Btn_Export.Name = "Btn_Export"; this.Btn_Export.Size = new System.Drawing.Size(177, 30); this.Btn_Export.TabIndex = 43; this.Btn_Export.Text = "Export device data"; this.Btn_Export.UseVisualStyleBackColor = true; this.Btn_Export.Visible = false; this.Btn_Export.Click += new System.EventHandler(this.Btn_Export_Click); // // Tmr_RefreshGui // this.Tmr_RefreshGui.Enabled = true; this.Tmr_RefreshGui.Interval = 25; this.Tmr_RefreshGui.Tick += new System.EventHandler(this.Tmr_RefreshGui_Tick); // // DsDiag // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.Black; this.ClientSize = new System.Drawing.Size(844, 679); this.Controls.Add(this.Btn_Export); this.Controls.Add(this.Txt_Log); this.Controls.Add(this.groupBox2); this.Controls.Add(this.groupBox1); this.Controls.Add(this.Pbox_Target); this.Controls.Add(this.label1); this.Controls.Add(this.Cbo_Dev); this.DoubleBuffered = true; this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "DsDiag"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "DemulShooter Devices Diagnostic"; this.WindowState = System.Windows.Forms.FormWindowState.Maximized; this.Load += new System.EventHandler(this.Ds_Diag_Load); this.Paint += new System.Windows.Forms.PaintEventHandler(this.Ds_Diag_Paint); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.Pbox_Target)).EndInit(); this.groupBox2.ResumeLayout(false); this.groupBox2.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.ComboBox Cbo_Dev; private System.Windows.Forms.Label label1; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.Label Lbl_OnClient; private System.Windows.Forms.Label label10; private System.Windows.Forms.Label Lbl_ClientSize; private System.Windows.Forms.Label label8; private System.Windows.Forms.Label Lbl_ScreenSize; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label Lbl_OnScreen; private System.Windows.Forms.Label label5; private System.Windows.Forms.Label Lbl_RawInput; private System.Windows.Forms.Label label2; private System.Windows.Forms.PictureBox Pbox_Target; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.FlowLayoutPanel FlowPanelButtons; private System.Windows.Forms.Label Lbl_Nbr_Buttons; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label Lbl_AxisY_Txt; private System.Windows.Forms.Label Lbl_Product; private System.Windows.Forms.Label label24; private System.Windows.Forms.Label Lbl_Nbr_Axis; private System.Windows.Forms.Label Lbl_Nbr_Axis_Txt; private System.Windows.Forms.Label Lbl_Manufacturer; private System.Windows.Forms.Label label17; private System.Windows.Forms.Label Lbl_dwType; private System.Windows.Forms.Label label19; private System.Windows.Forms.Label Lbl_VID; private System.Windows.Forms.Label label21; private System.Windows.Forms.Label Lbl_PID; private System.Windows.Forms.Label label23; private System.Windows.Forms.Label Lbl_AxisX_Txt; private System.Windows.Forms.ComboBox Cbo_AxisY; private System.Windows.Forms.ComboBox Cbo_AxisX; private System.Windows.Forms.RichTextBox Txt_Log; private System.Windows.Forms.Button Btn_Export; private System.Windows.Forms.Timer Tmr_RefreshGui; private System.Windows.Forms.Label label7; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label Lbl_AxisYMax; private System.Windows.Forms.Label Lbl_AxisYMin; private System.Windows.Forms.Label lbl14; private System.Windows.Forms.Label lbl13; private System.Windows.Forms.Label Lbl_AxisXMax; private System.Windows.Forms.Label Lbl_AxisXMin; } } ================================================ FILE: DsDiag/Ds_Diag.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; using DsCore; using DsCore.RawInput; using DsCore.Win32; namespace DsDiag { public partial class DsDiag : Form { public List _Controllers = new List(); public List _Btn_List = new List(); private string _SelectedDevice = String.Empty; /*** RAWINPUT data ***/ private int _ScreenWidth = 0; private int _ScreenHeight = 0; private int _ClientWidth = 0; private int _ClientHeight = 0; private int _OnScreenX = 0; private int _OnScreenY = 0; private int _OnClientX = -1000; //force drawing crosshair out of screen at start private int _OnClientY = -1000; /// /// Construcor /// public DsDiag() { InitializeComponent(); this.Text = "DemulShooter Devices Diagnostic " + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString(); //Fill the device list GetRawInputDevices(); foreach (RawInputController Controller in _Controllers) { //Cbo_Dev.Items.Add(Controller.DeviceName); Cbo_Dev.Items.Add("[" + Controller.DHidUsage + "] " + Controller.ManufacturerName + " - " + Controller.ProductName); } //Register to RawInput RawInputDevice[] rid = new RawInputDevice[3]; rid[0].UsagePage = HidUsagePage.GENERIC; rid[0].Usage = HidUsage.Joystick; rid[0].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[0].hwndTarget = this.Handle; rid[1].UsagePage = HidUsagePage.GENERIC; rid[1].Usage = HidUsage.Mouse; rid[1].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[1].hwndTarget = this.Handle; rid[2].UsagePage = HidUsagePage.GENERIC; rid[2].Usage = HidUsage.Gamepad; rid[2].dwFlags = RawInputDeviceFlags.RIDEV_INPUTSINK; rid[2].hwndTarget = this.Handle; if (!Win32API.RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) { MessageBox.Show("Failed to register raw input device(s).", "DemulShooter Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(0); } } #region GUI private void Ds_Diag_Load(object sender, System.EventArgs e) { GetScreenResolution(); GetClientSize(); //Hide PNG Target SetTargetLocation(-100, -100); } /// /// Change of selected device in the list /// private void Cbo_Dev_SelectionChangeCommitted(object sender, System.EventArgs e) { _OnClientX = -1000; _OnClientY = -1000; if (Cbo_Dev.Text.Length > 0) { RawInputController Controller = GetControllerFromName(_Controllers[Cbo_Dev.SelectedIndex].DeviceName); Lbl_dwType.Text = Controller.DeviceType.ToString() + " - " + Controller.DHidUsage; Lbl_Manufacturer.Text = Controller.ManufacturerName; Lbl_Product.Text = Controller.ProductName; Lbl_PID.Text = Controller.PID; Lbl_VID.Text = Controller.VID; _Btn_List.Clear(); Lbl_Nbr_Buttons.Text = Controller.NumberOfButtons.ToString(); FlowPanelButtons.Controls.Clear(); for (int i = 0; i < Controller.NumberOfButtons; i++) { Ds_Diag_Button b = new Ds_Diag_Button(i + 1); b.Width = 15; b.Height = 15; FlowPanelButtons.Controls.Add(b); _Btn_List.Add(b); } if (Controller.DeviceType != RawInputDeviceType.RIM_TYPEHID) { Lbl_AxisX_Txt.Visible = false; Lbl_AxisY_Txt.Visible = false; Lbl_Nbr_Axis_Txt.Visible = false; Lbl_Nbr_Axis.Visible = false; Cbo_AxisX.Visible = false; Cbo_AxisY.Visible = false; } else { Lbl_AxisX_Txt.Visible = true; Lbl_AxisY_Txt.Visible = true; Lbl_Nbr_Axis_Txt.Visible = true; Lbl_Nbr_Axis.Visible = true; Cbo_AxisX.Visible = true; Cbo_AxisY.Visible = true; Lbl_Nbr_Axis.Text = Controller.NumberOfAxis.ToString(); Cbo_AxisX.Items.Clear(); Cbo_AxisY.Items.Clear(); for (int i = 0; i < Controller.AxisList.Count; i++) { Cbo_AxisX.Items.Add("0x" + Controller.AxisList[i].ToString("X2")); Cbo_AxisY.Items.Add("0x" + Controller.AxisList[i].ToString("X2")); } if (Cbo_AxisX.Items.Count > 1) { Cbo_AxisX.SelectedItem = Cbo_AxisX.Items[1]; Cbo_AxisY.SelectedItem = Cbo_AxisX.Items[0]; Controller.Selected_AxisX = ushort.Parse(Cbo_AxisX.Text.Substring(2), System.Globalization.NumberStyles.HexNumber); Controller.Selected_AxisY = ushort.Parse(Cbo_AxisY.Text.Substring(2), System.Globalization.NumberStyles.HexNumber); } } _SelectedDevice = _Controllers[Cbo_Dev.SelectedIndex].DeviceName; Btn_Export.Visible = true; } else { Btn_Export.Visible = false; } } private void Cbo_AxisX_SelectionChangeCommitted(object sender, EventArgs e) { RawInputController c = GetControllerFromName(_Controllers[Cbo_Dev.SelectedIndex].DeviceName); c.Selected_AxisX = ushort.Parse(Cbo_AxisX.Text.Substring(2), System.Globalization.NumberStyles.HexNumber); } private void Cbo_AxisY_SelectionChangeCommitted(object sender, EventArgs e) { RawInputController c = GetControllerFromName(_Controllers[Cbo_Dev.SelectedIndex].DeviceName); c.Selected_AxisY = ushort.Parse(Cbo_AxisY.Text.Substring(2), System.Globalization.NumberStyles.HexNumber); } /// /// Export device HID specifications for analysis /// private void Btn_Export_Click(object sender, EventArgs e) { RawInputController c = GetControllerFromName(_Controllers[Cbo_Dev.SelectedIndex].DeviceName); String DataFile = Application.StartupPath + @"\DsDiag_Report_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".txt"; System.IO.StreamWriter sw = new System.IO.StreamWriter(DataFile, false); try { sw.WriteLine("----- Ds_Diag.exe Device Report File -----"); sw.WriteLine("Device Name = " + c.DeviceName); sw.WriteLine("Device Type = " + c.DeviceType); if (c.DeviceType == RawInputDeviceType.RIM_TYPEHID) sw.WriteLine("HID Usage = " + c.DHidUsage.ToString()); sw.WriteLine("VID = " + c.VID); sw.WriteLine("PID = " + c.PID); sw.WriteLine("Device Description = " + c.ManufacturerName + " - " + c.ProductName); sw.WriteLine("Number of Buttons = " + c.NumberOfButtons); sw.WriteLine("Number of Axis = " + c.NumberOfAxis); string s = string.Empty; for (int i = 0; i < c.NumberOfAxis; i++) { s += "0x" + c.AxisList[i].ToString("X2") + ", "; } sw.WriteLine("Axis List = " + s); if (c.DeviceType == RawInputDeviceType.RIM_TYPEHID) { sw.WriteLine("----- HID detailed data -----"); sw.WriteLine("Device Capabilities:"); sw.WriteLine("+ " + c.HID_Capabilities.ToString().Replace(", ", "\n+ ")); sw.WriteLine(""); sw.WriteLine("Button Capabilities :"); for (int i = 0; i < c.HID_ButtonCapabilitiesArray.Length; i++) { sw.WriteLine("+ [" + i.ToString("D2") + "]\n + " + c.HID_ButtonCapabilitiesArray[i].ToString().Replace(", ", "\n + ")); } sw.WriteLine(""); sw.WriteLine("Values Capabilities :"); for (int i = 0; i < c.HID_ValueCapabilitiesArray.Length; i++) { sw.WriteLine("+ [" + i.ToString("D2") + "]\n + " + c.HID_ValueCapabilitiesArray[i].ToString().Replace(", ", "\n + ")); } sw.WriteLine(""); sw.WriteLine("Values Capabilities :"); for (int i = 0; i < c.HID_OutputValueCapabilitiesArray.Length; i++) { sw.WriteLine("+ [" + i.ToString("D2") + "]\n + " + c.HID_OutputValueCapabilitiesArray[i].ToString().Replace(", ", "\n + ")); } } } catch (Exception ex) { MessageBox.Show(ex.Message.ToString()); } finally { sw.Close(); MessageBox.Show("Device report saved to :\n" + DataFile); } } #endregion #region RAW_INPUT /// /// Enumerates the Raw Input Devices and places their corresponding RawInputDevice structures into a List /// We just filter to remove Keyboard devices and keep Mouse + HID devices /// private void GetRawInputDevices() { uint deviceCount = 0; var dwSize = (Marshal.SizeOf(typeof(RawInputDeviceList))); if (Win32API.GetRawInputDeviceList(IntPtr.Zero, ref deviceCount, (uint)dwSize) == 0) { var pRawInputDeviceList = Marshal.AllocHGlobal((int)(dwSize * deviceCount)); Win32API.GetRawInputDeviceList(pRawInputDeviceList, ref deviceCount, (uint)dwSize); for (var i = 0; i < deviceCount; i++) { // On Window 8 64bit when compiling against .Net > 3.5 using .ToInt32 you will generate an arithmetic overflow. Leave as it is for 32bit/64bit applications RawInputDeviceList rid = (RawInputDeviceList)Marshal.PtrToStructure(new IntPtr((pRawInputDeviceList.ToInt64() + (dwSize * i))), typeof(RawInputDeviceList)); RawInputController c = new RawInputController(rid.hDevice, rid.dwType); if (c.DeviceType == RawInputDeviceType.RIM_TYPEHID || c.DeviceType == RawInputDeviceType.RIM_TYPEMOUSE) { _Controllers.Add(c); } } Marshal.FreeHGlobal(pRawInputDeviceList); return; } throw new Win32Exception(Marshal.GetLastWin32Error()); } /// /// Get Handle from DeviceName /// private RawInputController GetControllerFromName(String DeviceName) { foreach (RawInputController Controller in _Controllers) { if (Controller.DeviceName == DeviceName) { return Controller; } } return null; } /// /// Get events and process /// private bool ProcessRawInput(IntPtr LParam) { foreach (RawInputController Controller in _Controllers) { if (Controller.isSourceOfRawInputMessage(LParam)) { if (_SelectedDevice == Controller.DeviceName) { Controller.ProcessRawInputData(LParam); //Update Axis UpdateRawAxis(Controller); //Update Buttons for (int i = 0; i < Controller.Hid_Buttons.Length; i++) { _Btn_List[i].Activate(Controller.Hid_Buttons[i]); } break; } } } return true; } #endregion #region SCREEN public void GetScreenResolution() { _ScreenWidth = Screen.PrimaryScreen.Bounds.Width; _ScreenHeight = Screen.PrimaryScreen.Bounds.Height; Lbl_ScreenSize.Text = _ScreenWidth.ToString() + "x" + _ScreenHeight.ToString(); } public void GetClientSize() { _ClientWidth = this.ClientSize.Width; _ClientHeight = this.ClientSize.Height; Lbl_ClientSize.Text = _ClientWidth.ToString() + "x" + _ClientHeight.ToString(); } /// /// Contains value inside min-max range /// protected int Clamp(int val, int minVal, int maxVal) { if (val > maxVal) return maxVal; else if (val < minVal) return minVal; else return val; } /// /// Transforming 0x0000-0xFFFF absolute rawdata to absolute x,y position on Desktop resolution /// public int ScreenScale(int val, int fromMinVal, int fromMaxVal, int toMinVal, int toMaxVal) { return ScreenScale(val, fromMinVal, fromMinVal, fromMaxVal, toMinVal, toMinVal, toMaxVal); } protected int ScreenScale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal) { double fromRange; double frac; if (fromMaxVal > fromMinVal) { val = Clamp(val, fromMinVal, fromMaxVal); if (val > fromOffVal) { fromRange = (double)(fromMaxVal - fromOffVal); frac = (double)(val - fromOffVal) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMinVal); frac = (double)(val - fromOffVal) / fromRange; } else return toOffVal; } else if (fromMinVal > fromMaxVal) { val = Clamp(val, fromMaxVal, fromMinVal); if (val > fromOffVal) { fromRange = (double)(fromMinVal - fromOffVal); frac = (double)(fromOffVal - val) / fromRange; } else if (val < fromOffVal) { fromRange = (double)(fromOffVal - fromMaxVal); frac = (double)(fromOffVal - val) / fromRange; } else return toOffVal; } else return toOffVal; double toRange; if (toMaxVal > toMinVal) { if (frac >= 0) toRange = (double)(toMaxVal - toOffVal); else toRange = (double)(toOffVal - toMinVal); return toOffVal + (int)(toRange * frac); } else { if (frac >= 0) toRange = (double)(toOffVal - toMaxVal); else toRange = (double)(toMinVal - toOffVal); return toOffVal - (int)(toRange * frac); } } /// /// Convert screen location of pointer to Client area location /// private void ClientScale(AxisInfo AxisData) { //_OnClientX = mouse.pTarget.X * _ClientWidth / _ScreenWidth; //_OnClientY = mouse.pTarget.Y * _ClientHeight / _ScreenHeight; //_OnClientX = mouse.pTarget.X - this.ClientRectangle.Left; //_OnClientY = mouse.pTarget.Y - this.ClientRectangle.Top; POINT lp = new POINT(AxisData.pTarget.X, AxisData.pTarget.Y); Win32API.ScreenToClient(this.Handle, ref lp); _OnClientX = lp.X; _OnClientY = lp.Y; } #endregion #region WINDOW MESSAGE LOOP /*************** WM Boucle *********************/ protected const int WM_INPUT = 0x00FF; protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_INPUT: { //read in new mouse values. ProcessRawInput(m.LParam); } break; } base.WndProc(ref m); } #endregion /// /// Update labels on window with Device data /// /// public void UpdateRawAxis(RawInputController Controller) { Lbl_RawInput.Text = "[ 0x" + Controller.Computed_X.ToString("X8") + ", " + Controller.Computed_Y.ToString("X8") + " ]"; Lbl_AxisXMin.Text = Controller.Axis_X_Min.ToString() + " [ 0x" + Controller.Axis_X_Min.ToString("X8") + " ]"; Lbl_AxisXMax.Text = Controller.Axis_X_Max.ToString() + " [ 0x" + Controller.Axis_X_Max.ToString("X8") + " ]"; Lbl_AxisYMin.Text = Controller.Axis_Y_Min.ToString() + " [ 0x" + Controller.Axis_Y_Min.ToString("X8") + " ]"; Lbl_AxisYMax.Text = Controller.Axis_Y_Max.ToString() + " [ 0x" + Controller.Axis_Y_Max.ToString("X8") + " ]"; // Update screen/client info GetScreenResolution(); GetClientSize(); // Convert RAW to SCREEN Controller.Computed_X = ScreenScale(Controller.Computed_X, Controller.Axis_X_Min, Controller.Axis_X_Max, 0, _ScreenWidth); Controller.Computed_Y = ScreenScale(Controller.Computed_Y, Controller.Axis_Y_Min, Controller.Axis_Y_Max, 0, _ScreenHeight); _OnScreenX = Controller.Computed_X; _OnScreenY = Controller.Computed_Y; Lbl_OnScreen.Text = _OnScreenX.ToString() + "x" + _OnScreenY.ToString(); // Convert SCREEN to CLIENT AxisInfo i = new AxisInfo(); i.pTarget.X = Controller.Computed_X; i.pTarget.Y = Controller.Computed_Y; ClientScale(i); Lbl_OnClient.Text = _OnClientX.ToString() + "x" + _OnClientY.ToString(); // Display mark on client, moved to Timer to not overload GUI thread with too many rawinout data drawing //this.Invalidate(); //Custom crosshair with PNG, not displaying well at all.... //SetTargetLocation(i.pTarget.X, i.pTarget.Y); } //Draw Crasshair on repaint private void Ds_Diag_Paint(object sender, PaintEventArgs e) { // Border Pen RedPen = new Pen(Color.Red, 5); Rectangle rect = new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height); e.Graphics.DrawRectangle(RedPen, rect); RedPen = new Pen(Color.Red, 2); rect = new Rectangle(0, 0, this.ClientSize.Width / 2, this.ClientSize.Height); e.Graphics.DrawRectangle(RedPen, rect); rect = new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height / 2); e.Graphics.DrawRectangle(RedPen, rect); RedPen.Dispose(); //Crosshair Pen TargetPen = new Pen(Color.Red, 3); e.Graphics.DrawEllipse(TargetPen, _OnClientX - 20, _OnClientY - 20, 40, 40); e.Graphics.DrawLine(TargetPen, _OnClientX - 30, _OnClientY, _OnClientX - 10, _OnClientY); e.Graphics.DrawLine(TargetPen, _OnClientX +10, _OnClientY, _OnClientX +30, _OnClientY); e.Graphics.DrawLine(TargetPen, _OnClientX, _OnClientY - 30, _OnClientX, _OnClientY - 10); e.Graphics.DrawLine(TargetPen, _OnClientX, _OnClientY + 10, _OnClientX, _OnClientY + 30); TargetPen.Dispose(); } /// /// Display some custom Crosshair PNG in PictureBox /// /// X value in the window /// Y value in the window private void SetTargetLocation(int X, int Y) { Pbox_Target.Left = X - Pbox_Target.Width / 2; Pbox_Target.Top = Y - Pbox_Target.Height / 2; } //Used for Debug, not activated for release public void WriteLog(String Data) { Txt_Log.AppendText(Data + "\n"); Txt_Log.ScrollToCaret(); } /// /// Forcing the GUI redraw at a forced refresh rate /// If not, too many RAWINPUT event will block GUI thread from refreshing normally /// private void Tmr_RefreshGui_Tick(object sender, EventArgs e) { this.Invalidate(); } } class AxisInfo { public IntPtr devHandle; public string devName; public System.Drawing.Point pTarget; public int button; public AxisInfo() { devHandle = IntPtr.Zero; devName = String.Empty; pTarget.X = 0; pTarget.Y = 0; button = 0; } } } ================================================ FILE: DsDiag/Ds_Diag.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 AAABAAYAAAAAAAEAIABhJgAAZgAAAICAAAABACAAKAgBAMcmAABAQAAAAQAgAChCAADvLgEAMDAAAAEA IACoJQAAF3EBACAgAAABACAAqBAAAL+WAQAQEAAAAQAgAGgEAABnpwEAiVBORw0KGgoAAAANSUhEUgAA AQAAAAEACAYAAABccqhmAAAmKElEQVR42u2dd7weRbnHvxMSQm8C0rzBoDRBKSK6gNmJEJAiXKQGgggi SJMSkSKCF1ARqVGkBWmhCwhIkDZLyQIiCQii6AVDU7oUKTEhc/+YPdyAycl5Z/edZ/fd+X4+58MfnJl5 ns15f+/szFMUkZ4lSe1CwN7AdsDKwFCASWokgAVeAyYBZyljMml7I+FR0gZEukOS2rWA64BhH/5/hQB8 mPOBfZQx06Vtj4RjsLQBkepJUvtx4HZgiQ6G7YH7e/iatP2RcAySNiDSFX5GZx/+PnazWo+SNj4SjigA PUaS2hWBzUtMsb+0D5FwRAHoPTYoOX5DaQci4YgC0Ht8pOT4xa3W80g7EQlDFIDeo4p/03g71BKiAEQi LSYKQCTSYqIARCItJgpAJNJiogBEIi0mCkAk0mKiAEQiLSYKQCTSYqIARCItJgpAJNJiogBEIi0mCkAk 0mKiAEQiLSYKQCTSYqIARCItJgpAJNJiogBEIi0mCkAk0mKiAEQiLSYKQCTSYmJnoBqQpHYBQBc/qwHL AEOAV4AHgfPzTD0mbWcnWK1XBL4BfB5YGngPeBF4HLgTuE0Z87q0nW0nCoAASWoVsCqwGfBl4IsUjTtn QwocnKT2yDxTJ0rbPhCs1l8HzgLmnc3/HgUcAMywWt8L3AxMVMZMkba7jcTyz4EoPvRrADsA2wOreEyz XZ6pX81lnYOAU/v7nTk0B52VIcqYGT5+Wq03AO6i89fLp4CrgSuVMb/zWTvSOXEH0GWS1H4C2A33wff5 0M/K8Ulqr8kzZaX96ofj8DtbGgYcChxqtX4KuAq4UBnzqLRDvUwUgC6QpHZB4Ku4jrsjKpx6VdwZQS3P A6zWS+BeWcoyDBgLjLVa/x7XuvzSeGZQPVEAKiRJ7arAgcCuwMJdWmYVaioAwMpU/1r52eLnFKv1VcBp ypjJ0o72ClEASlK8248CDsId6nWboeWn6BpDujj3fMAYYIzV+h7gdOBaZcx70k43mSgAnoxI7eDpsCNw JLB6wKX/Lu17PzwXaJ0Ni5+pVuufAOOVMf+Wdr6JxECgDklSOyRJ7e7T3Tb8EsJ++N/FxQXUEmXMk8Cz AZdcETgTeMJqfYDVej7pZ9A0ogAMkCS1g5LU7gT8Gfgl8EkBMy7IM/WW9LOYC78QWHMF4AzgSav13rG9 +cCJAjAAktSOAO4HLgOGC5nxOO51o+6cDORCay+LC0B6xGr9FekH0QSiAPRDktrhSWp/DWS4k2gJpgEX ABvmmfqn9DOZG8qYabhD0dOAN4XMWA34tdU6s1qvKf1M6kw8BJwNSWqHAt8BjsKdPodkBjAJuA0XUfdA nql3pJ9JJyhj3gIOtlofBqyNC3X+Ei4mYv6ApowAJlutzwCOUcb8S/rZ1I0YCvwhktRq4GzCvuO/ClwP 3ADclmfqjRL2H4RgKHB/FId0GtgK2BpYruo1+uE54ABlzLUB16w9UQAKktQuBPwY2C/Qkq8DvwIuB7I8 U9Mr8uMgaioAs2K1VkCCC5HeEfhoN9ebhcuA/ZUxrwZar9ZEAQCS1G6Ee8/u9gHfTFz22wXADXmm3u2C LwfRAAGYleLUfhSwO7ANs88irJLngb2VMdeH8rGutPoMoAjmOQb3rt9NMXweOAc4N89UyHvyRlBE800E Jlqtl8QJwT7ASl1achncIeFZwMHKmMqFuCm09hYgSe3HpsMdwPfo3of/AWA0MCzP1DHxwz93lDEvK2N+ issr2Aq4vYvL7QPcb7VeVdpvKVopAElqNwUeAjbq0hI347Li1s8zdVmeqRim2iHKmJnKmBuVMRsD6wBX 4l6hqubTwINW69HSPkvQqleAInFnLO6wrxvidyPwgzxTv5f2tZcoqgXtaLVeBfg+sBPV/vstAEywWq8N HN6mBKPW7ACKunsTgJ90we87gS/kmdoqfvi7hzLmcWXMLsCauGvTqhkL3GS1Xlza11C0QgCS1C6Fe5fc ueKp/wJ8BdB5pu6T9rMtKGMeU8ZsjQswqjo5ahQwyWo9TNrPEPS8ABQluXJcddqqeAs4DFgzz9QNNS/R 1bMoY+4G1gP2Al6ucOrVgHuLV4KepqcFIEntusC9wCcqnPZ6YLU8UyfFwz15lDFWGXMerlLSBRVOvSxw l9V6Y2kfu0nPCkCS2g1w13xLVjTlK7iItW3yTD0j7V/kgyhjXlXGfB3YBHi6omkXAm60Wm8p7V+36EkB SFI7ErgFWKSiKW8APpVn6soGbPe7cVXWGJQxt+EOCS+oaMqhwDVW6+2lfesGPScASWpT4De4q52yTMPl BmydZ+oFad8GyCsVzNFoEVHGvFHsBnbC5VyUZQhwmdV6O2nfqqanBKDY9t9INSm8T+Ku9s5swLf+rDxe cvy7yphGC0AfypgrcHUcHq5gunmAS63WW0n7VSU9IwDFgd9NwIIVTHcr8Nk8U01sV/Uw5QpxVPX+XAuU Mf8LfAGXBViWIcBVVutNpP2qip4QgCS1K+E+/FW8848bAps3ofrO7CjSin9dYoqea8uljHlHGTMal/RV djfXdyawjrRfVdB4ASiCfG7GdaAtgwUOyTN14J2ZCpYK2yVOLTH2amnju4Uy5oe43gJlr28XwkUMflza p7I0WgCS1M6Hu5cve8//HjAmz1SZD05tyDM1GVccs1Mm4248ehZlzARc9ObbJaf6KC59eTFpn8rQWAEo EnvOpXyE3wxg+zxTE6R9qphv43LsB8pLwOheOQDsD2XMb3FdnMrWCFwFuKLJZcgbKwC4UNxdS87xHrBT nqmeqxNXRCluBXwXmNt5xkTgc8qYsjcIjaEII96C8juBUcAp0v54PwdpA3xIUrsxLtCnrP275Zm6WNqf bpOkdl5chdw1gUUmqZF931hPA6Y4KW8lVutRuKvjsn0Ndy1eLxpF4wQgSe3ywBRgqZJTHZZn6iRpf5pA UcDz48BHgHeAp5QxUjX/u+HfLsDFlPs8vI3bRf1R2p9OaJQAFDX8DK4xZBnOBPZvWIBPcIoy3t/Flc5a Ztb/hQs4ugdXC+FWZUxTIiXn5OsRwA9LTvM4sG7RF6ERNEoAktQeiyviWYabhsDWTbzqS1K7BrAGLsHp PdwWPu9GzILVehHca9b6A/l1XIm163Etu6uIvAuO1fpc4BslpxmvjCk7RzAaIwBJajfEfduUObh8CNgo z1RjOsQkqf0v3N31GNyp84eZjqt0dGieqcpq3VutLwF28Rz+58Kmi5QxjYkstFoPxh2Ilk0B/qoy5hpp fwZCIwSgaNrxB9x7qC9/xxXprH1l3iS1g3An+AcAIxnYv9NfgSTPVOnCGFbrT1NN/PxM3C7iTOA3Tbhi tFoviisgU6bt+6vA6k14LWrKNeCJlPvwvwN8pe4f/iS1CyapPRBXauw6XD+9gYr0J6nuOmq3iuYZhLtv vx543Gq9b3GuUFuUMa8DW+LiInxZAr9ArPD+ShswN4pefXeUnGb7PFO1DXFNUjs/Lu34MMrdbswAPpZn 6vky9litH6B73ZCfxxVm/UWdG3JYrTfE1ZEs06VotDKmiiSkrlHrHUAR6nt2yWmOqeuHf0RqByep3R+X enwS5a82B+Pu+8tSZQm1D7MMbqfyV6v1HlbrWv4NKmPuAb5VcprT6l5huJYPfxYOp1yX3l8Bx0s7MTuS 1Orp7lByHB+8YivLyhXMsXCAR7ACMB7XvvuLAdbrGGXM+cDpJaZYGteDorbUVgCKar5HlJjiEWD3PFO1 OnhKUrtsktorcK81n+rCEotK+9ghnwHutFpfWPQFrBtjKfcKupfVeiBXqSLUVgCAk/F//3od2LZu131J ancBHsW1xO4WTe32tBvwmNW6m8+mY4ouyTviXyhFAacX0ZS1o5YCkKR2E1zKpi9j8kzVJr49Se1SSWqv AS7BnRBHZs9SuOy6CcV1XC1QxrwMbI9/HYH1cXEctaN2ApCkdh7cgZgvJ+WZqk1OexHA9BDw39K2NIjR wENW6/WkDelDGfM73OuAL8fX8Qq0dgKAiz77jOfY+3Bln2pBktqxuNyF5aRtaSArAvdYrcuexFeGMmYc Lj7Dh48BB0r78GFqJQBF2upxnsP/BYwuauJJ+zFfcdB3Es19J68D8wJnWq3PsVqXTdetij1xUaU+HF6n VxuomQAAewD/5Tn2kDxTf5N2IEntkrgAklodZjWcvYDf1qH8ljLmVdzfqQ+LAwdL+zArtRGA4tvfd/t+ K3BeDXz4JO41JJG2pQfRuK69y0sbUpQUO99z+MF1ELI+aiMAuFPSFTzGvQt8Szq3P0ntasBdwEqSdvQ4 q+POBerwjMcCL3qMWwTYX9r4PmohAMXJ/+Gew3+cZ+oJYfvXBDKqjeiLzJ4VcYFDZSJES6OM+SeuWIoP 37ZaV9G6rjS1EABga/ziz5+l3JVhaYoiHYbyfQkiA2d5ILNaDxe240LgQY9xSwK7C9sO1EcADvEcd2ye qbJVXb1JUrsi8FtcrbxIWJYDbpM8E1DGWPx3AQfXIRFK3IAktZ8FNvAY+gRwkaDdS+MOH+MdvxwfB26W vFpTxtwO3O0x9BPA5lJ29yEuAPgfiJwodedf5O/fRHfTZiMDYw1crz7JeIsTPMeJHwaKCkCS2iVwPdw7 5UVcGWcpxgPrCq4f+SAjgTOkFi+uBR/xGDpKur+g9A5gDK7baqecnWdKpJpMktrvADtLrB3pl29ZrfcS XH+cxxiFC3ISQ0wAit5+e3oMnQmcI2TzSGpe4KHljBNs230pLg29U74m2VtQcgewNq5VVafcJFHcM0nt R3CvHdK7psicGQpcabVeKPTCRTOQSz2GLgdsEtrePiT/mH0be/qGYJZlPPHEvwmshN92vAp8/zbFagWI CEAR+edz+PcK8BsBe/fCBStFmsHuVuutQi+qjPk98JjH0G2s1guGthfkdgAbAct6jLusaHsdjCS1yyIc bRjx4myhpBuf26kFcI1ggiMlAL6pspcI2Ho6zSu0GXFfMBIHtpfieiV2ikj6eHABKLb/23oMfQL4XWBb N8fVgos0k2+Grshb9EK8x2PolyUOLyV2AAnwUY9xl4ZM+R2R2sHAqcGeSqQbSFXkneAxZj5gi8B2igiA 72GazxWLN9Phm1TTZCMiy/qED9y6Cte1uVOCF46VEIDNPMZMzjP151AGJqldGDgm3COJdJnjQtYULMqG 3ewxdJPQu5WgApCkdkH82i4H/fYHDiLm9/cSw/GLOi2Dz9/sEoWtwQi9AxhG5x2JLXB5KAMLkfp2yIcS CcLhgTMGr8dVqu6UoMlBoQXAJ9ghyzP1XEAb9yIW+OhFhuF6TgRBGfM2fj0EggYEhRaANzzG+JyoepGk dghwaLjHEQmMb+UpX3ziVnwSirwJLQBPAm918PuvA1cEtG8b/CoTR5rBp63WIwOudyswtYPft7jmscEI KgBFBZ9rOxjy08AdfvcO+TwiIgRrNaaMmQkc38EQUzQiDYbENeCxwEAKeT5IwBj8JLUr4SrLRHqbra3W IW94zmdgV4IzgCNDP4zgAlDU8N+W/k9IHwC2yDM1LaBpe9L5DUWkeQwhYGBQUTl4B9zrwJz4N7C7Mub+ 0A9DJBkoz9RvccVAzsHF+L+De9+/B9gX2CDP1AuBzYq9/NpD0Px7ZcybwKa4Ghi349La3wGewh0UrqOM CXbY/QHbJBatG0lq1wYmS9tREafnmTqozARW6xmAWJmqQAxXxog3k5UmlrdybCdtQIXMkDagIXxV2oA6 EAXAIVKMoUuEvDVpMltKG1AHWi8ARdrvatJ2VEjos5OmsoFkR6G60HoBmA5LAZJdZaqmiqzJ96SdCMBg XG2KVtN6AcAVYugVpgFVXCX9XdqRQLS+3kMUgN56Z55YUbdkn5bXTWRhaQOkab0A5Jl6CXhT2o6KOK2i ea6XdiQQL0obIE3rBaDgTmkDKuDyPFNV+XEl8A9phwKQSRsgTRQAh0ivwQqZQoWJTMqYd3FVkXqZa5Qx f5E2QpooAECeqRuAa6Tt8OQmYGSeKZ9aC3NEGXMlcLS0c13iYYS78taFKAD/zy7Aefg1dZDgYWDHPFNb 5Jl6rRsLKGOOx1WqnSrtbEW8DfwUSIrCna3nP3IBisYdq+EKFDZRICzwLvAqMLWoQTBgktSugisM8uni GQSrJjsXpgHP4QpG3J5n6o+hFi7aV28MpMAquE5JTcgVsLgCNE8AOTBRGdPxTqlo2LECsDgwL83LobG4 BKQ/KWM+EOPxviNFMcyjcO+SS0hbXBHTcanFVwPjq94mR3oTq/VQ3JfANsAGwMekbaqIV4CzgB8WNQud ABQNMG/Dr2R3U3gF+FaeqaukDYnUF6v1GOBE/JrXNoVHgY2VMS+oYsufA5+TtioQ++aZ+oW0EZH6YbUe B+wvbUcgcmCjQbgiBW358AOMS1K7rrQRkXphtd6f9nz4weVB7NQnAG1iHuAEaSMi9cFqvSBwnLQdAowZ hDvtbhujktQuI21EpDZsDCwmbYQAaw0CFpC2QgAFfEHaiEht6OXD7/5YaBDNu9Osil652omUp5fqQXSC amKgT1UMlTYgUhv+LW2AFG0WgHekDYjUhqD9+GqEHURvFcTohCpKZ0V6gz9JGyDEm4NoT/WXWXkD14Qk EgG4j94pCtMJDw4CLpC2QoCf55l6V9qISD1QxkzD9fBrG+cPwiXK3CFtSUB+TzuDPiL98z/AM9JGBOQW Zcy1g/JMWWB73Dao17ke2CTPVDwAjHyAoj7AKKAN7cImATvCB9OBhwAH4OKhPy5tYYW8gGvIOD7PVJt2 OhEPitz/g3Ah8qtI21MxTwDjgJ8rY2bAHIKAktQuByxJc68JLa6Axot5pgZc+SVJ7ca4PoFrAgtKOzEb ZgIv4W4w7saVAX8rxMJW69Vx+fHr4YKomhQ8Mw14EvfMrlTGvDxAnxfFpQUPpdkBczOBl5Qx/1HotclO VUaS2iWAK3Ax4U3iTVyBh+PyTHXlFNtqvTJwBq69dS/wNnCsMuYkaUPqQOsFIEmtwpWH/qK0LSV4Evhy nqlKq9xarb8MXEU9d0NlOUEZ8z1pI6Rp6ha/Sjaj2R9+gOHA7Ulql6pqQqv1WrhKyb344Qc4wmrda+/4 HRMFADaRNqAiVgB+XOF859JbfRM/zCDc7VeriQIAy0sbUCFjktR+pOwkVuuRwGelnQlA3AFIG1ADeikT bAgwsoJ5tpV2JBCt//tv/QOg9wI/VqtgjvWlnQhEW9qgz5EoAL2XFLRoBXP0UiBYf9wrbYA0UQCcAEyT NqJCqujYs7C0EwF4j3blwMyW1gtAnqm36Y324FXShLZfZblfGfOatBHStF4ACq6XNiASnF9LG1AHmhTP 3U2uxSVJtD4yskVcK7Go1XpZXBXiwbhmr//RsDMkYgJQdOf5Lq5DyUdxMdoP4gozTCjSlIOQZ+rvSWrv ATaSeh6RoExWxvw15IJW63WAk3EdlmflOav1kcDFypjgrelFXgGS1O4D3I+LxFoeJ0SLABq4GJiYpHah wGZNkHgWEREuDbmY1Xor3I1DOpv/vTxwIUJFaoILQJLakcCZ9H/QtClwWWDTrgBimbDeZzpwSajFimzK y4F55/KrR1mtg2ejSuwATmRg79pbJqkdE8qoPFOv4TLfIr3NjcqYFwKudzID7751ROiHEVQAktQOo7MY 82OKdN1QxLbhvc+ZoRayWq8KbNnBkBFW6/lDPozQO4A1O/z9lagmtn1A5Jm6F3gg6BOJhORRZcxtAdfr dAc7D7BiQPuCC4BPhFmw14CCnwZeLxKOU0ItZLVWwGiPoXM7K6iU0ALgU7Zq2yS1IbdFVwOVVtaJ1IKn CXj4h+s+vaLHuAHXsKyC0ALwhMeYhYGvhDIwz9RM4IRgTyQSihOVMdMDrrezx5i3cMFBwQgtAI/j14hx l8B2TgAeC7xmpHv8DVfhKAhW68HADh5D71PGzAz2VAgsAMW3660eQzerotJNB3a+BxwV7MFEus33A3/7 a2Bpj3G/DWgjIBMH8CuPMUPwU1Rv8kxdh6sWHGk2DxA+ytPn8A/8PhulkBCAG/GLuNtVwNaDcHnjkWZi gYNCxthbrefDr6TaFGXMk8GeTEFwAcgz9S9gosfQJEnt8MC2PozLEow0k/HKmDzwmpvj8lo6RSQKVaoe wJWe40LHBAAcDTwlsG6kHC8Ahwms63tgfbWArWICcD3wL49xuwUODe7bsewVcs1IJeytjPlnyAWt1ovR WehvH1NCpyf3ISIARRkun4IMwxHo4pNn6lYCxpBHSnORMkai4s9O+EXyhQxQ+gCSJcEu8hy3p5C9Y4mx AU3gCVyLewm+7jFmJi5dWARJAbgDF57ZKdsnqV08tLF5pt7BXUW+HXrtyICZBuysjOlKp+T+sFp/Gvic x9BblDFi/QnEBKAICvqlx9D5ENoF5Jn6I7C3xNqRAfFtZYxUNuc+nuPGC9kLyFcFPg+/e/b9k9SKlK7O M3UJcKrE2pF+OU8Zc7bEwlbrxYHdPIa+iHBFalEByDP1LHCDx9BhBI4M/BDfwQU0RepBBuwruP4++LVR P08ZI9qbUnoHAP6BNkclqZW6xXgPd+I7WWL9yAf4I7Bt4Fj/97FaLwAc7DF0JnCOhM2zIi4AeabuAP7g MfRT+KVcVmX3W8BmxNoBkjwDbBb6vv9D7Acs5THuWmWMeICZuAAU+FZqOT5J7VApo/NMvQRsAkyVsqHF /AMYqYx5VsoAq/USwJGew2txjlQXAbgU8PmHXBF3Py9Gnqmncemf4mreIp4HvqSM+V9hO34ALOYxLi9+ xKmFAOSZmg6c5Dn8yCS1ou2s80xNxXUVEgnnbBnPAl9UxvxJ0oii04/vweMPJboAzY5aCEDBuThl75QF AJHrn1nJM/UMTgTiwWD3+BOwgVTcfB9FxZ9z8fv8PATcJGn/rNRGAIpIux95Dt8kSa14gE6eqReAEcDN 0rb0IHcDGypjfKJHq+ZwYB3PscfW5dsfaiQABWfjdxYAcHKS2k9KO1BkD24JnCFtSw/xS2BjZUzQirmz o9j6H+M5fDI1a0VfKwHIMzUN+L7n8AWBy5PUBq2rPgc/3ssz9W1gd2K/wTJMBw5UxuwhHTADYLVeEHdg 7dtV+4g6fftDzQSg4ELgEc+x6xCw+cPcyDN1IfB5XDXkSGc8DYxQxtSpItMZwCqeY+/AryBuV6mdABRJ QoeWmGK/JLW+RRm74c/DwLoIJ300jKuAzyhj7pU2pA+r9e7AHr7DgbF1+/aHGgoAvF+Ao0xBh3OS1Hba h7Cb/ryVZ+obwFaAWOpnA3gFl867gzLmNWlj+rBar0W5xrHjlTFTpP2YHbUUgIKD8X9/XhC4LkntEtJO zEqeqRuB1YGzcLHgkf9nArC6MkasOMbssFp/BLgGl4buw+vA96T9mBO1FYA8U38Dji8xxXDgyhGp9T2w 6ZZfr+eZ+hbubOD+LixRu23mXHgUF9W3qzLmRWljZqW4778cKBNodoQy5gVpX+ZEbQWg4CeUK8P1pelw mrQTsyPP1AO4BpI741pXVYX4afkA+QcujXYtZcwd0sbMgROBjUuMf4AaZPz1R60FoAgR/gbltsv7JamV zBXvzz+bZ+py3MnyPviVSPswQZtLevAS8F1gJWXM2cqYWjZesVp/HTikxBQzgL3q6l8ftRYAgDxT91L+ au/0JLWbSvvSj4/T80ydDXwCFzvwaJnpKjCpG68RU4EDgWHKmJ8oY97pwhqVYLXeiPLh5ScoYx6W9mVu BK2x70uS2vlwUVSrlZjmDWCDPFNlPlwhfR6JSzb5Cq434kCYnGdq3bJrW61fwK+55X9MBdwG/By4IXTn W0/fhwO/A8o0o/0DsF4dgpfmRiMEACBJ7drAffjVXe/jaWD9PFM+SUdSfi+N6zazCy6eYE68DIzIM1W6 dLnV+mrgqyWm+AtwGXCBMmZquKdV2u/FcDuoMl8003Afft9gtqA0RgAAktQeApxccpoHcR+Ut6T98fB/ JVzjyVHAJ4H5cRmUtwMn5Zn6RxXrWK1TwHQ47A+4OPdrlTGNy4i0Wg8BfoMr8FKGg5Uxp0n7M1CaJgAK 94/05ZJT3QhsU9T2i8wGq/WPcFlvc+KvwCScUNwmWdu+Al8VLjbjmyWnmghs2YRXnT4aJQAARXDPg7hq QGU4N89U2X/wnsZqPQpX/HRZ3CvGo8AUYLIy5mVp+yr080jghJLTPA2so4x5RdqfTmicAAAkqV0X9+1T th7gCXmmahulVRVJalcAtsC92y4wSY1UwGu4Z3hD3a+quonVeg/K52lMx9Uq+J20P53SSAEASFK7Gy5z sCxH55kqE3FYW5LULgz8FNdJ6f1GKpPUyFl/7TFgdBOurKrGar0zrjFn2evwPZUx50v740Pt4wDmRJ6p i3CRgmU5Lknt0dL+VE2S2sWAu3Dvtf11UVoduMtqvba0zSGxWu8IXEz5z8DpTf3wU4Hz0hwBXFfBPP+T pNa3KGldGQ+sNcDfXQS4ymo9v7TRISi2/ZfRvzAOhFsQrkpdlkYLQFE7YDRQRd742CS1l9SholBZktR+ EXdd2Akr4Zpc9CxWa2W1PgInjmVffx8CtlPGzJD2qwyNFgB4v5jolsCfK5huF+CWJLVLSvtVEt/ch/2L K7Geo7jn/wXwwwqmewrYXKINedU0XgAA8ky9CmxKNck0I4AHktR+RtovH4quyZt7Dh8GrCftQ9VYrZfE bderqBz9Eq4dWSVBV9L0hADABzr0VJENtyJwb5LaPaX98mA1YOES47W0A1Vitf4cLo8krWC6f+KqE1ex 26wFPSMAAHmmngRG4nLNyzI/cF6S2glJaheR9q0DPlFyfE/cBlitB1mtDwbuAT5WwZRv4r75fRrZ1pae EgCAPFN/wan9MxVNORp4OEltKu3bACmbxVdWQMSxWi+LCxk/hYFnUvbHa7hv/sYF+syNnhMAeF8ENgKq ah65InBHktpxRXBNnfGtXdfHctIO+FKc8u+MC1nerKJpXwZ0L374oUcFACDP1FM4Eagqwk0B+wOPJan9 b2n/+qHsv+mi0g74YLX+L+AGXOOOqorBPovrTfCQtH/domcFAKDI+98IdwJcFSsA1ySpvTlJrW+TiDrT qGAgq/VQq/XhuGvgLSqc+lHgC8qY0vUV6kxPCwBAnqk3h7g/jKobc2wKPJKk9oweiBuYlUbEARTb/W2A P+KaylYpXBmwkTLGt09lY+h5AQC4M1MzisYchwBVZr4NAQ4AnkhS+/0GnA/0BFbrL+DyHK7FRTBWyTnA pnVqTNJNWiEAfeSZOhX3zV11zvYiwA+AqUlqj0pS28j36LpjtV7Pav0bXNmuDSuefgawrzJm7ybU8quK VgkAQJ6p24HPAr/vwvRL4JqZPJ2k9sQktctL+9t0iq3+CKv1zbhinb5Rjv3xPO6ar0z7r0bSOgEAyDM1 FUhw9QW7UQJ7EeAw4G9FINH60j43Dav1YKv1DrhErwy3c+sGt+Gak9wp7bMEtWqbFZKi6cjYJLW34wqL LNWFZYbgAolGJ6mdgnu/vCzP1OvS/tcVq/VyuAImewPd3EHNBI4BftTmikitFYA+8kxNTFK7BnAm5Uph z421cdlopySpvQ64eAjcememGp1OWgVW63lxW/uv425syubpz43Hga8pY7rRm7FRtF4AAPJMvQhsl6R2 R+BnQDev9ebH9QPceTq8lKT2Glz3WVPsSlqB1XoeXIzGjsD2lGvEMeBlceHBR9e5M1FIogDMQp6pK5LU GuAkYLcASy6F2+ruDbyepHYicBNwa5OalwyUouKQBrYufj4acPlHgX2UMZOkn0OdiALwIYrdwNeS1J6H a2m1ZqClF8WV4N4JIEnto8CduMq9E/NMvSb9bDqlaK/9GVxy1sbFf8vmKnTK28CxwGnKmNbssAZKFIA5 kGfq7hGpXWe6K5N1NGG2qLOyRvGzH/BmktoD8kxVUQW56xTv9EfhgqQWFzTlauBQZUwVhWJ6klZeAw6U IoLwdFy02Y8BqffGhYFfJqndRvqZzA2r9SDgcuD7yH347wM2UMZsHz/8/RMFYADkmXo9z9QRwMrAebhG EKFRwGlFya86szMglS35V2AHIFHGVNEmveeJAtABeaaezTO1F64x5y9wnWBDMgwXwFRn9hFY8y/AGGB1 ZcxVyphuBHf1JFEAPMgz9VSeqX2B4bhrpTcCLr+OtP9zonj3/3zAJR/DVXJeXRlzSdNLdEsQBaAEeab+ nmfqUFyNgANx30TdZjFpv/thScIcLE/EhQavoYy5tM2RfGWJtwAVkGfqTWBcktqf4UpR7Y2LaOvG831N 2t9+6GYW3ZvABOAMZcyfpB3tFaIAVEieKYv7dpqYpHZpYFdceOsaFS7zgLSf/fAK8CLlC5POyj24g9er lTFvSTvYa0QB6BJFQNEpuNj/dXGn01+lXAGLP1NNG7SuoIyxVuur8e9M1McTwJXARb1Ug7+ORAEIQJ6p B4EHge8mqV0bJwTb4pp4DJQZwH7FLqPO/Bi38+m0l8JU4ArcB39KPMkPQxSAwOSZmgJMAb6XpHYY7sxg U+BLzPlD8xzwzTxTd0jbPzeUMc9YrbfGReH1Fz35Hq6yz03FzyPxQx+eKACCFKXLzwbOHpHawdNdX77V gWWAobhWVH8A7mpSpqAyJrNarwx8DZfxtxwukOll3PZ+EnCLMuaf0ra2nUZUgI0MnCS1BwGn9vc7k9TI fudQxsS/i5YQ4wAikRYTBSASaTFRACKRFhMFIBJpMVEAIpEWEwUgEmkxUQAikRYTBSASaTFRACKRFhMF IBJpMVEAIpEWEwUgEmkxUQAikRYTBSASaTFRACKRFhMFIBJpMVEAIpEWEwUgEmkxUQAikRYTBSASaTFR AHqPsqW1Y2nuFhEFoPd4peT4l6UdiIQjCkDvcU/J8XdLOxAJRxSAHiPP1FTgxhJTjJP2IRKOKAC9yf74 vQqco4zJpI2PhCMKQA9StBwbCTzZwbAzgf2kbY+EJbaA6mGS1M4P7IXrRrwqrt9gX2swi9slTALOUsbU tu14pHv8H4LNi3DDgwePAAAAAElFTkSuQmCCKAAAAIAAAAAAAQAAAQAgAAAAAAAAAAEAEwsAABMLAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Rf9AOsv/QDr2oEGd+EFB//JBQf+2QUH/JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOjX/QDr6/0A6//9AOv+gQZ3/QUH//0FB//9B Qf/mQUH/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6n/9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf9mAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq3/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+g QZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgj/QDpF/0A6i/9AOrf/ QDq8/0A6i/9AOhMAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9B Qf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/xBBQf+cQUH/3UFB/99BQf+6QUH/dEFB/ypB Qf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Df9AOln/QDqt/0A69f9AOv//QDr//0A6//9AOv//QDr//0A6xf9AOgAAAAAAAAAAAAAAAAD/ QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/qUFB//9BQf//QUH//0FB//9BQf//QUH//0FB/9lBQf+EQUH/LAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoA/0A6Pv9AOqH/QDr0/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6KgAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9B Qf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/wVBQf/6QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/yEFB/2VBQf8KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoH/0A6Y/9AOtH/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDo8AAAAAAAAAAAA AAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/CEFB//5BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/+1B Qf+HQUH/FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoH/0A6bf9AOuP/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A69/9AOhAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/x0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/2QUH/jkFB/xYAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QD8B/0A6W/9AOt//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOvz/QDprAAAAAAAAAAAA AAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8uQUH/20FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/8UFB/3lBQf8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Lv9AOsP/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A65f9AOp3/QDpa/0A6GgAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8BQUH/KkFB/25B Qf+4QUH/+UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/9pB Qf9FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Bv9AOoH/QDr6/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDrn/0A6jv9AOjj/QDoBAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8RQUH/YkFB/8BBQf/+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+bQUH/DgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOiT/ QDrM/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/QDq3/0A6Tv9AOgQA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+g QZ3/QUH//0FB//9BQf//QUH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/yZBQf+PQUH/8UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/eQUH/NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpU/0A68v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOvv/QDqf/0A6KQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8QQUH/e0FB/+1BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/5QUH/aQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QD8B/0A6g/9AOv7/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/QDqm/0A6IwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/DkFB/4VBQf/2QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/l0FB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Bv9AOqX/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDrN/0A6OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/fQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/yBBQf+yQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/tEFB/wsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgr/QDq3/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr3/0A6dP9AOgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tf9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf9tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/wBB Qf9YQUH/7UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/wkFB/w4AAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoJ/0A6vP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A61v9AOi0A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqf/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/0gA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8dQUH/xEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/xUFB/wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Bf9AOrX/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOq3/QDoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOmf/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf/3QUH/DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8FQUH/l0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/vEFB/wYA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgH/QDqf/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDqM/0A6AgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6EP9AOuv/QDr//0A6/6BBnf9BQf//QUH//0FB/5YA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8AQUH/d0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/pUFB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6ev9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6fgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6O/9AOuX/QDr/oEGd/0FB//9BQf+9QUH/DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/a0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOkn/QDr9/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOocAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6B/9AOj6gQZ1NQUH/N0FB/wEA AAAAAAAAAEFB/xJBQf9VQUH/R0FB/xcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/d0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/+QUH/SwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDod/0A67f9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDqi/0A6AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgn/QDpM/0A6cv9AOlj/ QDoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8QQUH/4UFB//9BQf//QUH//0FB/95B Qf+eQUH/VUFB/w0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8AQUH/lkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/tQUH/HQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Av9AOsD/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6v/9AOgYAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOjz/QDqg/0A68v9AOv//QDr//0A6//9AOrgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/2VBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/9kFB/6dBQf9CQUH/AQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8EQUH/uUFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+9QUH/AgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpw/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOtn/QDoSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Af9AOlP/QDrM/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOikAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/TQUH/WkFB/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8PQUH/1kFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6IP9AOvX/QDr//0A6//9AOv//QDr//0A6//9AOv//QDru/0A6JgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOkD/ QDrN/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6RgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/00FB/0YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8kQUH/7kFB//9BQf//QUH//0FB//9BQf//QUH//0FB//JB Qf8bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqy/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6/P9AOkcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOhP/QDqj/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6lBQf8WAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9IQUH//UFB//9BQf//QUH//0FB//9BQf//QUH//0FB/6gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Rv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDp7AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpF/0A65v9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+pBQf9LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9/QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/zwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDrR/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6uf9AOgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoB/0A6fP9AOv3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//5B Qf+DQUH/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/wNBQf/BQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/xEFB/wAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6WP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOu7/QDoaAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Bv9AOqT/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+qQUH/CAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/yBBQf/zQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgH/QDrU/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgj/QDq2/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf+8QUH/CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/3FBQf//QUH//0FB//9BQf//QUH//0FB//9BQf/EQUH/AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6TP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOs7/QDoCAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoF/0A6tf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+7QUH/BwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/BUFB/9lB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf86AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq8/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Af9AOqP/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+pQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/W0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6gA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6J/9AOv7/QDr//0A6//9AOv//QDr//0A6//9AOtX/QDoBAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp6/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8EQUH/4kFB//9BQf//QUH//0FB//9BQf//QUH/+kFB/xcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqK/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6awAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Qv9AOv3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//1BQf9IAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6A/9AOuX/QDr//0A6//9AOv//QDr//0A6//9AOvf/QDoQAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhH/QDrl/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/+hBQf8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/x5BQf/+QUH//0FB//9BQf//QUH//0FB//9BQf/QAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpC/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6n/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6UAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOpf/QDr//0A6//9AOv//QDr//0A6//9AOv//QDpMAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOjz/QDr+/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/0EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/ZkFB//9BQf//QUH//0FB//9BQf//QUH//0FB/3wAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A65v9AOv//QDr//0A6//9AOv// QDr//0A66/9AOgUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoB/0A6yf9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/zkFB/wEA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8SQUH/+kFB//9B Qf//QUH//0FB//9BQf//QUH/zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOh3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDqUAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOk7/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+zQUH//0FB//9BQf//QUH//0FB//9BQf/8QUH/CAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Kf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOjkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6yP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1lB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf8WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoI/0A68v9AOv//QDr//0A6//9AOv//QDrO/0A6AAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOjf/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf89AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/CkFB/+xBQf//QUH//0FB//9BQf//QUH/7kFB/wIA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpq/0A6/v9AOv// QDr//0A67f9AOjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6m/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/6EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/WkFB//5BQf//QUH//0FB//9BQf9yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDo3/0A6eP9AOmz/QDoXAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgf/QDrw/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/80FB/wkAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/PEFB/5dBQf+cQUH/UUFB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Sf9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/TwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqS/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOkcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/eUFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+XAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOsn/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6NQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9YQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6zf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOuX/QDoFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/xFB Qf/yQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/xwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDqD/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr3/0A6SAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1xBQf/8QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgf/QDoY/0A6GP9AOhj/ QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/ QDoY/0A6GP9AOhj/QDoY/0A6GP9AOhj/QDoPAAAAAAAAAAAAAAAAAAAAAP9AOgX/QDqA/0A61/9AOuj/ QDro/0A66P9AOuj/QDro/0A66P9AOuj/QDro/0A66P9AOuj/QDro/0A66P9AOuj/QDro/0A66P9AOuj/ QDro/0A66P9AOuf/QDrS/0A6lP9AOiYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/zhBQf+pQUH/3kFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hB Qf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+hBQf/oQUH/6EFB/+ZBQf/EQUH/YAAAAAAA AAAAAAAAAAAAAABBQf8EQUH/FkFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhB Qf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/xhBQf8YQUH/GEFB/wYA AAAAAAAAAP9AOgX/QDqP/0A69/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDrj/0A6ff9AOgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8KQUH/mUFB//VBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/9kFB/45BQf8E/0A6hf9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6lgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/6FBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/4L/QDrn/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr8/0A6EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8SQUH//EFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/5v9AOvr/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDo4AAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/zhBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/5/0A6+f9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/PkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//n/QDrp/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrt/0A6BgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8XQUH/+0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/5/9AOov/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOmYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+AQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+I/0A6Bv9AOpn/ QDr7/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6/v9AOs3/QDpUAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9TQUH/v0FB//hBQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/6QUH/mEFB/wYAAAAAAAAAAP9AOgz/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/ QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/QDog/0A6IP9AOiD/ QDog/0A6IP9AOh7/QDoLAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDop/0A6p/9AOtf/QDre/0A63v9AOt7/ QDre/0A63v9AOt7/QDre/0A63v9AOt7/QDre/0A63v9AOt7/QDre/0A63v9AOt7/QDre/0A63v9AOtP/ QDqm/0A6SgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/01B Qf+zQUH/2kFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BB Qf/gQUH/4EFB/+BBQf/gQUH/4EFB/+BBQf/gQUH/30FB/9FBQf+WQUH/JQAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/A0FB/xhBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBB Qf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/yBBQf8gQUH/IEFB/wwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6E/9AOuv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6pf9AOgQAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf90QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/zQUH/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpp/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6eQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/GkFB//lB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+jAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOo3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrp/0A6AwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9jQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/7oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6hv9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDorAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/31B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/lQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpW/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkMA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDou/0A6df9AOm3/QDocAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgz/QDr2/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/8kFB/wgA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/F0FB/2pB Qf92QUH/NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6WP9AOvz/QDr//0A6//9AOvL/QDo7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOqr/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/zFBQf/uQUH//0FB//9BQf/+QUH/YwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgH/QDrl/0A6//9AOv//QDr//0A6//9AOtv/ QDoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6RP9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/zsA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8BQUH/0EFB//9B Qf//QUH//0FB//9BQf/uQUH/BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6F/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A61P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/ztBQf//QUH//0FB//9BQf//QUH//0FB//9BQf8kAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoN/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6owAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDpd/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/1MA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/lUFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/xoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDrY/0A6//9AOv//QDr//0A6//9AOv//QDrz/0A6CgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgP/QDrU/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/NQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/wVBQf/rQUH//0FB//9BQf//QUH//0FB//9BQf/lQUH/AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOor/QDr//0A6//9AOv// QDr//0A6//9AOv//QDpYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOkr/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/0AA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/SkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Nv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOrIAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOq3/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+lQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/QgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A63f9AOv// QDr//0A6//9AOv//QDr//0A6+v9AOhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Gf9AOuv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/50FB/xMA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/DUFB//VB Qf//QUH//0FB//9BQf//QUH//0FB/+ZBQf8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqB/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6cwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6T/9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//1BQf9HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9kQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/jAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOiD/ QDr9/0A6//9AOv//QDr//0A6//9AOv//QDra/0A6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6h/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/fwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/81B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf8qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrX/QDr//0A6//9AOv//QDr//0A6//9AOv// QDpSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoC/0A6r/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/6hBQf8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9CQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/wQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Rv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOtH/QDoDAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoI/0A6v/9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+5QUH/BwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/AEFB/8NB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6z/9AOv//QDr//0A6//9AOv// QDr//0A6//9AOmYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoL/0A6vv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/uUFB/wkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9XQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/2UFB/wMA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpU/0A6//9AOv//QDr//0A6//9AOv//QDr//0A67v9AOhoAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoJ/0A6rf9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkgA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6lBQf8HAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/EkFB/+dB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDrP/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6t/9AOgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoC/0A6h/9AOv7/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9+QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//5BQf+BQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+pQUH//0FB//9BQf//QUH//0FB//9BQf//QUH/2UFB/wQA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOkb/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6dgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Tv9AOuv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDpIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/35BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/pQUH/SQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/Z0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrP/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr7/0A6QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6F/9AOqz/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOkcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/fkFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/qEFB/xYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/zVBQf/4QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/v0FB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Iv9AOvb/QDr//0A6//9AOv//QDr//0A6//9AOv// QDrq/0A6IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOkb/QDrU/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf97QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/0UFB/0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8ZQUH/40FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//pBQf8rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6df9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrS/0A6DQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/ QDpa/0A60v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOuf/QDoGAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/1ZBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/0UFB/1hB Qf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/CEFB/8hBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/ggAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoD/0A6xv9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDq0/0A6AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6Q/9AOqb/QDr1/0A6//9AOv// QDrm/0A6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/DEFB/+hBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/1QUH/pkFB/0FBQf8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/wFBQf+oQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/89BQf8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoh/0A68P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDqT/0A6AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgv/QDox/0A6Lf9AOgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/OEFB/9lBQf//QUH//0FB/9xBQf+cQUH/VEFB/wwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/hUFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/1QUH/KgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpS/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDp0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDoun0GeQUFB/zpBQf8JAAAAAAAAAAAAAAAAQUH/AEFB/xZB Qf8OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2dBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqH/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/ QDpoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDow/0A62f9AOv+g QZ3/QUH//0FB/+1BQf9MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf9cQUH//UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+TAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/ QDqu/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv7/QDpzAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Df9AOuX/QDr//0A6/6BBnf9BQf//QUH//0FB//NBQf8UAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/Z0FB//1BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/uEFB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgn/QDrD/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDqU/0A6BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpn/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/AkFB/4hBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/8xBQf8NAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOg//QDrL/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrB/0A6GwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOqP/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/fgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/xZBQf+5QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/SQUH/EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhH/QDrI/0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDrs/0A6Vv9AOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6t/9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9NQUH/50FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/z0FB/xUAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOg3/QDq6/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6sP9AOh8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8ZQUH/p0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/8JBQf8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgX/ QDqe/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A69v9AOoP/QDoOAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8LQUH/e0FB//JB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf+oQUH/CAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgD/QDpu/0A6+/9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOuz/QDp7/0A6EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9B Qf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8MQUH/c0FB/+hBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/9QUH/eUFB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDo5/0A64P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDry/0A6kf9AOicAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8iQUH/ikFB/+5BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/5UFB/0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoP/0A6n/9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOsL/QDpl/0A6EwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9B Qf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/w9B Qf9fQUH/vEFB//5BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/6hB Qf8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Sf9AOtz/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr7/0A6vP9AOnL/QDov/0A6AgAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8BQUH/K0FB/21BQf+3QUH/+UFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+JBQf9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Cf9AOnz/QDry/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDrl/0A6PwAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/NkFB/+BBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//VBQf+DQUH/CwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhf/QDqQ/0A69v9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDrj/0A6AwAAAAAA AAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf/XQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//hBQf+XQUH/GwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoY/0A6h/9AOu3/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDomAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/GEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//BBQf+NQUH/HAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Cv9AOmX/QDrH/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOh8A AAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8TQUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/81B Qf9qQUH/DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoq/0A6gf9AOtb/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDrK/0A6AAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+g QZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+9QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/20FB/4VBQf8uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOif/QDpv/0A6tf9AOuD/QDrk/0A6rf9AOh4A AAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/xhBQf+nQUH/40FB/+JBQf+5QUH/c0FB/ysAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv// QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv// QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/ QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9B Qf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDq4/0A6//9AOv//QDr/oEGd/0FB//9BQf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOrj/QDr//0A6//9AOv+gQZ3/QUH//0FB//9B Qf//QUH/ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6uP9AOv//QDr//0A6/6BBnf9BQf//QUH//0FB//9BQf+CAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq4/0A6//9AOv//QDr/oEGd/0FB//9B Qf//QUH//0FB/4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOrf/QDr//0A6//9AOv+gQZ3/QUH//0FB//9BQf//QUH/gQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6nv9AOv//QDr//0A6/6BBnf9B Qf//QUH//0FB//9BQf9oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDo2/0A6+/9AOv//QDr/oEGd/0FB//9BQf//QUH/6EFB/xIAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpH/0A6zv9AOvig QZ36QUH/9UFB/7pBQf8pAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////4D///////////////////8Af//////////////// ///AH///////////////////wB///////////////////8Af///////////////////AH/////////// ////////wB///////////////////8Af///////////////////AH///////////////////wB////// /////////////8Af///////////////////AH///////////////////wB///////////////////8Af /////////////////gPAH8A///////////////ABwB/AD/////////////+AAcAfgAH////////////+ AAHAH4AAf///////////+AABwB/AAB///////////+AAA8AfwAAH///////////AAAfAH+AAA/////// ////AAA/wB/+AAD//////////gAB/8Af/8AAf/////////wAD//AH//wAD/////////wAD//wB///AAP ////////4AD//8Af//8AB////////8AB///AH///gAP///////+AB///wB///+AB////////AA///8Af ///wAP///////gAf///AP///+AB///////4Af///4D////4Af//////8AP////Bh////AD//////+AD/ //g/wB///wAf//////AB///gP8AD//+AD//////wA///AB/AAP//wA//////4Af//gAfwAB//+AH//// /+AP//gAH8AAH//wB//////AH//wAB/AAA//+AP/////gB//wAAfwAAD//gB/////4A//4AAH8AAAf/8 Af////8Af/8AAB/AAAD//gD/////AH/+AAAfwAAAf/4A/////wD//AAAH8AAAD//AP////4A//wAAB/A AAA//wB////+Af/4AAAfwAAAH/+Af////AH/8AAAH8AAAA//gH////wD//AAAB/AAAAP/8A////8A//g AAAfwAAAB//AP///+AP/wAAAH8AAAAP/wD////gH/8AAAB/AAAAD/+Af///4B//AAAAfwAAAA//gH/// +Af/gAAAH8AAAAH/4B////wP/4AAAB/AAAAB//A////+H/8AAAAfwAAAAP/4P///////AAAAH8AAAAD/ /////////wAAAB/AAAAA//////////8AAAAfwAAAAP//////////AAAAH8AAAAD//////////wAAAD/g AAAA/////8AAAA8AAAB/8AAAAeAAAAMAAAAB//////////+AAAAAAAAAAf//////////gAAAAAAAAAD/ /////////wAAAAAAAAAA//////////8AAAAAAAAAAP//////////AAAAAAAAAAD//////////wAAAAAA AAAB//////////+AAAAAAAAAA///////////wAAAAMAAAA+AAAD/8AAAAfAAAAP/////AAAAP+AAAAD/ /////////wAAAD/AAAAA//////////8AAAAfwAAAAP//////////AAAAH8AAAAD//////////wAAAB/A AAAA///////+H/8AAAAfwAAAAP/4f////A//gAAAH8AAAAH/8D////gH/4AAAB/AAAAB/+Af///4B/+A AAAfwAAAA//gH///+Af/wAAAH8AAAAP/4B////wD/8AAAB/AAAAD/8Af///8A//gAAAfwAAAB//AP/// /AP/8AAAH8AAAA//wD////wB//AAAB/AAAAP/4A////+Af/4AAAfwAAAH/+Af////gD//AAAH8AAAD// gH////8A//wAAB/AAAA//wD/////AH/+AAAfwAAAf/4A/////wB//wAAH8AAAP/+AP////+AP/+AAB/A AAH//AH/////gB//wAAfwAAD//wB/////8Af//AAH8AAD//4A//////gD//4AB/AAB//8AP/////4Af/ /gAfwAB//+AH//////AD//8AH8AA///AD//////wAf//wD/AA///gA//////+AD///h/4B///4Af//// //wA////8HH///8AP//////+AH///+A////+AH///////gA////AH////AB///////8AD///wB////AA ////////gAf//8Af///gAf///////8AB///AH///wAP////////gAP//wB///wAH////////8AA//8Af //wAD/////////gAD//AH//wAB/////////+AAP/wB//wAB//////////wAAf8Af/gAA///////////A AAfAH+AAA///////////4AADwB/AAAf///////////gAAcAfwAAf///////////+AAHAH4AAf/////// /////4ABwB+AAf/////////////wAcAfwA///////////////APAH8B/////////////////wB////// /////////////8Af///////////////////AH///////////////////wB///////////////////8Af ///////////////////AH///////////////////wB///////////////////8Af//////////////// ///AH///////////////////wB///////////////////8Af///////////////////AH/////////// ////////wB///////////////////+A//////////ygAAABAAAAAgAAAAAEAIAAAAAAAAEAAABMLAAAT CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOl3/QDrwcUHN+kFB/7BBQf8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrV/0A6/3FBzv9BQf//QUH/OQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A62/9AOv9xQc7/QUH//0FB/0AA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/ QDr/cUHO/0FB//9BQf9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDrb/0A6/3FBzv9BQf//QUH/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A62/9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9B Qf9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhr/QDpq/0A6tP9AOtz/QDqY/0A6AAAAAAD/ QDrb/0A6/3FBzv9BQf//QUH/QAAAAAAAAAAAAAAAAEFB/5VBQf/vQUH/y0FB/4FBQf8sAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoC/0A6Tf9AOrf/QDr8/0A6//9AOv// QDr//0A6//9AOhkAAAAA/0A62/9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAEFB/wNBQf/9QUH//0FB//9B Qf//QUH//0FB/8tBQf9fQUH/BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9APwD/QDpQ/0A61P9AOv// QDr//0A6//9AOv//QDr//0A6//9AOtf/QDoEAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9AAAAAAAAAAAAA AAAAQUH/tEFB//9BQf//QUH//0FB//9BQf//QUH//0FB/+BBQf9gQUH/AgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOiL/ QDq7/0A6//9AOv//QDr//0A6//9AOv//QDrd/0A6h/9AOj7/QDoHAAAAAAAAAAD/QDrb/0A6/3FBzv9B Qf//QUH/QAAAAAAAAAAAAAAAAEFB/wBBQf8mQUH/cUFB/8hBQf//QUH//0FB//9BQf//QUH//0FB/8dB Qf8qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOlv/QDry/0A6//9AOv//QDr//0A6/v9AOrH/QDpB/0A6AQAAAAAAAAAAAAAAAAAAAAAA AAAA/0A62/9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/LUFB/59B Qf/6QUH//0FB//9BQf//QUH/90FB/2YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Av9AOor/QDr//0A6//9AOv//QDr//0A6wP9AOjIAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9BQf8/AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/JUFB/7JBQf//QUH//0FB//9BQf//QUH/lEFB/wMAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOp//QDr//0A6//9AOv//QDr1/0A6Zv9AOgEA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrU/0A6/3FBzv9BQf//QUH/LQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8AQUH/WEFB//BBQf//QUH//0FB//9B Qf+lQUH/AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOpb/QDr//0A6//9AOv// QDri/0A6LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6mP9AOv9x Qc7/QUH/40FB/wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8nQUH/3UFB//9BQf//QUH//0FB/5pBQf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOnD/ QDr//0A6//9AOv//QDrh/0A6IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOg//QDqKckHMoUFB/zNBQf8EQUH/J0FB/wYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/xtBQf/dQUH//0FB//9BQf//QUH/cQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOjj/QDr6/0A6//9AOv//QDrv/0A6KgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOg//QDpn/0A6r/9AOoUAAAAAAAAAAAAAAAAAAAAAQUH/lUFB//9BQf/3QUH/vEFB/2pB Qf8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/J0FB/+1BQf//QUH//0FB//pB Qf83AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgj/QDrZ/0A6//9AOv//QDr7/0A6RAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6EP9AOoj/QDry/0A6//9AOv//QDr//0A6HAAAAAAAAAAAAAAAAEFB/75B Qf//QUH//0FB//9BQf//QUH/9EFB/4xBQf8SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf9CQUH/+0FB//9BQf//QUH/10FB/wcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp+/0A6//9AOv//QDr//0A6cAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6T/9AOuj/QDr//0A6//9AOv//QDr//0A6//9AOiQA AAAAAAAAAAAAAABBQf+/QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/6kFB/1MAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/3FBQf//QUH//0FB//9BQf95AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoW/0A68/9AOv// QDr//0A6sP9AOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6iP9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDokAAAAAAAAAAAAAAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/i0FB/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8BQUH/tUFB//9BQf//QUH/8EFB/xIA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6iP9AOv//QDr//0A68/9AOhoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoB/0A6nP9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf+gQUH/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/x1B Qf/1QUH//0FB//9BQf9/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Cv9AOu7/QDr//0A6//9AOogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6h/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABB Qf+/QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/4oAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/kEFB//9BQf//QUH/6EFB/wYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOlz/QDr//0A6//9AOv3/QDofAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Tv9AOv7/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDokAAAAAAAAAAAAAAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/ydBQf//QUH//0FB//9BQf9RAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq2/0A6//9AOv// QDq8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6D/9AOuf/QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+hBQf8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/yUFB//9B Qf//QUH/qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoH/0A6+f9AOv//QDr//0A6YQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOob/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/iQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/3BBQf//QUH//0FB//JBQf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6DP9AOvz/QDr//0A68/9AOg4AAAAAAAAAAAAAAAAAAAAAAAAAAP9AOg7/ QDrx/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDokAAAAAAAAAAAA AAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//JB Qf8PAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8ZQUH/+kFB//9BQf/7QUH/BgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpo/0A6uP9AOk0AAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpk/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/ZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2VBQf/MQUH/cQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+9QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/7kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOuX/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOvn/QDoOAAAAAAAAAAAAAAAAQUH/lkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/kAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoI/0A6DP9AOgz/QDoM/0A6DP9AOgz/QDoM/0A6DP9AOgz/QDoM/0A6DP9AOgz/QDoKAAAAAAAAAAD/ QDqB/0A67/9AOvP/QDrz/0A68/9AOvP/QDrz/0A68/9AOvP/QDrz/0A68/9AOtn/QDpZAAAAAAAAAAAA AAAAAAAAAEFB/xdBQf+3QUH/8UFB//NBQf/zQUH/80FB//NBQf/zQUH/80FB//NBQf/zQUH/80FB//NB Qf/qQUH/cgAAAABBQf8BQUH/C0FB/wxBQf8MQUH/DEFB/wxBQf8MQUH/DEFB/wxBQf8MQUH/DEFB/wxB Qf8MQUH/BwAAAAD/QDqG/0A6/f9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOtf/QDonAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8rQUH/40FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//1BQf+F/0A6+P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6kQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/kUFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/9/9AOvj/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOogAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/5RB Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//j/ QDqK/0A6/v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOsj/ QDoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8gQUH/xEFB//1BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//5BQf+JAAAAAP9AOgv/QDoQ/0A6EP9AOhD/QDoQ/0A6EP9AOhD/QDoQ/0A6EP9AOhD/ QDoQ/0A6EP9AOgoAAAAAAAAAAP9AOkr/QDrf/0A67v9AOu7/QDru/0A67v9AOu7/QDru/0A67v9AOu7/ QDrs/0A6u/9AOioAAAAAAAAAAAAAAAAAAAAAQUH/HUFB/79BQf/uQUH/70FB/+9BQf/vQUH/70FB/+9B Qf/vQUH/70FB/+9BQf/vQUH/70FB/9lBQf9SAAAAAAAAAABBQf8HQUH/EEFB/xBBQf8QQUH/EEFB/xBB Qf8QQUH/EEFB/xBBQf8QQUH/EEFB/xBBQf8LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq9/0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDrY/0A6AQAAAAAAAAAAAAAAAEFB/51BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/1wAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tv9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOhsAAAAAAAAAAAAAAABB Qf++QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/7gA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOmH/QDq4/0A6UgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOmv/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDokAAAAAAAAAAAAAAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf9nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/TkFB/7dBQf9lAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgb/QDr5/0A6//9AOvb/ QDoTAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoR/0A69P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/yQUH/DwAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/D0FB//NB Qf//QUH/+0FB/woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoD/0A69f9AOv//QDr//0A6aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOo3/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/iAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/2FBQf//QUH//0FB//hBQf8HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOq//QDr//0A6//9AOsIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoS/0A66v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDokAAAAAAAAAAAA AAAAQUH/v0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/6EFB/xAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf+7QUH//0FB//9BQf+1AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpY/0A6//9AOv//QDr+/0A6IgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOlX/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6JAAAAAAAAAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8cQUH//EFB//9BQf//QUH/XQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6CP9AOuz/ QDr//0A6//9AOosAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6jv9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/4oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/hEFB//9B Qf//QUH/70FB/woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDqF/0A6//9AOv//QDr0/0A6GgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/ QDqi/0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDokAAAAAAAAAAAAAAAAQUH/v0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/59BQf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/FkFB//BBQf//QUH//0FB/4sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Ff9AOvP/QDr//0A6//9AOq//QDoAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Av9AOo3/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6JAAAAAAA AAAAAAAAAEFB/79BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/4pBQf8CAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/6hBQf//QUH//0FB//VBQf8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp+/0A6//9AOv// QDr//0A6bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6VP9AOur/QDr//0A6//9AOv// QDr//0A6//9AOiQAAAAAAAAAAAAAAABBQf+/QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/6UFB/1IA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2VBQf//QUH//0FB//9BQf+EAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Cf9AOtr/QDr//0A6//9AOvr/QDpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoS/0A6jP9AOvT/QDr//0A6//9AOvn/QDoPAAAAAAAAAAAAAAAAQUH/tEFB//9BQf//QUH//0FB//9B Qf/zQUH/ikFB/xEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/zpBQf/4QUH//0FB//9B Qf/eQUH/CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDo6/0A6+/9AOv//QDr//0A67P9AOiYAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoR/0A6af9AOpf/QDpLAAAAAAAAAAAAAAAAAAAAAEFB/0tB Qf/2QUH/9kFB/7tBQf9qQUH/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/yFB Qf/pQUH//0FB//9BQf/9QUH/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOnb/QDr//0A6//9AOv// QDrc/0A6GgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgz/ QDqCcUHNnkFB/1EAAAAAQUH/BkFB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAEFB/xdBQf/YQUH//0FB//9BQf//QUH/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoB/0A6nv9AOv//QDr//0A6//9AOtz/QDomAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDqW/0A6/3FBzv9BQf/8QUH/HQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAEFB/yNBQf/YQUH//0FB//9BQf//QUH/pEFB/wEAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgT/QDqp/0A6//9AOv//QDr//0A68P9AOlf/QDoAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A61v9AOv9xQc7/QUH//0FB/0AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1JBQf/tQUH//0FB//9BQf//QUH/rUFB/wUA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6A/9AOpf/QDr//0A6//9AOv// QDr//0A6sf9AOiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9B Qf9BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/IUFB/6xBQf//QUH//0FB//9B Qf//QUH/nEFB/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoA/0A6aP9AOvf/QDr//0A6//9AOv//QDr6/0A6n/9AOi4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDrb/0A6/3FBzv9BQf//QUH/QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8rQUH/m0FB//lB Qf//QUH//0FB//9BQf/5QUH/bUFB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDos/0A6yf9AOv//QDr//0A6//9AOv//QDr//0A6yf9AOnL/ QDoo/0A6AQAAAAAAAAAA/0A62/9AOv9xQc7/QUH//0FB/0EAAAAAAAAAAAAAAABBQf8AQUH/JkFB/3BB Qf/GQUH//0FB//9BQf//QUH//0FB//9BQf/MQUH/LwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgL/QDph/0A64f9AOv// QDr//0A6//9AOv//QDr//0A6//9AOsL/QDoBAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9BAAAAAAAAAAAA AAAAQUH/u0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/+NBQf9lQUH/AwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgb/QDpg/0A6yv9AOv//QDr//0A6//9AOv//QDr//0A6EQAAAAD/QDrb/0A6/3FBzv9B Qf//QUH/QQAAAAAAAAAAQUH/C0FB//9BQf//QUH//0FB//9BQf//QUH/zUFB/2JBQf8HAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDor/0A6f/9AOsn/QDrw/0A6pf9AOgAA AAAA/0A62/9AOv9xQc7/QUH//0FB/0EAAAAAAAAAAAAAAABBQf+fQUH/8UFB/8pBQf+BQUH/LQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9BAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrb/0A6/3FBzv9BQf//QUH/QQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A62/9AOv9x Qc7/QUH//0FB/0EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOtv/QDr/cUHO/0FB//9BQf9BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrb/0A6/3FBzv9BQf//QUH/QQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A61f9AOv9xQc7/QUH//0FB/zoA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOl7/ QDrxcUHO+0FB/7JBQf8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA////+D/////////4P/////////g/////////+D/////////4P/////////g/ ////////+D////////wIOD//////4AgwB/////+ACDgB/////wAYOAD////+APg/gH////gH+D/gH/// 8A/4P/AP///gP/g//Af//+B/+A/+B///wP+Hgf8D//+B/gOAf4H//4P8A4A/wf//A/ADgA/A//8H4AOA B+D//g/gA4AH8H/+D8ADgAPwf/4fgAOAAfh//B+AA4AB+D/8HwADgAD4P/4/AAOAAPx///8AA4AA//// /wADgAD//4ADAAeAAIABAAD/////AAAAAP////8AAAAA/////wAAAAD/////AACAAwAHgADAAf//AAOA AP////8AA4AA///+PwADgAD8f/wfAAOAAPg//B+AA4AB+D/+H4ADgAH4f/4PwAOAA/B//g/gA4AH8H// B+ADgAfg//8D8AOAD+D//4P8A4A/wf//gf4DgH+B///A/4eB/wP//+B/+E/+B///4D/4P/wH///wD/g/ +A////gH+D/gH////AH4P4A/////ABg4AP////+ACDgB/////+AIMAf//////Ag4P///////+D////// ///4P/////////g/////////+D/////////4P/////////g/////////+D////8oAAAAMAAAAGAAAAAB ACAAAAAAAAAkAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Cf9AOsmU Qan7QUH/ngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6JP9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgX/QDpF/0A6jv9AOpj/QDoR/0A6Jf9AOv+U Qan/QUH/7wAAAAAAAAAAQUH/DUFB/6BBQf+fQUH/VkFB/wsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDob/0A6iP9AOuj/QDr//0A6//9AOv// QDpQ/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAQUH/PUFB//9BQf//QUH//0FB//FBQf+WQUH/JEFB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6DP9AOoX/QDr0/0A6//9AOv// QDr//0A62P9AOpL/QDoO/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAQUH/BkFB/31BQf/JQUH//EFB//9B Qf//QUH/+EFB/5BBQf8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoz/0A62f9AOv// QDr//0A69/9AOpr/QDoz/0A6AwAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7wAAAAAAAAAAAAAAAAAAAABB Qf8BQUH/JkFB/4tBQf/xQUH//0FB//9BQf/fQUH/OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOlb/ QDr2/0A6//9AOv3/QDql/0A6HAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/7QAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBQf8VQUH/mkFB//xBQf//QUH/+EFB/1xBQf8AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6W/9AOvr/QDr//0A68P9AOlT/QDoBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6FP9AOvqU Qan/QUH/zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/AEFB/0tBQf/tQUH//0FB//tB Qf9eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDo//0A6+f9AOv//QDru/0A6MwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6AP9AOm2VQai4QUH/QUFB/xJBQf8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8vQUH/7EFB//9BQf/5QUH/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOhf/QDrl/0A6//9AOvb/QDpAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Dv9AOmj/QDq7/0A6fgAAAAAAAAAAQUH/CUFB/+5BQf/6QUH/w0FB/2tBQf8PAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAQUH/PUFB//ZBQf//QUH/5UFB/xYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Af9AOqT/QDr//0A6/v9AOmIAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgP/QDpl/0A66P9AOv//QDr//0A62QAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf/pQUH/aEFB/wMAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/2JBQf/+QUH//0FB/6FBQf8AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Of9AOvz/QDr//0A6nP9AOgEA AAAAAAAAAAAAAAAAAAAA/0A6Dv9AOrD/QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/7JBQf8PAAAAAAAAAAAAAAAAAAAAAEFB/wFBQf+fQUH//0FB//xB Qf80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6tP9AOv// QDrp/0A6DgAAAAAAAAAAAAAAAAAAAAD/QDoN/0A6yP9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAA AAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/JQUH/DgAAAAAAAAAAAAAAAAAAAABB Qf8QQUH/7EFB//9BQf+uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoj/0A6/f9AOv//QDp8AAAAAAAAAAAAAAAAAAAAAP9AOgP/QDqv/0A6//9AOv//QDr//0A6//9AOv// QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/sUFB/wMA AAAAAAAAAAAAAAAAAAAAQUH/g0FB//9BQf/7QUH/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDp9/0A6//9AOvr/QDofAAAAAAAAAAAAAAAAAAAAAP9AOmT/QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB/2YAAAAAAAAAAAAAAAAAAAAAQUH/JUFB//xBQf//QUH/cwAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDrE/0A6//9AOrz/QDoAAAAAAAAAAAAAAAAA/0A6Df9AOuf/ QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB/+hBQf8OAAAAAAAAAAAAAAAAQUH/AUFB/8dBQf//QUH/uwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqZ/0A69v9AOksAAAAAAAAAAAAAAAAA AAAA/0A6Z/9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf9pAAAAAAAAAAAAAAAAAAAAAEFB/1tB Qf/8QUH/mgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoD/0A6DP9AOgAA AAAAAAAAAAAAAAAAAAAA/0A6vv9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAA AAAAQUH/EEFB//5BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf/AAAAAAAAAAAAA AAAAAAAAAEFB/wBBQf8WQUH/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A65v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6tQAAAAAAAAAAQUH/BUFB/9pBQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6RP9AOoj/ QDqJ/0A6if9AOon/QDqJ/0A6if9AOon/QDqJ/0A6iP9AOnr/QDoX/0A6P/9AOnb/QDp3/0A6d/9AOnf/ QDp3/0A6d/9AOnf/QDpo/0A6EQAAAAAAAAAAAAAAAEFB/yBBQf9wQUH/d0FB/3dBQf93QUH/d0FB/3dB Qf93QUH/d0FB/3VBQf82QUH/HEFB/4BBQf+JQUH/iUFB/4lBQf+JQUH/iUFB/4lBQf+JQUH/iUFB/4hB Qf9E/0A68f9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDqiAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/okFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/x/0A68v9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDqYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/oUFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/x/0A6R/9AOov/QDqM/0A6jP9AOoz/QDqM/0A6jP9AOoz/ QDqM/0A6i/9AOnX/QDoQ/0A6Jv9AOnD/QDpz/0A6c/9AOnP/QDpz/0A6c/9AOnP/QDpV/0A6BQAAAAAA AAAAAAAAAEFB/yVBQf9wQUH/dEFB/3RBQf90QUH/dEFB/3RBQf90QUH/dEFB/29BQf8mQUH/EUFB/3BB Qf+LQUH/jEFB/4xBQf+MQUH/jEFB/4xBQf+MQUH/jEFB/4tBQf9HAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6xP9AOv//QDr//0A6//9AOv//QDr//0A6//9AOv// QDr//0A6jQAAAAAAAAAAQUH/BkFB/99BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDoC/0A6DP9AOgAAAAAAAAAAAAAAAAAAAAAA/0A6wP9AOv//QDr//0A6//9AOv// QDr//0A6//9AOv//QDr//0A61QAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/AAAAAAAAAAAAAAAAAAAAAAEFB/wBBQf8MQUH/AgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDqR/0A69v9AOlAAAAAAAAAAAAAAAAAAAAAA/0A6bP9AOv// QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf9oAAAAAAAAAAAAAAAAAAAAAEFB/0xBQf/1QUH/lgAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDq+/0A6//9AOsH/QDoAAAAAAAAAAAAA AAAA/0A6D/9AOur/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/+hBQf8OAAAAAAAAAAAAAAAAQUH/AEFB/7xB Qf//QUH/wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDp4/0A6//9AOvv/ QDoiAAAAAAAAAAAAAAAAAAAAAP9AOmn/QDr//0A6//9AOv//QDr//0A6//9AOv//QDr//0A62gAAAAAA AAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH//0FB/2YAAAAAAAAAAAAAAAAA AAAAQUH/HkFB//pBQf//QUH/fQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDog/0A6/f9AOv//QDp/AAAAAAAAAAAAAAAAAAAAAP9AOgP/QDqz/0A6//9AOv//QDr//0A6//9AOv// QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9BQf//QUH/sEFB/wMA AAAAAAAAAAAAAAAAAAAAQUH/ekFB//9BQf/9QUH/IwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6sv9AOv//QDrq/0A6DwAAAAAAAAAAAAAAAAAAAAD/QDoP/0A6y/9AOv// QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9BQf//QUH//0FB//9B Qf/JQUH/DgAAAAAAAAAAAAAAAAAAAABBQf8MQUH/5kFB//9BQf+3AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6OP9AOvz/QDr//0A6m/9AOgEAAAAAAAAAAAAAAAAA AAAA/0A6D/9AOrP/QDr//0A6//9AOv//QDr//0A62gAAAAAAAAAAQUH/EEFB//9BQf//QUH//0FB//9B Qf//QUH//0FB/7FBQf8OAAAAAAAAAAAAAAAAAAAAAEFB/wBBQf+VQUH//0FB//1BQf88AAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Af9AOqX/QDr//0A6/f9AOl8A AAAAAAAAAAAAAAAAAAAAAAAAAP9AOgP/QDpo/0A66f9AOv//QDr//0A61QAAAAAAAAAAQUH/D0FB//5B Qf//QUH//0FB//9BQf/pQUH/Z0FB/wMAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/1pBQf/9QUH//0FB/6lB Qf8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOhj/ QDrn/0A6//9AOvX/QDo8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6D/9AOmv/QDqu/0A6VwAAAAAA AAAAQUH/A0FB/8BBQf/6QUH/wkFB/2pBQf8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/OEFB//RB Qf//QUH/6UFB/xoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpD/0A6+v9AOv//QDrr/0A6LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6AP9AOmeUQam1QUH/XEFB/wFBQf8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB Qf8rQUH/6UFB//9BQf/7QUH/RgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Yf9AOvz/QDr//0A67P9AOkr/QDoAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Ff9AOvmUQan/QUH/5kFB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAQUH/AEFB/0dBQf/qQUH//0FB//xBQf9mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6AP9AOl7/QDr4/0A6//9AOvz/ QDqZ/0A6FAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAABBQf8TQUH/lkFB//tBQf//QUH/+UFB/2JBQf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDo7/0A64P9AOv//QDr//0A68f9AOoz/QDon/0A6AQAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEA AAAAAAAAAAAAAABBQf8BQUH/JUFB/4lBQf/wQUH//0FB//9BQf/iQUH/PgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Ef9AOpH/QDr4/0A6//9AOv//QDr8/0A6yv9AOoH/QDoJ/0A6Jf9AOv+U Qan/QUH/70FB/wEAAAAAQUH/CEFB/35BQf/JQUH//EFB//9BQf//QUH/+EFB/5RBQf8SAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgD/QDok/0A6lv9AOvH/QDr//0A6//9AOv// QDpI/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAQUH/Q0FB//9BQf//QUH//0FB//JBQf+YQUH/JkFB/wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgv/ QDpV/0A6nf9AOqX/QDoS/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAQUH/EEFB/6RBQf+fQUH/VkFB/wwA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+UQan/QUH/70FB/wEA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Jf9AOv+U Qan/QUH/70FB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6JP9AOv+UQan/QUH/70FB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Cf9AOsqUQan8QUH/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA///4f///AAD///h///8AAP//+H///wAA///4f///AAD///h///8AAP//AGD//wAA//wAYB//AAD/ 8ABgD/8AAP/gGHgH/wAA/4D4fwH/AAD/gfh/gf8AAP8H+B/g/wAA/g/DA/B/AAD8HwMA+D8AAPweAwB4 PwAA/DwDADw/AAD4eAMAHh8AAPh4AwAeHwAA+HADAA4fAAD48AMADx8AAPjwAwAPHwAA//ADAA//AAAA AAOAAAAAAAAP///wAAAAAA////AAAAAAAAOAAAAAAP/wAwAP/wAA+PADAA8fAAD48AMADx8AAPhwAwAO HwAA+HgDAB4fAAD4eAMAHh8AAPw8AwA8PwAA/B4DAHg/AAD8HwMA+D8AAP4PwwPwfwAA/wf4H+D/AAD/ gfg/gf8AAP+A+D8B/wAA/+AYOAf/AAD/8AAgD/8AAP/4ACAf/wAA//8AIP//AAD///g///8AAP//+D// /wAA///4P///AAD///g///8AAP//+H///wAAKAAAACAAAABAAAAAAQAgAAAAAAAAEAAAEwsAABMLAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6TLdAhfpBQf97AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpuuECE/0FB/58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOm64QIT/QUH/nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6If9AOmT/QDom/0A6brhAhP9BQf+fAAAAAEFB/yVBQf9uQUH/KwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QD8A/0A6Sf9AOsH/QDr+/0A6//9AOn3/QDpuuECE/0FB/58AAAAAQUH/bUFB//9B Qf//QUH/ykFB/1JBQf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6F/9AOrP/QDr//0A66/9AOof/QDox/0A6Av9AOm64QIT/QUH/nwAAAABB Qf8AQUH/JkFB/31BQf/mQUH//0FB/7pBQf8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOin/QDri/0A6/P9AOor/QDoNAAAAAAAAAAAAAAAA/0A6bLhAhP9B Qf+bAAAAAAAAAAAAAAAAAAAAAEFB/wlBQf+CQUH/+0FB/+RBQf8rAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoc/0A65f9AOvj/QDpMAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDoqtkCGykFB/0dBQf8LAAAAAAAAAAAAAAAAAAAAAAAAAABBQf9IQUH/9kFB/+ZBQf8cAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOsL/QDr+/0A6VwAAAAAAAAAAAAAAAP9AOgT/ QDpi/0A6xf9AOmgAAAAAQUH/VUFB//1BQf/JQUH/ZEFB/wQAAAAAAAAAAAAAAABBQf9WQUH//kFB/8JB Qf8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpi/0A6//9AOogAAAAAAAAAAAAAAAD/ QDoi/0A6zf9AOv//QDr//0A6kgAAAABBQf9fQUH//0FB//9BQf//QUH/zkFB/yMAAAAAAAAAAAAAAABB Qf+JQUH//0FB/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOt3/QDre/0A6BgAAAAAA AAAA/0A6Iv9AOub/QDr//0A6//9AOv//QDqSAAAAAEFB/19BQf//QUH//0FB//9BQf//QUH/50FB/yMA AAAAAAAAAEFB/wdBQf/hQUH/2UFB/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDpE/0A6//9AOnYA AAAAAAAAAP9AOgT/QDrN/0A6//9AOv//QDr//0A6//9AOpIAAAAAQUH/X0FB//9BQf//QUH//0FB//9B Qf//QUH/zkFB/wQAAAAAAAAAAEFB/3xBQf//QUH/PgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOoL/ QDr8/0A6HAAAAAAAAAAA/0A6Yf9AOv//QDr//0A6//9AOv//QDr//0A6kgAAAABBQf9fQUH//0FB//9B Qf//QUH//0FB//9BQf//QUH/YwAAAAAAAAAAQUH/IkFB//5BQf99AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Gv9AOkEAAAAAAAAAAAAAAAD/QDrG/0A6//9AOv//QDr//0A6//9AOv//QDqRAAAAAEFB/19B Qf//QUH//0FB//9BQf//QUH//0FB//9BQf/IAAAAAAAAAAAAAAAAQUH/TEFB/xwAAAAAAAAAAAAAAAD/ QDoC/0A6Bv9AOgb/QDoG/0A6Bv9AOgb/QDoFAAAAAP9AOtX/QDr5/0A6+f9AOvn/QDr5/0A68/9AOlgA AAAAQUH/K0FB/+lBQf/5QUH/+UFB//lBQf/5QUH/+UFB/9BBQf8AQUH/BkFB/wZBQf8GQUH/BkFB/wZB Qf8GQUH/Av9AOt7/QDr//0A6//9AOv//QDr//0A6//9AOv//QDqkAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFB/6dBQf//QUH//0FB//9B Qf//QUH//0FB//9BQf/e/0A64P9AOv//QDr//0A6//9AOv//QDr//0A6//9AOpoAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/nkFB//9B Qf//QUH//0FB//9BQf//QUH//0FB/9//QDoD/0A6CP9AOgj/QDoI/0A6CP9AOgj/QDoHAAAAAP9AOrn/ QDr3/0A69/9AOvf/QDr3/0A66f9AOkEAAAAAQUH/L0FB/+tBQf/3QUH/90FB//dBQf/3QUH/90FB/8AA AAAAQUH/BkFB/whBQf8IQUH/CEFB/whBQf8IQUH/AwAAAAAAAAAAAAAAAP9AOhj/QDpCAAAAAAAAAAAA AAAA/0A6yP9AOv//QDr//0A6//9AOv//QDr//0A6jwAAAABBQf9fQUH//0FB//9BQf//QUH//0FB//9B Qf//QUH/xwAAAAAAAAAAAAAAAEFB/0FBQf8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6fv9AOv3/ QDofAAAAAAAAAAD/QDpl/0A6//9AOv//QDr//0A6//9AOv//QDqSAAAAAEFB/19BQf//QUH//0FB//9B Qf//QUH//0FB//9BQf9iAAAAAAAAAABBQf8cQUH//EFB/4EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ QDpC/0A6//9AOngAAAAAAAAAAP9AOgX/QDrP/0A6//9AOv//QDr//0A6//9AOpIAAAAAQUH/X0FB//9B Qf//QUH//0FB//9BQf//QUH/zUFB/wQAAAAAAAAAAEFB/3VBQf//QUH/RQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP9AOgL/QDrc/0A63/9AOgcAAAAAAAAAAP9AOiT/QDro/0A6//9AOv//QDr//0A6kgAAAABB Qf9fQUH//0FB//9BQf//QUH//0FB/+dBQf8jAAAAAAAAAABBQf8FQUH/3EFB/95BQf8DAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP9AOmL/QDr//0A6hwAAAAAAAAAAAAAAAP9AOiT/QDrP/0A6//9AOv// QDqRAAAAAEFB/19BQf//QUH//0FB//9BQf/OQUH/IwAAAAAAAAAAAAAAAEFB/4NBQf//QUH/ZQAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Av9AOsT/QDr+/0A6VAAAAAAAAAAAAAAAAP9AOgT/ QDpk/0A6wP9AOlUAAAAAQUH/QEFB//pBQf/JQUH/ZEFB/wQAAAAAAAAAAAAAAABBQf9RQUH//UFB/8ZB Qf8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6Hv9AOuf/QDr2/0A6RwAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6KLVAhsdBQf9aQUH/AgAAAAAAAAAAAAAAAAAAAAAAAAAAQUH/REFB//VB Qf/oQUH/HwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6LP9AOuX/ QDr7/0A6gv9AOgkAAAAAAAAAAAAAAAD/QDpsuECE/0FB/6AAAAAAAAAAAAAAAAAAAAAAQUH/CEFB/39B Qf/7QUH/5kFB/y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6Gv9AOrv/QDr//0A65v9AOn7/QDon/0A6AP9AOm64QIT/QUH/oAAAAABBQf8AQUH/JUFB/3xB Qf/lQUH//0FB/71BQf8bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6Af9AOlL/QDrK/0A6//9AOv//QDp1/0A6brhAhP9BQf+gAAAAAEFB/3FB Qf//QUH//0FB/8tBQf9UQUH/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoq/0A6bv9AOin/QDpuuECE/0FB/6AA AAAAQUH/KEFB/29BQf8rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOm64 QIT/QUH/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA/0A6brhAhP9BQf+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAD/QDpNt0CF+0FB/3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//H////x////8f///4Ef//wBA//4AQH/8HHg/+Hw+H/DhBw/x wQOP4YEBh+MBAMfjAQDH5wEA5wEBAAAA//8AAP//AAEBAIDnAQDn4wEAx+MBAMfhgQGH8cEDj/DhBw/4 fD4f/Bx4P/4AQH//AED//+BH///8f////H////x//ygAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAAT CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy0BwrUFB/0cAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QDoI/0A6I81AbrZBQf9QQUH/JUFB/wsA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgb/QDp//0A6zP9AOmzNQG62QUH/UEFB/2RB Qf/LQUH/g0FB/wYAAAAAAAAAAAAAAAAAAAAAAAAAAP9AOgf/QDq6/0A6dP9AOgMAAAAAyUBymEFB/zsA AAAAQUH/AkFB/3FBQf+7QUH/BwAAAAAAAAAAAAAAAAAAAAD/QDqJ/0A6dwAAAAD/QDo9/0A6yf9AOj5B Qf+sQUH/y0FB/z4AAAAAQUH/d0FB/4gAAAAAAAAAAAAAAAD/QDoS/0A6zP9AOgL/QDo9/0A6+f9AOv// QDpJQUH/r0FB//9BQf/5QUH/PUFB/wJBQf/NQUH/EAAAAAAAAAAA/0A6J/9AOlYAAAAA/0A6yf9AOv// QDr//0A6SUFB/69BQf//QUH//0FB/8oAAAAAQUH/W0FB/yYAAAAA/0A6ef9AOoP/QDqD/0A6av9AOnT/ QDp9/0A6e/9AOhZBQf9FQUH/fUFB/31BQf9yQUH/a0FB/4NBQf+DQUH/ef9AOnr/QDqE/0A6hP9AOmj/ QDps/0A6e/9AOnj/QDoQQUH/RkFB/3xBQf98QUH/bkFB/2hBQf+EQUH/hEFB/3oAAAAA/0A6Jf9AOlcA AAAA/0A6y/9AOv//QDr//0A6SEFB/69BQf//QUH//0FB/8oAAAAAQUH/VkFB/ycAAAAAAAAAAP9AOhH/ QDrN/0A6Av9AOj7/QDr5/0A6//9AOklBQf+vQUH//0FB//lBQf89QUH/AUFB/8xBQf8SAAAAAAAAAAAA AAAA/0A6iv9AOnYAAAAA/0A6Pv9AOsj/QDo5QUH/pkFB/8tBQf89AAAAAEFB/3RBQf+LAAAAAAAAAAAA AAAAAAAAAP9AOgf/QDq7/0A6cf9AOgIAAAAAyUBzl0FB/z8AAAAAQUH/AkFB/3BBQf+8QUH/CAAAAAAA AAAAAAAAAAAAAAAAAAAA/0A6B/9AOoP/QDrL/0A6Z81AbrZBQf9QQUH/ZUFB/8tBQf+EQUH/BwAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0A6C/9AOibNQG62QUH/UEFB/yZBQf8LAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy0BwrUFB/0cAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA/n8AAPgfAADgBwAAwkMAAMgTAACAAQAAkAkAAAAAAAAAAAAAkAkAAIABAADI EwAAwkMAAOAHAAD4HwAA/n8AAA== ================================================ FILE: DsDiag/Ds_Diag_Button.Designer.cs ================================================ namespace DsDiag { partial class Ds_Diag_Button { /// /// Variable nécessaire au concepteur. /// private System.ComponentModel.IContainer components = null; /// /// Nettoyage des ressources utilisées. /// /// true si les ressources managées doivent être supprimées ; sinon, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Code généré par le Concepteur de composants /// /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// private void InitializeComponent() { this.Pnl_Background = new System.Windows.Forms.Panel(); this.Lbl_Number = new System.Windows.Forms.Label(); this.Pnl_Background.SuspendLayout(); this.SuspendLayout(); // // Pnl_Background // this.Pnl_Background.BackColor = System.Drawing.Color.Crimson; this.Pnl_Background.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.Pnl_Background.Controls.Add(this.Lbl_Number); this.Pnl_Background.Dock = System.Windows.Forms.DockStyle.Fill; this.Pnl_Background.Location = new System.Drawing.Point(0, 0); this.Pnl_Background.Name = "Pnl_Background"; this.Pnl_Background.Size = new System.Drawing.Size(26, 26); this.Pnl_Background.TabIndex = 0; // // Lbl_Number // this.Lbl_Number.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.Lbl_Number.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Lbl_Number.ForeColor = System.Drawing.Color.White; this.Lbl_Number.Location = new System.Drawing.Point(-2, 0); this.Lbl_Number.Name = "Lbl_Number"; this.Lbl_Number.Size = new System.Drawing.Size(26, 22); this.Lbl_Number.TabIndex = 0; this.Lbl_Number.Text = "1"; this.Lbl_Number.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // Ds_Diag_Button // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.Pnl_Background); this.Name = "Ds_Diag_Button"; this.Size = new System.Drawing.Size(26, 26); this.Pnl_Background.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Panel Pnl_Background; private System.Windows.Forms.Label Lbl_Number; } } ================================================ FILE: DsDiag/Ds_Diag_Button.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DsDiag { public partial class Ds_Diag_Button : UserControl { private int _Number; public Ds_Diag_Button(int Number) { InitializeComponent(); _Number = Number; Lbl_Number.Text = Number.ToString(); } public void Activate(bool isActivated) { if (isActivated) Pnl_Background.BackColor = Color.Green; else Pnl_Background.BackColor = Color.Crimson; } } } ================================================ FILE: DsDiag/Ds_Diag_Button.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DsDiag/Program.cs ================================================ using System; using System.Collections.Generic; using System.Windows.Forms; namespace DsDiag { static class Program { /// /// Point d'entrée principal de l'application. /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new DsDiag()); } } } ================================================ FILE: DsDiag/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("Ds_Diag")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Ds_Diag")] [assembly: AssemblyCopyright("Argonlefou")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("8bf3d631-f741-48d8-acb8-8f2096b1094e")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("16.0.0")] ================================================ FILE: DsDiag/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. // //------------------------------------------------------------------------------ namespace DsDiag.Properties { using System; /// /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. /// // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder // à l'aide d'un outil, tel que ResGen ou Visual Studio. // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen // avec l'option /str ou régénérez votre projet VS. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DsDiag.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Remplace la propriété CurrentUICulture du thread actuel pour toutes /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Recherche une ressource localisée de type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap sharps1 { get { object obj = ResourceManager.GetObject("sharps1", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } } } ================================================ FILE: DsDiag/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\sharps1.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ================================================ FILE: DsDiag/Properties/Settings.Designer.cs ================================================ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. // //------------------------------------------------------------------------------ namespace DsDiag.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } } } ================================================ FILE: DsDiag/Properties/Settings.settings ================================================  ================================================ FILE: DsDiag/app.config ================================================ ================================================ FILE: README.md ================================================ DemulShooter is a software interfering with (mostly) emulators to allow users to play railshooter games with up to 4 lightguns or HID devices (Joystick, analog controllers...). Except for some emulators (MAME, supermodel), most of them do not support RawInput protocol and thus dual player mode with mouse-alike devices. It was greatly inspired by the TroubleShooter 2 Software, which unfortunatelly did not support Demul and can be troublesome for users having swappable USB devices. DemulShooter also provides outputs signal to enable LEDs and Force Feedback on lightgun/cabinets. See WIKI for detailed instructions : https://github.com/argonlefou/DemulShooter/wiki A lot of troubleshooting and information are available in DemulShooter's thread on BYOAC forum : http://forum.arcadecontrols.com/index.php/topic,149714.0.html ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/DCOP_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 EXIT=27 [SYSTEM] ;Default data folder is in "%userProfile%\AppData\LocalLow\AppData\LocalLow\clairstyler _ Klockworks\DCOP\" ;Set this to "1" to save data in "\NVRAM\" folder from game root path. SAVE_TO_GAME_FOLDER=1 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.dcop"; public const String pluginName = "DCOP_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "DCOP_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 1; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int)KeyCode.Alpha0); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public static bool SaveToGameFolder = false; //Game is setting Arduino PINS to set outputs : // Pin 02 => Game Gun // Pin 03 => Direct Hit (YELLOW LIGHT) // Pin 08 => Police Light Bar // Pin 16 => Red Light (CONTINUE / GAME OVER) // Pin 19 => Game gun (YELLOW FLASH) public enum ArduinoPin { PlayerGun_Solenoid = 2, DirectHit_Light = 3, Police_LightBar = 8, DirectHit_Light2 = 12, ArduinoReady_Light = 13, EnemyGun_Solenoid = 15, RedLightContinue = 16, EnemyGun2_Solenoid = 17, WhiteStrobe_Flasher = 18, GameGun_Light = 19 } public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadBoolValue("SYSTEM", "SAVE_TO_GAME_FOLDER", ref SaveToGameFolder); Plugin_IniFile.IniReadIntValue("VIDEO", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("VIDEO", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("VIDEO", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("VIDEO", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() { if (ForceResolution) Screen.SetResolution(ScreenWidth, ScreenHeight, Fullscreen); } public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} //Make sure to remove the crosshair at the time we get the packet, as the game updates the display only at certain times (change of weapon, etc....) if (!CrossHairVisibility) UnityEngine.Cursor.visible = false; } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/Uduino/mUduinoConnection_DesktopSerial.cs ================================================ using HarmonyLib; using Uduino; namespace BepInEx_DemulShooter_Plugin { class mUduinoConnection_DesktopSerial { /// /// disable Arduino lookups and hangs for connections failed /// [HarmonyPatch(typeof(UduinoConnection_DesktopSerial), "Discover")] class TurnOnPin { static bool Prefix(ref string[] portNames) { DemulShooter_Plugin.MyLogger.LogMessage("UduinoConnection_DesktopSerial.Discover()"); portNames = new string[0]; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/Uduino/mUduinoManager.cs ================================================ using HarmonyLib; using Uduino; namespace BepInEx_DemulShooter_Plugin { class mUduinoManager { /// /// Trigger the siren and police light /// [HarmonyPatch(typeof(UduinoManager), "arduinoWrite")] class arduinoWrite { static bool Prefix(UduinoDevice target, int pin, int value, string typeOfPin, string bundle=null) { //DemulShooter_Plugin.MyLogger.LogWarning("UduinoManager.arduinoWrite() => pin=" + pin + ", value=" + value); /*if (bundle != null) DemulShooter_Plugin.MyLogger.LogMessage("UduinoManager.arduinoWrite() => target=" + target.ToString() + ", pin=" + pin.ToString() + ", value=" + value.ToString() + ", typeOfPin=" + typeOfPin.ToString() + ", bundle=" + bundle.ToString()); else DemulShooter_Plugin.MyLogger.LogMessage("UduinoManager.arduinoWrite() => target=" + target.ToString() + ", pin=" + pin.ToString() + ", value=" + value.ToString() + ", typeOfPin=" + typeOfPin.ToString() + ", bundle=null"); */ //DemulShooter_Plugin.MyLogger.LogMessage("UduinoManager.arduinoWrite() => pin=" + pin.ToString() + ", value=" + value.ToString() + ", typeOfPin=" + typeOfPin.ToString()); if (value != 0) { if (pin == (int)DemulShooter_Plugin.ArduinoPin.PlayerGun_Solenoid) DemulShooter_Plugin.OutputData.GunRecoil = 1; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.DirectHit_Light) DemulShooter_Plugin.OutputData.DirectHit = 1; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.Police_LightBar) DemulShooter_Plugin.OutputData.Police_LightBar = 1; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.ArduinoReady_Light) DemulShooter_Plugin.OutputData.GreenTestLight = 1; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.RedLightContinue) DemulShooter_Plugin.OutputData.RedLight = 1; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.WhiteStrobe_Flasher) DemulShooter_Plugin.OutputData.WhiteStrobe = 1; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.GameGun_Light) DemulShooter_Plugin.OutputData.GunLight = 1; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.DirectHit_Light2) DemulShooter_Plugin.MyLogger.LogWarning("UduinoManager.arduinoWrite() => DirectHit_Light2 ON"); else if (pin == (int)DemulShooter_Plugin.ArduinoPin.EnemyGun_Solenoid) DemulShooter_Plugin.MyLogger.LogWarning("UduinoManager.arduinoWrite() => Enemy Gun Solenoid ON"); else if (pin == (int)DemulShooter_Plugin.ArduinoPin.EnemyGun2_Solenoid) DemulShooter_Plugin.MyLogger.LogWarning("UduinoManager.arduinoWrite() => Enemy Gun Solenoid 2 ON"); } else { if (pin == (int)DemulShooter_Plugin.ArduinoPin.PlayerGun_Solenoid) DemulShooter_Plugin.OutputData.GunRecoil = 0; if (pin == (int)DemulShooter_Plugin.ArduinoPin.DirectHit_Light) DemulShooter_Plugin.OutputData.DirectHit = 0; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.Police_LightBar) DemulShooter_Plugin.OutputData.Police_LightBar = 0; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.ArduinoReady_Light) DemulShooter_Plugin.OutputData.GreenTestLight = 0; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.RedLightContinue) DemulShooter_Plugin.OutputData.RedLight = 0; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.WhiteStrobe_Flasher) DemulShooter_Plugin.OutputData.WhiteStrobe = 0; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.GameGun_Light) DemulShooter_Plugin.OutputData.GunLight = 0; else if (pin == (int)DemulShooter_Plugin.ArduinoPin.DirectHit_Light2) DemulShooter_Plugin.MyLogger.LogWarning("UduinoManager.arduinoWrite() => DirectHit_Light2 OFF"); else if (pin == (int)DemulShooter_Plugin.ArduinoPin.EnemyGun_Solenoid) DemulShooter_Plugin.MyLogger.LogWarning("UduinoManager.arduinoWrite() => Enemy Gun Solenoid OFF"); else if (pin == (int)DemulShooter_Plugin.ArduinoPin.EnemyGun2_Solenoid) DemulShooter_Plugin.MyLogger.LogWarning("UduinoManager.arduinoWrite() => Enemy Gun Solenoid 2 OFF"); } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mGunScript.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mGunScript { /// /// Get ammo info /// [HarmonyPatch(typeof(GunScript), "Update")] class Update { static bool Prefix(bool ___gunEnabled, int ___ammoCount) { if (___gunEnabled) DemulShooter_Plugin.OutputData.P1_Ammo = (byte)___ammoCount; else DemulShooter_Plugin.OutputData.P1_Ammo = 0; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mHighscoreManager.cs ================================================ using System.IO; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mHighscoreManager { /// /// Create local save folder if needed /// [HarmonyPatch(typeof(HighscoreManager), "Awake")] class Awake { static void Postfix(HighscoreManager __instance) { if (DemulShooter_Plugin.SaveToGameFolder) { if (!Directory.Exists(__instance.path)) { Directory.CreateDirectory(__instance.path); } } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mLifeTracker.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mLifeTracker { /// /// Get life info /// [HarmonyPatch(typeof(LifeTracker), "Update")] class Update { static bool Prefix(int ___lifes) { DemulShooter_Plugin.OutputData.P1_Life = (byte)___lifes; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mPauseGame.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mPauseGame { /// /// Replace the ESC key used to go in pause by TEST key /// [HarmonyPatch(typeof(PauseGame))] [HarmonyPatch("Update")] public static class Update { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (var i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("GetKeyUp")) { if (codes[i - 1].opcode == OpCodes.Ldc_I4_S) { if (codes[i - 1].operand.ToString().Equals("27")) { codes[i - 1].operand = DemulShooter_Plugin.Test_Key.KeyCode; DemulShooter_Plugin.MyLogger.LogMessage("PauseGame.Update(): Patched ESC key"); break; } } } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mSetMousecursor.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mSetMousecursor { /// /// Remove crosshair /// [HarmonyPatch(typeof(SetMouseCursor), "ShowCursor")] class ShowCursor { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogWarning("SetMousecursor.ShowCursor()"); if (DemulShooter_Plugin.CrossHairVisibility == false) UnityEngine.Cursor.visible = false; else UnityEngine.Cursor.visible = true; return false; } } [HarmonyPatch(typeof(SetMouseCursor), "SetCursor")] class SetCursor { static bool Prefix(ref int cursorNumber) { DemulShooter_Plugin.MyLogger.LogWarning("SetMousecursor.SetCursor() => cursorNumber=" + cursorNumber); if (DemulShooter_Plugin.CrossHairVisibility == false) cursorNumber = -1; //-1 makes the original function call HideCursor() return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mU_hitboxHit.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { /*class mU_hitboxHit { /// /// Trigger the yellow light /// [HarmonyPatch(typeof(U_hitboxHit), "Update")] class TurnOnPin { static bool Prefix(bool ___turnOnLed) { //Dcop_Plugin.MyLogger.LogMessage("mU_hitboxHit.Update()"); if (___turnOnLed) Dcop_Plugin.OutputData.P1_Damaged = 1; return true; } } }*/ } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mU_onContinueCountDown.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { /*class mU_onContinueCountDown { /// /// Trigger the RED light /// [HarmonyPatch(typeof(U_onContinueCountdown), "TurnOnPin")] class TurnOnPin { static bool Prefix() { Dcop_Plugin.MyLogger.LogMessage("mU_onContinueCountdown.TurnOnPin()"); Dcop_Plugin.OutputData.RedLight = 1; return true; } } [HarmonyPatch(typeof(U_onContinueCountdown), "TurnOffPin")] class TurnOffPin { static bool Prefix() { Dcop_Plugin.MyLogger.LogMessage("mU_onContinueCountdown.TurnOffPin()"); Dcop_Plugin.OutputData.RedLight = 0; return true; } } }*/ } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mU_onPlayerShoot.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { /*class mU_onPlayerShoot { /// /// Trigger the gun recoil and gun light /// [HarmonyPatch(typeof(U_onPlayerShoot), "TurnOnPin")] class TurnOnPin { static bool Prefix() { Dcop_Plugin.MyLogger.LogMessage("mU_onPlayerShoot.TurnOnPin()"); Dcop_Plugin.OutputData.P1_Recoil = 1; return true; } } }*/ } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Patch/mU_policeLight.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { /*class mU_policeLight { /// /// Trigger the siren and police light /// [HarmonyPatch(typeof(U_policeLight), "TurnOnPin")] class TurnOnPin { static bool Prefix() { Dcop_Plugin.MyLogger.LogMessage("mU_policeLight.TurnOnPin()"); Dcop_Plugin.OutputData.Police_LightBar = 1; return true; } } [HarmonyPatch(typeof(U_policeLight), "TurnOffPin")] class TurnOffPin { static bool Prefix() { Dcop_Plugin.MyLogger.LogMessage("mU_policeLight.TurnOffPin()"); Dcop_Plugin.OutputData.Police_LightBar = 0; return true; } } }*/ } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("DCOP_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DCOP_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage("TcpOutputData.Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError("TcpOutputData.Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte GunRecoil = 0; public byte DirectHit = 0; public byte Police_LightBar = 0; public byte GreenTestLight = 0; public byte RedLight = 0; public byte WhiteStrobe = 0; public byte GunLight = 0; public byte P1_Life = 0; public byte P1_Ammo = 0; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.CoreModule.dll UnityEngine.InputLegacyModule.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/UnityPlugin_BepInEx_DCOP.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin DCOP_BepInEx_DemulShooter_Plugin v4.8 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.CoreModule.dll UnityLibs\UnityEngine.InputLegacyModule.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DCOP/UnityPlugin_BepInEx_DCOP.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_DCOP", "UnityPlugin_BepInEx_DCOP.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BasePlugin { public const String pluginGuid = "argonlefou.demulshooter.drakon"; public const String pluginName = "Drakon_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "Drakon_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int)KeyCode.Alpha0); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; public static TcpOutputData OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; public static bool IsLevelChosen = false; //Saved instance to enter the game with a shoot isntead of Start button public static InactivePlayerState[] InactiveStates = new InactivePlayerState[2]; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public static bool IsMouseLockedRequired = false; public static bool SaveToGameFolder = false; public override void Load() { Instance = this; // Plugin startup logic MyLogger = base.Log; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadBoolValue("SYSTEM", "SAVE_TO_GAME_FOLDER", ref SaveToGameFolder); Plugin_IniFile.IniReadIntValue("Video", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("Video", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("Video", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("Video", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Drakon_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 EXIT=27 [SYSTEM] ;Default data folder is in "%userProfile%/AppData/LocalLow/Sarbakan/09038_Adrenaline_Skyride_Turret/" ;Set this to "1" to save data in "/NVRAM/" folder from game root path. SAVE_TO_GAME_FOLDER=1 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/INIFile.cs ================================================ using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet=CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/SBK/Matrix/mMatrixManager.cs ================================================ using System.Runtime.CompilerServices; using HarmonyLib; using SBK.Matrix; namespace BepInEx_DemulShooter_Plugin.Patch.SBK_Matrix { class mMatrixManager { /// /// Forcing Dongle present data at start and change a few dongle details /// [HarmonyPatch(typeof(MatrixManager), "Awake")] class Awake { static bool Prefix(MatrixManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mMatrixManager.Awake()"); mset_IsDonglePresent.Set_IsDonglePresent(__instance, true); mset_mb_Init.Set_mb_Init(__instance, true); mset_DongleId.Set_DongleID(__instance, 11); return true; } static void Postfix(MatrixManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mMatrixManager.Awake() - Postfix"); DemulShooter_Plugin.MyLogger.LogMessage(" --> DeviceId=" + __instance.GetDeviceId().ToString()); DemulShooter_Plugin.MyLogger.LogMessage(" --> SerialNumber=" + __instance.GetDongleSerialNumber.ToString()); DemulShooter_Plugin.MyLogger.LogMessage(" --> IsDonglePresent=" + __instance.IsDonglePresent.ToString()); } } [HarmonyPatch(typeof(MatrixManager), "GetDeviceId")] class GetDeviceId { static bool Prefix(MatrixManager __instance, ref string __result) { DemulShooter_Plugin.MyLogger.LogMessage("mMatrixManager.GetDeviceId()"); __result = "4r60N13F0U"; return false; } } [HarmonyPatch(typeof(MatrixManager), "ReadSerial")] class ReadSerial { static bool Prefix(MatrixManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mMatrixManager.ReadSerial()"); return true; } } [HarmonyPatch(typeof(MatrixManager), "DongleId", MethodType.Getter)] class DongleId { static bool Prefix(MatrixManager __instance, ref short __result) { __result = 11; return false; } } [HarmonyPatch(typeof(MatrixManager), "GetDongleSerialNumber", MethodType.Getter)] class GetDongleSerialNumber { static bool Prefix(MatrixManager __instance, ref int __result) { __result = 1; //Bigger values creates Unity error from time to time during gameplay (???) return false; } } #region Dummy Calls [HarmonyPatch(typeof(MatrixManager), "set_DongleId")] class mset_DongleId { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void Set_DongleID(object instance, short value) { //Used to call the private method } } [HarmonyPatch(typeof(MatrixManager), "set_IsDonglePresent")] class mset_IsDonglePresent { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void Set_IsDonglePresent(object instance, bool value) { //Used to call the private method } } [HarmonyPatch(typeof(MatrixManager), "set_mb_Init")] class mset_mb_Init { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void Set_mb_Init(object instance, bool value) { //Used to call the private method } } #endregion } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/SBK/PneumaticSeatsystem/mPneumaticSeatManager.cs ================================================ using System.Runtime.CompilerServices; using HarmonyLib; using SBK.PneumaticSeatSystem; namespace BepInEx_DemulShooter_Plugin.Patch.SBK_PneumaticSeatsystem { class mPneumaticSeatManager { [HarmonyPatch(typeof(PneumaticSeatManager), "Awake")] class Awake { static bool Prefix(PneumaticSeatManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.Awake()"); return true; } } /// /// Forcing Isconnected to true /// [HarmonyPatch(typeof(SBK.PneumaticSeatSystem.PneumaticSeatManager), "Connect")] class Connect { static bool Prefix(PneumaticSeatManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.Connect()"); mset_IsConnected.Set_IsConnected(__instance, true); return false; } } [HarmonyPatch(typeof(SBK.PneumaticSeatSystem.PneumaticSeatManager), "GetInitialisationInfo")] class GetInitialisationInfo { static bool Prefix(PneumaticSeatManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.GetInitialisationInfo()"); return true; } } /// /// Forcing IsInitIsDone to true /// [HarmonyPatch(typeof(PneumaticSeatManager), "Init")] class Init { static bool Prefix(PneumaticSeatManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.Init()"); mset_IsInitDone.Set_IsInitDone(__instance, true); //__instance.SkipInitTest(); //__instance.StartGame(); return false; } } [HarmonyPatch(typeof(PneumaticSeatManager), "StartInitTest")] class StartInitTest { static bool Prefix(PneumaticSeatManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.StartInitTest()"); return true; } } [HarmonyPatch(typeof(PneumaticSeatManager), "SkipInitTest")] class SkipInitTest { static bool Prefix(PneumaticSeatManager __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.SkipInitTest()"); return true; } } [HarmonyPatch(typeof(PneumaticSeatManager), "Update")] class Update { static bool Prefix() { //Plugin.myLogger.LogMessage("mPneumaticSeatManager.Update()"); return true; } } [HarmonyPatch(typeof(PneumaticSeatManager), "get_IsInitDone")] class get_IsInitDone { static bool Prefix(ref bool __result) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.get_IsInitDone()"); /*__result = true; return false;*/ return true; } void postfix(ref bool __result) { DemulShooter_Plugin.MyLogger.LogMessage("mPneumaticSeatManager.get_IsInitDone() postfix : __result=" + __result); //__result = true; } } #region Dummy Calls [HarmonyPatch(typeof(PneumaticSeatManager), "set_IsConnected")] class mset_IsConnected { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void Set_IsConnected(object instance, bool value) { //Used to call the private method } } [HarmonyPatch(typeof(PneumaticSeatManager), "set_IsInitDone")] class mset_IsInitDone { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void Set_IsInitDone(object instance, bool value) { //Used to call the private method } } #endregion } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/SBK/RS232/mRS232DLL.cs ================================================ using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; namespace BepInEx_DemulShooter_Plugin.Patch.SBK_RS232 { class mRS232DLL { [HarmonyPatch(typeof(SBK.RS232.RS232DLL), "GetActivePortsIndex")] class GetActivePortsIndex { static bool Prefix(/*ref int[] __result*/) { //Drakon_Plugin.MyLogger.LogMessage("GetActivePortsIndex Prefix"); return true; } static void Postfix(ref Il2CppStructArray __result) { /*__result = new Il2CppStructArray(0); Drakon_Plugin.MyLogger.LogMessage("GetActivePortsIndex Postfix, __result.length = " + __result.Length.ToString()); if (__result.Length > 0) { for (int i = 0; i < __result.Length; i++) { Drakon_Plugin.MyLogger.LogMessage("-> COM Index " + __result[i].ToString() + " is active : " ); } }*/ } } [HarmonyPatch(typeof(SBK.RS232.RS232DLL), "GetActivePortsNames")] class GetActivePortsNames { static bool Prefix() { //Drakon_Plugin.MyLogger.LogMessage("GetActivePortsNames Prefix"); return true; } static void Postfix (ref Il2CppStringArray __result) { __result = new Il2CppStringArray(0); /*Drakon_Plugin.MyLogger.LogMessage("GetActivePortsName Postfix, __result.length = " + __result.Length.ToString()); if (__result.Length > 0) { for (int i = 0; i < __result.Length; i++) { Drakon_Plugin.MyLogger.LogMessage("-> PortName " + __result[i].ToString() + " is active : "); } }*/ } } /*[HarmonyPatch(typeof(SBK.RS232.RS232DLL), "GetPortIndexByName")] class GetPortIndexByName { static bool Prefix(string Name) { Drakon_Plugin.MyLogger.LogMessage("GetPortIndexByName prefix: Name=" + Name); return true; } static void Postfix(string Name, ref int __result) { Drakon_Plugin.MyLogger.LogMessage("GetPortIndexByName Postfix, index= " + __result.ToString()); __result = 1; } }*/ /*[HarmonyPatch(typeof(SBK.RS232.RS232DLL), "Open", new Type[] { typeof(int), typeof(int) })] class Open1 { static bool Prefix(int comport_number, int baudrate = 115200) { Plugin.myLogger.LogMessage("mRS232DLL.Open1() : comport_number=" + comport_number.ToString() + ", baudrate=" + baudrate.ToString()); return true; } }*/ /*[HarmonyPatch(typeof(SBK.RS232.RS232DLL), "Open", new Type[] { typeof(int), typeof(int), typeof(byte[]), typeof(int) })] class Open2 { static bool Prefix(int comport_number, int baudrate, byte[] mode, int flowctrl) { Plugin.myLogger.LogMessage("mRS232DLL.Open2() : comport_number=" + comport_number.ToString() + ", baudrate=" + baudrate.ToString() + ", mode.length=" + mode.Length.ToString() + ", flowctrl=" + flowctrl.ToString()); return true; } }*/ } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/SBK/Skyride_Turret/mTurret.cs ================================================ using System.Runtime.CompilerServices; using HarmonyLib; using SBK.Skyride.Turret; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch.SBK_Skyride_Turret { internal class mTurret { /// /// Check for each player if FireBreath is activated, to create our own Rumble output /// [HarmonyPatch(typeof(Turret), "Update")] class Update { static bool Prefix(Turret __instance) { SBK.ArcadePlayer.Player CurrentPlayer = mget_arcadePlayer.Get_arcadePlayer(__instance); if (mget_FireBreathEnabled.Get_FireBreathEnabled(__instance)) DemulShooter_Plugin.OutputData.Rumble[CurrentPlayer.id] = 1; else DemulShooter_Plugin.OutputData.Rumble[CurrentPlayer.id] = 0; return true; } } /// /// Intercepting the FireBullet event to create our own recoil output /// [HarmonyPatch(typeof(Turret), "FireBullet")] class FireBullet { static bool Prefix(Turret __instance, Vector3 hitScreenPos) { //DemulShooter_Plugin.MyLogger.LogWarning("SBK.Skyride.Turret.Turret.Firebullet() : ,hitScreenPos=" + hitScreenPos.ToString() + ", arcadePlayer.id=" + mget_arcadePlayer.Get_arcadePlayer(__instance).id.ToString() + ", arcadePlayer.name=" + mget_arcadePlayer.Get_arcadePlayer(__instance).name); SBK.ArcadePlayer.Player CurrentPlayer = mget_arcadePlayer.Get_arcadePlayer(__instance); DemulShooter_Plugin.OutputData.Recoil[CurrentPlayer.id] = 1; return true; } } #region Dummy Calls [HarmonyPatch(typeof(Turret), "get_arcadePlayer")] class mget_arcadePlayer { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static SBK.ArcadePlayer.Player Get_arcadePlayer(object instance) { //Used to call the private method return null; } } [HarmonyPatch(typeof(Turret), "get_FireBreathEnabled")] class mget_FireBreathEnabled { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static bool Get_FireBreathEnabled(object instance) { //Used to call the private method return true; } } #endregion } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/SBK/TurretSystem/mTurretManager.cs ================================================ using System.Runtime.CompilerServices; using HarmonyLib; using SBK.TurretSystem; namespace BepInEx_DemulShooter_Plugin.Patch.SBK_TurretSystem { class mTurretManager { [HarmonyPatch(typeof(TurretManager), "Awake")] class Awake { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("mTurretManager.Awake()"); return true; } } [HarmonyPatch(typeof(TurretManager), "Connect")] class Connect { static bool Prefix(TurretManager __instance) { //DemulShooter_Plugin.MyLogger.LogMessage("mTurretManager.Connect()"); return true; } } [HarmonyPatch(typeof(TurretManager), "Init")] class Init { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("mTurretManager.Init()"); return true; } } /// /// Debug build needs the original function to run so that the mouse can work normally /// Release build needs to force the result or the Turret Error Message will be displayed /// [HarmonyPatch(typeof(TurretManager), "IsConnected")] class IsConnected { static bool Prefix(ref bool __result) { if (!UnityEngine.Debug.isDebugBuild) { __result = true; return false; } else { return true; } } } [HarmonyPatch(typeof(TurretManager), "Update")] class Update { static bool Prefix() { //Plugin.myLogger.LogMessage("mPneumaticSeatManager.Update()"); return true; } } /*[HarmonyPatch(typeof(TurretManager), "IsInitDone", MethodType.Getter)] class get_IsInitDone { static bool Prefix(TurretManager __instance) { Plugin.myLogger.LogMessage("mTurretManager.get_IsInitDone()"); Plugin.myLogger.LogMessage(TurretManager.IsInitDone.ToString()); return true; } void postfix(ref bool __result) { Plugin.myLogger.LogMessage("mTurretManager.get_IsInitDone() postfix : __result=" + __result); //__result = true; } }*/ [HarmonyPatch(typeof(TurretManager), "set_IsInitDone")] class mset_IsInitDone { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void Set_IsInitDone(object instance, bool value) { //Used to call the private method } } [HarmonyPatch(typeof(TurretManager), "GetFirmwareVersion")] class mGetFirmwareVersion { static bool Prefix(int controllerID, ref string __result) { DemulShooter_Plugin.MyLogger.LogWarning("mTurretManager.GetFirmwareVersion() : controllerID=" + controllerID); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/SBK/mApplicationManager.cs ================================================ using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin.Patch.SBK_ { class mApplicationManager { /// /// Changing Resolution based on the INI file /// [HarmonyPatch(typeof(SBK.ApplicationManager), "Start")] class start { static void Postfix() { if (DemulShooter_Plugin.ForceResolution) Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); } } /// /// Using this Update loop, running during the whole game, to implement our logic /// Usually this is in a custom MonoBehaviour element created with the plugin, but I still have to find how to add one under IL2CPP /// [HarmonyPatch(typeof(SBK.ApplicationManager), "Update")] class Update { static bool Prefix() { //Credits are always 0 /* DemulShooter_Plugin.OutputData.P1_Credits = (byte)PlayerUtility.GetPlayerCredit(0); DemulShooter_Plugin.OutputData.P2_Credits = (byte)PlayerUtility.GetPlayerCredit(1); */ UnityEngine.Cursor.visible = false; //Auto closing console-debug window object developerConsoleVisible = null; var mInfo = typeof(UnityEngine.Debug).GetMethod("get_developerConsoleVisible", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); if (mInfo != null) { developerConsoleVisible = mInfo.Invoke(null, null); bool mVisible = (bool)developerConsoleVisible; if (mVisible) { var setConsoleVisible_MethodInfo = typeof(UnityEngine.Debug).GetMethod("set_developerConsoleVisible", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); if (setConsoleVisible_MethodInfo != null) { DemulShooter_Plugin.MyLogger.LogMessage("Debug.set_developerConsoleVisible() found, trying to call it..."); setConsoleVisible_MethodInfo.Invoke(null, new object[] { false }); } else { DemulShooter_Plugin.MyLogger.LogError("Debug.ClearDeveloperConsole() NOT found !"); } } } else { DemulShooter_Plugin.MyLogger.LogError("Debug.ClearDeveloperConsole() NOT found !"); } //Custom Button handling DemulShooter_Plugin.Exit_Key.SetButton(Input.GetKey((KeyCode)DemulShooter_Plugin.Exit_Key.KeyCode)); DemulShooter_Plugin.Test_Key.SetButton(Input.GetKey((KeyCode)DemulShooter_Plugin.Test_Key.KeyCode)); for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { DemulShooter_Plugin.PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)DemulShooter_Plugin.PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); DemulShooter_Plugin.PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)DemulShooter_Plugin.PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!DemulShooter_Plugin.EnableInputHack) { DemulShooter_Plugin.PluginControllers[i].SetAimingValues(Input.mousePosition); DemulShooter_Plugin.PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); DemulShooter_Plugin.PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (DemulShooter_Plugin.Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Fetching some outputs if (PlayerUtility.GetPlayer(SkyrideUtils.PlayerID.One).IsPlaying()) DemulShooter_Plugin.OutputData.Start_Led[0] = 0; else DemulShooter_Plugin.OutputData.Start_Led[0] = 1; if (PlayerUtility.GetPlayer(SkyrideUtils.PlayerID.Two).IsPlaying()) DemulShooter_Plugin.OutputData.Start_Led[1] = 0; else DemulShooter_Plugin.OutputData.Start_Led[1] = 1; //Checking for a change in output to send or not byte[] bOutputData = DemulShooter_Plugin.OutputData.ToByteArray(); byte[] bOutputDataBefore = DemulShooter_Plugin.OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { DemulShooter_Plugin.SendOutputs(); break; } } //Save current state DemulShooter_Plugin.OutputDataBefore.Update(bOutputData); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mChooseLevelWindow.cs ================================================ using System.Runtime.CompilerServices; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mChooseLevelWindow { /// /// To select a level, look like 2 conditions are needed : /// - 1) The LevelSelectorPanel must be highlighted /// - 2) The WindowTurretfired method must be called with Vector2 axis data corresponding to the Panel location highlighted /// That way P1 can only choose the level pointed by him, and not validate a level pointed by P2 /// /// Calling LevelSlectorPanel.LevelSelected directly select the Level.but we need to find how to get the good one fired at /// /// THe LevelSelectorPanel.WindowTurretFired() has a Vector2 parameter, and coordinates are not in -1/+1 range : /// It's corrsponding to each LevelSelectorPanel "position" (in Unity Expliorer) referential, not the screen or world /// /// Looks like the position we want is between those bounds : /// -8.0 ; -5.0 for lower left /// +8.0 ; +5.0 for upper right /// Should be easu to get from the -1.0/ 1.0 axis we have with demulshooter /// [HarmonyPatch(typeof(ChooseLevelWindow), "Update")] class LevelUpdate { static bool Prefix(ChooseLevelWindow __instance) { //Get the IsLevelChosen flag for later use DemulShooter_Plugin.IsLevelChosen = mget_IsLevelChosen.get_IsLevelChosen(__instance); Il2CppSystem.Collections.Generic.List Panels = mget_panelList.Get_panelList(__instance); if (Panels != null && Panels.Count > 0) { for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { if (DemulShooter_Plugin.PluginControllers[i].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { foreach (LevelSelectorPanel p in Panels) { Vector2 v = DemulShooter_Plugin.PluginControllers[i].GetAimingPosition(); v.x = (2.0f * v.x /(float)Screen.width) - 1.0f; v.y = (2.0f * v.y / (float)Screen.height) - 1.0f; DemulShooter_Plugin.MyLogger.LogMessage("Player" + (i + 1).ToString() + " : " + v.ToString()); var methodInfo = typeof(LevelSelectorPanel).GetMethod("WindowTurretFired"); /// Convert [-1.0; +1.0] range Gun value to [-8.0, +8.0] for X and [-5.0, +5.0] for Y Vector2 VWorldPos = new Vector2(DemulShooter_Plugin.PluginControllers[i].GetAimingPosition().x * 8, DemulShooter_Plugin.PluginControllers[i].GetAimingPosition().y * 5); methodInfo.Invoke(p, new object[] { (SkyrideUtils.PlayerID)i, VWorldPos }); } } } } return true; } } #region Dummy Calls [HarmonyPatch(typeof(ChooseLevelWindow), "get_IsLevelChosen")] class mget_IsLevelChosen { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static bool get_IsLevelChosen(object instance) { //Used to call the private method return false; } } [HarmonyPatch(typeof(ChooseLevelWindow), "get_panelList")] class mget_panelList { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static Il2CppSystem.Collections.Generic.List Get_panelList(object instance) { //Used to call the private method return null; } } #endregion } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mContinueWindow.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { /// /// Adding the possibility to send a "START button pressed" signal by shooting (like it is written on screen) /// internal class mContinueWindow { [HarmonyPatch(typeof(ContinueWindow), "Update")] class Update { static bool Prefix(ContinueWindow __instance) { for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger) && DemulShooter_Plugin.InactiveStates[i] != null) { DemulShooter_Plugin.MyLogger.LogMessage("mContinueWindow.Update() : P" + (i + 1).ToString() +" START pressed"); var methodInfo = typeof(InactivePlayerState).GetMethod("OnPlayerStartButtonPressed"); methodInfo.Invoke(DemulShooter_Plugin.InactiveStates[i], new object[] { PlayerUtility.GetPlayer(i) }); methodInfo = typeof(ContinueWindow).GetMethod("OnPlayerStartButtonPressed"); methodInfo.Invoke(__instance, new object[] { PlayerUtility.GetPlayer(i) }); } } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mHudWindow.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mHudWindow { /// /// Using this class to intercept damage info on screen /// [HarmonyPatch(typeof(HudWindow), "OnPlayerHitByEnemy")] class OnPlayerHitByEnemy { static bool Prefix(EnemyManager.IEnemyTypeProvider typeProvider, SkyrideUtils.PlayerID player) { DemulShooter_Plugin.MyLogger.LogMessage("mHudWindow.OnPlayerHitByEnemy() : PlayerId=" + player.ToString()); if (player == SkyrideUtils.PlayerID.One) { DemulShooter_Plugin.OutputData.Damaged[0] = 1; } else if (player == SkyrideUtils.PlayerID.Two) { DemulShooter_Plugin.OutputData.Damaged[1] = 1; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mInactivePlayerState.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mInactivePlayerState { /// /// Saving each player's IncativePlayerState instance uppon entering them /// So that we can use it later to call the "OnPlayerButtonPressed" method of that instace /// [HarmonyPatch(typeof(InactivePlayerState), "OnEnter")] class OnEnter { static bool Prefix(InactivePlayerState __instance) { DemulShooter_Plugin.MyLogger.LogMessage("InactivePlayerState.OnEnter() : " + __instance.name + ", " + __instance.Controller.name + ", " + __instance.controller.id); DemulShooter_Plugin.InactiveStates[__instance.controller.id] = __instance; return true; } } /// /// Resetting the instance to null so that the event can be sent only once when the KEY is pressed /// [HarmonyPatch(typeof(InactivePlayerState), "OnExit")] class OnExit { static bool Prefix(InactivePlayerState __instance) { DemulShooter_Plugin.MyLogger.LogMessage("InactivePlayerState.OnExit() : " + __instance.name + ", " + __instance.Controller.name + ", " + __instance.controller.id); DemulShooter_Plugin.InactiveStates[__instance.controller.id] = null; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mInputsManager.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { /// /// Those 2 functions are called in a loop, so we can just intercept the calls and insert our own data /// class mInputsManager { [HarmonyPatch(typeof(InputsManager), "SendButtonEvent")] class SendButtonEvent { static bool Prefix(int player, ref bool shooting) { //DemulShooter_Plugin.MyLogger.LogMessage("mInputsManager.SendButtonEvent() : player=" + player.ToString() + ", shooting=" + shooting.ToString()); shooting = DemulShooter_Plugin.PluginControllers[player].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger); return true; } } [HarmonyPatch(typeof(InputsManager), "SendPositionEvent")] class SendPositionEvent { static bool Prefix(int player, ref UnityEngine.Vector3 viewportPosition) { //DemulShooter_Plugin.MyLogger.LogMessage("mInputsManager.SendPositionEvent(): player=" + player.ToString() + ", viewportPosition=" + viewportPosition.ToString()); Vector3 v = DemulShooter_Plugin.PluginControllers[player].GetAimingPosition(); v.x = (2.0f * v.x / (float)Screen.width) - 1.0f; v.y = (2.0f * v.y / (float)Screen.height) - 1.0f; viewportPosition = v; //DemulShooter_Plugin.MyLogger.LogMessage("mInputsManager.SendPositionEvent(): player=" + player.ToString() + ", viewportPosition=" + viewportPosition.ToString()); return true; } } #region Dummy Calls /* [HarmonyPatch(typeof(InputsManager), "SendButtonEvent")] class mSendButtonEvent { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void SendButtonEvent(object instance, int player, bool shooting) { //Used to call the private method } } [HarmonyPatch(typeof(InputsManager), "SendPositionEvent")] class mSendPositionEvent { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void SendPositionEvent(object instance, int player, UnityEngine.Vector3 viewportPosition) { //Used to call the private method } }*/ #endregion } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mLaser2D.cs ================================================ using System.Runtime.CompilerServices; using HarmonyLib; using UnityEngine; // Old patch to remove only Laser sights // When the no-crosshair patch was just changing crosshair Texture scale to 0 namespace BepInEx_DemulShooter_Plugin.Patch { /// /// Removing the Lasers on screen /// internal class mLaser2D { /* [HarmonyPatch(typeof(Laser2D), "Awake")] class Awake { static bool Prefix(Laser2D __instance) { if (!Drakon_Plugin.CrossHairVisibility) { RectTransform r = mget_rect.Get_rect(__instance); r.gameObject.SetActive(false); return false; } else { return true; } return true; } } [HarmonyPatch(typeof(Laser2D), "Start")] class Start { static bool Prefix(Laser2D __instance) { if (!Drakon_Plugin.CrossHairVisibility) { RectTransform r = mget_rect.Get_rect(__instance); r.gameObject.SetActive(false); return false; } else { return true; } } } [HarmonyPatch(typeof(Laser2D), "OnTargetMoved")] class OnTargetMoved { static bool Prefix(Laser2D __instance, Target t, ref UnityEngine.Vector2 position) { if (!Drakon_Plugin.CrossHairVisibility) { RectTransform r = mget_rect.Get_rect(__instance); r.gameObject.SetActive(false); return false; } else { return true; } } } #region Dummy Calls [HarmonyPatch(typeof(Laser2D), "get_rect")] class mget_rect { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static RectTransform Get_rect(object instance) { //Used to call the private method return null; } } #endregion */ } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mPlayerWindow.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mPlayerWindow { /// /// Adding the possibility to send a "START button pressed" signal by shooting (like it is written on screen) /// [HarmonyPatch(typeof(PlayerWindow), "Update")] class Update { static bool Prefix(PlayerWindow __instance) { for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger) && DemulShooter_Plugin.InactiveStates[i] != null) { DemulShooter_Plugin.MyLogger.LogMessage("mPlayerWindow.Update() : P" + (i + 1).ToString() + " START pressed"); var methodInfo = typeof(InactivePlayerState).GetMethod("OnPlayerStartButtonPressed"); methodInfo.Invoke(DemulShooter_Plugin.InactiveStates[i], new object[] { PlayerUtility.GetPlayer(i) }); } } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mTarget.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mTarget { /// /// Change the crosshair position for both players /// /// Old bhack version: /// The crosshair needs to be on an item in menu to activate the item, it's not just the rect drawing /// Putting the RectTransform to not active is goosd to remove crosshair in-game, but can't validate in menu /// changing the localscale to (0,0,0) make it disappear and still active to clicks ! /// /// New hack version: /// Check if we are in Level Select screen (== IsLevelChosen is false), if not we can put the crosshairs AND the lasers away from sight /// at the same time [HarmonyPatch(typeof(Target), "OnTurretPosition")] class OnTurretPosition { static bool Prefix(int player, ref Vector3 viewportPosition, Target __instance) { //Plugin.myLogger.LogMessage("mTarget.OnTurretPosition() : player=" + player.ToString() + ", viewportPosition=" + viewportPosition.ToString()); /*if (DemulShooter_Plugin.CrossHairVisibility == false) { RectTransform r = __instance.RectTransform; r.localScale = new Vector3(0, 0, 0); }*/ if (!DemulShooter_Plugin.CrossHairVisibility && DemulShooter_Plugin.IsLevelChosen) viewportPosition = new Vector3(-2.0f, -2.0f, 0); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mTitleWindow.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { /// /// Adding the possibility to send a "START button pressed" signal by shooting (like it is written on screen) /// internal class mTitleWindow { [HarmonyPatch(typeof(TitleWindow), "Update")] class Update { static bool Prefix(TitleWindow __instance) { for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger) && DemulShooter_Plugin.InactiveStates[i] != null) { DemulShooter_Plugin.MyLogger.LogMessage("mTitleWindow.Update() : P" + (i + 1).ToString() + " START pressed"); var methodInfo = typeof(InactivePlayerState).GetMethod("OnPlayerStartButtonPressed"); methodInfo.Invoke(DemulShooter_Plugin.InactiveStates[i], new object[] { PlayerUtility.GetPlayer(i) }); methodInfo = typeof(TitleWindow).GetMethod("OnPlayerStartButtonPressed"); methodInfo.Invoke(__instance, new object[] { PlayerUtility.GetPlayer(i) }); } } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Patch/mUnityEngine.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mUnityEngine { /// /// Game automatically ask for set_Fullscreen() whatever we choose /// Disabling that function will stop the game from changing screen mode after we set our own in ApplicationManager::Awake() /// Note that it does not prevent us to choose full screen or ALT+RETURN during game /// [HarmonyPatch(typeof(UnityEngine.Screen), "set_fullScreen")] class set_fullScreen { static bool Prefix(ref bool value) { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("Drakon_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Drakon_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 //Folowing command can work OR cause issue with Unity, especially if .NET 4.0 is required and Mono may miss some functionality _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); //Following way is more compatible in case of trouble //FieldInfo[] fiBuffer = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); //_DataFields = new FieldInfo[fiBuffer.Length]; //string[] fiNames = new string[fiBuffer.Length]; //for (int i = 0; i < fiNames.Length; i++) //{ // fiNames[i] = fiBuffer[i].Name; //} //Array.Sort(fiNames); //for (int i = 0; i < _DataFields.Length; i++) //{ // foreach (FieldInfo fi in fiBuffer) // { // if (fi.Name == fiNames[i]) // { // _DataFields[i] = fi; // break; // } // } //} foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError(this.GetType().Name + ".Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // _Writer.Write((bool)o); //else if (ReferenceEquals(t, typeof(byte))) // _Writer.Write((byte)o); //else if (ReferenceEquals(t, typeof(char))) // _Writer.Write((char)o); //else if (ReferenceEquals(t, typeof(decimal))) // _Writer.Write((decimal)o); //else if (ReferenceEquals(t, typeof(double))) // _Writer.Write((double)o); //else if (ReferenceEquals(t, typeof(float))) // _Writer.Write((float)o); ///*else if (ReferenceEquals(t, typeof(nint))) // _Writer.Write((nint)o); //else if (ReferenceEquals(t, typeof(nuint))) // _Writer.Write((nuint)o);*/ //else if (ReferenceEquals(t, typeof(long))) // _Writer.Write((long)o); //else if (ReferenceEquals(t, typeof(sbyte))) // _Writer.Write((sbyte)o); //else if (ReferenceEquals(t, typeof(short))) // _Writer.Write((short)o); //else if (ReferenceEquals(t, typeof(uint))) // _Writer.Write((uint)o); //else if (ReferenceEquals(t, typeof(ulong))) // _Writer.Write((ulong)o); //else if (ReferenceEquals(t, typeof(ushort))) // _Writer.Write((ushort)o); //else if (ReferenceEquals(t, typeof(int))) // _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // return _Reader.ReadBoolean(); //else if (ReferenceEquals(t, typeof(byte))) // return _Reader.ReadByte(); //else if (ReferenceEquals(t, typeof(char))) // return _Reader.ReadChar(); //else if (ReferenceEquals(t, typeof(decimal))) // return _Reader.ReadDecimal(); //else if (ReferenceEquals(t, typeof(double))) // return _Reader.ReadDouble(); //else if (ReferenceEquals(t, typeof(float))) // return _Reader.ReadSingle(); ///*else if (ReferenceEquals(t, typeof(nint))) // return _Reader. //else if (ReferenceEquals(t, typeof(nuint))) // return _Reader.*/ //else if (ReferenceEquals(t, typeof(long))) // return _Reader.ReadInt64(); //else if (ReferenceEquals(t, typeof(sbyte))) // return _Reader.ReadSByte(); //else if (ReferenceEquals(t, typeof(short))) // return _Reader.ReadInt16(); //else if (ReferenceEquals(t, typeof(uint))) // return _Reader.ReadUInt32(); //else if (ReferenceEquals(t, typeof(ulong))) // return _Reader.ReadUInt64(); //else if (ReferenceEquals(t, typeof(ushort))) // return _Reader.ReadUInt16(); //else if (ReferenceEquals(t, typeof(int))) // return _Reader.ReadInt32(); //else // return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Start_Led = null; public byte[] Recoil = null; public byte[] Damaged = null; public byte[] Rumble = null; public int[] Credits = null; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll BepInEx.Core.dll BepInEx.Unity.IL2CPP.dll Il2CppInterop.Runtime.dll Il2Cppmscorlib.dll SBK.ArcadePlayer.dll SBK.BaseService.dll SBK.Core.dll SBK.CreditManagement.dll SBK.DynamicStateMachine.dll SBK.Matrix.dll UnityEngine.CoreModule.dll UnityEngine.InputLegacyModule.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/UnityPlugin_BepInEx_Drakon.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin Drakon_BepInEx_DemulShooter_Plugin v4.8 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false False UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.Core.dll UnityLibs\BepInEx.Unity.IL2CPP.dll UnityLibs\Il2CppInterop.Runtime.dll UnityLibs\Il2Cppmscorlib.dll UnityLibs\SBK.ArcadePlayer.dll UnityLibs\SBK.BaseService.dll UnityLibs\SBK.Core.dll UnityLibs\SBK.CreditManagement.dll UnityLibs\SBK.DynamicStateMachine.dll UnityLibs\SBK.Matrix.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.CoreModule.dll UnityLibs\UnityEngine.InputLegacyModule.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_DRK/UnityPlugin_BepInEx_Drakon.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_Drakon", "UnityPlugin_BepInEx_Drakon.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.marssortie"; public const String pluginName = "MarsSortie_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "MarsSortie_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 4; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("Video", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("Video", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("Video", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("Video", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(new Vector3(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, 0)); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Action, Input.GetMouseButton(2) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i] / Screen.width, _InputData.Axis_Y[i] / Screen.height)); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.Action, _InputData.ChangeWeapon[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/MarsSortie_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/MRCQ/mGameDatabase.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mGameDatabase { /// /// Called in a loop /// Station is player : 0 to 5 /// We can get credits value for outputs /// /// Also check GetcoinsIn() ??? /// [HarmonyPatch(typeof(MRCQ.GameDataBase), "GetCredit")] class GetCredits { static bool Prefix(int station = -1, int record = -1, bool total = false) { //MarsSortie_Test_BepInEx_Plugin.MyLogger.LogMessage("MRCQ.GameDataBase.GetCredits(): station=" + station + ", record=" + record + ",total=" + total ); return true; } static void Postfix(ref int __result, int station = -1, int record = -1, bool total = false) { if (station >= 0 && station <= 4) DemulShooter_Plugin.OutputData.Credits[station] = __result; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/MRCQ/mHardware.cs ================================================ using System; using HarmonyLib; using MRCQ; namespace BepInEx_DemulShooter_Plugin.Patch { class mHardware { /// /// axisName is "x01" ... "x06" and "y01" ... "y06" /// X value is 0.0f (left) to 1.0f (right) /// Y value is 0.0f (Bottom) to 1.0f (top) /// [HarmonyPatch(typeof(Hardware), "GetInputAxis", new Type[] { typeof(string) })] class GetInputAxis { static void Postfix(string axisName, ref float __result) { switch (axisName) { case "x01": __result = DemulShooter_Plugin.PluginControllers[0].Axis_X; break; case "x02": __result = DemulShooter_Plugin.PluginControllers[1].Axis_X; break; case "x03": __result = DemulShooter_Plugin.PluginControllers[2].Axis_X; break; case "x04": __result = DemulShooter_Plugin.PluginControllers[3].Axis_X; break; case "y01": __result = DemulShooter_Plugin.PluginControllers[0].Axis_Y; break; case "y02": __result = DemulShooter_Plugin.PluginControllers[1].Axis_Y; break; case "y03": __result = DemulShooter_Plugin.PluginControllers[2].Axis_Y; break; case "y04": __result = DemulShooter_Plugin.PluginControllers[3].Axis_Y; break; default: __result = 0.0f; break; } } } /// /// Update buttons with our own values by sending signals /// [HarmonyPatch(typeof(Hardware), "Update")] class Update { static bool Prefix() { for (int i = 0; i < 4; i++) { //Trigger input bool flag = false; if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) flag = true; MRCQ.Hardware.SetInputKey(i, "startGameKey", flag); MRCQ.Hardware.SetInputKey(i, "fire", flag); //Switch Weapon flag = false; if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Action)) flag = true; MRCQ.Hardware.SetInputKey("switchWeapon0" + ((i + 1).ToString()), flag); } return true; } } [HarmonyPatch(typeof(Hardware), "SetOutputKey", new Type[] { typeof(string), typeof(bool) })] class SetOutputKey { static void Prefix(string keyName, bool keyStatus) { //MarsSortie_BepInEx_Plugin.MyLogger.LogMessage("MRCQ.Hardware.SetOutputKey(): keyName=" + keyName + " ,keyS=" + keyStatus); switch (keyName) { case "flashlight": DemulShooter_Plugin.OutputData.Flashlight = keyStatus ? (byte)1 : (byte)0; break; case "start01": DemulShooter_Plugin.OutputData.IsPlaying[0] = keyStatus ? (byte)1 : (byte)0; break; case "start02": DemulShooter_Plugin.OutputData.IsPlaying[1] = keyStatus ? (byte)1 : (byte)0; break; case "start03": DemulShooter_Plugin.OutputData.IsPlaying[2] = keyStatus ? (byte)1 : (byte)0; break; case "start04": DemulShooter_Plugin.OutputData.IsPlaying[3] = keyStatus ? (byte)1 : (byte)0; break; case "fire01": DemulShooter_Plugin.OutputData.Recoil[0] = keyStatus ? (byte)1 : (byte)0; break; case "fire02": DemulShooter_Plugin.OutputData.Recoil[1] = keyStatus ? (byte)1 : (byte)0; break; case "fire03": DemulShooter_Plugin.OutputData.Recoil[2] = keyStatus ? (byte)1 : (byte)0; break; case "fire04": DemulShooter_Plugin.OutputData.Recoil[3] = keyStatus ? (byte)1 : (byte)0; break; default: break; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/MRCQ/mLocalization.cs ================================================ using HarmonyLib; using System.Reflection; using System.Collections.Generic; namespace BepInEx_DemulShooter_Plugin.Patch { class mLocalization { /// /// List available Languages /// [HarmonyPatch] class LoadMask { static MethodBase TargetMethod() { MethodInfo[] mis = AccessTools.TypeByName("MRCQ.Localization").GetMethods(BindingFlags.Instance | BindingFlags.NonPublic); foreach (MethodInfo mi in mis) { if (mi.Name.Equals("LoadMask")) return mi; } return null; } static bool Prefix(List ___languages) { DemulShooter_Plugin.MyLogger.LogMessage("MRCQ.Localization.LoadMask(): Available languages:"); for (int i = 0; i < ___languages.Count; i++) { DemulShooter_Plugin.MyLogger.LogMessage(___languages[i]); } return true; } } /// /// Force SetLanguage as English /// [HarmonyPatch] class SetLanguage { static MethodBase TargetMethod() { MethodInfo[] mis = AccessTools.TypeByName("MRCQ.Localization").GetMethods(BindingFlags.Static | BindingFlags.NonPublic); foreach (MethodInfo mi in mis) { if (mi.Name.Equals("SetLanguage")) return mi; } return null; } static bool Prefix(ref string language) { DemulShooter_Plugin.MyLogger.LogMessage("MRCQ.Localization.SetLanguage(): language=" + language); language = "English"; return true; } } /// /// Force word translation to be used in English /// Degaut call has language empty string, even if English is set above as a Language /// [HarmonyPatch] class Tran { static MethodBase TargetMethod() { MethodInfo[] mis = AccessTools.TypeByName("MRCQ.Localization").GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo mi in mis) { if (mi.Name.Equals("Tran")) return mi; } return null; } static bool Prefix(string str, ref string language) { //DemulShooter_Plugin.MyLogger.LogMessage("MRCQ.Localization.Tran(): str=" + str + ", language=" + language); language = "English"; return true; } static void Postfix(string str, string language, string __result) { //DemulShooter_Plugin.MyLogger.LogMessage("MRCQ.Localization.Tran(): __result=" + __result); } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/MRCQ/mSignalKey.cs ================================================ using System.Xml; using HarmonyLib; using MRCQ; namespace BepInEx_DemulShooter_Plugin.Patch { class mSignalKey { /// /// Replacing hardware encoded keyboard keys to MAME-compatible map /// [HarmonyPatch(typeof(SignalKey), MethodType.Constructor, new System.Type[] { typeof(XmlNode), typeof (bool) })] class Ctor { static void Postfix(XmlNode signal, bool isInputSignal, ref string ___simulator, string ___device, string ___name) { //DemulShooter_Plugin.MyLogger.LogMessage("---------- SIGNAL-----------"); //DemulShooter_Plugin.MyLogger.LogMessage("IsInput=" + isInputSignal); //DemulShooter_Plugin.MyLogger.LogMessage("name=" + ___name); //DemulShooter_Plugin.MyLogger.LogMessage("device=" + ___device); //DemulShooter_Plugin.MyLogger.LogMessage("key='" + ___simulator + "'"); bool flag = false; string oldsim = ___simulator; switch (___simulator) { case "1": ___simulator = "5"; flag = true; break; case "2": ___simulator = "6"; flag = true; break; case "3": ___simulator = "7"; flag = true; break; case "4": ___simulator = "8"; flag = true; break; /*case "q": ___simulator = "1"; flag = true; break; case "w": ___simulator = "2"; flag = true; break; case "e": ___simulator = "3"; flag = true; break; case "r": ___simulator = "4"; flag = true; break;*/ default: break; } if (flag) DemulShooter_Plugin.MyLogger.LogMessage("MRCQ.SignalKey.Ctor(): changed Key for " + ___name + " Signal from [" + oldsim + "] to [" + ___simulator + "]" ); } } /// /// Blocking Keyboard/mouse inputs for gameplay /// Only keeping TEST menu controls /// [HarmonyPatch(typeof(SignalKey), "Update")] class Update { static bool Prefix(ref string ___simulator, string ___outputKeyName, bool ___isVKey, ref int ___mouse) { //Remove mouse buttons if (___mouse != -1) ___mouse = -1; //Remove unwanted keyboard buttons if (___simulator != null) { if (___simulator.Equals("5") || ___simulator.Equals("6") || ___simulator.Equals("7") || ___simulator.Equals("8") || ___simulator.Equals("space") || ___simulator.Equals("up") || ___simulator.Equals("down") || ___simulator.Equals("left") || ___simulator.Equals("right")) return true; else ___simulator = null; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/MRCQ/mSuperDogManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mSuperDogManager { /// /// Removing dongle check /// [HarmonyPatch(typeof(MRCQ.SuperDogManager), "DoCheckKey")] class Update { static bool Prefix(int featureId, MRCQ.SuperDogManager.EncryptionArray enArr, ref bool __result) { DemulShooter_Plugin.MyLogger.LogMessage("MRCQ.SuperDogManager.DoCheckKey()"); __result = true; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/MRCQ/mSystemError.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mSystemError { /// /// Removing upperleft red messages on screen /// [HarmonyPatch(typeof(MRCQ.SystemError), "SetIOBoardError")] class SetIOBoardError { static bool Prefix(string error) { //MarsSortie_Test_BepInEx_Plugin.MyLogger.LogMessage("MRCQ.SystemError.SetIOBoardError(): error=" + error); return false; } } [HarmonyPatch(typeof(MRCQ.SystemError), "SetLaserCamError")] class SetLaserCamError { static bool Prefix(string error) { //MarsSortie_Test_BepInEx_Plugin.MyLogger.LogMessage("MRCQ.SystemError.SetLaserCamError(): error=" + error); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/Now/mBulletCounter.cs ================================================ using HarmonyLib; using Now; namespace BepInEx_DemulShooter_Plugin.Patch { class mBulletCounter { [HarmonyPatch(typeof(BulletCounter), "SetBulletCount")] class SetBulletCount { static bool Prefix(PlayerPanel ___playerPanel, int count) { DemulShooter_Plugin.MyLogger.LogMessage("SetBulletCount: " + ___playerPanel.playerIndex + ", count=" + count); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/Now/mPlayerWeapon.cs ================================================ using System; using HarmonyLib; using Now; namespace BepInEx_DemulShooter_Plugin.Patch { class mPlayerWeapon { /*[HarmonyPatch(typeof(Now.PlayerWeapon), "Fire")] class Fire { static void Postfix(bool __result, PlayerWeapon __instance) { byte bRecoil = 0; if (__result) bRecoil = 1; switch (__instance.PlayerIndex) { case 0: MarsSortie_BepInEx_Plugin.OutputData.P1_Recoil = bRecoil; break; case 1: MarsSortie_BepInEx_Plugin.OutputData.P2_Recoil = bRecoil; break; case 2: MarsSortie_BepInEx_Plugin.OutputData.P3_Recoil = bRecoil; break; case 3: MarsSortie_BepInEx_Plugin.OutputData.P3_Recoil = bRecoil; break; default: break; } } } [HarmonyPatch(typeof(Now.PlayerWeapon), "Update")] class Update { static void Postfix(PlayerWeapon __instance) { switch (__instance.PlayerIndex) { case 0: MarsSortie_BepInEx_Plugin.OutputData.P1_Ammo = (UInt16)__instance.ammo; break; case 1: MarsSortie_BepInEx_Plugin.OutputData.P2_Ammo = (UInt16)__instance.ammo; break; case 2: MarsSortie_BepInEx_Plugin.OutputData.P3_Ammo = (UInt16)__instance.ammo; break; case 3: MarsSortie_BepInEx_Plugin.OutputData.P4_Ammo = (UInt16)__instance.ammo; break; default: break; } } }*/ } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Patch/mArcadeShowCursor.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mArcadeShowCursor { /// /// Using that call to insert Resolution Change /// [HarmonyPatch(typeof(ArcadeShowCursor), "OnEnter")] class OnEnter { static bool Prefix(ArcadeShowCursor __instance) { DemulShooter_Plugin.MyLogger.LogWarning("ArcadeShowCursor.Onenter()"); __instance.showCursor = false; if (DemulShooter_Plugin.ForceResolution) Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("MarsSortie_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("MarsSortie_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 //Folowing command can work OR cause issue with Unity, especially if .NET 4.0 is required and Mono may miss some functionality _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); //Following way is more compatible in case of trouble //FieldInfo[] fiBuffer = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); //_DataFields = new FieldInfo[fiBuffer.Length]; //string[] fiNames = new string[fiBuffer.Length]; //for (int i = 0; i < fiNames.Length; i++) //{ // fiNames[i] = fiBuffer[i].Name; //} //Array.Sort(fiNames); //for (int i = 0; i < _DataFields.Length; i++) //{ // foreach (FieldInfo fi in fiBuffer) // { // if (fi.Name == fiNames[i]) // { // _DataFields[i] = fi; // break; // } // } //} foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError(this.GetType().Name + ".Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // _Writer.Write((bool)o); //else if (ReferenceEquals(t, typeof(byte))) // _Writer.Write((byte)o); //else if (ReferenceEquals(t, typeof(char))) // _Writer.Write((char)o); //else if (ReferenceEquals(t, typeof(decimal))) // _Writer.Write((decimal)o); //else if (ReferenceEquals(t, typeof(double))) // _Writer.Write((double)o); //else if (ReferenceEquals(t, typeof(float))) // _Writer.Write((float)o); ///*else if (ReferenceEquals(t, typeof(nint))) // _Writer.Write((nint)o); //else if (ReferenceEquals(t, typeof(nuint))) // _Writer.Write((nuint)o);*/ //else if (ReferenceEquals(t, typeof(long))) // _Writer.Write((long)o); //else if (ReferenceEquals(t, typeof(sbyte))) // _Writer.Write((sbyte)o); //else if (ReferenceEquals(t, typeof(short))) // _Writer.Write((short)o); //else if (ReferenceEquals(t, typeof(uint))) // _Writer.Write((uint)o); //else if (ReferenceEquals(t, typeof(ulong))) // _Writer.Write((ulong)o); //else if (ReferenceEquals(t, typeof(ushort))) // _Writer.Write((ushort)o); //else if (ReferenceEquals(t, typeof(int))) // _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // return _Reader.ReadBoolean(); //else if (ReferenceEquals(t, typeof(byte))) // return _Reader.ReadByte(); //else if (ReferenceEquals(t, typeof(char))) // return _Reader.ReadChar(); //else if (ReferenceEquals(t, typeof(decimal))) // return _Reader.ReadDecimal(); //else if (ReferenceEquals(t, typeof(double))) // return _Reader.ReadDouble(); //else if (ReferenceEquals(t, typeof(float))) // return _Reader.ReadSingle(); ///*else if (ReferenceEquals(t, typeof(nint))) // return _Reader. //else if (ReferenceEquals(t, typeof(nuint))) // return _Reader.*/ //else if (ReferenceEquals(t, typeof(long))) // return _Reader.ReadInt64(); //else if (ReferenceEquals(t, typeof(sbyte))) // return _Reader.ReadSByte(); //else if (ReferenceEquals(t, typeof(short))) // return _Reader.ReadInt16(); //else if (ReferenceEquals(t, typeof(uint))) // return _Reader.ReadUInt32(); //else if (ReferenceEquals(t, typeof(ulong))) // return _Reader.ReadUInt64(); //else if (ReferenceEquals(t, typeof(ushort))) // return _Reader.ReadUInt16(); //else if (ReferenceEquals(t, typeof(int))) // return _Reader.ReadInt32(); //else // return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] ChangeWeapon = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] WeaponId = null; public int[] Ammo = null; public int[] Credits = null; public byte Flashlight = 0; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll PlayMaker.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/UnityPlugin_BepInEx_MarsSortie.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin MarsSortie_BepInEx_DemulShooter_Plugin v3.5 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\PlayMaker.dll UnityLibs\UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MARSS/UnityPlugin_BepInEx_MarsSortie.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_MarsSortie", "UnityPlugin_BepInEx_MarsSortie.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.missionimpossible"; public const String pluginName = "MissionImpossible_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "MissionImpossible_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Original size is 1920p Horizontal public static readonly float ORIGINAL_WIDTH = 1920.0f; public static readonly float ORIGINAL_HEIGHT = 1080.0f; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("Video", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("Video", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("Video", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("Video", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); //Player 2 = Mouse + SHIFT if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { if (Input.GetKey(KeyCode.Mouse0)) PluginControllers[1].SetButton(PluginController.MyInputButtons.TriggerLeft, 1); else PluginControllers[1].SetButton(PluginController.MyInputButtons.TriggerLeft, 0); if (Input.GetKey(KeyCode.Mouse1)) PluginControllers[1].SetButton(PluginController.MyInputButtons.TriggerRight, 1); else PluginControllers[1].SetButton(PluginController.MyInputButtons.TriggerRight, 0); } else { if (Input.GetKey(KeyCode.Mouse0)) PluginControllers[0].SetButton(PluginController.MyInputButtons.TriggerLeft, 1); else PluginControllers[0].SetButton(PluginController.MyInputButtons.TriggerLeft, 0); if (Input.GetKey(KeyCode.Mouse1)) PluginControllers[0].SetButton(PluginController.MyInputButtons.TriggerRight, 1); else PluginControllers[0].SetButton(PluginController.MyInputButtons.TriggerRight, 0); } } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Coin for (int i = 0; i < MAX_PLAYERS; i++) { if (PluginControllers[i].GetButtonDown(PluginController.MyInputButtons.Coin)) CreditManager.Instance.AddOneCoin(); } //Retrieving Output data if (CreditManager.Instance != null) DemulShooter_Plugin.OutputData.Credits = (int)CreditManager.Instance.CurrentCredit; if (PlayerManager.Instance != null) { for (int i = 0; i < 2; i++) { Player p = PlayerManager.Instance.GetPlayer(i); if (p != null) { DemulShooter_Plugin.OutputData.Life[i] = p.Life; if (p.PMode >= PlayerMode.PM_GAME && p.PMode <= PlayerMode.PM_GAME_OVER) DemulShooter_Plugin.OutputData.IsPlaying[i] = 1; else DemulShooter_Plugin.OutputData.IsPlaying[i] = 0; } } } //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.TriggerLeft, _InputData.TriggerL[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.TriggerRight, _InputData.TriggerR[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/MissionImpossible_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 P1_COIN=53 P2_COIN=54 TEST=48 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/UGGameShell/mGame.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { /*class mGame { /// /// Intercepting Damaged event /// [HarmonyPatch(typeof(UGGameShell.Game), "PlayerDamaged")] class PlayerDamaged { static bool Prefix(PID pid) { DemulShooter_Plugin.MyLogger.LogMessage("UGGameShell.Game.PlayerDamaged() : pid=" + pid.ToString()); switch (pid) { case PID.PID_ONE: DemulShooter_Plugin.OutputData.Damaged[0] = 1; break; case PID.PID_TWO: DemulShooter_Plugin.OutputData.Damaged[1] = 1; break; case PID.PID_BOTH: DemulShooter_Plugin.OutputData.Damaged[0] = 1; DemulShooter_Plugin.OutputData.Damaged[1] = 1; break; default: break; } return true; } } /// /// Intercepting HP refresh event /// [HarmonyPatch(typeof(UGGameShell.Game), "UpdateHP")] class UpdateHP { static bool Prefix(PID pid, int hp) { DemulShooter_Plugin.MyLogger.LogMessage("UGGameShell.Game.UpdateHP() : pid=" + pid.ToString()); switch (pid) { case PID.PID_ONE: DemulShooter_Plugin.OutputData.Life[0] = (uint)hp; break; case PID.PID_TWO: DemulShooter_Plugin.OutputData.Life[1] = (uint)hp; break; default: break; } return true; } } }*/ } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/UGGameShell/mGameSetting.cs ================================================ using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; using UGGameShell; namespace BepInEx_DemulShooter_Plugin.Patch { class mGameSetting { /// /// Using local file from folder /// [HarmonyPatch(typeof(GameSetting), "LoadFile")] class LoadFile { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "/../ShellData/GameSettings.ini") { code[i].operand = "/NVRAM/GameSettings.ini"; } } return code; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/UGGameShell/mShellData.cs ================================================ using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; using UGGameShell; namespace BepInEx_DemulShooter_Plugin.Patch { class mShellData { /// /// Using local file from folder /// [HarmonyPatch(typeof(UGGameShell.ShellData), "LoadFile")] class LoadFile { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "/../ShellData/ShellData.ini") { code[i].operand = "/NVRAM/ShellData.ini"; } } return code; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mBootSequence.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mBootSequence { /// /// Changing resolution /// [HarmonyPatch(typeof(BootSequence), "Start")] class Start { static void Postfix() { if (DemulShooter_Plugin.ForceResolution) { DemulShooter_Plugin.MyLogger.LogMessage("BootSequence.Start(): Changind Screen resolution to " + DemulShooter_Plugin.ScreenWidth + "x" + DemulShooter_Plugin.ScreenHeight + ", Fullscreen=" + DemulShooter_Plugin.Fullscreen); UnityEngine.Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mConfigManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mConfigManager { /// /// Unlock Mission 3 /// [HarmonyPatch(typeof(ConfigManager), MethodType.Constructor)] class CCtor { static void Postfix(ref int ___m_mission1Progress, ref int ___m_mission2Progress, ref int ___m_mission3Progress, ref bool ___m_mission3Unlocked) { //___m_mission3Unlocked = true; } } [HarmonyPatch(typeof(ConfigManager), "GetAssetBundleCacheDirOnUnityEditor")] class GetAssetBundleCacheDirOnUnityEditor { static void Postfix(ref string __result) { DemulShooter_Plugin.MyLogger.LogMessage("ConfigManager.GetAssetBundleCacheDirOnUnityEditor() -> result=" + __result); __result = "../NVRAM/GameData/MI_Cache/"; } } /// /// This one is used in the current version, fixing PAth to local folder /// [HarmonyPatch(typeof(ConfigManager), "GetAssetBundleCacheDirOnExe")] class GetAssetBundleCacheDirOnExe { static void Postfix(ref string __result) { DemulShooter_Plugin.MyLogger.LogMessage("ConfigManager.GetAssetBundleCacheDirOnExe() -> result=" + __result); __result = "../NVRAM/GameData/MI_Cache/"; } } /* [HarmonyPatch(typeof(ConfigManager), "Awake")] class Awake { static void Postfix(ref int ___SCREEN_WIDTH, ref int ___SCREEN_HEIGHT) { DemulShooter_Plugin.MyLogger.LogMessage("ConfigManager.Awake() -> Screen=" + ___SCREEN_WIDTH + "x" + ___SCREEN_HEIGHT); } }*/ } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mGameLogManager.cs ================================================ using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; namespace BepInEx_DemulShooter_Plugin.Patch { class mGameLogManager { /// /// Using local file from folder /// [HarmonyPatch(typeof(GameLogManager), "Start")] class Start { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "../GameData/MI_Log/") { code[i].operand = BepInEx.Paths.GameRootPath + "/NVRAM/GameData/MI_Log/"; } } return code; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mGameObjPoolManager.cs ================================================ using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; using System; namespace BepInEx_DemulShooter_Plugin.Patch { class mGameObjPoolManager { /// /// Change folder to local one /// [HarmonyPatch(typeof(GameObjPoolManager), "DebugListenPoolRecord")] class DebugListenPoolRecord { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == ("../GameData/MI_EffectPoolLog/")) { code[i].operand = BepInEx.Paths.GameRootPath + "/NVRAM/GameData/MI_EffectPoolLog/"; } } return code; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mHistoryDataManager.cs ================================================ using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; using System; namespace BepInEx_DemulShooter_Plugin.Patch { class mHistoryDataManager { //--------------------------------------------// //Use a lot of Hardcoded patch to store file // //Changing them to local folder // //--------------------------------------------// [HarmonyPatch(typeof(HistoryDataManager), "Start")] class Start { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && ((string)code[i].operand).StartsWith("../")) { string s = (string)code[i].operand; s = s.Replace("../", BepInEx.Paths.GameRootPath + "/NVRAM/"); code[i].operand = s; } } return code; } } [HarmonyPatch(typeof(HistoryDataManager), "GenerateHistoryData", new Type[]{typeof(int), typeof(string)})] class GenerateHistoryData { static bool Prefix(int stage, ref string folderPath) { DemulShooter_Plugin.MyLogger.LogMessage("HistoryDataManager.GenerateHistoryData() : folderPath=" + folderPath); if (folderPath.StartsWith("../")) folderPath = folderPath.Replace("../", BepInEx.Paths.GameRootPath + "/NVRAM/"); DemulShooter_Plugin.MyLogger.LogMessage("--> HistoryDataManager.GenerateHistoryData() : folderPath=" + folderPath); return true; } } [HarmonyPatch(typeof(HistoryDataManager), "GenerateHistoryDataFromFile")] class GenerateHistoryDataFromFile { static bool Prefix(ref string folderPath) { DemulShooter_Plugin.MyLogger.LogMessage("HistoryDataManager.GenerateHistoryDataFromFile() : folderPath=" + folderPath); if (folderPath.StartsWith("../")) folderPath = folderPath.Replace("../", BepInEx.Paths.GameRootPath + "/NVRAM/"); DemulShooter_Plugin.MyLogger.LogMessage("--> HistoryDataManager.GenerateHistoryDataFromFile() : folderPath=" + folderPath); return true; } } [HarmonyPatch(typeof(HistoryDataManager), "ReadHistoryDataFromFile")] class ReadHistoryDataFromFile { static bool Prefix(ref string folderPath) { DemulShooter_Plugin.MyLogger.LogMessage("HistoryDataManager.ReadHistoryDataFromFile() : folderPath=" + folderPath); if (folderPath.StartsWith("../")) folderPath = folderPath.Replace("../", BepInEx.Paths.GameRootPath + "/NVRAM/"); DemulShooter_Plugin.MyLogger.LogMessage("--> HistoryDataManager.ReadHistoryDataFromFile() : folderPath=" + folderPath); return true; } } [HarmonyPatch(typeof(HistoryDataManager), "ReadPlayerGameDataFromFile")] class ReadPlayerGameDataFromFile { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "../GameData/MI_PlayerData/PlayerGameData.txt") { code[i].operand = BepInEx.Paths.GameRootPath + "/NVRAM/GameData/MI_PlayerData/PlayerGameData.txt"; } } return code; } } [HarmonyPatch(typeof(HistoryDataManager), "ReadPlayerMADataFromFile")] class ReadPlayerMADataFromFile { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "../GameData/MI_PlayerData/PlayerMAData.txt") { code[i].operand = BepInEx.Paths.GameRootPath + "/NVRAM/GameData/MI_PlayerData/PlayerMAData.txt"; } } return code; } } [HarmonyPatch(typeof(HistoryDataManager), "SavePlayerGameDataToFlie")] class SavePlayerGameDataDataToFlie { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "../GameData/MI_PlayerData/PlayerGameData.txt") { code[i].operand = BepInEx.Paths.GameRootPath + "/NVRAM/GameData/MI_PlayerData/PlayerGameData.txt"; } } return code; } } [HarmonyPatch(typeof(HistoryDataManager), "SavePlayerMADataDataToFlie")] class SavePlayerMADataDataToFlie { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "../GameData/MI_PlayerData/PlayerMAData.txt") { code[i].operand = BepInEx.Paths.GameRootPath + "/NVRAM/GameData/MI_PlayerData/PlayerMAData.txt"; } } return code; } } [HarmonyPatch(typeof(HistoryDataManager), "SaveProgressDataToFile", new Type[] { typeof(StageName), typeof(string) })] class SaveProgressDataToFile { static bool Prefix(StageName stage, ref string folderPath) { DemulShooter_Plugin.MyLogger.LogMessage("HistoryDataManager.SaveProgressDataToFile() : folderPath=" + folderPath); if (folderPath.StartsWith("../")) folderPath = folderPath.Replace("../", BepInEx.Paths.GameRootPath + "/NVRAM/"); DemulShooter_Plugin.MyLogger.LogMessage("--> HistoryDataManager.SaveProgressDataToFile() : folderPath=" + folderPath); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mInputManager.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mInputManager { [HarmonyPatch(typeof(InputManager), "Start")] class Start { static void Postfix(InputManager __instance, InputMode[] ___m_inputMode) { DemulShooter_Plugin.MyLogger.LogMessage("InputManager.Start(): " + ___m_inputMode[0].ToString() + ", " + ___m_inputMode[1].ToString()); } } /// /// Set both player to mouse mode /// By default, without shell, it would be AUTOPLAY mode /// [HarmonyPatch(typeof(InputManager), "ChangeInputMode")] class ChangeInputMode { static bool Prefix(InputManager __instance, ref InputMode[] ___m_inputMode) { DemulShooter_Plugin.MyLogger.LogMessage("InputManager.ChangeInputMode(): " + ___m_inputMode[0].ToString() + ", " + ___m_inputMode[1].ToString()); ___m_inputMode[0] = InputMode.MOUSE; ___m_inputMode[1] = InputMode.MOUSE; return true; } } /// /// Replacing BUTTON data /// [HarmonyPatch(typeof(InputManager), "GetGameButtonOn")] class GetGameButtonOn { static bool Prefix(InputType it, ref bool __result) { switch (it) { case InputType.P1L: __result = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerLeft); break; case InputType.P1R: __result = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerRight); break; case InputType.P2L: __result = DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerLeft); break; case InputType.P2R: __result = DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerRight); break; case InputType.P1START: __result = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start); break; case InputType.P2START: __result = DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start); break; default: __result = false; break; } return false; } } [HarmonyPatch(typeof(InputManager), "GetGameButtonPress")] class GetGameButtonPress { static bool Prefix(InputType it, ref bool __result) { switch (it) { case InputType.P1L: __result = DemulShooter_Plugin.PluginControllers[0].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerLeft); break; case InputType.P1R: __result = DemulShooter_Plugin.PluginControllers[0].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerRight); break; case InputType.P2L: __result = DemulShooter_Plugin.PluginControllers[1].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerLeft); break; case InputType.P2R: __result = DemulShooter_Plugin.PluginControllers[1].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerRight); break; case InputType.P1START: __result = DemulShooter_Plugin.PluginControllers[0].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start); break; case InputType.P2START: __result = DemulShooter_Plugin.PluginControllers[1].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start); break; default: __result = false; break; } return false; } } [HarmonyPatch(typeof(InputManager), "GetGameButtonRelease")] class GetGameButtonRelease { static bool Prefix(InputType it, ref bool __result) { switch (it) { case InputType.P1L: __result = DemulShooter_Plugin.PluginControllers[0].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerLeft); break; case InputType.P1R: __result = DemulShooter_Plugin.PluginControllers[0].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerRight); break; case InputType.P2L: __result = DemulShooter_Plugin.PluginControllers[1].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerLeft); break; case InputType.P2R: __result = DemulShooter_Plugin.PluginControllers[1].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.TriggerRight); break; case InputType.P1START: __result = DemulShooter_Plugin.PluginControllers[0].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start); break; case InputType.P2START: __result = DemulShooter_Plugin.PluginControllers[1].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start); break; default: __result = false; break; } return false; } } /// /// Replacing Axis data /// [HarmonyPatch(typeof(InputManager), "GetPosition")] class GetPosition { static bool Prefix(PID pid, ref Vector3 __result) { if (pid == PID.PID_ONE) { __result = DemulShooter_Plugin.PluginControllers[0].GetAimingPosition(); } else if (pid == PID.PID_TWO) { __result = DemulShooter_Plugin.PluginControllers[1].GetAimingPosition(); } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mModecontroller.cs ================================================ using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; namespace BepInEx_DemulShooter_Plugin.Patch { class mModecontroller { /// /// Disabling Always-On-Top window mode /// [HarmonyPatch(typeof(ModeController), "FixedUpdate")] class FixedUpdate { static bool Prefix() { return false; } } /// /// Forcing cursor ON /// [HarmonyPatch(typeof(ModeController), "Update")] class Update { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); /*for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldc_I4_0) { code[i].opcode = OpCodes.Ldc_I4_1; } }*/ return code; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mPlayer.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mPlayer { /// /// Removing crosshair (old, replaced) /// /*[HarmonyPatch(typeof(Player), "UpdatePlayerUI")] class UpdatePlayerUI { static bool Prefix(Player __instance, PlayerMode ___m_playerMode) { if (UIManager.Instance != null) { if (___m_playerMode >= PlayerMode.PM_GAME && ___m_playerMode <= PlayerMode.PM_GAME_OVER) { if (__instance.TwoHandGun != null) { if (DemulShooter_Plugin.CrossHairVisibility) EventCenter.GenerateEvent(127, __instance.TwoHandGun.PGid, __instance.TwoHandGun.GetForesightPos()); else { if (___m_playerMode == PlayerMode.PM_GAME) EventCenter.GenerateEvent(127, __instance.TwoHandGun.PGid, new Vector3(-1.0f, -1.0f)); else EventCenter.GenerateEvent(127, __instance.TwoHandGun.PGid, __instance.TwoHandGun.GetForesightPos()); } } else if (__instance.PlayerGunL != null && __instance.PlayerGunR != null) { if (DemulShooter_Plugin.CrossHairVisibility) { EventCenter.GenerateEvent(127, __instance.PlayerGunL.PGid, __instance.PlayerGunL.GetForesightPos()); EventCenter.GenerateEvent(127, __instance.PlayerGunR.PGid, __instance.PlayerGunR.GetForesightPos()); } else { if (___m_playerMode == PlayerMode.PM_GAME) { EventCenter.GenerateEvent(127, __instance.PlayerGunL.PGid, new Vector3(-1.0f, -1.0f)); EventCenter.GenerateEvent(127, __instance.PlayerGunR.PGid, new Vector3(-1.0f, -1.0f)); } else { EventCenter.GenerateEvent(127, __instance.PlayerGunL.PGid, __instance.PlayerGunL.GetForesightPos()); EventCenter.GenerateEvent(127, __instance.PlayerGunR.PGid, __instance.PlayerGunR.GetForesightPos()); } } } } if (__instance.TwoHandGun != null) { EventCenter.GenerateEvent(23, __instance.m_pid, __instance.TwoHandGun.SurplusBulletNum, __instance.TwoHandGun.SurplusBulletNum, __instance.TwoHandGun.MaxBulletNum); } else if (__instance.PlayerGunL != null && __instance.PlayerGunR != null) { EventCenter.GenerateEvent(23, __instance.m_pid, __instance.PlayerGunL.SurplusBulletNum, __instance.PlayerGunR.SurplusBulletNum, __instance.PlayerGunL.MaxBulletNum); } } return false; } }*/ } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mPlayerGunBase.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mPlayerGunBase { /// /// Intercept event to create Recoil /// [HarmonyPatch(typeof(PlayerGunBase), "Shoot")] class Shoot { static bool Prefix(PlayerGunBase __instance) { if (__instance.Pid == PID.PID_ONE) DemulShooter_Plugin.OutputData.Recoil[0] = 1; else if (__instance.Pid == PID.PID_TWO) DemulShooter_Plugin.OutputData.Recoil[1] = 1; return true; } } /// /// Get ammo count /// [HarmonyPatch(typeof(PlayerGunBase), "Update")] class Update { static bool Prefix(PlayerGunBase __instance) { if (__instance.Pid == PID.PID_ONE) { if (__instance.PGid == PlayerGunID.PGID_P1GL) DemulShooter_Plugin.OutputData.AmmoGunL[0] = __instance.Magazine.BulletNum; else if (__instance.PGid == PlayerGunID.PGID_P1GR) DemulShooter_Plugin.OutputData.AmmoGunR[0] = __instance.Magazine.BulletNum; } else if (__instance.Pid == PID.PID_TWO) { if (__instance.PGid == PlayerGunID.PGID_P2GL) DemulShooter_Plugin.OutputData.AmmoGunL[1] = __instance.Magazine.BulletNum; else if (__instance.PGid == PlayerGunID.PGID_P2GR) DemulShooter_Plugin.OutputData.AmmoGunR[1] = __instance.Magazine.BulletNum; } return true; } } /// /// If Screen res has changed, there's an offset between the 2D reticle position (matching the screen size) and the real bullet position (matching original size) /// We can fix it here /// [HarmonyPatch(typeof(PlayerGunBase), "CalcShotSight")] class CalcShotSight { static bool Prefix(PlayerGunBase __instance, ref UnityEngine.Vector2 pos) { if (DemulShooter_Plugin.ForceResolution) { pos.x = pos.x * (DemulShooter_Plugin.ORIGINAL_WIDTH / (float)DemulShooter_Plugin.ScreenWidth); pos.y = pos.y * (DemulShooter_Plugin.ORIGINAL_WIDTH / (float)DemulShooter_Plugin.ScreenWidth); } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mShellManager.cs ================================================ using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; using UGGameShell; namespace BepInEx_DemulShooter_Plugin.Patch { class mShellManager { /// /// Activating possibility to play without SHELL /// [HarmonyPatch(typeof(ShellManager), "Awake")] class Awake { static bool Prefix(ref bool ___SHELL_ONLY) { DemulShooter_Plugin.MyLogger.LogMessage("ShellManager.Awake()"); ___SHELL_ONLY = false; return true; } } /// /// Blocking Security counter increment /// [HarmonyPatch(typeof(ShellManager), "Update")] class Update { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count - 1; i++) { if (code[i].opcode == OpCodes.Ldfld && code[i].operand.ToString().Contains("m_securityCounter")) { if (code[i + 1].opcode == OpCodes.Ldc_I4_1) code[i + 1].opcode = OpCodes.Ldc_I4_0; } } return code; } } /// /// Blocking Security counter increment /// [HarmonyPatch(typeof(ShellManager), "AnalyzeCommand")] class AnalyzeCommand { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count - 2; i++) { if (code[i].opcode == OpCodes.Ldfld && code[i].operand.ToString().Contains("m_securityCounter")) { if (code[i + 1].opcode == OpCodes.Ldc_I4_1 && code[i+2].opcode == OpCodes.Add) code[i + 1].opcode = OpCodes.Ldc_I4_0; } } return code; } } /// /// Intercepting outputs command sent to Shell /// [HarmonyPatch(typeof(ShellManager), "SendCommand")] class SendCommand { static bool Prefix(CMD_ID cmdid, byte[] data) { //DemulShooter_Plugin.MyLogger.LogMessage("ShellManager.SendCommand() : cmdid=" + cmdid.ToString()); //Damage if (cmdid == CMD_ID.GAME_MSG_PLAYER_DAMAGE) { if (data[0] == 1) DemulShooter_Plugin.OutputData.Damaged[0] = 1; else if (data[0] == 2) DemulShooter_Plugin.OutputData.Damaged[1] = 1; else if (data[0] == 0) { DemulShooter_Plugin.OutputData.Damaged[0] = 1; DemulShooter_Plugin.OutputData.Damaged[1] = 1; } } //Life /*else if (cmdid == CMD_ID.GAME_MSG_CURRENT_HEALTH) { if (data[0] == 1) DemulShooter_Plugin.OutputData.Life[0] = data[1]; else if (data[0] == 2) DemulShooter_Plugin.OutputData.Life[1] = data[1]; else if (data[0] == 3) { DemulShooter_Plugin.OutputData.Life[0] = data[1]; DemulShooter_Plugin.OutputData.Life[1] = data[1]; } }*/ return true; } } [HarmonyPatch(typeof(ShellManager), "GetCommandID")] class GetcommandId { static void Postfix(byte[] data, int length, CMD_ID __result) { //DemulShooter_Plugin.MyLogger.LogMessage("ShellManager.GetcommandId() : result=" + __result.ToString()); } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mUGNetConnector.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mUGNetConnector { /// /// Force SinglePlay /// [HarmonyPatch(typeof(UGNetConnector), "Awake")] class Awake { static void Postfix(ref bool ___m_isSinglePlay, bool ___m_isHost) { ___m_isSinglePlay = true; DemulShooter_Plugin.MyLogger.LogMessage("UGNetConnector.Awake() : m_isSinglePlay=" + ___m_isSinglePlay + ", m_isHost=" + ___m_isHost); } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mUIManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mUIManager { /// /// Changing Unity ScreenResolution will only impact 3D rendering /// 2D sprites will stay on original resolution rendering, but there's a general 2DCanvas here to be able to change that /// [HarmonyPatch(typeof(UIManager), "Update")] class Update { static void Postfix(UIManager __instance) { if (DemulShooter_Plugin.ForceResolution) { foreach (UnityEngine.UI.CanvasScaler cs in __instance.MainCanvas2D.GetComponents()) { cs.scaleFactor = (float)DemulShooter_Plugin.ScreenWidth / DemulShooter_Plugin.ORIGINAL_WIDTH; } } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Patch/mUIShootPoint.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mUIShootPoint { /// /// Removing Crosshair /// [HarmonyPatch(typeof(UIShootPoint), "Update")] class Update { static bool Prefix(UnityEngine.GameObject ___m_shootPointOn) { if (!DemulShooter_Plugin.CrossHairVisibility) { if ((PlayerManager.Instance.GetPlayer(PID.PID_ONE).PMode >= PlayerMode.PM_GAME && PlayerManager.Instance.GetPlayer(PID.PID_ONE).PMode <= PlayerMode.PM_GAME_OVER) || (PlayerManager.Instance.GetPlayer(PID.PID_TWO).PMode >= PlayerMode.PM_GAME && PlayerManager.Instance.GetPlayer(PID.PID_TWO).PMode <= PlayerMode.PM_GAME_OVER)) { ___m_shootPointOn.SetActive(false); } } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, TriggerLeft, TriggerRight, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("MissionImpossible_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("MissionImpossible_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { DemulShooter_Plugin.PrintStackTrace(); try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 //Folowing command can work OR cause issue with Unity, especially if .NET 4.0 is required and Mono may miss some functionality //_DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); //Following way is more compatible in case of trouble FieldInfo[] fiBuffer = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); _DataFields = new FieldInfo[fiBuffer.Length]; string[] fiNames = new string[fiBuffer.Length]; for (int i = 0; i < fiNames.Length; i++) { fiNames[i] = fiBuffer[i].Name; } Array.Sort(fiNames); for (int i = 0; i < _DataFields.Length; i++) { foreach (FieldInfo fi in fiBuffer) { if (fi.Name == fiNames[i]) { _DataFields[i] = fi; break; } } } foreach (FieldInfo fi in _DataFields) { DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Found Field " + fi.Name); //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError(this.GetType().Name + ".Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ //if (t == typeof(bool)) // _Writer.Write((bool)o); //else if (t == typeof(byte)) // _Writer.Write((byte)o); //else if (t == typeof(char)) // _Writer.Write((char)o); //else if (t == typeof(decimal)) // _Writer.Write((decimal)o); //else if (t == typeof(double)) // _Writer.Write((double)o); //else if (t == typeof(float)) // _Writer.Write((float)o); ///*else if (t == typeof(nint)) // _Writer.Write((nint)o); //else if (t == typeof(nuint)) // _Writer.Write((nuint)o);*/ //else if (t == typeof(long)) // _Writer.Write((long)o); //else if (t == typeof(sbyte)) // _Writer.Write((sbyte)o); //else if (t == typeof(short)) // _Writer.Write((short)o); //else if (t == typeof(uint)) // _Writer.Write((uint)o); //else if (t == typeof(ulong)) // _Writer.Write((ulong)o); //else if (t == typeof(ushort)) // _Writer.Write((ushort)o); //else if (t == typeof(int)) // _Writer.Write((int)o); /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ if (ReferenceEquals(t, typeof(bool))) _Writer.Write((bool)o); else if (ReferenceEquals(t, typeof(byte))) _Writer.Write((byte)o); else if (ReferenceEquals(t, typeof(char))) _Writer.Write((char)o); else if (ReferenceEquals(t, typeof(decimal))) _Writer.Write((decimal)o); else if (ReferenceEquals(t, typeof(double))) _Writer.Write((double)o); else if (ReferenceEquals(t, typeof(float))) _Writer.Write((float)o); /*else if (ReferenceEquals(t, typeof(nint))) _Writer.Write((nint)o); else if (ReferenceEquals(t, typeof(nuint))) _Writer.Write((nuint)o);*/ else if (ReferenceEquals(t, typeof(long))) _Writer.Write((long)o); else if (ReferenceEquals(t, typeof(sbyte))) _Writer.Write((sbyte)o); else if (ReferenceEquals(t, typeof(short))) _Writer.Write((short)o); else if (ReferenceEquals(t, typeof(uint))) _Writer.Write((uint)o); else if (ReferenceEquals(t, typeof(ulong))) _Writer.Write((ulong)o); else if (ReferenceEquals(t, typeof(ushort))) _Writer.Write((ushort)o); else if (ReferenceEquals(t, typeof(int))) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ //if (t == typeof(bool)) // return _Reader.ReadBoolean(); //else if (t == typeof(byte)) // return _Reader.ReadByte(); //else if (t == typeof(char)) // return _Reader.ReadChar(); //else if (t == typeof(decimal)) // return _Reader.ReadDecimal(); //else if (t == typeof(double)) // return _Reader.ReadDouble(); //else if (t == typeof(float)) // return _Reader.ReadSingle(); ///*else if (t == typeof(nint)) // return _Reader. //else if (t == typeof(nuint)) // return _Reader.*/ //else if (t == typeof(long)) // return _Reader.ReadInt64(); //else if (t == typeof(sbyte)) // return _Reader.ReadSByte(); //else if (t == typeof(short)) // return _Reader.ReadInt16(); //else if (t == typeof(uint)) // return _Reader.ReadUInt32(); //else if (t == typeof(ulong)) // return _Reader.ReadUInt64(); //else if (t == typeof(ushort)) // return _Reader.ReadUInt16(); //else if (t == typeof(int)) // return _Reader.ReadInt32(); //else // return null; /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ if (ReferenceEquals(t, typeof(bool))) return _Reader.ReadBoolean(); else if (ReferenceEquals(t, typeof(byte))) return _Reader.ReadByte(); else if (ReferenceEquals(t, typeof(char))) return _Reader.ReadChar(); else if (ReferenceEquals(t, typeof(decimal))) return _Reader.ReadDecimal(); else if (ReferenceEquals(t, typeof(double))) return _Reader.ReadDouble(); else if (ReferenceEquals(t, typeof(float))) return _Reader.ReadSingle(); /*else if (ReferenceEquals(t, typeof(nint))) return _Reader. else if (ReferenceEquals(t, typeof(nuint))) return _Reader.*/ else if (ReferenceEquals(t, typeof(long))) return _Reader.ReadInt64(); else if (ReferenceEquals(t, typeof(sbyte))) return _Reader.ReadSByte(); else if (ReferenceEquals(t, typeof(short))) return _Reader.ReadInt16(); else if (ReferenceEquals(t, typeof(uint))) return _Reader.ReadUInt32(); else if (ReferenceEquals(t, typeof(ulong))) return _Reader.ReadUInt64(); else if (ReferenceEquals(t, typeof(ushort))) return _Reader.ReadUInt16(); else if (ReferenceEquals(t, typeof(int))) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] TriggerL = null; public byte[] TriggerR = null; public byte[] Reload = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/TcpOutputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Life = null; public int[] AmmoGunL = null; public int[] AmmoGunR = null; public int Credits = 0; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.CoreModule.dll UnityEngine.UI.dll UnityEngine.UIModule.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/UnityPlugin_BepInEx_MissionImpossible.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin MissionImpossible_BepInEx_DemulShooter_Plugin v4.8 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.CoreModule.dll UnityLibs\UnityEngine.UI.dll UnityLibs\UnityEngine.UIModule.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIA/UnityPlugin_BepInEx_MissionImpossible.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_MissionImpossible", "UnityPlugin_BepInEx_MissionImpossible.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using MIB; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.mib"; public const String pluginName = "MIB_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "2.0.0.0"; public const String pluginConfigFile = "MIB_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Coin_Key = new PluginControllerButton((int) KeyCode.Alpha5); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; public static bool GunVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("Video", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("Video", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("Video", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("Video", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Coin_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "COIN")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Coin_Key.SetButton(Input.GetKey((KeyCode)Coin_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); if (MIBCore.Instance != null) { MIBCore.Instance.InputPosition[0] = PluginControllers[0].GetAimingPosition(); MIBCore.Instance.InputPosition[1] = PluginControllers[1].GetAimingPosition(); } //COIN KEYS if (Coin_Key.GetButtonDown()) { MIBCore.Instance.UniversalCredits++; if (MIBCore.Instance.CreditsController != null) { MIBCore.Instance.CreditsController.UpdateCredit(0, MIBCore.Instance.UniversalCredits); } } //START KEYS for (int i = 0; i < MAX_PLAYERS; i++) { if (PluginControllers[i].GetButtonDown(PluginController.MyInputButtons.Start)) HandleGameStart(i + 1); } //Fetching Outputs for (int i = 0; i < MAX_PLAYERS; i++) { try { OutputData.IsPlaying[i] = Global.IsPlayerAI(i) ? (byte)0 : (byte)1; } catch{ } //OutputData.Ammo[i] = Global.GetGun(); } //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; GunVisibility = _InputData.HideGuns == 1 ? false : true; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; OutputData.NeuralyzerLamp = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } private void HandleGameStart(int playerIndex) { if (MIBCore.Instance == null) { return; } CreditsUI creditsUI = UnityEngine.Object.FindObjectOfType(); if (creditsUI == null) { return; } AttractScreen attractScreen = UnityEngine.Object.FindObjectOfType(); if (attractScreen != null) { if (MIBCore.Instance.FreePlay || MIBCore.Instance.UniversalCredits >= MIBCore.Instance.GameCost) { if (!MIBCore.Instance.FreePlay) { MIBCore.Instance.UniversalCredits -= MIBCore.Instance.GameCost; } creditsUI.UpdateCredit(0, MIBCore.Instance.UniversalCredits); attractScreen.CreditUsed(new CreditInfo(creditsUI.gameObject, playerIndex)); } return; } EndScreenAP endScreenAP = UnityEngine.Object.FindObjectOfType(); if (endScreenAP != null) { if (MIBCore.Instance.FreePlay || MIBCore.Instance.UniversalCredits >= MIBCore.Instance.ContinueCost) { if (!MIBCore.Instance.FreePlay) { MIBCore.Instance.UniversalCredits -= MIBCore.Instance.ContinueCost; } creditsUI.UpdateCredit(0, MIBCore.Instance.UniversalCredits); endScreenAP.CreditUsed(new CreditInfo(creditsUI.gameObject, playerIndex)); } return; } if ((MIBCore.Instance.Players & playerIndex) == 0 && MIBCore.Instance.Players != 3) { if (MIBCore.Instance.FreePlay || MIBCore.Instance.UniversalCredits >= MIBCore.Instance.GameCost) { if (!MIBCore.Instance.FreePlay) { MIBCore.Instance.UniversalCredits -= MIBCore.Instance.GameCost; } creditsUI.UpdateCredit(0, MIBCore.Instance.UniversalCredits); MIBCore.Instance.PlayerPressedStart(playerIndex); } return; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/MIB_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 COIN=53 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/MIB/mMIBClient.cs ================================================ using HarmonyLib; using MIB; namespace BepInEx_DemulShooter_Plugin { internal class mMIBClient { [HarmonyPatch(typeof(MIBClient), "IsConnected")] class IsConnected { static bool Prefix(ref bool __result) { __result = true; return false; } } [HarmonyPatch(typeof(MIBClient), "TriggerRecoil")] class TriggerRecoil { static bool Prefix(MIBData_Recoil data) { DemulShooter_Plugin.OutputData.Motor[data.PlayerID - 1] = data.StartRecoil ? (byte)1 : (byte)0; /*string s = string.Concat(new string[] { "TriggerRecoil(playerid: ", data.PlayerID.ToString(), ", FinishRecoil: ", data.FinishRecoil.ToString(), ", Start Recoil: ", data.StartRecoil.ToString(), ", GunType: ", data.GunType.ToString() });*/ return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/MIB/mMIBCore.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; using MIB; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mMIBCore { [HarmonyPatch(typeof(MIB.MIBCore), "AuthenticatedData")] class AuthenticatedData { static bool Prefix(ref bool __result) { DemulShooter_Plugin.MyLogger.LogMessage("MIBCore.AuthenticatedData()"); __result = true; return false; } } /// /// Keep inputs enabled /// [HarmonyPatch(typeof(MIBCore), "Update")] class Update { static bool Prefix(ref int ___m_securityCounter) { ___m_securityCounter = 0; return true; } static void Postfix() { MIBCore.DisableInputs = false; } } /// /// Replace the path from hardcoded "C:/Sega/ShellData" to game local folder /// [HarmonyPatch(typeof(MIBCore), "GetCreditInformaion")] class GetCreditInformaion { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "C:/Sega/ShellData/ShellData.ini") { code[i].operand = BepInEx.Paths.GameRootPath + "/ShellData/ShellData.ini"; } } return code; } } /// /// Replace the path from hardcoded "C:/Sega/ShellData" to game local folder /// [HarmonyPatch(typeof(MIBCore), "SetCalibration")] class SetCalibration { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "C:/Sega/ShellData/ShellData.ini") { code[i].operand = BepInEx.Paths.GameRootPath + "/ShellData/ShellData.ini"; } } return code; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/MIB/mMIBGameSettings.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Text; using HarmonyLib; using MIB; using UnityEngine; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mMIBGameSettings { [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); /// /// Replace the path from hardcoded "C:/Sega/ShellData" to game local folder /// [HarmonyPatch(typeof(MIBGameSettings), "LoadFile")] class LoadFile { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "C:/Sega/ShellData") { code[i].operand = BepInEx.Paths.GameRootPath + "/ShellData"; } } return code; } } /// /// Replacing the original DllImport function by this declaration including Unicode char for path /// [HarmonyPatch(typeof(MIBGameSettings), "GetPrivateProfileString")] class mGetPrivateProfileString { static bool Prefix(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("MIBGameSettings.GetPrivateProfileString():"); DemulShooter_Plugin.MyLogger.LogMessage("lpKeyName=" + lpKeyName + ", lpDefault=" + lpDefault + ", lpFileName=" + lpFileName); __result = GetPrivateProfileString(lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize, lpFileName); return false; } static void Postfix(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName, int __result) { DemulShooter_Plugin.MyLogger.LogMessage("MIBGameSettings.GetPrivateProfileString() POSTFIX:"); DemulShooter_Plugin.MyLogger.LogMessage("result=" + __result + ", lpReturnedString=" + lpReturnedString.ToString()); } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mCreditsUI.cs ================================================ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using HarmonyLib; using MIB; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mCreditsUI { [HarmonyPatch(typeof(CreditsUI), "Start")] class Start { static void Postfix(CreditsUI __instance, ref int ___UniversalCredit) { DemulShooter_Plugin.MyLogger.LogMessage("CreditsUI.Start()"); ___UniversalCredit = 0; } } [HarmonyPatch(typeof(CreditsUI), "Update")] class Update { static void Postfix(CreditsUI __instance, int ___UniversalCredit) { DemulShooter_Plugin.OutputData.Credits = ___UniversalCredit; } } /*[HarmonyPatch(typeof(CreditsUI), "CheckCreditTaken")] class CheckCreditTaken { static bool Prefix(CreditsUI __instance) { if (!MIBCore.DisableInputs) { if (Input.GetKeyDown(KeyCode.Alpha1)) { __instance.StartCoroutine(mWaitToUse.WaitToUse(__instance, 1)); } if (Input.GetKeyDown(KeyCode.Alpha2)) { base.StartCoroutine(this.WaitToUse(2)); } } DemulShooter_Plugin.MyLogger.LogMessage("CreditsUI.Start()"); foreach (MethodInfo method in typeof(UnityEngine.Input).GetMethods(BindingFlags.Static | BindingFlags.Public)) { if (method.Name.Equals("GetButtonDown")) { } } return true; } }*/ /*[HarmonyPatch(typeof(CreditsUI), "WaitToUse")] class mWaitToUse { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void WaitToUse(object instance, int p) { //Used to call the private method } }*/ } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mGlobal.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mGlobal { /// /// Using this to get Damage output /// [HarmonyPatch(typeof(Global), "AddDamage")] class AddDamage { static bool Prefix(int index, float dam) { if (!Global.IsPlayerAI(index)) DemulShooter_Plugin.OutputData.Damaged[index] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mGun.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mGun { /// /// Using this to get recoil /// [HarmonyPatch(typeof(Gun), "Fire")] class Fire { static void Prefix(Gun __instance) { int PlayerIndex = __instance.Holster.PlayerIndex; if (!Global.IsPlayerAI(PlayerIndex)) DemulShooter_Plugin.OutputData.Recoil[PlayerIndex] = 1; } } /// /// Removing gun model from screen /// [HarmonyPatch(typeof(Gun), "Update")] class Upddate { static void Prefix(Gun __instance) { if (!DemulShooter_Plugin.GunVisibility) __instance.transform.localScale = new Vector3(); } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mGunRig.cs ================================================ using HarmonyLib; using MIB; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mGunRig { [HarmonyPatch(typeof(GunRig), "Update")] class Update { static bool Prefix(GunRig __instance, ref float ___RecoilTimer, ref bool ___RecoilActive, Gun ___Current, Crosshair ___PlayerCrosshair) { bool flag = false; if (__instance.PlayerIndex == 0 && MIBCore.Instance.Players != 2) { if (__instance.CanRecoil) { if (!MIBCore.DisableInputs) { if (DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { flag = true; ___RecoilTimer = 0.25f; } else if (!DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { flag = false; } } } else { flag = false; } if (flag) { if (!___RecoilActive) { Lights.Play((__instance.PlayerIndex == 0) ? "GUN1_FIRE" : "GUN2_FIRE"); if (!___Current.FinishRecoil) { MIBCore.Instance.SendMessage("TriggerRecoil", new MIBData_Recoil((byte)(__instance.PlayerIndex + 1), ___Current.FinishRecoil, true, (byte)___Current.GunRecoil)); } ___RecoilActive = true; } } else { if (___RecoilActive && ___RecoilTimer <= 0f) { Lights.Play((__instance.PlayerIndex == 0) ? "GUN1_OFF" : "GUN2_OFF"); if (!___Current.FinishRecoil) { MIBCore.Instance.SendMessage("TriggerRecoil", new MIBData_Recoil((byte)(__instance.PlayerIndex + 1), ___Current.FinishRecoil, false, (byte)___Current.GunRecoil)); } ___RecoilActive = false; } ___RecoilTimer -= Time.deltaTime; } if (___RecoilActive) { ___Current.DoFire(false); ___PlayerCrosshair.StopSpinning(); //DemulShooter_Plugin.MyLogger.LogMessage("GunRig.Update(): DoFire P1"); } else { ___PlayerCrosshair.StartSpinning(); } } //Player 2 if (__instance.PlayerIndex == 1 && MIBCore.Instance.Players != 1) { if (__instance.CanRecoil) { if (!MIBCore.DisableInputs) { if (DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { flag = true; ___RecoilTimer = 0.25f; } else if (!DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { flag = false; } } } else { flag = false; } if (flag) { if (!___RecoilActive) { Lights.Play("GUN2_FIRE"); if (!___Current.FinishRecoil) { MIBCore.Instance.SendMessage("TriggerRecoil", new MIBData_Recoil((byte)(__instance.PlayerIndex + 1), ___Current.FinishRecoil, true, (byte)___Current.GunRecoil)); } ___RecoilActive = true; } } else { if (___RecoilActive && ___RecoilTimer <= 0f) { Lights.Play("GUN2_OFF"); if (!___Current.FinishRecoil) { MIBCore.Instance.SendMessage("TriggerRecoil", new MIBData_Recoil((byte)(__instance.PlayerIndex + 1), ___Current.FinishRecoil, false, (byte)___Current.GunRecoil)); } ___RecoilActive = false; } ___RecoilTimer -= Time.deltaTime; } if (___RecoilActive) { ___Current.DoFire(false); ___PlayerCrosshair.StopSpinning(); //DemulShooter_Plugin.MyLogger.LogMessage("GunRig.Update(): DoFire P2"); return false; } ___PlayerCrosshair.StartSpinning(); } return false; } } } [HarmonyPatch(typeof(GunRig), "ShowGunUI")] class ShowGunUI { static void Postfix(ref bool ___HideUi, ref bool ___GunLower, GunRig __instance) { if (Input.GetKeyDown(KeyCode.H)) { DemulShooter_Plugin.MyLogger.LogMessage("GunRig.ShowGunUI()"); ___HideUi = false; ___GunLower = true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mInitialisation.cs ================================================ using HarmonyLib; using MIB; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mInitialisation { [HarmonyPatch(typeof(Initialisation), "Start")] class Start { static void Postfix() { if (DemulShooter_Plugin.ForceResolution) Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); MIBGameSettings.LoadFile(); MIBCore.Instance.GetCreditInformaion(); } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mLaserEmitter.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mLaserEmitter { /// /// Removing Laser pointer from gun /// [HarmonyPatch(typeof(LaserEmitter), "OnEnable")] class OnEnable { static void Postfix(LaserEmitter __instance) { if (!DemulShooter_Plugin.GunVisibility) __instance.GetComponent().enabled = false; } } [HarmonyPatch(typeof(LaserEmitter), "Start")] class Start { static void Postfix(LaserEmitter __instance) { if (!DemulShooter_Plugin.GunVisibility) __instance.GetComponent().enabled = false; } } [HarmonyPatch(typeof(LaserEmitter), "Update")] class Update { static void Postfix(LaserEmitter __instance) { if (!DemulShooter_Plugin.GunVisibility) __instance.GetComponent().enabled = false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mLights.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mLights { [HarmonyPatch(typeof(Lights), "Play")] class Play { static bool Prefix(string s) { //DemulShooter_Plugin.MyLogger.LogMessage("Lights.Play(" + s + ")"); if (s == "NEURO") { DemulShooter_Plugin.OutputData.NeuralyzerLamp = 1; } else if (s == "GUN1_FIRE") { DemulShooter_Plugin.OutputData.GunLight[0] = 1; } else if (s == "GUN2_FIRE") { DemulShooter_Plugin.OutputData.GunLight[1] = 1; } else if (s == "GUN1_OFF") { DemulShooter_Plugin.OutputData.GunLight[1] = 0; } else if (s == "GUN2_OFF") { DemulShooter_Plugin.OutputData.GunLight[1] = 0; } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mPatchTemplate.cs ================================================ using HarmonyLib; using System.Reflection; using System.Collections.Generic; using System.Reflection.Emit; namespace BepInEx_DemulShooter_Plugin {/* class mPatchTemplate { /// /// Simple Prefix / postfix patch /// [HarmonyPatch(typeof(BulletCounter), "SetBulletCount")] class SetBulletCount { static bool Prefix(PlayerPanel ___playerPanel, int count) { DemulShooter_Plugin.MyLogger.LogMessage("SetBulletCount: " + ___playerPanel.playerIndex + ", count=" + count); return true; } static void Postfix(PlayerPanel ___playerPanel, int count) { DemulShooter_Plugin.MyLogger.LogMessage("SetBulletCount: " + ___playerPanel.playerIndex + ", count=" + count); } } /// /// When easy find Class/Method is not possible, using reflexion to get them (unexposed internal class, etc...) /// [HarmonyPatch] class ExecuteCMD { static MethodBase TargetMethod() { MethodInfo[] mis = AccessTools.TypeByName("InputManager").GetMethods(BindingFlags.Instance | BindingFlags.NonPublic); foreach (MethodInfo mi in mis) { if (mi.Name.Equals("SetInput")) return mi; } return null; } static bool Prefix(InputManager __instance) { DemulShooter_Plugin("Found it !"); return false; } } /// /// Simple Transpiler /// [HarmonyPatch(typeof(TimeAttackResult), "GetRanking")] class GetRanking { static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { var code = new List(instructions); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "D:\\TimeAttackResultHolder.bin") { code[i].operand = "NVRAM\\TimeAttackResultHolder.bin"; } if (code[i].opcode == OpCodes.Ldstr && (string)code[i].operand == "D:\\") { code[i].operand = "NVRAM\\"; } } return code; } } } */ } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mRS232.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mRS232 { /// /// Prevent the game to open Serial COM /// [HarmonyPatch(typeof(RS232), "Connect")] class Connect { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mUI.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mUI { /// /// Removing in-game crosshairs /// [HarmonyPatch(typeof(UI), "Update")] class Update { static bool Prefix(UI __instance) { if (!DemulShooter_Plugin.CrossHairVisibility) { __instance.Crosshairs[0].enabled = false; __instance.Crosshairs[1].enabled = false; } return true; } } [HarmonyPatch(typeof(UI), "PulseReticule")] class PulseReticule { static bool Prefix(UI __instance, int Index) { if (!DemulShooter_Plugin.CrossHairVisibility) { return false; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mUIPlayerCrosshair.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mUIPlayerCrosshair { /// /// Removing corsshairs, but not used in-game /// [HarmonyPatch(typeof(UIPlayerCrosshairs), "Update")] class Update { static bool Prefix(UIPlayerCrosshairs __instance) { if (!DemulShooter_Plugin.CrossHairVisibility) { __instance.Player1.enabled = false; __instance.Player2.enabled = false; return false; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Patch/mUnityEngine.cs ================================================ using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mUnityEngine { /// /// Replacing OPluginControllers.getButtonDown as the flag is already used in the main plugin /// So just using the Keycode as GetKeyDown is valid untill end of frame, no matter ther number of request /// [HarmonyPatch(typeof(UnityEngine.Input), "GetButtonDown", new System.Type[] { typeof(string) })] class GetButtonDown { static bool Prefix(string buttonName, ref bool __result) { if (buttonName.Equals("PlayerStart1")) { __result = Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode); return false; } else if (buttonName.Equals("PlayerStart2")) { __result = Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode); return false; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("MIB_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("MIB_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.0.0.0")] [assembly: AssemblyFileVersion("2.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 //Folowing command can work OR cause issue with Unity, especially if .NET 4.0 is required and Mono may miss some functionality _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); //Following way is more compatible in case of trouble //FieldInfo[] fiBuffer = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); //_DataFields = new FieldInfo[fiBuffer.Length]; //string[] fiNames = new string[fiBuffer.Length]; //for (int i = 0; i < fiNames.Length; i++) //{ // fiNames[i] = fiBuffer[i].Name; //} //Array.Sort(fiNames); //for (int i = 0; i < _DataFields.Length; i++) //{ // foreach (FieldInfo fi in fiBuffer) // { // if (fi.Name == fiNames[i]) // { // _DataFields[i] = fi; // break; // } // } //} foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError(this.GetType().Name + ".Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // _Writer.Write((bool)o); //else if (ReferenceEquals(t, typeof(byte))) // _Writer.Write((byte)o); //else if (ReferenceEquals(t, typeof(char))) // _Writer.Write((char)o); //else if (ReferenceEquals(t, typeof(decimal))) // _Writer.Write((decimal)o); //else if (ReferenceEquals(t, typeof(double))) // _Writer.Write((double)o); //else if (ReferenceEquals(t, typeof(float))) // _Writer.Write((float)o); ///*else if (ReferenceEquals(t, typeof(nint))) // _Writer.Write((nint)o); //else if (ReferenceEquals(t, typeof(nuint))) // _Writer.Write((nuint)o);*/ //else if (ReferenceEquals(t, typeof(long))) // _Writer.Write((long)o); //else if (ReferenceEquals(t, typeof(sbyte))) // _Writer.Write((sbyte)o); //else if (ReferenceEquals(t, typeof(short))) // _Writer.Write((short)o); //else if (ReferenceEquals(t, typeof(uint))) // _Writer.Write((uint)o); //else if (ReferenceEquals(t, typeof(ulong))) // _Writer.Write((ulong)o); //else if (ReferenceEquals(t, typeof(ushort))) // _Writer.Write((ushort)o); //else if (ReferenceEquals(t, typeof(int))) // _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // return _Reader.ReadBoolean(); //else if (ReferenceEquals(t, typeof(byte))) // return _Reader.ReadByte(); //else if (ReferenceEquals(t, typeof(char))) // return _Reader.ReadChar(); //else if (ReferenceEquals(t, typeof(decimal))) // return _Reader.ReadDecimal(); //else if (ReferenceEquals(t, typeof(double))) // return _Reader.ReadDouble(); //else if (ReferenceEquals(t, typeof(float))) // return _Reader.ReadSingle(); ///*else if (ReferenceEquals(t, typeof(nint))) // return _Reader. //else if (ReferenceEquals(t, typeof(nuint))) // return _Reader.*/ //else if (ReferenceEquals(t, typeof(long))) // return _Reader.ReadInt64(); //else if (ReferenceEquals(t, typeof(sbyte))) // return _Reader.ReadSByte(); //else if (ReferenceEquals(t, typeof(short))) // return _Reader.ReadInt16(); //else if (ReferenceEquals(t, typeof(uint))) // return _Reader.ReadUInt32(); //else if (ReferenceEquals(t, typeof(ulong))) // return _Reader.ReadUInt64(); //else if (ReferenceEquals(t, typeof(ushort))) // return _Reader.ReadUInt16(); //else if (ReferenceEquals(t, typeof(int))) // return _Reader.ReadInt32(); //else // return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte HideGuns = 0; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Motor = null; public byte[] Damaged = null; public byte NeuralyzerLamp = 0; public byte[] GunLight = null; public int Credits = 0; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp-firstpass.dll Assembly-CSharp.dll UnityEngine.CoreModule.dll UnityEngine.InputLegacyModule.dll UnityEngine.UI.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/UnityPlugin_BepInEx_MIB.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin MIB_BepInEx_DemulShooter_Plugin v4.8 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\Assembly-CSharp-firstpass.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.CoreModule.dll UnityLibs\UnityEngine.InputLegacyModule.dll UnityLibs\UnityEngine.UI.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/UnityPlugin_BepInEx_MIB.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_MIB", "UnityPlugin_BepInEx_MIB.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_MIB/mAmwaysOnTop.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { internal class mAmwaysOnTop { /// /// Deactivating the attempt of pinning the game window, it's messing with other windows and I don't like that /// [HarmonyPatch(typeof(AlwaysOnTop), "AssignTopmostWindow")] class AssignTopmostWindow { static bool Prefix(string WindowTitle, bool MakeTopmost) { DemulShooter_Plugin.MyLogger.LogMessage("AlwaysOnTop.AssignTopmostWindow(): WindowTitle=" + WindowTitle + ", MakeTopMost=" + MakeTopmost); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.nha2"; public const String pluginName = "NightHunter2_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "NightHunter2_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); public static PluginControllerButton Coin_Key = new PluginControllerButton((int) KeyCode.Alpha5); public static PluginControllerButton MenuDown_Key = new PluginControllerButton((int) KeyCode.DownArrow); public static PluginControllerButton MenuSelect_Key = new PluginControllerButton((int) KeyCode.Return); public static PluginControllerButton WheelLeft_Key = new PluginControllerButton((int) KeyCode.LeftArrow); public static PluginControllerButton WheelRight_Key = new PluginControllerButton((int) KeyCode.RightArrow); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; public static bool GunVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; private static readonly string SPRITE_1P_BLUE_FILE = "BepInEx\\plugins\\Assets\\image_1P_Blue.png"; private static readonly string SPRITE_1P_RED_FILE = "BepInEx\\plugins\\Assets\\image_1P_Red.png"; private static readonly string SPRITE_2P_BLUE_FILE = "BepInEx\\plugins\\Assets\\image_2P_Blue.png"; private static readonly string SPRITE_2P_RED_FILE = "BepInEx\\plugins\\Assets\\image_2P_Red.png"; public static Sprite Sprite_1P_Blue; public static Sprite Sprite_1P_Red; public static Sprite Sprite_2P_Blue; public static Sprite Sprite_2P_Red; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("Video", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("Video", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("Video", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("Video", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); Coin_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "COIN")); MenuDown_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_DOWN")); MenuSelect_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_SELECT")); WheelLeft_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "WHEEL_LEFT")); WheelRight_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "WHEEL_RIGHT")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); Texture2D texture = LoadTextureFromFile(SPRITE_1P_BLUE_FILE); Sprite_1P_Blue = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero); texture = LoadTextureFromFile(SPRITE_1P_RED_FILE); Sprite_1P_Red = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero); texture = LoadTextureFromFile(SPRITE_2P_BLUE_FILE); Sprite_2P_Blue = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero); texture = LoadTextureFromFile(SPRITE_2P_RED_FILE); Sprite_2P_Red = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero); harmony.PatchAll(); } public void Start() { } public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); Coin_Key.SetButton(Input.GetKey((KeyCode)Coin_Key.KeyCode)); MenuDown_Key.SetButton(Input.GetKey((KeyCode)MenuDown_Key.KeyCode)); MenuSelect_Key.SetButton(Input.GetKey((KeyCode)MenuSelect_Key.KeyCode)); WheelLeft_Key.SetButton(Input.GetKey((KeyCode)WheelLeft_Key.KeyCode)); WheelRight_Key.SetButton(Input.GetKey((KeyCode)WheelRight_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Action, Input.GetMouseButton(2) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Custom buttons handling for (int i = 0; i < MAX_PLAYERS; i++) { if (PluginControllers[i].GetButtonDown(PluginController.MyInputButtons.Reload)) input_obj_change_bullet.change_weapon(i + 1); // PlayerNum is 1 or 2 if (PluginControllers[i].GetButtonDown(PluginController.MyInputButtons.Action)) input_obj_big_power.big_power_work(i + 1); // PlayerNum is 1 or 2 } //Fetching Outputs try { OutputData.Credits = zhichi_hanshu_houtai.get_no_use_coins(); for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Life[i] = game_run_core.my_static_game_run.mygame_players.get_game_player(i + 1).get_curr_blood(); OutputData.GunType[i] = (int)zhichi_hanshu_gun_wheel_mark_manage.zchs_get_gun_type(i + 1); OutputData.IsPlaying[i] = zhichi_hanshu_game_players.get_player_by_num(i + 1).is_living() ? (byte)1 : (byte)0; } } catch { } //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.Action, _InputData.Action[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, _InputData.ChangeWeapon[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; GunVisibility = _InputData.HideGuns == 1 ? false : true; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } private static Texture2D LoadTextureFromFile(string FilePath) { Texture2D tex = null; byte[] fileData; if (File.Exists(FilePath)) { fileData = File.ReadAllBytes(FilePath); tex = new Texture2D(2, 2); tex.LoadImage(fileData); //..this will auto-resize the texture dimensions. MyLogger.LogMessage(string.Concat(new object[] { "Texture created from ", FilePath, " : width = ", tex.width, ", height = ", tex.height, ",", tex.dimension.ToString() })); } else { MyLogger.LogError("TokiPlugin.Awake() => File not found : " + FilePath); } return tex; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/NightHunter2_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 COIN=53 TEST=48 MENU_DOWN=274 MENU_SELECT=13 WHEEL_LEFT=276 WHEEL_RIGHT=275 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mGame Device_data_gun_core.cs ================================================ using HarmonyLib; using UnityEngine; using System; namespace BepInEx_DemulShooter_Plugin { class mGame_Device_data_gun_core { /// /// Sometimes, activating P2 after P2 change the mouse from P1 to P2, thus disabing P1 (why ??) /// Easiest thing to do is to always return true here /// [HarmonyPatch(typeof(Game_Device_data_gun_core), "device_is_can_work")] class device_is_can_work { static bool Prefix(Game_Device_data_gun_core __instance, int device_num, ref bool __result) { //DemulShooter_Plugin.MyLogger.LogMessage("mGame_Device_data_gun_core.device_is_can_work() => player: " + __instance.mygame_player_num + ", myis_use_mouse: " + __instance.myis_use_mouse + ", result: " + __result); __result = true; return false; } } /// /// Called by the game to get mouse coordinates to handle Guns /// [HarmonyPatch(typeof(Game_Device_data_gun_core), "get_gun_pos_by_mouse")] class get_gun_pos_by_mouse { static bool Prefix(Game_Device_data_gun_core __instance, ref Vector3 __result) { int PlayerNum = __instance.get_player_num() - 1; Vector3 mouse_pos = DemulShooter_Plugin.PluginControllers[PlayerNum].GetAimingPosition(); //DemulShooter_Plugin.MyLogger.LogMessage("mGame_Device_data_gun_core.get_gun_pos_by_mouse() => player: " + __instance.mygame_player_num + ", Value: " + mouse_pos.ToString()); __result = zhichi_hanshu_pos.change_mouse_positon_to_gun_pos(mouse_pos); return false; } } /// /// Same thing for Fire triggering /// [HarmonyPatch(typeof(Game_Device_data_gun_core), "is_gun_fire")] class is_gun_fire { static bool Prefix(Game_Device_data_gun_core __instance, ref bool __result) { __result = false; int PlayerNum = __instance.get_player_num() - 1; if (__instance.is_single_fire()) { if (DemulShooter_Plugin.PluginControllers[PlayerNum].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { __result = true; } } else { if (DemulShooter_Plugin.PluginControllers[PlayerNum].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { __result = true; } } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mconfig_gun.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mconfig_gun { /// /// Force MOUSE use all the time /// [HarmonyPatch(typeof(config_gun), "get_gun_input_way")] class get_gun_input_way { static bool Prefix(ref config_gun.GUN_INPUT_WAY __result) { //DemulShooterPlugin.MyLogger.LogMessage("mconfig_gun.get_gun_input_way()"); __result = config_gun.GUN_INPUT_WAY.MOUSE; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mdog_check_new.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mdog_check_new { /// /// Dongle bypass /// [HarmonyPatch(typeof(dog_check_new), "is_dog_ok_in_start")] class is_dog_ok_in_start { static bool Prefix(ref bool __result) { DemulShooter_Plugin.MyLogger.LogMessage("mdog_check_new.is_dog_ok_in_start()"); dog_check_new.myis_has_dog_in_update = true; __result = dog_check_new.is_dog_ok_in_update(); return false; } } /// /// Dongle bypass /// [HarmonyPatch(typeof(dog_check_new), "is_dog_ok_in_update")] class is_dog_ok_in_update { static bool Prefix(ref bool __result) { //DemulShooter_Plugin.MyLogger.LogMessage("mdog_check_new.is_dog_ok_in_update()"); __result = true; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mgame_device_sub_gun.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mgame_device_sub_gun { /// /// Called for each shoot, for recoil) /// [HarmonyPatch(typeof(game_device_sub_gun), "fire_obj_list")] class fire_obj_list { static bool Prefix(game_device_sub_gun __instance) { int PlayerNum = __instance.get_player_num(); //DemulShooter_Plugin.MyLogger.LogMessage("mgame_device_sub_gun.fire_obj_list() => Player: " + PlayerNum); DemulShooter_Plugin.OutputData.Recoil[PlayerNum - 1] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mgame_mark_3d_obj.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mgame_mark_3d_obj { /// /// Remove Crosshair /// [HarmonyPatch(typeof(game_mark_3d_obj), "set_mark_obj")] class set_mark_obj { static bool Prefix(int num, game_mark_3d_obj __instance) { if (!DemulShooter_Plugin.CrossHairVisibility) { //DemulShooter_Plugin.MyLogger.LogMessage("mgame_mark_3d_obj.set_mark_obj() => num: " + num); __instance.mymark_num = num; for (int i = 0; i < __instance.mymark_obj_list.Count; i++) { GameObject gameObject = __instance.mymark_obj_list[i]; gameObject.SetActive(false); } return false; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mgame_player_is_hit.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { /// /// Find when a player is hit for custom outputs /// class mgame_player_is_hit { [HarmonyPatch(typeof(game_player_is_hit), "player_is_hit")] class player_is_hit { static bool Prefix(game_base game_hit_obj1, game_player_is_hit __instance) { //DemulShooter_Plugin.MyLogger.LogWarning("mgame_player_is_hit.player_is_hit() => userNum: " + __instance.get_user_num() + ", life: " + __instance.get_life()); if (__instance.player_status_is_can_be_hit()) DemulShooter_Plugin.OutputData.Damaged[__instance.get_user_num() - 1] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mgun_body.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mgun_body { /// /// Removing 3D gun model on screen /// [HarmonyPatch(typeof(gun_body), "late_update_work_for_gun_num")] class late_update_work_for_gun_num { static bool Prefix(int num, gun_body __instance) { if (!DemulShooter_Plugin.GunVisibility) { foreach (Component c in __instance.gameObject.GetComponentsInChildren()) { //DemulShooter_Plugin.MyLogger.LogMessage(" +Component: " + c.ToString() + " [GameObject=" + c.gameObject.ToString() + "]"); if (/*c.name.StartsWith("Gun_A") /*|| */c.name.Equals("golden_eagle") /*|| c.name.StartsWith("FX_gun")*/) c.gameObject.SetActive(false); } } //Does not work for player 2 crosshair ??? /*if (NightHunterArcadePlugin.Configurator.RemoveCrosshair) { __instance.get_gun_body_target(1).SetActive(false); //P1 __instance.get_gun_body_target(2).SetActive(false); //P2 }*/ return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mgun_body_tx_wuti.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mgun_body_tx_wuti { /// /// Remove laser sight /// [HarmonyPatch(typeof(gun_body_tx_wuti), "update_linerenderer")] class update_linerenderer { static bool Prefix(gun_body_tx_wuti __instance, int num, gun_body gun_body1) { //NightHunterArcadePlugin.MyLogger.LogMessage("gun_fire_work.update_linerenderer()"); if (!DemulShooter_Plugin.GunVisibility) { if (__instance.mylinerenderer != null) { __instance.mylinerenderer.enabled = false; // <------------------------- Remove sight line } return false; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/minput_manage.cs ================================================ using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin { /// /// Change Keyboard KEYS /// class minput_manage { [HarmonyPatch(typeof(input_manage), "init_input_obj_list")] class init_input_obj_list { static bool Prefix(input_manage __instance) { DemulShooter_Plugin.MyLogger.LogMessage("minput_manage.init_input_obj_list()"); input_manage my_base = __instance; my_base.add_input_obj(new input_obj_start_game((KeyCode)DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode, 1)); my_base.add_input_obj(new input_obj_start_game((KeyCode)DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode, 2)); //my_base.add_input_obj(new input_obj_pass_movie(KeyCode.Space)); my_base.add_input_obj(new input_obj_pass_movie((KeyCode)DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)); my_base.add_input_obj(new input_obj_pass_movie((KeyCode)DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)); my_base.add_input_obj(new input_obj_goto_houtai((KeyCode)DemulShooter_Plugin.Test_Key.KeyCode)); my_base.add_input_obj(new input_obj_insert_coin((KeyCode)DemulShooter_Plugin.Coin_Key.KeyCode, 1)); my_base.add_input_obj(new input_obj_insert_coin((KeyCode)DemulShooter_Plugin.Coin_Key.KeyCode, 2)); /*my_base.add_input_obj(new input_obj_change_gun(KeyCode.Keypad1, 1)); my_base.add_input_obj(new input_obj_change_gun(KeyCode.Keypad2, 2)); my_base.add_input_obj(new input_obj_xiaoqiang(KeyCode.Keypad3, 1)); my_base.add_input_obj(new input_obj_xiaoqiang(KeyCode.Keypad4, 2)); my_base.add_input_obj(new input_obj_change_input_way(KeyCode.Y));*/ /*if (!DemulShooter_Plugin.EnableInputHack) { my_base.add_input_obj(new input_obj_change_bullet(KeyCode.Mouse1, 1)); my_base.add_input_obj(new input_obj_change_bullet(KeyCode.Mouse1, 2)); my_base.add_input_obj(new input_obj_big_power(KeyCode.Mouse2, 1)); my_base.add_input_obj(new input_obj_big_power(KeyCode.Mouse2, 2)); } else { //DemulShooter is handling }*/ return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/minput_obj_change_bullet.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class minput_obj_change_bullet { [HarmonyPatch(typeof(input_obj_change_bullet), "change_weapon")] class mchange_weapon { static bool Prefix(int player_num) { DemulShooter_Plugin.MyLogger.LogMessage("minput_obj_change_bullet.change_weapon() => player_num: " + player_num.ToString()); if (!zhichi_hanshu_gun_wheel_mark_manage.gun_is_can_fire()) { return false; } game_player game_player = zhichi_hanshu_game_player.get_game_player(player_num); if (!game_player.is_living()) { return false; } game_player.change_weapon(); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mnew_game_gui_connect_player_slider.cs ================================================ using HarmonyLib; using UnityEngine.UI; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mnew_game_gui_connect_player_slider { /// /// Changing the GUI image of the lifebar holder : /// Without laser, crosshair and gun, there is no more clue to know which is the selected weapon (blue or red) /// Changing the life/energy holder with a red/blue indicator will fix that /// [HarmonyPatch(typeof(new_game_gui_connect_player_slider), "update_work_data")] class update_work_data { static void Postfix(new_game_gui_connect_player_slider __instance) { if (!DemulShooter_Plugin.CrossHairVisibility && !DemulShooter_Plugin.GunVisibility) { game_player game_player = (game_player)__instance.myreal_obj; int PlayerNum = game_player.get_user_num(); // 1 or 2 string PlayerGunColor = zhichi_hanshu_gun_wheel_mark_manage.get_gun_wheel_mark_manage().get_gun_type(PlayerNum).ToString(); Image component = __instance.get_show_obj().GetComponent(); foreach (Component c in component.transform.parent.gameObject.GetComponentsInChildren()) { if (c.name == "di") { Image img = c.GetComponent(); if (PlayerNum == 1) { if (PlayerGunColor.Contains("RED")) img.sprite = DemulShooter_Plugin.Sprite_1P_Red; else if (PlayerGunColor.Contains("BLUE")) img.sprite = DemulShooter_Plugin.Sprite_1P_Blue; } if (PlayerNum == 2) { if (PlayerGunColor.Contains("RED")) img.sprite = DemulShooter_Plugin.Sprite_2P_Red; else if (PlayerGunColor.Contains("BLUE")) img.sprite = DemulShooter_Plugin.Sprite_2P_Blue; } break; } } //DemulShooter_Plugin.MyLogger.LogWarning("mnew_game_gui_connect_player_slider.update_work_data() => game_player.get_user_num: " + game_player.get_user_num() + " - color: " + game_player.get_fire_weapon().mybullet_color.ToString() + "config: " + zhichi_hanshu_gun_wheel_mark_manage.get_gun_wheel_mark_manage().get_gun_type(PlayerNum).ToString()); } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mshow_info_manage_for_objs.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mshow_info_manage_for_objs { /// /// Removing those Keyboard shortcut that are used to display various Debug string on screen /// [HarmonyPatch(typeof(show_info_manage_for_objs), "init_show_info_obj_list")] class init_show_info_obj_list { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("mshow_info_manage_for_objs.init_show_info_obj_list()"); /*base.init_show_info_obj_list(); base.add_show_info_obj(this.myshow_info_obj_wheel_value_R, KeyCode.R); base.add_show_info_obj(this.myshow_info_obj_reload_config, KeyCode.C); base.add_show_info_obj(this.myshow_info_obj_movie_and_movie_time, KeyCode.S); base.add_show_info_obj(this.myshow_info_obj_to_save_to_file_last_time, KeyCode.D); base.add_show_info_obj(this.myshow_info_obj_gun_shank, KeyCode.F); base.add_show_info_obj(this.myshow_info_obj_zhen, KeyCode.P); base.add_show_info_obj(this.myshow_info_obj_show_gun_color_light, KeyCode.L); base.add_show_info_obj(this.myshow_info_obj_gun_audio, KeyCode.W); base.add_show_info_obj(this.myshow_info_obj_debug_mem_begin, KeyCode.E);*/ return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mtest_screen.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mtest_screen { /// /// Changing resolution when the game is opening it's window /// [HarmonyPatch(typeof(test_screen), "Start")] class start { static bool Prefix() { if (DemulShooter_Plugin.ForceResolution) { DemulShooter_Plugin.MyLogger.LogMessage("test_screen.start() => Changing resolution to " + DemulShooter_Plugin.ScreenWidth + "x" + DemulShooter_Plugin.ScreenHeight + " fullscreen: " + DemulShooter_Plugin.Fullscreen); UnityEngine.Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, (int)DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); return false; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mzhichi_hanshu_pos.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mzhichi_hanshu_pos { /// /// Used to map Window coordinates to UI coordinates : UI is fixed on [-960, 960] for X and [-540, 540] for Y (1920p resolution) /// We need to scale down the coordinates before putting it in the good range /// [HarmonyPatch(typeof(zhichi_hanshu_pos), "change_to_min")] class change_to_min { static bool Prefix(Vector3 vec_input, ref Vector3 __result) { //NightHunterArcadePlugin.MyLogger.LogMessage("mzhichi_hanshu_pos.change_to_min() => mouse_pos: " + vec_input.ToString()); __result = vec_input; __result.x = (vec_input.x * zhichi_hanshu_pos.PIXEL_WIDTH / Screen.width) - (zhichi_hanshu_pos.PIXEL_WIDTH / 2); __result.y = (vec_input.y * zhichi_hanshu_pos.PIXEL_HEIGHT / Screen.height) - (zhichi_hanshu_pos.PIXEL_HEIGHT / 2); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Patch/mzzp_houtai_manage_check_control.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mzzp_houtai_manage_check_control { /// /// Replacing Keyboard keys used in SERVICE menu /// [HarmonyPatch(typeof(zzp_houtai_manage_check_control), "check_for_zzp_houtai_control")] class check_for_zzp_houtai_control { static bool Prefix(zzp_houtai_manage_check_control __instance) { //NightHunterArcade2_Plugin.MyLogger.LogMessage("mzzp_houtai_manage_check_control.check_for_zzp_houtai_control()"); if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.MenuDown_Key.KeyCode)) { __instance.add_key_work(); } if (Input.GetKeyDown(KeyCode.UpArrow)) // <---Not working, funtion null { __instance.del_key_work(); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.MenuSelect_Key.KeyCode)) { __instance.queding_work(); } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("NHA2_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("NHA2_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 //Folowing command can work OR cause issue with Unity, especially if .NET 4.0 is required and Mono may miss some functionality _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); //Following way is more compatible in case of trouble //FieldInfo[] fiBuffer = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); //_DataFields = new FieldInfo[fiBuffer.Length]; //string[] fiNames = new string[fiBuffer.Length]; //for (int i = 0; i < fiNames.Length; i++) //{ // fiNames[i] = fiBuffer[i].Name; //} //Array.Sort(fiNames); //for (int i = 0; i < _DataFields.Length; i++) //{ // foreach (FieldInfo fi in fiBuffer) // { // if (fi.Name == fiNames[i]) // { // _DataFields[i] = fi; // break; // } // } //} foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError(this.GetType().Name + ".Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // _Writer.Write((bool)o); //else if (ReferenceEquals(t, typeof(byte))) // _Writer.Write((byte)o); //else if (ReferenceEquals(t, typeof(char))) // _Writer.Write((char)o); //else if (ReferenceEquals(t, typeof(decimal))) // _Writer.Write((decimal)o); //else if (ReferenceEquals(t, typeof(double))) // _Writer.Write((double)o); //else if (ReferenceEquals(t, typeof(float))) // _Writer.Write((float)o); ///*else if (ReferenceEquals(t, typeof(nint))) // _Writer.Write((nint)o); //else if (ReferenceEquals(t, typeof(nuint))) // _Writer.Write((nuint)o);*/ //else if (ReferenceEquals(t, typeof(long))) // _Writer.Write((long)o); //else if (ReferenceEquals(t, typeof(sbyte))) // _Writer.Write((sbyte)o); //else if (ReferenceEquals(t, typeof(short))) // _Writer.Write((short)o); //else if (ReferenceEquals(t, typeof(uint))) // _Writer.Write((uint)o); //else if (ReferenceEquals(t, typeof(ulong))) // _Writer.Write((ulong)o); //else if (ReferenceEquals(t, typeof(ushort))) // _Writer.Write((ushort)o); //else if (ReferenceEquals(t, typeof(int))) // _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // return _Reader.ReadBoolean(); //else if (ReferenceEquals(t, typeof(byte))) // return _Reader.ReadByte(); //else if (ReferenceEquals(t, typeof(char))) // return _Reader.ReadChar(); //else if (ReferenceEquals(t, typeof(decimal))) // return _Reader.ReadDecimal(); //else if (ReferenceEquals(t, typeof(double))) // return _Reader.ReadDouble(); //else if (ReferenceEquals(t, typeof(float))) // return _Reader.ReadSingle(); ///*else if (ReferenceEquals(t, typeof(nint))) // return _Reader. //else if (ReferenceEquals(t, typeof(nuint))) // return _Reader.*/ //else if (ReferenceEquals(t, typeof(long))) // return _Reader.ReadInt64(); //else if (ReferenceEquals(t, typeof(sbyte))) // return _Reader.ReadSByte(); //else if (ReferenceEquals(t, typeof(short))) // return _Reader.ReadInt16(); //else if (ReferenceEquals(t, typeof(uint))) // return _Reader.ReadUInt32(); //else if (ReferenceEquals(t, typeof(ulong))) // return _Reader.ReadUInt64(); //else if (ReferenceEquals(t, typeof(ushort))) // return _Reader.ReadUInt16(); //else if (ReferenceEquals(t, typeof(int))) // return _Reader.ReadInt32(); //else // return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Action = null; public byte[] ChangeWeapon = null; public byte HideGuns = 0; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Life = null; public int[] GunType = null; public int Credits = 0; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.UI.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/UnityPlugin_BepInEx_NHA2.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin NightHunter2_BepInEx_DemulShooter_Plugin v3.5 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.UI.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_NHA2/UnityPlugin_BepInEx_NHA2.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_NHA2", "UnityPlugin_BepInEx_NHA2.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.operationwolf"; public const String pluginName = "OperationWolf_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "OperationWolf_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; public enum PlayerType : int { Player1 = 0, Player2 } //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); public static PluginControllerButton P2_Grenade_Key = new PluginControllerButton((int) KeyCode.F); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static bool IsMouseLockedRequired = false; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); P2_Grenade_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P2_GRENADE")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); P2_Grenade_Key.SetButton(Input.GetKey((KeyCode)P2_Grenade_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Action, Input.GetMouseButton(2) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, _InputData.Reload[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.Action, _InputData.ChangeWeapon[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/INIFile.cs ================================================ using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet=CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/OperationWolf_BepInEx_DemulShooter_Plugin.ini ================================================ [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P2_GRENADE=102 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mCursorMouseMouve.cs ================================================ using HarmonyLib; using UnityEngine; using Virtuallyz.VRShooter.IO; namespace BepInEx_DemulShooter_Plugin { class mCursorMouseMouve { [HarmonyPatch(typeof(CursorMouseMove), "MouseMove")] class MouseMove { static bool Prefix(CursorMouseMove __instance, ref Vector3 __result) { __result = Vector3.zero; return false; } } /// /// Replacing the original function where the procedure is different before Cursor Locked and not Locked /// Here it is replaced by our own Lock flag ans changed the relative input reading by mouse position /// [HarmonyPatch(typeof(CursorMouseMove), "Mouse2DUpdate")] class Mouse2DUpdate { static bool Prefix(CursorMouseMove __instance, Camera ___PCCamera, ref Vector3 ___Cursor2DPos, float ___LastDistanceFromScreen, Animator ___viewportController) { if (!___PCCamera) { return false; } Vector3 vector6; if (DemulShooter_Plugin.IsMouseLockedRequired) { //Custom option to hide Crosshairs if (DemulShooter_Plugin.CrossHairVisibility) __instance.Target.SetActive(true); else __instance.Target.SetActive(false); //Vector3 MouseAxis = Input.mousePosition; Vector3 MouseAxis = Vector3.zero; if (__instance.name.Equals("MainController")) MouseAxis = DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player1].GetAimingPosition(); else if (__instance.name.Equals("PlayerTwoController")) MouseAxis = DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player2].GetAimingPosition(); //OpWolf_Plugin.MyLogger.LogMessage("Virtuallyz.VRShooter.IO.Mouse2DUpdate() => " + __instance.name + " data : " + MouseAxis.ToString()); ___Cursor2DPos.x = MouseAxis.x - (Screen.width / 2.0f); ___Cursor2DPos.y = MouseAxis.y - (Screen.height / 2.0f); ___Cursor2DPos.z = 1.0f; Vector3 vector = new Vector3((float)Screen.width * 0.5f, (float)Screen.height * 0.5f, 0f); float num = 1f; float num2 = 1f - num; Vector3 vector2 = vector * num; Vector3 zero = Vector3.zero; ___Cursor2DPos.x = Mathf.Clamp(___Cursor2DPos.x, -vector.x, vector.x); ___Cursor2DPos.y = Mathf.Clamp(___Cursor2DPos.y, -vector.y, vector.y); Vector3 vector4 = ___Cursor2DPos; vector4.x = Mathf.Abs(vector4.x); vector4.y = Mathf.Abs(vector4.y); vector4 -= vector2; vector4.x /= (float)Screen.width * 0.5f * num2; vector4.y /= (float)Screen.height * 0.5f * num2; Vector2 vector5 = new Vector2(10f, 10f); if (___Cursor2DPos.x < -vector2.x) { zero.y = -Mathf.Lerp(0f, vector5.y, vector4.x); } if (___Cursor2DPos.x > vector2.x) { zero.y = Mathf.Lerp(0f, vector5.y, vector4.x); } if (___Cursor2DPos.y < -vector2.y) { zero.x = Mathf.Lerp(0f, vector5.x, vector4.y); } if (___Cursor2DPos.y > vector2.y) { zero.x = -Mathf.Lerp(0f, vector5.x, vector4.y); } vector6 = ___Cursor2DPos + vector; __instance.Target.transform.position = ___PCCamera.ScreenToWorldPoint(vector6); Quaternion localRotation = Quaternion.Euler(zero.x, zero.y, 0f); __instance.transform.localRotation = localRotation; } else { vector6 = Input.mousePosition; if (__instance.depthPositionFixed) { vector6 = new Vector3(vector6.x, vector6.y, 0f); } __instance.Target.transform.position = ___PCCamera.ScreenToWorldPoint(vector6); } ___PCCamera.ScreenPointToRay(vector6); if (___viewportController) { Vector2 vector7 = ___PCCamera.ScreenToViewportPoint(vector6); ___viewportController.SetFloat("Viewport_X", vector7.x); ___viewportController.SetFloat("Viewport_Y", vector7.y); ___viewportController.SetBool("isRight", __instance.isRight); ___viewportController.SetBool("isLongWeapon", __instance.isLongWeapon); } vector6.z = ___LastDistanceFromScreen; __instance.GetForceTarget().transform.position = ___PCCamera.ScreenToWorldPoint(vector6); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mGameManager.cs ================================================ using HarmonyLib; using Virtuallyz.VRShooter; namespace BepInEx_DemulShooter_Plugin { class mGameManager { /// /// On LevelStopped (quit or died) => set Ammo and Life to 0 /// [HarmonyPatch(typeof(GameManager), "OnLevelActStopped")] class Update { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogWarning("Virtuallyz.VRShooter.GameManager.OnLevelActStopped()"); for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { DemulShooter_Plugin.OutputData.Life[i] = 0; DemulShooter_Plugin.OutputData.Ammo[i] = 0; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mInterfaceModule.cs ================================================ using HarmonyLib; using Virtuallyz.VRShooter.Characters; using Virtuallyz.VRShooter.Modules; using Virtuallyz.VRShooter.Weapons; namespace BepInEx_DemulShooter_Plugin { class mInterfaceModule { /// /// Intercapting call to LifeBar update to get Life value of player /// [HarmonyPatch(typeof(InterfaceLifeModule), "UpdateLifebars")] class UpdateLifebars { static bool Prefix(Character character) { //OpWolf_Plugin.MyLogger.LogMessage("Virtuallyz.Modules.InterfaceLifeModule.UpdateLifebars() => " + character.ToString()); //OpWolf_Plugin.MyLogger.LogMessage("Virtuallyz.Modules.InterfaceLifeModule.UpdateLifebars() => currentHP: " + character.CurrentHP + ", isFine: " + character.IsFine + ", is BadlyHurt: " + character.IsBadlyHurt + ", isDead: " + character.IsDead); if (character.ToString().Equals("Player")) { DemulShooter_Plugin.OutputData.Life[0] = (byte)character.CurrentHP; DemulShooter_Plugin.OutputData.Life[1] = (byte)character.CurrentHP; } return true; } } /// /// Intercapting call to Weapon UI update to get Ammo value of player weapon /// [HarmonyPatch(typeof(InterfaceWeaponModule), "UpdateWeaponUIInformation")] class UpdateWeaponUIInformation { static bool Prefix(Weapon weapon) { if (weapon != null && weapon.Owner.ToString().Equals("Player")) { ushort Ammo = 0; if (weapon is ProjectileWeapon ) { //OpWolf_Plugin.MyLogger.LogMessage("Virtuallyz.Modules.InterfaceWeaponModule.UpdateWeaponUIInformation(): weapon.Name: " + weapon.name + ", weapon.ControllerId: " + weapon.ControllerId); //OpWolf_Plugin.MyLogger.LogMessage("Virtuallyz.Modules.InterfaceLifeModule.UpdateWeaponUIInformation() => Projectile"); ProjectileWeapon w = weapon as ProjectileWeapon; Ammo = (ushort)w.CurrentChargerAmmosInt; //OpWolf_Plugin.MyLogger.LogMessage("Virtuallyz.Modules.InterfaceLifeModule.UpdateWeaponUIInformation() => CurrentChargerAmmosInt: " + w.CurrentChargerAmmosInt); } if (weapon.ControllerId == 0) DemulShooter_Plugin.OutputData.Ammo[0] = Ammo; else if (weapon.ControllerId == 1) DemulShooter_Plugin.OutputData.Ammo[1] = Ammo; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mPlayer.cs ================================================ using HarmonyLib; using Virtuallyz.VRShooter.Characters.Players; namespace BepInEx_DemulShooter_Plugin { class mPlayer { /// /// Intercept dammage event to send output to Demulshooter /// [HarmonyPatch(typeof(Player), "OnDamageTaken")] class OnDamageTaken { static bool Prefix(Player __instance) { //DemulShooter_Plugin.MyLogger.LogMessage("Virtuallyz.VRShooter.Characters.Players.OnDamageTaken()"); DemulShooter_Plugin.OutputData.Damaged[0] = 1; DemulShooter_Plugin.OutputData.Damaged[1] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mPlayerInOutModule.cs ================================================ using HarmonyLib; using Virtuallyz.VRShooter.IO; namespace BepInEx_DemulShooter_Plugin { class mPlayerInOutModule { /// /// Disable the "Pause" popup when the 2nd controller is missing /// [HarmonyPatch(typeof(PlayerInOutModule), "OnMissingControllerTriggered")] class OnMissingControllerTriggered { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mScreenDuckHunt_InOutSystem.cs ================================================ using System; using System.Reflection; using HarmonyLib; using UnityPlugin_BepInEx_Core; using Virtuallyz.VRShooter.IO; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mScreenDuckHunt_InOutSystem { /// /// Replacing the Cursor Locked command by a flag of our own /// Cursor will stay unlocked and info is stored for the mouse update function /// [HarmonyPatch(typeof(ScreenDuckHunt_InOutSystem), "ResetCursorNormalState")] class ResetCursorNormalState { static bool Prefix(ref bool ___UIMode) { DemulShooter_Plugin.MyLogger.LogMessage("mScreenDuckHunt_InOutSystem.ResetCursorNormalState()"); ___UIMode = false; //Cursor.lockState = CursorLockMode.Locked; DemulShooter_Plugin.IsMouseLockedRequired = true; return false; } } /// /// Replacing the Cursor Locked command by a flag of our own /// Cursor will stay unlocked and info is stored for the mouse update function /// [HarmonyPatch(typeof(ScreenDuckHunt_InOutSystem), "UnlockCursor")] class UnlockCursor { static bool Prefix(ref bool ___UIMode, bool keepWeapon, bool withPointers = true) { DemulShooter_Plugin.MyLogger.LogMessage("mScreenDuckHunt_InOutSystem.UnlockCursor()"); ___UIMode = true; //Cursor.lockState = CursorLockMode.None; DemulShooter_Plugin.IsMouseLockedRequired = false; return false; } } /// /// Use Demulshooter command to control the players actions /// [HarmonyPatch(typeof(Virtuallyz.VRShooter.IO.ScreenDuckHunt_InOutSystem), "OnUpdate")] class OnUpdate { static bool Prefix(ScreenDuckHunt_InOutSystem __instance) { /* Custom Inputs for Player 1 */ if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player1].GetButtonDown(PluginController.MyInputButtons.Trigger)) { UseMethodFromName(__instance, "FirePressed", new object[] { 0 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player1].GetButton(PluginController.MyInputButtons.Trigger)) { UseMethodFromName(__instance, "FireHold", new object[] { 0 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player1].GetButtonUp(PluginController.MyInputButtons.Trigger)) { UseMethodFromName(__instance, "FireReleased", new object[] { 0 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player1].GetButtonDown(PluginController.MyInputButtons.Reload)) { UseMethodFromName(__instance, "Reload", new object[] { 0 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player1].GetButtonUp(PluginController.MyInputButtons.Reload)) { UseMethodFromName(__instance, "ReloadReleased", new object[] { 0 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player1].GetButtonDown(PluginController.MyInputButtons.Action)) { UseMethodFromName(__instance, "SelectNextWeapon", new object[] { 0 }); } /* Custom Inputs for Player 2 */ if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player2].GetButtonDown(PluginController.MyInputButtons.Trigger)) { UseMethodFromName(__instance, "FirePressed", new object[] { 1 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player2].GetButton(PluginController.MyInputButtons.Trigger)) { UseMethodFromName(__instance, "FireHold", new object[] { 1 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player2].GetButtonUp(PluginController.MyInputButtons.Trigger)) { UseMethodFromName(__instance, "FireReleased", new object[] { 1 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player2].GetButtonDown(PluginController.MyInputButtons.Reload)) { UseMethodFromName(__instance, "Reload", new object[] { 1 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player2].GetButtonUp(PluginController.MyInputButtons.Reload)) { UseMethodFromName(__instance, "ReloadReleased", new object[] { 1 }); } if (DemulShooter_Plugin.PluginControllers[(int)DemulShooter_Plugin.PlayerType.Player2].GetButtonDown(PluginController.MyInputButtons.Action)) { UseMethodFromName(__instance, "SelectNextWeapon", new object[] { 1 }); } //Adding a keyboard key for Grenade P2 if (DemulShooter_Plugin.P2_Grenade_Key.GetButtonDown()) { UseMethodFromName(__instance, "SelectGrenade", new object[] { 1 }); } if (DemulShooter_Plugin.P2_Grenade_Key.GetButton()) { UseMethodFromName(__instance, "HoldGrenade", new object[] { 1 }); } if (DemulShooter_Plugin.P2_Grenade_Key.GetButtonUp()) { UseMethodFromName(__instance, "UnSelectAnyGrenade", new object[] { 1 }); } return true; } } private static void UseMethodFromName(ScreenDuckHunt_InOutSystem Instance, string MethodName, object[] Parameters) { //OpWolf_Plugin.MyLogger.LogMessage("Searching for the following method : " + MethodName); try { MethodInfo m = Instance.GetType().GetMethod(MethodName, BindingFlags.Instance | BindingFlags.NonPublic); if (m != null) { //OpWolf_Plugin.MyLogger.LogMessage("Found " + MethodName + ", Invoking... "); m.Invoke(Instance, Parameters); } } catch (Exception Ex) { DemulShooter_Plugin.MyLogger.LogError("Error trying to run method " + MethodName + " from " + Instance.GetType().ToString() + " : " + Ex.Message.ToString()); } } /// /// Disable base controll for some actions in the game, to replace them later by DemulShooter /// Putting back the other one that we need to be enabled /// [HarmonyPatch(typeof(Virtuallyz.VRShooter.IO.ScreenDuckHunt_InOutSystem), "HookInput")] class HookInput { static bool Prefix(ScreenDuckHunt_InOutSystem __instance, ScreenView ___CurrentView) { AddEvent(__instance, ___CurrentView, "InvokeAnyButtonPushed", "AnyButtonPushed"); AddEvent2(__instance, ___CurrentView, "InvokeOnSkipButtonDown", "SkipButtonDown"); AddEvent2(__instance, ___CurrentView, "InvokeOnSkipButtonUp", "SkipButtonUp"); AddEvent(__instance, ___CurrentView, "InvokeOnSkipButtonHold", "SkipButtonHold"); //AddEvent(__instance, ___CurrentView, "InvokeOnResetButtonDown", "ResetButtonDown"); //AddEvent(__instance, ___CurrentView, "InvokeOnResetButtonUp", "ResetButtonUp"); AddEvent(__instance, ___CurrentView, "UpdateDone", "UpdateDone"); //AddEvent(__instance, ___CurrentView, "MouseLeftButtonPressed", "FirePressed"); //AddEvent(__instance, ___CurrentView, "MouseLeftButtonHold", "FireHold"); //AddEvent(__instance, ___CurrentView, "MouseLeftButtonReleased", "FireReleased"); //AddEvent(__instance, ___CurrentView, "OnMissingControllerTriggered", "MissingControllerTriggered"); //AddEvent(__instance, ___CurrentView, "SelectGrenade", "MouseRightButtonPressed"); //AddEvent(__instance, ___CurrentView, "HoldGrenade", "MouseRightButtonHold"); //AddEvent(__instance, ___CurrentView, "UnSelectAnyGrenade", "MouseRightButtonReleased"); AddEvent(__instance, ___CurrentView, "WeaponSelectInterface", "WeaponNumberUpdate"); AddEvent(__instance, ___CurrentView, "UIMove", "DirectionalArrowsUpdate"); AddEvent(__instance, ___CurrentView, "SelectGrenade", "GButtonPressed"); AddEvent(__instance, ___CurrentView, "HoldGrenade", "GButtonHold"); AddEvent(__instance, ___CurrentView, "UnSelectAnyGrenade", "GButtonReleased"); AddEvent(__instance, ___CurrentView, "OnUISelectReleased", "SelectButtonReleased"); AddEvent(__instance, ___CurrentView, "OnUISelectPushed", "SelectButtonPressed"); AddEvent(__instance, ___CurrentView, "OnUISelectBack", "BackButtonReleased"); AddEvent(__instance, ___CurrentView, "UseHeal", "HealButtonPressed"); AddEvent(__instance, ___CurrentView, "InvokeCoverPushed", "CrouchKeyPressed"); AddEvent(__instance, ___CurrentView, "InvokeCoverHold", "CrouchKeyHold"); AddEvent(__instance, ___CurrentView, "InvokeCoverReleased", "CrouchKeyReleased"); AddEvent(__instance, ___CurrentView, "OnSettingsButtonPushed", "SettingsButtonClicked"); AddEvent(__instance, ___CurrentView, "OnStartModeController", "StartModeController"); return false; } } public static void AddEvent(ScreenDuckHunt_InOutSystem Instance, ScreenView CurrentView, string Method, string Event) { PlayerInOutSystem playerSystem = Instance as PlayerInOutSystem; MethodInfo mInfo = playerSystem.GetType().GetMethod(Method, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (mInfo != null) { EventInfo eventInfo = CurrentView.GetType().GetEvent(Event); if (eventInfo != null) { Type eventType = eventInfo.EventHandlerType; Delegate del = Delegate.CreateDelegate(eventType, Instance, mInfo); eventInfo.AddEventHandler(CurrentView, del); DemulShooter_Plugin.MyLogger.LogMessage("Succesfully added handler '" + Method + "' to event '" + Event + "'"); } else DemulShooter_Plugin.MyLogger.LogError("Impossible to find Event " + Event); } else DemulShooter_Plugin.MyLogger.LogError("Impossible to find Method " + Method); } //Multiple MethodDefinition, can cause bugs public static void AddEvent2(ScreenDuckHunt_InOutSystem Instance, ScreenView CurrentView, string Method, string Event) { PlayerInOutSystem playerSystem = Instance as PlayerInOutSystem; MethodInfo[] mInfo = playerSystem.GetType().GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); foreach (MethodInfo m in mInfo) { if (m.Name.Equals(Method)) { try { EventInfo eventInfo = CurrentView.GetType().GetEvent(Event); if (eventInfo != null) { Type eventType = eventInfo.EventHandlerType; DemulShooter_Plugin.MyLogger.LogMessage("Found " + m + " of Type " + eventType.ToString()); Delegate del = Delegate.CreateDelegate(eventType, Instance, m); eventInfo.AddEventHandler(CurrentView, del); DemulShooter_Plugin.MyLogger.LogMessage("Succesfully added handler '" + Method + "' to event '" + Event + "'"); } else DemulShooter_Plugin.MyLogger.LogError("Impossible to find Event " + Event); } catch { } } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mScreenMouseLook_InOutSystem.cs ================================================ using HarmonyLib; using Virtuallyz.VRShooter.IO; namespace BepInEx_DemulShooter_Plugin { /// /// Replacing the Cursor Locked command by a flag of our own /// Cursor will stay unlocked and info is stored for the mouse update function /// class mScreenMouseLook_InOutSystem { [HarmonyPatch(typeof(ScreenMouseLook_InOutSystem), "ResetCursorNormalState")] class ResetCursorNormalState { static bool Prefix(ref ScreenView ___currentView, ref bool ___UIMode) { DemulShooter_Plugin.MyLogger.LogMessage("mScreenMouseLook_InOutSystem.ResetCursorNormalState()"); ___currentView.SetMouseLook(true); ___UIMode = false; //Cursor.lockState = CursorLockMode.Locked; DemulShooter_Plugin.IsMouseLockedRequired = true; return false; } } [HarmonyPatch(typeof(ScreenMouseLook_InOutSystem), "UnlockCursor")] class UnlockCursor { static bool Prefix(ref ScreenView ___currentView, ref bool ___UIMode, bool keepWeapon, bool withPointers = true) { DemulShooter_Plugin.MyLogger.LogMessage("mScreenDuckHunt_InOutSystem.UnlockCursor()"); ___currentView.SetMouseLook(false); ___UIMode = true; //Cursor.lockState = CursorLockMode.None; DemulShooter_Plugin.IsMouseLockedRequired = false; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mScreenView.cs ================================================ using HarmonyLib; using System.Collections.Generic; using Virtuallyz.VRShooter.IO; using System.Runtime.CompilerServices; using Virtuallyz.VRShooter.Characters.Players; namespace BepInEx_DemulShooter_Plugin { class _mScreenView { /// /// Force non-detection of Gamepad to not mess with the rest of the game /// Game will only display keyboard keys on screen, and Gamepads button will not be used out of DS controls /// [HarmonyPatch(typeof(ScreenView), "CheckInputDevicesSolo")] class CheckInputDevicesSolo { static bool Prefix(ScreenView __instance, /*bool ___UseKeyBoard, */ ref PlayerController ___keyboardController, Dictionary ___associatedGamepads, PlayerController[] ___controllers) { if (___keyboardController == null) { DemulShooter_Plugin.MyLogger.LogMessage("Virtuallyz.VRShooter.IO.CheckInputDevicesSolo() => Associate controller 0 to Keyboard"); } ___associatedGamepads.Clear(); ___keyboardController = ___controllers[0]; ___controllers[0].SetDevice(UnityEngine.InputSystem.Keyboard.current); mSetUseKeyboard.SetUseKeyboard(__instance, true); return false; } } [HarmonyPatch(typeof(ScreenView), "SetUseKeyboard")] class mSetUseKeyboard { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void SetUseKeyboard(object instance, bool useKeyboard) { //Used to call the private method } } /// /// Called for multiplayer Gamepad assignment, this way no Gamepad is found /// [HarmonyPatch(typeof(ScreenView), "GetFreeGamepad")] class GetFreeGamepad { static bool Prefix(ref UnityEngine.InputSystem.Gamepad __result) { __result = null; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Patch/mWeapon.cs ================================================ using HarmonyLib; using Virtuallyz.VRShooter.Characters; using Virtuallyz.VRShooter.Weapons; namespace BepInEx_DemulShooter_Plugin { class mWeapon { /// /// Intercept the shoot event to create Output to DemulShooter /// [HarmonyPatch(typeof(Weapon), "Shoot")] class Shoot { static bool Prefix(Character ___owner, Weapon __instance) { //OpWolf_Plugin.MyLogger.LogMessage("Virtuallyz.VRShooter.Weapons.Weapon.Shoot() => " + ___owner.ToString()); //Owner of the Weapon is a character, and the Character class is overriding the ToString() method : //If the owner if a Player, it will return "Player" //Else, a NPC name if (___owner.ToString().Equals("Player")) DemulShooter_Plugin.OutputData.Recoil[__instance.ControllerId] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("OperationWolf_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("OperationWolf_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 //Folowing command can work OR cause issue with Unity, especially if .NET 4.0 is required and Mono may miss some functionality _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); //Following way is more compatible in case of trouble //FieldInfo[] fiBuffer = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); //_DataFields = new FieldInfo[fiBuffer.Length]; //string[] fiNames = new string[fiBuffer.Length]; //for (int i = 0; i < fiNames.Length; i++) //{ // fiNames[i] = fiBuffer[i].Name; //} //Array.Sort(fiNames); //for (int i = 0; i < _DataFields.Length; i++) //{ // foreach (FieldInfo fi in fiBuffer) // { // if (fi.Name == fiNames[i]) // { // _DataFields[i] = fi; // break; // } // } //} foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError(this.GetType().Name + ".Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // _Writer.Write((bool)o); //else if (ReferenceEquals(t, typeof(byte))) // _Writer.Write((byte)o); //else if (ReferenceEquals(t, typeof(char))) // _Writer.Write((char)o); //else if (ReferenceEquals(t, typeof(decimal))) // _Writer.Write((decimal)o); //else if (ReferenceEquals(t, typeof(double))) // _Writer.Write((double)o); //else if (ReferenceEquals(t, typeof(float))) // _Writer.Write((float)o); ///*else if (ReferenceEquals(t, typeof(nint))) // _Writer.Write((nint)o); //else if (ReferenceEquals(t, typeof(nuint))) // _Writer.Write((nuint)o);*/ //else if (ReferenceEquals(t, typeof(long))) // _Writer.Write((long)o); //else if (ReferenceEquals(t, typeof(sbyte))) // _Writer.Write((sbyte)o); //else if (ReferenceEquals(t, typeof(short))) // _Writer.Write((short)o); //else if (ReferenceEquals(t, typeof(uint))) // _Writer.Write((uint)o); //else if (ReferenceEquals(t, typeof(ulong))) // _Writer.Write((ulong)o); //else if (ReferenceEquals(t, typeof(ushort))) // _Writer.Write((ushort)o); //else if (ReferenceEquals(t, typeof(int))) // _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // return _Reader.ReadBoolean(); //else if (ReferenceEquals(t, typeof(byte))) // return _Reader.ReadByte(); //else if (ReferenceEquals(t, typeof(char))) // return _Reader.ReadChar(); //else if (ReferenceEquals(t, typeof(decimal))) // return _Reader.ReadDecimal(); //else if (ReferenceEquals(t, typeof(double))) // return _Reader.ReadDouble(); //else if (ReferenceEquals(t, typeof(float))) // return _Reader.ReadSingle(); ///*else if (ReferenceEquals(t, typeof(nint))) // return _Reader. //else if (ReferenceEquals(t, typeof(nuint))) // return _Reader.*/ //else if (ReferenceEquals(t, typeof(long))) // return _Reader.ReadInt64(); //else if (ReferenceEquals(t, typeof(sbyte))) // return _Reader.ReadSByte(); //else if (ReferenceEquals(t, typeof(short))) // return _Reader.ReadInt16(); //else if (ReferenceEquals(t, typeof(uint))) // return _Reader.ReadUInt32(); //else if (ReferenceEquals(t, typeof(ulong))) // return _Reader.ReadUInt64(); //else if (ReferenceEquals(t, typeof(ushort))) // return _Reader.ReadUInt16(); //else if (ReferenceEquals(t, typeof(int))) // return _Reader.ReadInt32(); //else // return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Reload = null; public byte[] ChangeWeapon = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public byte[] Life = null; public int[] Ammo = null; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll Unity.InputSystem.dll UnityEngine.AnimationModule.dll UnityEngine.CoreModule.dll UnityEngine.InputLegacyModule.dll UnityEngine.UI.dll UnityEngine.dll Virtuallyz.VRShooter.dll Virtuallyz.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/UnityPlugin_BepInEx_OperationWolf.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin OperationWolf_BepInEx_DemulShooter_Plugin v4.8 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\Unity.InputSystem.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.AnimationModule.dll UnityLibs\UnityEngine.CoreModule.dll UnityLibs\UnityEngine.InputLegacyModule.dll UnityLibs\UnityEngine.UI.dll UnityLibs\Virtuallyz.dll UnityLibs\Virtuallyz.VRShooter.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_OWR/UnityPlugin_BepInEx_OperationWolf.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_OperationWolf", "UnityPlugin_BepInEx_OperationWolf.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using Artoncode.Core; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.pointblankx"; public const String pluginName = "PointBlankX_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "PointBlankX_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; public static readonly string PLAYER_1_NAME = "P1"; public static readonly string PLAYER_2_NAME = "P2"; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Service_Key = new PluginControllerButton((int) KeyCode.Alpha9); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); public static PluginControllerButton Coin_Key = new PluginControllerButton((int) KeyCode.Alpha5); public static PluginControllerButton MenuUp_Key = new PluginControllerButton((int) KeyCode.UpArrow); public static PluginControllerButton MenuDown_Key = new PluginControllerButton((int) KeyCode.DownArrow); public static PluginControllerButton MenuSelect_Key = new PluginControllerButton((int) KeyCode.Return); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static bool IsMouseLockedRequired = false; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution private bool _ScreenChanged = false; public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; FieldInfo BNUsioController_isTestOn = null; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("Video", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("Video", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("Video", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("Video", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Service_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "SERVICE")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); Coin_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "COIN")); MenuUp_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_UP")); MenuDown_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_DOWN")); MenuSelect_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_SELECT")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); //Getting a reference to the Test bool value to set it later foreach(FieldInfo fi in typeof(BNUsioController).GetFields(BindingFlags.NonPublic | BindingFlags.Static)) { if (fi.Name.Equals("isTestOn")) { BNUsioController_isTestOn = fi; break; } } harmony.PatchAll(); } public void Start() { Cursor.visible = false; } public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); Coin_Key.SetButton(Input.GetKey((KeyCode)Coin_Key.KeyCode)); MenuUp_Key.SetButton(Input.GetKey((KeyCode)MenuUp_Key.KeyCode)); MenuDown_Key.SetButton(Input.GetKey((KeyCode)MenuDown_Key.KeyCode)); MenuSelect_Key.SetButton(Input.GetKey((KeyCode)MenuSelect_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Entering TEST MODE //Bool can not beaccessed in Class because it's private if (Test_Key.GetButtonDown()) { bool b = (bool)BNUsioController_isTestOn.GetValue(null); BNUsioController_isTestOn.SetValue(null, !b); } //Changing the resolution in the Start() event may be too soon, so checking in the update() loop and make it happen only once if ( ForceResolution && !_ScreenChanged) { Screen.SetResolution(ScreenWidth, ScreenHeight, Fullscreen); MyLogger.LogWarning("PointBlankX_Plugin.Update() => Changed Res"); _ScreenChanged = true; } Singleton.shared().isCrosshairVisible = CrossHairVisibility; //Updating Credits int iCredits = (int)Singleton.shared().coinPool + (int)Singleton.shared().serviceCredit; OutputData.Credits = (byte)iCredits; //Updating Life and Bullets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Life[i] = 0; OutputData.Ammo[i] = 0; Player p = Player.getPlayer("P" + (i + 1).ToString()); if (p != null && p.playerData.state == PlayerData.PlayerState.Active) { OutputData.Life[i] = (byte)p.getHealth(); int Ammo = p.getAmmo(); if (Ammo > 0) OutputData.Ammo[i] = Ammo; } } //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/PointBlankX_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 COIN=53 TEST=48 EXIT=27 MENU_UP=273 MENU_DOWN=274 MENU_SELECT=13 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("PointBlankX_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PointBlankX_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage("TcpOutputData.Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError("TcpOutputData.Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] Recoil = null; public byte[] StartLED = null; public byte[] PlayerLED = null; public byte[] Life = null; public int[] Ammo = null; public byte Credits = 0; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp-firstpass.dll Assembly-CSharp.dll UnityEngine.UI.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/UnityPlugin_BepInEx_PointBlankX.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin PointBlankX_BepInEx_DemulShooter_Plugin v3.5 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\Assembly-CSharp-firstpass.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.UI.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/UnityPlugin_BepInEx_PointBlankX.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_PointBlankX", "UnityPlugin_BepInEx_PointBlankX.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mBNUsioController.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin { class mBNUsioController { /// /// Intercepting START Led conmmands to send to DemulShooter /// [HarmonyPatch(typeof(BNUsioController), "setStartLED")] class setStartLED { static void Postfix(int idx, bool isOn) { //DemulShooter_Plugin.MyLogger.LogMessage("mBNUsioController.setStartLED() => idx : " + idx.ToString() + ", isOn: " + isOn.ToString()); DemulShooter_Plugin.OutputData.StartLED[idx - 1] = isOn == true ? (byte)1 : (byte)0; } } /// /// Intercepting Cabinet Led conmmands to send to DemulShooter /// [HarmonyPatch(typeof(BNUsioController), "setLED")] class setLED { static void Postfix(int idx, bool isOn) { //DemulShooter_Plugin.MyLogger.LogMessage("mBNUsioController.setLED() => idx : " + idx.ToString() + ", isOn: " + isOn.ToString()); DemulShooter_Plugin.OutputData.PlayerLED[idx - 1] = isOn == true ? (byte)1 : (byte)0; } } [HarmonyPatch(typeof(BNUsioController), "update")] class Update { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (var i = 0; i < codes.Count - 1; i++) { if (codes[i + 1].opcode == OpCodes.Call && codes[i + 1].operand.ToString().Contains("GetKey")) { //For menu navigation and start, it's easy to replace the KeyCodes from the instruction with our own if (codes[i].opcode == OpCodes.Ldc_I4_S || codes[i].opcode == OpCodes.Ldc_I4) { if (codes[i].operand.ToString().Equals("57")) codes[i].operand = DemulShooter_Plugin.Service_Key.KeyCode; else if (codes[i].operand.ToString().Equals("273")) codes[i].operand = DemulShooter_Plugin.MenuUp_Key.KeyCode; else if (codes[i].operand.ToString().Equals("274")) codes[i].operand = DemulShooter_Plugin.MenuDown_Key.KeyCode; else if (codes[i].operand.ToString().Equals("13")) codes[i].operand = DemulShooter_Plugin.MenuSelect_Key.KeyCode; else if (codes[i].operand.ToString().Equals("49")) codes[i].operand = DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; else if (codes[i].operand.ToString().Equals("50")) codes[i].operand = DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; } //On the other hand, TEST key (BACKSPACE) has no operand has it's loaded directly with a Ldc_I4_8 //So replacing it by "0" will deactivate the key press, and the boolean will be set in the main plugin Upd else if (codes[i].opcode == OpCodes.Ldc_I4_8) { codes[i].opcode = OpCodes.Ldc_I4_0; } } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mBootSceneController.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mBootSceneController { /// /// Transpiler to change the "true" parameter for fullscreen to "false" /// [HarmonyPatch(typeof(BootSceneController))] [HarmonyPatch("Start")] public static class Start { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (var i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Ldc_I4_1) { codes[i].opcode = OpCodes.Ldc_I4_0; break; } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mConfig.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mConfig { /// /// Transpiler to change the default SERIAL number from nothing (000000-000000/E) to something else /// [HarmonyPatch(typeof(Config))] [HarmonyPatch("loadConfig")] public static class loadConfig { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (var i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Ldstr && ((string)(codes[i].operand)).Equals(@"000000-000000/E")) { codes[i].operand = @"04R60N013F0U/A"; break; } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mErrorMessageHandler.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mErrorMessageHandler { /// /// Remove the COIN ERROR message displayed when START button are pressed /// [HarmonyPatch(typeof(ErrorMessageHandler), "setCoinError")] class setCoinError { static bool Prefix(ref bool isError) { isError = false; return true; } } [HarmonyPatch(typeof(ErrorMessageHandler), "setServiceError")] class setServiceError { static bool Prefix(ref bool isError) { isError = false; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mGlobalData.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mGlobalData { /// /// Set the "crosshairvisible" flag as we want /// [HarmonyPatch(typeof(GlobalData), MethodType.Constructor)] class Ctor { static void Postfix(ref bool ___isCrosshairVisible) { ___isCrosshairVisible = DemulShooter_Plugin.CrossHairVisibility; DemulShooter_Plugin.MyLogger.LogMessage("mGlobalData() => isCrosshairVisible: " + ___isCrosshairVisible); } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mInputWrapper.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mInputWrapper { /// /// Force Credits key Handling /// [HarmonyPatch(typeof(InputWrapper), "isCreditAdded")] class isCreditAdded { static bool Prefix(ref bool __result) { //DemulShooter_Plugin.MyLogger.LogMessage("InputWrapper.isCreditAdded()"); __result = DemulShooter_Plugin.Coin_Key.GetButtonDown(); return false; } } /// /// Force Start Keys handling /// //[HarmonyPatch(typeof(InputWrapper), "isStartPressed")] //class isStartPressed //{ // static bool Prefix(string playerName, ref bool __result) // { // __result = false; // //DemulShooter_Plugin.MyLogger.LogWarning("InputWrapper.isStartPressed() => " + playerName); // if (playerName.Equals("P1") && DemulShooter_Plugin.PluginControllers[0].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start)) // { // DemulShooter_Plugin.MyLogger.LogWarning("InputWrapper.isStartPressed() => P1 Start Detected"); // __result = true; // return false; // } // if (playerName.Equals("P2") && DemulShooter_Plugin.PluginControllers[1].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start)) // { // DemulShooter_Plugin.MyLogger.LogWarning("InputWrapper.isStartPressed() => P2 start Detected"); // __result = true; // return false; // } // /*for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) // { // if (playerName.Equals("P" + (i + 1).ToString()) && DemulShooter_Plugin.PluginControllers[i].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start)) // { // DemulShooter_Plugin.MyLogger.LogWarning("InputWrapper.isStartPressed() => P" + (i + 1).ToString() + " Start Detected"); // __result = true; // return false; // } // }*/ // return false; // } //} /// /// Feeding gun trigger with DemulShooter /// [HarmonyPatch(typeof(InputWrapper), "isShooting")] class isShooting { static bool Prefix(string playerName, ref int ___p1ShootFrameCount, ref int ___p2ShootFrameCount, ref float ___p1ShootHoldTime, ref float ___p2ShootHoldTime, float ___rapidFireShootInterval, ref bool __result, bool allowRapidFire = false) { //DemulShooter_Plugin.MyLogger.LogWarning("InputWrapper.isShooting() => Player: " + playerName + "allowRapidFire: " + allowRapidFire); if (playerName == "P1") { if (!DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { ___p1ShootFrameCount = 0; ___p1ShootHoldTime = 0f; __result = false; return false; } ___p1ShootFrameCount++; ___p1ShootHoldTime += Time.deltaTime; if (allowRapidFire) { bool flag3 = false; if (___p1ShootHoldTime > ___rapidFireShootInterval) { flag3 = true; ___p1ShootHoldTime -= ___rapidFireShootInterval; } __result = ___p1ShootFrameCount == 1 || flag3; return false; } __result = ___p1ShootFrameCount == 1; return false; } else { if (!DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { ___p2ShootFrameCount = 0; ___p2ShootHoldTime = 0f; __result = false; return false; } ___p2ShootFrameCount++; ___p2ShootHoldTime += Time.deltaTime; if (allowRapidFire) { bool flag4 = false; if (___p2ShootHoldTime > ___rapidFireShootInterval) { flag4 = true; ___p2ShootHoldTime -= ___rapidFireShootInterval; } __result = ___p2ShootFrameCount == 1 || flag4; return false; } __result = ___p2ShootFrameCount == 1; return false; } } } /// /// Feeding gun position /// ///Coordinates goes from [0.0, 0.0] in bottom left corner to [1.0, 1.0] in upper right when the game is on a 16/9 display ration ///If the ratio is different, the Y value must go over 1.0 or else there will be some offset /// [HarmonyPatch(typeof(InputWrapper), "getPositionOnScreen")] class getPositionOnScreen { static bool Prefix(string playerName, ref Vector2 __result) { Vector2 v = new Vector2(); if (playerName.Equals("P1")) { v = DemulShooter_Plugin.PluginControllers[0].GetAimingPosition(); } else if (playerName.Equals("P2")) { v = DemulShooter_Plugin.PluginControllers[1].GetAimingPosition(); } float Y_Max = (16.0f / 9.0f) / ((float)Screen.width / (float)Screen.height); __result.x = v.x / (float)Screen.width; __result.y = v.y / (float)Screen.height * Y_Max; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mPlayer.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mPlayer { /// /// Intercept the shoot event to create Output to DemulShooter /// [HarmonyPatch(typeof(Player), "shoot")] class Shoot { static bool Prefix(Vector2 positionOnScreen, Player __instance) { DemulShooter_Plugin.MyLogger.LogMessage("mPlayer.Shoot() => Name: " + __instance.getName()); //Filtering Active player state, because this function is called in Demo mode also if (__instance.playerData.state == PlayerData.PlayerState.Active) { if (__instance.getName() == "P1") DemulShooter_Plugin.OutputData.Recoil[0] = 1; else if (__instance.getName() == "P2") DemulShooter_Plugin.OutputData.Recoil[1] = 1; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mTitleController.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.patch { class mTitleController { /// /// Transpiler remove the "PLEASE INSERT COIN" nag when a player is not playing /// The game is detecting Gun at screen (of course, with the plugin) and displays the message after 3 seconds /// To remove the message, either set the gun "OffScreen" (T/Y keys) /// Or like here, remove the Time variable to be inscreased by the timer so that the 3 seconds will never be reached /// [HarmonyPatch(typeof(TitleController))] [HarmonyPatch("Update")] public static class Start { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (var i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("get_deltaTime")) { codes[i].opcode = OpCodes.Ldc_R4; codes[i].operand = 0.0f; DemulShooter_Plugin.MyLogger.LogMessage("UIPlayerPanel.Update(): Patched INSERT COIN Title/Demo panel"); break; } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PBX/patch/mUIPlayerPanel.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.patch { class mUIPlayerPanel { /// /// Transpiler remove the "PLEASE INSERT COIN" nag when a player is not playing /// The game is detecting Gun at screen (of course, with the plugin) and displays the message after 3 seconds /// To remove the message, either set the gun "OffScreen" (T/Y keys) /// Or like here, remove the Time variable to be inscreased by the timer so that the 3 seconds will never be reached /// [HarmonyPatch(typeof(UIPlayerPanel))] [HarmonyPatch("Update")] public static class Start { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (var i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("get_deltaTime")) { codes[i].opcode = OpCodes.Ldc_R4; codes[i].operand = 0.0f; DemulShooter_Plugin.MyLogger.LogMessage("UIPlayerPanel.Update(): Patched INSERT COIN panel"); break; } //Other solution is to change the JE by JNE on the Gun detection /*if (codes[i].opcode == OpCodes.Callvirt && codes[i].operand.ToString().Contains("isGunInRange")) { PointBlankX_Plugin.MyLogger.LogMessage(codes[i+1].opcode.Name.ToString() + " - " + codes[i+1].operand.ToString()); codes[i + 1].opcode = OpCodes.Br_S; PointBlankX_Plugin.MyLogger.LogMessage("UIPlayerPanel.Update(): patched INSERT COIN Panel"); PointBlankX_Plugin.MyLogger.LogMessage(codes[i + 1].opcode.Name.ToString() + " - " + codes[i + 1].operand.ToString()); break; }*/ } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.pvz"; public const String pluginName = "PVZ_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "PVZ_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 1; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key; public static PluginControllerButton Test_Key; //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } Exit_Key = new PluginControllerButton((int)KeyCode.Escape); Test_Key = new PluginControllerButton((int)KeyCode.Alpha0); // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("VIDEO", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("VIDEO", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("VIDEO", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("VIDEO", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/INIFile.cs ================================================ using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet=CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/Patch/mInput.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mInput { /// /// Overriding original button detection by the game with our own controls /// [HarmonyPatch(typeof(Input), "GetButton")] class GzetButton { static bool Prefix(string buttonName, ref bool __result) { if (buttonName.Equals("Fire1") && DemulShooter_Plugin.EnableInputHack) { __result = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger); return false; } return true; } } /// /// Overriding original button detection by the game with our own controls /// [HarmonyPatch(typeof(Input), "GetButtonDown")] class GetButtonDown { static bool Prefix(string buttonName, ref bool __result) { if (buttonName.Equals("Fire1") && DemulShooter_Plugin.EnableInputHack) { __result = DemulShooter_Plugin.PluginControllers[0].GetButtonDown(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger); //if (__result) // DemulShooter_Plugin.MyLogger.LogWarning("Trigger down"); return false; } return true; } } /// /// Overriding original button detection by the game with our own controls /// [HarmonyPatch(typeof(Input), "GetButtonUp")] class GetButtonUp { static bool Prefix(string buttonName, ref bool __result) { if (buttonName.Equals("Fire1") && DemulShooter_Plugin.EnableInputHack) { __result = DemulShooter_Plugin.PluginControllers[0].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger); //if (__result) // DemulShooter_Plugin.MyLogger.LogWarning("Trigger up"); return false; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/Patch/mPvzCore.cs ================================================ using System.Reflection; using HarmonyLib; using Pvz; using UnityEngine; using System; namespace BepInEx_DemulShooter_Plugin { class mPvzCore { /// /// Force the game position data to be filled with our own /// [HarmonyPatch(typeof(PvzCore), "UpdateInputPosition")] class UpdateInputPosition { static void Postfix(Pvz.PvzCore __instance) { if (DemulShooter_Plugin.EnableInputHack) { PropertyInfo[] Pis = __instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); foreach (PropertyInfo Pi in Pis) { if (Pi.Name.Equals("inputPosition")) { Pi.SetValue(__instance, new Vector3(DemulShooter_Plugin.PluginControllers[0].Axis_X, DemulShooter_Plugin.PluginControllers[0].Axis_Y), null); break; } } foreach (PropertyInfo Pi in Pis) { if (Pi.Name.Equals("normalizedInputPosition")) { Pi.SetValue(__instance, new Vector3(__instance.inputPosition.x / __instance.screenSize.x, __instance.inputPosition.y / __instance.screenSize.y), null); break; } } } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/Patch/mPvzCrosshair.cs ================================================ using HarmonyLib; using Pvz; namespace BepInEx_DemulShooter_Plugin.Patch { class mPvzCrosshair { /// /// Hiding crosshair if needed /// [HarmonyPatch(typeof(PvzCrosshair), "OnGUI")] class OnGUI { static bool Prefix(ref bool ___show) { if (!DemulShooter_Plugin.CrossHairVisibility) ___show = false; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("PVZ_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PVZ_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/PvZ_BepInEx_DemulShooter_Plugin.ini ================================================ [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 //Folowing command can work OR cause issue with Unity, especially if .NET 4.0 is required and Mono may miss some functionality _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); //Following way is more compatible in case of trouble //FieldInfo[] fiBuffer = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); //_DataFields = new FieldInfo[fiBuffer.Length]; //string[] fiNames = new string[fiBuffer.Length]; //for (int i = 0; i < fiNames.Length; i++) //{ // fiNames[i] = fiBuffer[i].Name; //} //Array.Sort(fiNames); //for (int i = 0; i < _DataFields.Length; i++) //{ // foreach (FieldInfo fi in fiBuffer) // { // if (fi.Name == fiNames[i]) // { // _DataFields[i] = fi; // break; // } // } //} foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().Name + ".Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError(this.GetType().Name + ".Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // _Writer.Write((bool)o); //else if (ReferenceEquals(t, typeof(byte))) // _Writer.Write((byte)o); //else if (ReferenceEquals(t, typeof(char))) // _Writer.Write((char)o); //else if (ReferenceEquals(t, typeof(decimal))) // _Writer.Write((decimal)o); //else if (ReferenceEquals(t, typeof(double))) // _Writer.Write((double)o); //else if (ReferenceEquals(t, typeof(float))) // _Writer.Write((float)o); ///*else if (ReferenceEquals(t, typeof(nint))) // _Writer.Write((nint)o); //else if (ReferenceEquals(t, typeof(nuint))) // _Writer.Write((nuint)o);*/ //else if (ReferenceEquals(t, typeof(long))) // _Writer.Write((long)o); //else if (ReferenceEquals(t, typeof(sbyte))) // _Writer.Write((sbyte)o); //else if (ReferenceEquals(t, typeof(short))) // _Writer.Write((short)o); //else if (ReferenceEquals(t, typeof(uint))) // _Writer.Write((uint)o); //else if (ReferenceEquals(t, typeof(ulong))) // _Writer.Write((ulong)o); //else if (ReferenceEquals(t, typeof(ushort))) // _Writer.Write((ushort)o); //else if (ReferenceEquals(t, typeof(int))) // _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { /***This method may not work with Mono using .Net4.0 as op_equality may be missing depending on Mono core ***/ if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; /*** Use this method if .Net4.0 is required, but the previous one crash becaus eof missing op_equality ***/ //if (ReferenceEquals(t, typeof(bool))) // return _Reader.ReadBoolean(); //else if (ReferenceEquals(t, typeof(byte))) // return _Reader.ReadByte(); //else if (ReferenceEquals(t, typeof(char))) // return _Reader.ReadChar(); //else if (ReferenceEquals(t, typeof(decimal))) // return _Reader.ReadDecimal(); //else if (ReferenceEquals(t, typeof(double))) // return _Reader.ReadDouble(); //else if (ReferenceEquals(t, typeof(float))) // return _Reader.ReadSingle(); ///*else if (ReferenceEquals(t, typeof(nint))) // return _Reader. //else if (ReferenceEquals(t, typeof(nuint))) // return _Reader.*/ //else if (ReferenceEquals(t, typeof(long))) // return _Reader.ReadInt64(); //else if (ReferenceEquals(t, typeof(sbyte))) // return _Reader.ReadSByte(); //else if (ReferenceEquals(t, typeof(short))) // return _Reader.ReadInt16(); //else if (ReferenceEquals(t, typeof(uint))) // return _Reader.ReadUInt32(); //else if (ReferenceEquals(t, typeof(ulong))) // return _Reader.ReadUInt64(); //else if (ReferenceEquals(t, typeof(ushort))) // return _Reader.ReadUInt16(); //else if (ReferenceEquals(t, typeof(int))) // return _Reader.ReadInt32(); //else // return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Ammo = null; public int[] Credits = null; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/UnityPlugin_BepInEx_PVZ.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin PVZ_BepInEx_DemulShooter_Plugin v3.5 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_PVZ/UnityPlugin_BepInEx_PVZ.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_PVZ", "UnityPlugin_BepInEx_PVZ.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Demulshooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using BepInEx; using HarmonyLib; using SBK; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.rabbidshollywood"; public const String pluginName = "RabbidsHollywood_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "RabbidsHollywood_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 4; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); public static PluginControllerButton MenuUp_Key = new PluginControllerButton((int) KeyCode.UpArrow); public static PluginControllerButton MenuDown_Key = new PluginControllerButton((int) KeyCode.DownArrow); public static PluginControllerButton MenuSelect_Key = new PluginControllerButton((int) KeyCode.Return); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public static string GameLanguage = "EN"; public static bool IsDemoMode = false; public static Type EnumType = null; public static bool SaveToGameFolder = false; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadBoolValue("SYSTEM", "SAVE_TO_GAME_FOLDER", ref SaveToGameFolder); Plugin_IniFile.IniReadIntValue("VIDEO", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("VIDEO", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("VIDEO", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("VIDEO", "FORCE_RESOLUTION", ref ForceResolution); //Language GameLanguage = Plugin_IniFile.IniReadValue("System", " LANGUAGE"); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); Player[] playerList = Singleton.Instance.m_PlayerList; //Logger.LogMessage("DemulSHooter_Plugin.Update() => PlayerList.Length = : " + playerList.Length.ToString()); for (int i = 0; i < playerList.Length; i++) { //Logger.LogMessage("DemulSHooter_Plugin.Update() => PlayerList[" + i + "].m_IsPlaying = : " + playerList[i].m_IsPlaying); //Logger.LogMessage("DemulSHooter_Plugin.Update() => PlayerList[" + i + "].Alive = : " + playerList[i].Alive); if (playerList[i].m_IsPlaying && playerList[i].Alive) { //Logger.LogMessage("DemulSHooter_Plugin.Update() => PlayerList[" + i + "] Alive & Playing, Health=" + playerList[i].m_Health); if (playerList[i].m_Health >= 0) OutputData.Life[i] = playerList[i].m_Health; } else { OutputData.Life[i] = 0; } OutputData.Credits[i] = Singleton.Instance.GetCoinCount((ID)i); } //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/INIFile.cs ================================================ using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet=CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/NAEMGFFONID.cs ================================================ using System; using System.IO; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mNAEMGFFONID { [HarmonyPatch(typeof(NAEMGFFONID), "NEBJKGCFIGH")] class NEBJKGCFIGH { static bool Prefix(Stream JHKDDOIIIDL, byte[] HHALPJMFHLH, byte[] FPKDJCDHHDJ, ref byte[] __result) { DemulShooter_Plugin.MyLogger.LogMessage("NAEMGFFONID.NEBJKGCFIGH()"); if (JHKDDOIIIDL == null) { throw new ArgumentNullException("i_PlainStrm"); } if (HHALPJMFHLH == null || HHALPJMFHLH.Length == 0) { throw new ArgumentNullException("i_Key"); } if (FPKDJCDHHDJ == null || FPKDJCDHHDJ.Length == 0) { throw new ArgumentNullException("i_IV"); } if (JHKDDOIIIDL.Length <= 0L) { __result = new byte[0]; return false; } byte[] array = new byte[JHKDDOIIIDL.Length]; JHKDDOIIIDL.Read(array, 0, (int)JHKDDOIIIDL.Length); __result = array; return false; } } [HarmonyPatch(typeof(NAEMGFFONID), "IPMDECODIIL")] class IPMDECODIIL { static bool Prefix(Stream ENOLCGLNMAP, byte[] HHALPJMFHLH, byte[] FPKDJCDHHDJ, ref byte[] __result, byte[] ___ACLKDGFAFKE) { DemulShooter_Plugin.MyLogger.LogMessage("NAEMGFFONID.IPMDECODIIL()"); if (ENOLCGLNMAP == null) { throw new ArgumentNullException("i_CipherStrm"); } if (HHALPJMFHLH == null || HHALPJMFHLH.Length == 0) { throw new ArgumentNullException("Key"); } if (___ACLKDGFAFKE == null || ___ACLKDGFAFKE.Length == 0) { throw new ArgumentNullException("IV"); } if (ENOLCGLNMAP.Length <= 0L) { __result= new byte[0]; return false; } byte[] array = new byte[ENOLCGLNMAP.Length]; ENOLCGLNMAP.Read(array, 0, (int)ENOLCGLNMAP.Length); __result = array; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/SBK/Matrix/mMatrixManager.cs ================================================ using System.Reflection; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mMatrixManager { /// /// Method is called in MatrixManager.Awake() /// Forcing return SBK.Matrix.MatrixManager.MPLBPAMKFJP.MATRIX_NO_ERROR, but the enum in internal and we can't access the type /// So, returning the enum value (= 0) /// [HarmonyPatch] class KDLGNPHHMKI { static MethodBase TargetMethod() { foreach (MethodInfo mi in AccessTools.TypeByName("SBK.Matrix.MatrixManager").GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) { if (mi.Name.Equals("KDLGNPHHMKI")) { return mi; } } return null; } static bool Prefix(ref object __result) { DemulShooter_Plugin.MyLogger.LogWarning("SBK.MatrixManager.KDLGNPHHMKI()"); //DemulShooter_Plugin.PrintStackTrace(); __result = 0; return false; } } ///Ducon also patched: ///SBK.Matrix.MatrixManager.EGIEJAEHODM() ///SBK.Matrix.MatrixManager.KFPJOBFGLKO() ///SBK.Matrix.MatrixManager.FDEMJBICGOM() ///SBK.Matrix.MatrixManager.BPMOIOJDHDD() ///SBK.Matrix.MatrixManager.MBCKFLLBDFN() ///SBK.Matrix.MatrixManager.EOMEICJLBHH() ///SBK.Matrix.MatrixManager.MGILPFJAAEE() ///SBK.Matrix.MatrixManager.GEAIHJMFPEJ() } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/SBK/Sixensecore/mDevice.cs ================================================  using System.Reflection; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mDevice { class sxCoreGetNumConnectedTrackedDevices { static MethodBase TargetMethod() { MethodInfo[] mis = AccessTools.TypeByName("Sixensecore.Plugin.Device").GetMethods(BindingFlags.Public | BindingFlags.Static); foreach (MethodInfo mi in mis) { if (mi.Name.Equals("sxCoreGetNumConnectedTrackedDevices")) return mi; } return null; } static bool Prefix(ref uint __result) { DemulShooter_Plugin.MyLogger.LogMessage("Sixensecore.Device.Plugin.sxCoreGetNumConnectedTrackedDevices"); __result = 4; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/SBK/mApplicationManager.cs ================================================ using System; using HarmonyLib; using Microsoft.Win32; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mApplicationManager { /// /// The original function restarts the game if the fullscreen is disabled /// And it's overriding FullScreen option in the registry, forcing it to 1 again /// [HarmonyPatch(typeof(SBK.ApplicationManager), "Awake")] class Awake { static void Postfix() { DemulShooter_Plugin.MyLogger.LogMessage("mApplicationManager.Awake(), Screen=" + Screen.width + ", " + Screen.height + "," + Screen.fullScreen); if (DemulShooter_Plugin.ForceResolution) { Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); } else { try { RegistryKey key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Sarbakan\\RabbidsShooter"); if (key != null) { System.Object o = key.GetValue("Screenmanager Is Fullscreen mode_h3981298716"); if (o != null) { if ((int)o == 1) Screen.SetResolution(Screen.width, Screen.height, true); else if ((int)o == 0) Screen.SetResolution(Screen.width, Screen.height, false); DemulShooter_Plugin.MyLogger.LogMessage("mApplicationManager.Awake(), FullScreen now is " + Screen.fullScreen); } else { DemulShooter_Plugin.MyLogger.LogMessage("mApplicationManager.Awake() : Can't find Registry Value : HK_CURRENT_USER\\SOFTWARE\\Sarbakan\\RabbidsShooter\\Screenmanager Is Fullscreen mode_h3981298716"); } } else { DemulShooter_Plugin.MyLogger.LogMessage("mApplicationManager.Awake() : Can't find Registry Key : HK_CURRENT_USER\\SOFTWARE\\Sarbakan\\RabbidsShooter"); } } catch (Exception Ex) { DemulShooter_Plugin.MyLogger.LogMessage("mApplicationManager.Awake() : Error reading fullscreen registry value. " + Ex.Message.ToString()); } } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mArcadeGamePlayManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mArcadeGamePlayManager { [HarmonyPatch(typeof(ArcadeGameplayManager), "LoadDemoMode")] class LoadDemoMode { static bool Prefix(WorldManager.WorldIndex i_World) { //DemulShooter_Plugin.MyLogger.LogMessage("mArcadeGameplayManager.LoadDemoMode()"); DemulShooter_Plugin.IsDemoMode = true; return true; } } [HarmonyPatch(typeof(ArcadeGameplayManager), "OnDemoStateExit")] class OnDemoStateExit { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mArcadeGameplayManager.OnDemoStateExit()"); DemulShooter_Plugin.IsDemoMode = false; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mArcadeManager.cs ================================================ using HarmonyLib; using SixenseCore; namespace BepInEx_DemulShooter_Plugin { class mArcadeManager { [HarmonyPatch(typeof(ArcadeManager), "CheckIOBoard")] class CheckIOBoard { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.CheckIOBoard()"); return false; } } [HarmonyPatch(typeof(ArcadeManager), "GunFatalError")] class GunFatalError { static bool Prefix(LocID i_Loc, Hardware i_Hardware, int i_Error) { DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.GunFatalError()"); return false; } } [HarmonyPatch(typeof(ArcadeManager), "GunMalfunctionCheck")] class GunMalfunctionCheck { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.GunMalfunctionCheck()"); return false; } } /// /// No ! /// [HarmonyPatch(typeof(ArcadeManager), "KillGame")] class KillGame { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.KillGame()"); return false; } } /// /// Removes the error screen /// [HarmonyPatch(typeof(ArcadeManager), "PushErrorPopup")] class PushErrorPopup { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.PushErrorPopup()"); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mCrosshairWindow.cs ================================================ using System; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mCrosshairWindow { [HarmonyPatch(typeof(CrosshairWindow), "CrosshairMove")] class CrosshairMove { /// /// Original function is calculating the pixel position of the Crosshair with what seems to be a fixed range [1920x1080] /// This is creating an offset between the float real position of the aim [0, 1] and the crosshair drawing /// /// static bool Prefix(Vector3 i_Viewport, ID i_PlayerID, CrosshairWindow __instance) { //If in Attractmode, we let the game handle the crosshair without messing with it if (DemulShooter_Plugin.IsDemoMode) return true; //UnityEngine.Debug.Log("mCrosshairWindow.CrosshairMove(), i_PlayerID=" + i_PlayerID.ToString() + ", i_Viewport=" + i_Viewport.ToString()); if (SBK.SceneSingleton.Exists()) { for (int i = 0; i < __instance.m_Crosshairs.Length; i++) { if (__instance.m_Crosshairs[i].m_ID == i_PlayerID && SBK.Singleton.Instance.GetPlayerByID(i_PlayerID).Alive) { float fRatio = (float)Screen.width / (float)Screen.height; float fMaxY = 1080.0f; float fMaxX = fRatio * fMaxY; Vector3 v = new Vector3(); v.x = DemulShooter_Plugin.PluginControllers[(int)i_PlayerID].Axis_X / (float)Screen.width * fMaxX; v.y = DemulShooter_Plugin.PluginControllers[(int)i_PlayerID].Axis_Y / (float)Screen.height * fMaxY; //UnityEngine.Debug.LogError("mCrosshairWindow.CrosshairMove() => v = " + v.ToString()); __instance.m_Crosshairs[i].m_CrosshairTr.anchoredPosition = v; break; } } return false; } return false; } /// /// Using the postfix to hide crosshair from screen if option is activated /// /// static void Postfix(Vector3 i_Viewport, ID i_PlayerID, CrosshairWindow __instance) { //If in Attractmode, we let the game handle the crosshair without messing with it if (DemulShooter_Plugin.IsDemoMode || SBK.Singleton.Instance.CurrentState == ArcadeGameplayManager.State.ChooseWorld) return; if (!DemulShooter_Plugin.CrossHairVisibility) { for (int i = 0; i < __instance.m_Crosshairs.Length; i++) { __instance.m_Crosshairs[i].m_CrosshairTr.anchoredPosition = new Vector2(-100.0f, -100.0f); } } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mGunDetection.cs ================================================ using System; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mGunDetection { /// /// This funtion is used to run system command, mostly for gun flashing BUT ALSO TO REBOOT COMPUTER (!!) if the fullscreen is disabled /// So...Nope ! /// [HarmonyPatch(typeof(GunDetection), "ExternCall")] class ExternCall { static bool Prefix(ref bool __result, string ao_Command, string ao_Arguments, string[] ao_Outputs = null) { //DemulShooter_Plugin.MyLogger.LogMessage("mGunDetection.ExternCall() : ao_Command=" + ao_Command + ", ao_Arguments=" + ao_Arguments + ", ao_Outputs=" + ao_Outputs); __result = true; return false; } } [HarmonyPatch(typeof(GunDetection), "IsBaseFound")] class IsBaseFound { static bool Prefix(ref bool __result) { __result = true; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mKaboomAdrioFxPlusFourFeeders.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mKaboomAdrioFxPlusFourFeeders { /// /// Prevent the Lib to search and write to some devices on some computers /// May cause the game to hang /// [HarmonyPatch(typeof(KaboomAdrioFxPlusFourFeeders), "_Initialize")] class _Initialize { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("KaboomAdrioFxPlusFourFeeders._Initialize()"); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mKaboomManager.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; using KaboomOutput; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin { class mKaboomManager { /// /// Use this funtion to generate DemulShooter custom recoil output /// [HarmonyPatch(typeof(KaboomManager), "StartMotorPwm")] class StartMotorPwm { static bool Prefix(KaboomOutput.MotorPwmId MotorId, byte un8PwmDutyCycle, ushort iMsDuration = 144) { //UnityEngine.Debug.Log("KaboomManager.StartMotorPwm() : MotorId=" + MotorId.ToString() + ", un8PwmDutyCycle=" + un8PwmDutyCycle + ", iMsDuration=" + iMsDuration); if (MotorId.ToString().Contains("P1")) DemulShooter_Plugin.OutputData.Recoil[0] = 1; else if (MotorId.ToString().Contains("P2")) DemulShooter_Plugin.OutputData.Recoil[1] = 1; else if (MotorId.ToString().Contains("P3")) DemulShooter_Plugin.OutputData.Recoil[2] = 1; else if (MotorId.ToString().Contains("P4")) DemulShooter_Plugin.OutputData.Recoil[3] = 1; return true; } } /// /// Use this to intercept Light ON status /// [HarmonyPatch(typeof(KaboomManager), "SetPlayerLight")] class SetPlayerLight { static bool Prefix(OutputPlayerLight i_Mask) { //UnityEngine.Debug.Log("KaboomManager.SetPlayerLight() : i_Mask=" + i_Mask.ToString()); if (i_Mask == OutputPlayerLight.OUTPUT_P1) DemulShooter_Plugin.OutputData.StartLamp[0] = 1; else if (i_Mask == OutputPlayerLight.OUTPUT_P2) DemulShooter_Plugin.OutputData.StartLamp[1] = 1; else if (i_Mask == OutputPlayerLight.OUTPUT_P3) DemulShooter_Plugin.OutputData.StartLamp[2] = 1; else if (i_Mask == OutputPlayerLight.OUTPUT_P4) DemulShooter_Plugin.OutputData.StartLamp[3] = 1; else if (i_Mask == OutputPlayerLight.OUTPUT_ALL) { DemulShooter_Plugin.OutputData.StartLamp[0] = 1; DemulShooter_Plugin.OutputData.StartLamp[1] = 1; DemulShooter_Plugin.OutputData.StartLamp[2] = 1; DemulShooter_Plugin.OutputData.StartLamp[3] = 1; } return true; } } /// /// Use this to intercept Light OFF status /// [HarmonyPatch(typeof(KaboomManager), "ClearPlayerLight")] class ClearPlayerLight { static bool Prefix(OutputPlayerLight i_Mask) { //UnityEngine.Debug.Log("KaboomManager.ClearPlayerLight() : i_Mask=" + i_Mask.ToString()); if (i_Mask == OutputPlayerLight.OUTPUT_P1) DemulShooter_Plugin.OutputData.StartLamp[0] = 0; else if (i_Mask == OutputPlayerLight.OUTPUT_P2) DemulShooter_Plugin.OutputData.StartLamp[1] = 0; else if (i_Mask == OutputPlayerLight.OUTPUT_P3) DemulShooter_Plugin.OutputData.StartLamp[2] = 0; else if (i_Mask == OutputPlayerLight.OUTPUT_P4) DemulShooter_Plugin.OutputData.StartLamp[3] = 0; else if (i_Mask == OutputPlayerLight.OUTPUT_ALL) { DemulShooter_Plugin.OutputData.StartLamp[0] = 0; DemulShooter_Plugin.OutputData.StartLamp[1] = 0; DemulShooter_Plugin.OutputData.StartLamp[2] = 0; DemulShooter_Plugin.OutputData.StartLamp[3] = 0; } return true; } } /// /// Changing some default keyboard controls /// [HarmonyPatch(typeof(KaboomManager), "Update")] class Update { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (int i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("GetKey")) { if (codes[i - 1].opcode == OpCodes.Ldc_I4_S || codes[i - 1].opcode == OpCodes.Ldc_I4) { switch (codes[i - 1].operand.ToString()) { case "27": codes[i - 1].operand = DemulShooter_Plugin.Exit_Key.KeyCode; break; case "99": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode; break; case "118": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode; break; case "98": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[2].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode; break; case "110": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[3].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode; break; case "111": codes[i - 1].operand = DemulShooter_Plugin.Test_Key.KeyCode; break; case "257": codes[i - 1].operand = DemulShooter_Plugin.MenuUp_Key.KeyCode; break; case "258": codes[i - 1].operand = DemulShooter_Plugin.MenuSelect_Key.KeyCode; break; case "259": codes[i - 1].operand = DemulShooter_Plugin.MenuDown_Key.KeyCode; break; case "103": codes[i - 1].operand = 0; break; case "282": codes[i - 1].operand = 0; break; default: break; } } } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mLEDManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mLEDManager { /// /// Disable COM polling to search for LEDs /// [HarmonyPatch(typeof(LEDManager), "Awake")] class Awake { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mLanguageLocalizer.cs ================================================ using System; using System.Collections.Generic; using HarmonyLib; using static SBK.Attribute.TimeSpanAttribute; namespace BepInEx_DemulShooter_Plugin { class mLanguageLocalizer { /// /// Available Languages : EN / FR / JA / ZH /// [HarmonyPatch(typeof(SBK.Localization.LanguageLocalizer), "SwitchLanguage")] class SwitchLanguage { static bool Prefix(ref SBK.Localization.LanguageCode i_Code, List ___m_AvailableLanguages) { DemulShooter_Plugin.MyLogger.LogMessage("mLanguageLocalizer.SwitchLanguage(), i_Code=" + i_Code); DemulShooter_Plugin.MyLogger.LogMessage("mLanguageLocalizer.SwitchLanguage(), Available Languages :"); foreach (String s in ___m_AvailableLanguages) { DemulShooter_Plugin.MyLogger.LogMessage(s); } DemulShooter_Plugin.MyLogger.LogMessage("mLanguageLocalizer.SwitchLanguage() : Trying to read custom config file..."); switch (DemulShooter_Plugin.GameLanguage) { case "EN": { i_Code = SBK.Localization.LanguageCode.EN; } break; case "FR": { i_Code = SBK.Localization.LanguageCode.FR; } break; case "JA": { i_Code = SBK.Localization.LanguageCode.JA; } break; case "ZH": { i_Code = SBK.Localization.LanguageCode.ZH; } break; default: { i_Code = SBK.Localization.LanguageCode.EN; } break; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mLevelSelectorWindow.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mLevelSelectorWindow { [HarmonyPatch(typeof(LevelSelectorWindow), "PlayerPressTrigger")] class PlayerPressTrigger { static bool Prefix(Vector3 i_Pos, ID i_Player) { //DemulShooter_Plugin.MyLogger.LogMessage("mLevelSelectorWindow.PlayerPressTrigger() : i_Player=" + i_Player.ToString() + ", v=" + i_Pos.ToString()); return true; } } [HarmonyPatch(typeof(LevelSelectorWindow), "InitPanels")] class InitPanels { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mLevelSelectorWindow.InitPanels()"); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mNewInputManager.cs ================================================ using System; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mNewInputManager { /// /// The original function is lopp-called when gun are not detected to mouse-control P1 and P2 /// By overriding it, we can inject data for all 4 players inside /// [HarmonyPatch(typeof(NewInputManager), "MouseControl")] class MouseControl { static bool Prefix (bool ___m_AllInputEnable, bool ___m_AreCrosshairsEnabled, bool[] ___m_HoldTrigger, NewInputManager __instance) { //UnityEngine.Debug.Log("mNewInputManager.MouseControl()"); if (___m_AllInputEnable) { Vector3 i_Arg = new Vector3(); for (int i = 0; i < 4; i++) { if (SBK.Singleton.Instance.GunEnabled(i)) { i_Arg.x = DemulShooter_Plugin.PluginControllers[i].Axis_X / (float)Screen.width; i_Arg.y = DemulShooter_Plugin.PluginControllers[i].Axis_Y / (float)Screen.height; EventManager.TriggerEvent(NewInputManager.BaseMessage.PlayerCrosshairMove.ToString(), i_Arg, (ID)i); } if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { //Rha_Plugin.MyLogger.LogMessage("mNewInputManager.MouseControl(" + i + ") => Trigger Pushed"); EventManager.TriggerEvent(NewInputManager.BaseMessage.PlayerPressTrigger.ToString(), i_Arg, (ID)i); if (___m_AreCrosshairsEnabled) { EventManager.TriggerEvent(NewInputManager.BaseMessage.PlayerShoot.ToString(), i_Arg, (ID)i); } else if (!___m_HoldTrigger[i]) { ___m_HoldTrigger[i] = true; if (SBK.Singleton.Instance.CurrentState == ArcadeGameplayManager.State.PlayState) { SBK.Singleton.Instance.Play(SBK.Audio.AudioID_Global.ui_cantshoot, null); } } } else if (DemulShooter_Plugin.PluginControllers[i].GetButtonUp(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { //Rha_Plugin.MyLogger.LogMessage("mNewInputManager.MouseControl(" + i + ") => Trigger Released"); if (___m_AreCrosshairsEnabled) { EventManager.TriggerEvent(NewInputManager.BaseMessage.PlayerTriggerRelease.ToString(), (ID)i); } ___m_HoldTrigger[i] = false; } } } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mPlayerManager.cs ================================================ using System; using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mPlayerManager { /// /// Getting Damaged flag /// [HarmonyPatch(typeof(PlayerManager), "GetDamage")] class GetDamage { static bool Prefix(BaseDamage.Info i_Info, ID i_ID) { DemulShooter_Plugin.MyLogger.LogMessage("PlayerManager.GetDamage() : i_ID=" + i_ID.ToString()); if ((int)i_ID < 4) { DemulShooter_Plugin.OutputData.Damaged[(int)i_ID] = 1; } return true; } } /// /// This one uses 2 keys [G] and [K] for god mode and bots /// [HarmonyPatch(typeof(PlayerManager), "Update")] class Update { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (int i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("GetKeyDown")) { if (codes[i - 1].opcode == OpCodes.Ldc_I4_S && codes[i - 1].operand.ToString().Equals("103")) codes[i - 1].operand = 0; if (codes[i - 1].opcode == OpCodes.Ldc_I4_S && codes[i - 1].operand.ToString().Equals("107")) codes[i - 1].operand = 0; } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mPlayerPrefs.cs ================================================ using System; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mPlayerPrefs { [HarmonyPatch(typeof(PlayerPrefs), "DeleteKey")] class DeleteKey { /// /// Filter Registry Key removing to not remove FullScreen option /// /// static bool Prefix(String key) { DemulShooter_Plugin.MyLogger.LogMessage("mPlayerPrefs.DeleteKey(), key=" + key); if (key.Contains("Screenmanager Is Fullscreen")) return false; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mSBKInputManager.cs ================================================ using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mSBKInputManager { /// /// change those keys too ?? (right, left, down, select ???) /// [HarmonyPatch(typeof(SBKInputManager), "GetRightKeyDown")] class GetRightKeyDown { static void Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("SBKInputManager.GetRightKeyDown()"); } } /// /// Changing default Start buttons Keys /// [HarmonyPatch(typeof(SBKInputManager), "GetPlayerKeyDown")] class Update { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (int i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("GetKeyDown")) { if (codes[i - 1].opcode == OpCodes.Ldc_I4) { switch (codes[i - 1].operand.ToString()) { case "257": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; case "259": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; case "263": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[2].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; case "265": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[3].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; default: break; } } } } return codes; } } [HarmonyPatch(typeof(SBKInputManager), "GetActivePlayerKeyDown")] class GetActivePlayerKeyDown { static IEnumerable Transpiler(IEnumerable instructions) { var codes = new List(instructions); for (int i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("GetKeyDown")) { if (codes[i - 1].opcode == OpCodes.Ldc_I4) { switch (codes[i - 1].operand.ToString()) { case "257": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; case "259": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; case "263": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[2].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; case "265": codes[i - 1].operand = DemulShooter_Plugin.PluginControllers[3].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode; break; default: break; } } } } return codes; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mWorldManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mWorldManager { /// /// [L] is supposed to bring the world manager screen /// [HarmonyPatch(typeof(WorldManager), "Update")] class Update { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Patch/mWorldSelectorWindow.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mWorldSelectorWindow { [HarmonyPatch(typeof(WorldSelectorWindow), "ManageMouseOver")] class ManageMouseOver { static bool Prefix() { //UnityEngine.Debug.LogError("mWorldSelectorWindow.ManageMouseOver()"); return false; } } /// /// Function called at the level select /// Keep it in case of need to change values for crosshair/selection offset /// [HarmonyPatch(typeof(WorldSelectorWindow), "PlayerPressTrigger")] class PlayerPressTrigger { static bool Prefix(Vector3 i_Pos, ID i_Player) { //DemulShooter_Plugin.MyLogger.LogMessage("mWorldSelectorWindow.PlayerPressTrigger() : i_Player=" + i_Player.ToString() + ", v=" + i_Pos.ToString()); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("RabbidsHollywood_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("RabbidsHollywood_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/RabbidsHollywood_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [SYSTEM] LANGUAGE=FR ;Default data folder is in "%userProfile%\AppData\LocalLow\AppData\LocalLow\Sarbakan\RabbidsShooter\" ;Set this to "1" to save data in "\NVRAM\" folder from game root path. SAVE_TO_GAME_FOLDER=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 P3_START=51 P4_START=52 P1_COIN=53 P2_COIN=54 P3_COIN=55 P4_COIN=56 MENU_UP=273 MENU_DOWN=274 MENU_SELECT=13 TEST=48 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage("TcpOutputData.Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError("TcpOutputData.Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] StartLamp = null; public byte[] Recoil = null; public byte[] Damaged = null; public float[] Life = null; public int[] Ammo = null; public int[] Credits = null; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.CoreModule.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/UnityPlugin_BepInEx_RabbidsHollywood.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin RabbidsHollywood_BepInEx_DemulShooter_Plugin v3.5 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.CoreModule.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RHA/UnityPlugin_BepInEx_RabbidsHollywood.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_RabbidsHollywood", "UnityPlugin_BepInEx_RabbidsHollywood.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/DemulShooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.nerfarcade"; public const String pluginName = "NerfArcade_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "NerfArcade_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); public static PluginControllerButton Service_Key = new PluginControllerButton((int) KeyCode.Alpha9); public static PluginControllerButton VolumeUp_Key = new PluginControllerButton((int) KeyCode.UpArrow); public static PluginControllerButton VolumeDown_Key = new PluginControllerButton((int) KeyCode.DownArrow); public static PluginControllerButton DBV_Key = new PluginControllerButton((int) KeyCode.Alpha7); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public static int CabTemplate = 0; private const float ORIGINAL_WIDTH = 1920.0f; private const float ORIGINAL_HEIGHT = 1080.0f; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("System", "CAB_TEMPLATE", ref CabTemplate); Plugin_IniFile.IniReadIntValue("Video", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("Video", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("Video", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("Video", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); Service_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "SERVICE")); VolumeUp_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "VOLUME_UP")); VolumeDown_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "VOLUME_DOWN")); DBV_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "DBV")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() { } public void Update() { //if (ForceResolution) // ScaleDisplay(); //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); Service_Key.SetButton(Input.GetKey((KeyCode)Service_Key.KeyCode)); VolumeUp_Key.SetButton(Input.GetKey((KeyCode)VolumeUp_Key.KeyCode)); VolumeDown_Key.SetButton(Input.GetKey((KeyCode)VolumeDown_Key.KeyCode)); DBV_Key.SetButton(Input.GetKey((KeyCode)DBV_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Get Lamp status as Int16 : 0-100% OutputData.P1_Lmp_Start = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P1Start) * 100.0f); OutputData.P1_Lmp_SeatPuck = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P1SeatPuck) * 100.0f); OutputData.P1_Lmp_SeatMarquee = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P1SeatMarquee) * 100.0f); OutputData.P1_Lmp_SeatRear_R = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P1SeatSpeaker_Red) * 100.0f); OutputData.P1_Lmp_SeatRear_O = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P1SeatSpeaker_Green) * 100.0f); OutputData.P1_Lmp_SeatRear_B = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P1SeatSpeaker_Blue) * 100.0f); OutputData.P2_Lmp_Start = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P2Start) * 100.0f); OutputData.P2_Lmp_SeatPuck = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P2SeatPuck) * 100.0f); OutputData.P2_Lmp_SeatMarquee = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P2SeatMarquee) * 100.0f); OutputData.P2_Lmp_SeatRear_R = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P2SeatSpeaker_Red) * 100.0f); OutputData.P2_Lmp_SeatRear_O = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P2SeatSpeaker_Green) * 100.0f); OutputData.P2_Lmp_SeatRear_B = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.P2SeatSpeaker_Blue) * 100.0f); OutputData.Cab_Lmp_R = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.SeatCabRearTMolding_Red) * 100.0f); OutputData.Cab_Lmp_G = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.SeatCabRearTMolding_Green) * 100.0f); OutputData.Cab_Lmp_B = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.SeatCabRearTMolding_Blue) * 100.0f); OutputData.Cab_Lmp_RearSeat = (UInt16)(SingletonMonoBehaviour.Instance.GetLastLightValue(CabinetLight.SeatCabDownLighting) * 100.0f); OutputData.Credits[0] = (uint)AuditManager.CoinAudits.m_PlayerCoinAudits[0].m_TotalUnspent; OutputData.Credits[1] = (uint)AuditManager.CoinAudits.m_PlayerCoinAudits[1].m_TotalUnspent; //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// If the game is not developped to be scaled with window size, using that function every frame /// will check the root canvas and, if the scale setting is not set, will scale it to the current window size /// private void ScaleDisplay() { float fScale = (float)Screen.width / ORIGINAL_WIDTH; Canvas[] allObjects = UnityEngine.Object.FindObjectsOfType(); foreach (Canvas c in allObjects) { if (!c.name.Equals("CANVAS_Diagnostic")) { //MyLogger.LogMessage("Canvas Name: " + c.name + " , Changed scale to " + fScale); c.scaleFactor = fScale; } } } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/NerfArcade_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [System] ;0 = USA_CARD_SWIPE ;1 = USA_COIN ;2 = CEC_COIN ;3 = CEC_CARD ;4 = DB_CARD ;5 = UK ;6 = EUROPE ;7 = JAPAN ;8 = CHINA, ;9 = AUSTRALIA CAB_TEMPLATE=6 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 P1_COIN=53 P2_COIN=54 VOLUME_UP=273 VOLUME_DOWN=274 DBV=55 TEST=48 EXIT=27 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Patch/mGame.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mGame { /// /// Remove mouse cursor /// [HarmonyPatch(typeof(Game), "Awake")] class Awake { static void Postfix() { //DemulShooter_Plugin.MyLogger.LogMessage("Game.Awake()"); UnityEngine.Cursor.visible = false; } } /// /// Force periodic reboot to always OFF /// [HarmonyPatch(typeof(Game), "ShouldPeriodicReboot")] class ShouldPeriodicReboot { static bool Prefix(ref bool __result) { //DemulShooter_Plugin.MyLogger.LogMessage("Game.ShouldPeriodicReboot()"); __result = false; return false; } } /// /// Force periodic reboot to always OFF /// //[HarmonyPatch(typeof(Game), "Start")] //class Start //{ // static bool Prefix() // { // //DemulShooter_Plugin.MyLogger.LogMessage("Game.Start()"); // if (DemulShooter_Plugin.ForceResolution) // Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); // return true; // } //} } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Patch/mIOManager.cs ================================================ using System.Reflection; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mIOManager { /// /// Force not using IO board /// [HarmonyPatch(typeof(IOManager), "set_m_UseRio")] class set_m_UseRio { static bool Prefix(ref bool value) { DemulShooter_Plugin.MyLogger.LogMessage("IOManager.set_m_UseRio() : value=" + value); value = false; return false; } } /// /// Update buttons based on Keyboard / Custom input data /// That function is called when m_UseRio is set to false /// [HarmonyPatch(typeof(IOManager), "UpdateInput_Unity")] class UpdateInput_Unity { static bool Prefix(DigitalInputData[] ___m_DigitalInputData, IOManager __instance) { //DemulShooter_Plugin.MyLogger.LogMessage("IOManager.ButtonNewlyPressed() : inputIn=" + inputIn.ToString()); for (int i = 0; i < ___m_DigitalInputData.Length; i++) { ___m_DigitalInputData[i].switchCntLast = ___m_DigitalInputData[i].switchCnt; uint iKeyPressed = 0; switch (i) { case (int)DigitalGameInput.Coin1: iKeyPressed = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Coin) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.Coin2: iKeyPressed = DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Coin) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.DBV: iKeyPressed = DemulShooter_Plugin.DBV_Key.GetButton() ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.GunShoulder1: iKeyPressed = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Action) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.GunShoulder2: iKeyPressed = DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Action) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.GunTrigger1: iKeyPressed = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.GunTrigger2: iKeyPressed = DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.Service: iKeyPressed = DemulShooter_Plugin.Service_Key.GetButton() ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.Start1: iKeyPressed = DemulShooter_Plugin.PluginControllers[0].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.Start2: iKeyPressed = DemulShooter_Plugin.PluginControllers[1].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Start) ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.Test: iKeyPressed = DemulShooter_Plugin.Test_Key.GetButton() ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.VolumeDown: iKeyPressed = DemulShooter_Plugin.VolumeDown_Key.GetButton() ? (uint)1 : (uint)0; break; case (int)DigitalGameInput.VolumeUp: iKeyPressed = DemulShooter_Plugin.VolumeUp_Key.GetButton() ? (uint)1 : (uint)0; break; default: break; } ___m_DigitalInputData[i].switchCnt = iKeyPressed; if (___m_DigitalInputData[i].switchCntLast == ___m_DigitalInputData[i].switchCnt) { ___m_DigitalInputData[i].time += Time.deltaTime; } else { ___m_DigitalInputData[i].time = 0f; } } MethodInfo mi = __instance.GetType().GetMethod("SetAnalogValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (mi != null) { mi.Invoke(__instance, new object[] { AnalogGameInput.Gun1X, DemulShooter_Plugin.PluginControllers[0].GetAimingPosition().x / (float)Screen.width }); mi.Invoke(__instance, new object[] { AnalogGameInput.Gun1Y, DemulShooter_Plugin.PluginControllers[0].GetAimingPosition().y / (float)Screen.height }); mi.Invoke(__instance, new object[] { AnalogGameInput.Gun2X, DemulShooter_Plugin.PluginControllers[1].GetAimingPosition().x / (float)Screen.width }); mi.Invoke(__instance, new object[] { AnalogGameInput.Gun2Y, DemulShooter_Plugin.PluginControllers[1].GetAimingPosition().y / (float)Screen.height }); } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Patch/mPlayerInfo.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mPlayerInfo { /// /// Creating recoil data /// [HarmonyPatch(typeof(PlayerInfo), "ShotFired")] class ShotFired { static bool Prefix(int ___m_playerIndex) { DemulShooter_Plugin.OutputData.Recoil[___m_playerIndex] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Patch/mPlayerReticle.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mPlayerReticle { /// /// Remove laser and reticle ?? /// [HarmonyPatch(typeof(PlayerReticle), "LateUpdate")] class LateUpdate { static bool Prefix(ref float ___m_LaserStartWidth, Animator[] ___m_ReticleAnimators, GunState ___m_CurrentGunState, PlayerInfo ___m_PlayerInfo, int ___m_PlayerIndex) { if (!DemulShooter_Plugin.CrossHairVisibility) { //Removing reticle by changing the animator scale to 0 if (___m_PlayerInfo.m_playerState == PlayerState.NameEntry && SingletonMonoBehaviour.Instance.NameEntry_ShouldShowReticle(___m_PlayerIndex)) { ___m_ReticleAnimators[(int)___m_CurrentGunState].transform.localScale = new Vector3(0.75f, 0.75f, 0.75f); } else { ___m_ReticleAnimators[(int)___m_CurrentGunState].transform.localScale = new Vector3(); } //Removing laser by changing it's width to 0 ___m_LaserStartWidth = 0.0f; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Patch/mSys_Linux.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mSys_Linux { /// /// Deactivating linux kernel operation /// [HarmonyPatch(typeof(Sys_Linux), "CreateUSBMountRules")] class CreateUSBMountRules { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("Sys_Linux.CreateUSBMountRules()"); return false; } } [HarmonyPatch(typeof(Sys_Linux), "CreateFlushBufferScript")] class CreateFlushBufferScript { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("Sys_Linux.CreateFlushBufferScript()"); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Patch/mUnityNatives.cs ================================================ using HarmonyLib; using System; using System.Runtime.InteropServices; namespace BepInEx_DemulShooter_Plugin { class mUnityNatives { [HarmonyPatch(typeof(UnityNatives), "ReadTicketsOwed")] class ReadTicketsOwed { static bool Prefix(ref int p1Tickets, ref int p2Tickets) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.ReadTicketsOwed()"); return false; } } [HarmonyPatch(typeof(UnityNatives), "WriteTicketsOwed")] class WriteTicketsOwed { static bool Prefix(int p1Tickets, int p2Tickets) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.WriteTicketsOwed()"); return false; } } /// /// Coin audit is read using some native linux lib, and causing an error because it does not exists under windows /// Deactivating the read untill it can be replaced by a custom functions /// [HarmonyPatch(typeof(UnityNatives), "ReadCoinAuditsFromFile")] class ReadCoinAuditsFromFile { static bool Prefix(ref CoinAudits_ForReadWrite coinAud, ref bool __result) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.ReadCoinAuditsFromFile()"); __result = false; return false; } } [HarmonyPatch(typeof(UnityNatives), "WriteCoinAuditsToFile")] class WriteCoinAuditsToFile { static bool Prefix(ref CoinAudits_ForReadWrite coinAud) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.WriteCoinAuditsToFile()"); return false; } } [HarmonyPatch(typeof(UnityNatives), "GetPeriodicRebootInfo")] class GetPeriodicRebootInfo { static bool Prefix(ref float rebootTime, ref float rebootDelay) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.GetPeriodicRebootInfo()"); return false; } } [HarmonyPatch(typeof(UnityNatives), "GetRIOKeys")] class GetRIOKeys { static bool Prefix(IntPtr pub, IntPtr priv) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.GetRIOKeys()"); return false; } } [HarmonyPatch(typeof(UnityNatives), "SetHaspLoginDelegate")] class SetHaspLoginDelegate { static bool Prefix([MarshalAs(UnmanagedType.FunctionPtr)] HaspLoginDelegate callbackPointer) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.SetHaspLoginDelegate()"); return false; } } /// /// Simulate dongle Login /// [HarmonyPatch(typeof(UnityNatives), "HaspLogin")] class HaspLogin { static bool Prefix(int featureID, ref int handle, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.HaspLogin()"); __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "SetHaspLogoutDelegate")] class SetHaspLogoutDelegate { static bool Prefix([MarshalAs(UnmanagedType.FunctionPtr)] HaspLogoutDelegate callbackPointer) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.SetHaspLogoutDelegate()"); return false; } } /// /// Simulate Dongle Logout /// [HarmonyPatch(typeof(UnityNatives), "HaspLogout")] class HaspLogout { static bool Prefix(int handle, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.HaspLogout()"); __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "SetHaspReadDelegate")] class SetHaspReadDelegate { static bool Prefix([MarshalAs(UnmanagedType.FunctionPtr)] HaspReadDelegate callbackPointer) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.SetHaspReadDelegate()"); return false; } } /// /// Get dongle serial number in 'dongleVersion' variable /// [HarmonyPatch(typeof(UnityNatives), "HaspReadDongleVersion")] class HaspReadDongleVersion { static bool Prefix(int handle, ref int dongleVersion, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.HaspReadDongleVersion()"); dongleVersion = 1; __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "HaspReadLifetimeCoinCount")] class HaspReadLifetimeCoinCount { static bool Prefix(int handle, ref int coinCount, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.HaspReadLifetimeCoinCount()"); coinCount = 0; __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "HaspReadCountryCode")] class HaspReadCountryCode { static bool Prefix(int handle, ref byte countryCode, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.HaspReadCountryCode()"); countryCode = 0; __result = (int)HaspErrorCode.STATUS_OK; return false; } } /// /// Get Cabinet type in 'cabType' variable : /// Must return 0 ? [HarmonyPatch(typeof(UnityNatives), "HaspReadCabType")] class HaspReadCabType { static bool Prefix(int handle, ref byte cabType, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspReadCabType() : handle=" + handle); cabType = (byte)FactorySetUpConsts.CABINET_IDS.DEFAULT; //0 __result = (int)HaspErrorCode.STATUS_OK; return false; } } /// /// Get Cabinet system in 'cabTemplate' variable (between 0 to 9), in FactorySetUpConsts.TEMPLATE_IDS enum /// Value is -1 so : /// 0 = USA_COIN /// ... /// 9 = NUM_IDS /// [HarmonyPatch(typeof(UnityNatives), "HaspReadCabTemplate")] class HaspReadCabTemplate { static bool Prefix(int handle, ref byte cabTemplate, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspReadCabTemplate() : handle=" + handle); __result = (int)HaspErrorCode.STATUS_OK; cabTemplate = (byte)DemulShooter_Plugin.CabTemplate; return false; } } /// /// Get cab serial number in 'serialNum' variable /// [HarmonyPatch(typeof(UnityNatives), "HaspReadCabSerialNum")] class HaspReadCabSerialNum { static bool Prefix(int handle, ref int serialNum, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspReadCabSerialNum() : handle=" + handle); serialNum = 11871514; __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "SetHaspWriteDelegate")] class SetHaspWriteDelegate { static bool Prefix([MarshalAs(UnmanagedType.FunctionPtr)] HaspWriteDelegate callbackPointer) { DemulShooter_Plugin.MyLogger.LogMessage("UnityNatives.SetHaspWriteDelegate()"); return false; } } [HarmonyPatch(typeof(UnityNatives), "HaspWriteLifetimeCoinCount")] class HaspWriteLifetimeCoinCount { static bool Prefix(int handle, ref int coinCount, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspWriteLifetimeCoinCount() : handle=" + handle + ", coinCount=" + coinCount); __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "HaspWriteCountryCode")] class HaspWriteCountryCode { static bool Prefix(int handle, ref byte countryCode, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspWriteCountryCode() : handle=" + handle + ", countryCode=" + countryCode); __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "HaspWriteCabType")] class HaspWriteCabType { static bool Prefix(int handle, ref byte cabType, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspWriteCabType() : handle=" + handle + ", cabType=" + cabType); __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "HaspWriteCabTemplate")] class HaspWriteCabTemplate { static bool Prefix(int handle, ref byte cabTemplate, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspWriteCabTemplate() : handle=" + handle + ", cabTemplate=" + cabTemplate); __result = (int)HaspErrorCode.STATUS_OK; return false; } } [HarmonyPatch(typeof(UnityNatives), "HaspWriteCabSerialNum")] class HaspWriteCabSerialNum { static bool Prefix(int handle, ref int serialNum, ref int __result) { DemulShooter_Plugin.MyLogger.LogMessage("DongleControl.HaspWriteCabSerialNum() : handle=" + handle + ", serialNum=" + serialNum); __result = (int)HaspErrorCode.STATUS_OK; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("NerfArcade_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("NerfArcade_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage("TcpOutputData.Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError("TcpOutputData.Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public byte[] Recoil = null; public UInt32[] Credits = null; public UInt16 P1_Lmp_Start = 0; public UInt16 P1_Lmp_SeatPuck = 0; public UInt16 P1_Lmp_SeatMarquee = 0; public UInt16 P1_Lmp_SeatRear_R = 0; public UInt16 P1_Lmp_SeatRear_O = 0; public UInt16 P1_Lmp_SeatRear_B = 0; public UInt16 P2_Lmp_Start = 0; public UInt16 P2_Lmp_SeatPuck = 0; public UInt16 P2_Lmp_SeatMarquee = 0; public UInt16 P2_Lmp_SeatRear_R = 0; public UInt16 P2_Lmp_SeatRear_O = 0; public UInt16 P2_Lmp_SeatRear_B = 0; public UInt16 Cab_Lmp_R = 0; public UInt16 Cab_Lmp_G = 0; public UInt16 Cab_Lmp_B = 0; public UInt16 Cab_Lmp_RearSeat = 0; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.AnimationModule.dll UnityEngine.CoreModule.dll UnityEngine.InputModule.dll UnityEngine.UIModule.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/UnityPlugin_BepInEx_NerfArcade.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin NerfArcade_BepInEx_DemulShooter_Plugin v4.8 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.AnimationModule.dll UnityLibs\UnityEngine.CoreModule.dll UnityLibs\UnityEngine.InputModule.dll UnityLibs\UnityEngine.UIModule.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_RTNA/UnityPlugin_BepInEx_NerfArcade.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_NerfArcade", "UnityPlugin_BepInEx_NerfArcade.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/DemulShooter_Plugin.cs ================================================ using System; using System.Diagnostics; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using SBK; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.tombraider"; public const String pluginName = "TombRaider_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "TombRaider_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 4; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int) KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); public static PluginControllerButton MenuUp_Key = new PluginControllerButton((int) KeyCode.UpArrow); public static PluginControllerButton MenuDown_Key = new PluginControllerButton((int) KeyCode.DownArrow); public static PluginControllerButton MenuSelect_Key = new PluginControllerButton((int) KeyCode.Return); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; public static bool SaveToGameFolder = false; public static string GameLanguage = "EN"; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadBoolValue("SYSTEM", "SAVE_TO_GAME_FOLDER", ref SaveToGameFolder); Plugin_IniFile.IniReadIntValue("VIDEO", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("VIDEO", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("VIDEO", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("VIDEO", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); MenuUp_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_UP")); MenuDown_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_DOWN")); MenuSelect_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_SELECT")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); MenuUp_Key.SetButton(Input.GetKey((KeyCode)MenuUp_Key.KeyCode)); MenuDown_Key.SetButton(Input.GetKey((KeyCode)MenuDown_Key.KeyCode)); MenuSelect_Key.SetButton(Input.GetKey((KeyCode)MenuSelect_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Fetching Outputs try { Player[] playerList = Singleton.Instance.m_PlayerList; for (int i = 0; i < MAX_PLAYERS; i++) { if (!Singleton.Instance.IsDemo && playerList[i].IsAlive()) OutputData.IsPlaying[i] = 1; else OutputData.IsPlaying[i] = 0; OutputData.Life[i] = playerList[i].Health > 0.0f ? playerList[i].Health : 0.0f; OutputData.Ammo[i] = playerList[i].CurrentGun.ClipRemaining; OutputData.Credits[i] = Singleton.Instance.GetCoinCount((ID)i); } } catch { } //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, _InputData.Reload[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintMethodInfo() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/INIFile.cs ================================================ using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet=CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/SBK/Encryption/mDataTransform.cs ================================================ using System.IO; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mDataTransform { /// /// Desactivating Data Encryption for save files /// Default behavior is using AES encryption but some error happen when trying to read them, and game is not saving data correctly /// [HarmonyPatch(typeof(SBK.Encryption.DataTransform), "EncryptStreamToBytes_Aes")] class EncryptStreamToBytes_Aes { static bool Prefix(Stream i_PlainStrm, byte[] i_Key, byte[] i_IV, ref byte[] __result) { __result = new byte[i_PlainStrm.Length]; i_PlainStrm.Read(__result, 0, (int)i_PlainStrm.Length); return false; } } [HarmonyPatch(typeof(SBK.Encryption.DataTransform), "DecryptStreamFromBytes_Aes")] class DecryptStreamFromBytes_Aes { static bool Prefix(Stream i_CipherStrm, byte[] i_Key, byte[] i_IV, ref byte[] __result) { __result = new byte[i_CipherStrm.Length]; i_CipherStrm.Read(__result, 0, (int)i_CipherStrm.Length); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/TRA/Timeline/Skip/mSkipMixerBehaviour.cs ================================================ using HarmonyLib; using UnityEngine.Playables; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mSkipMixerBehaviour { /// /// Remove Some functions used by Alpha1...Alpha9 Keys /// [HarmonyPatch(typeof(TRA.Timeline.Skip.SkipMixerBehaviour), "ProcessFrame")] class ProcessFrame { static bool Prefix(Playable i_Playable, FrameData i_Info, object i_PlayerData) { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mArcadeManager.cs ================================================ using System; using System.Diagnostics; using System.Runtime.InteropServices; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mArcadeManager { [HarmonyPatch(typeof(ArcadeManager), "CheckIOBoard")] class CheckIOBoard { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.CheckIOBoard()"); return false; } } [HarmonyPatch(typeof(ArcadeManager), "GunMalfunctionCheck")] class GunMalfunctionCheck { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.GunMalfunctionCheck()"); return false; } } //Removes the error screen [HarmonyPatch(typeof(ArcadeManager), "PushErrorPopup")] class PushErrorPopup { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("ArcadeManager.PushErrorPopup()"); return false; } } /// /// Changing resolution if needed /// [HarmonyPatch(typeof(ArcadeManager), "Start")] class Start { static bool Prefix() { if (DemulShooter_Plugin.ForceResolution) Screen.SetResolution(DemulShooter_Plugin.ScreenWidth, DemulShooter_Plugin.ScreenHeight, DemulShooter_Plugin.Fullscreen); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mCheatsManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mCheatsManager { /// /// Remove cheats using keyboard keys /// [HarmonyPatch(typeof(SBK.CheatsManager), "Update")] class Update { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mCrosshairWindow.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mCrosshairWindow { [HarmonyPatch(typeof(CrosshairWindow), "CrosshairMove")] class CrosshairMove { /// /// Original function is calculating the pixel position of the Crosshair with what seems to be a fixed range [1920x1080] /// This is creating an offset between the float real position of the aim [0, 1] and the crosshair drawing /// /// static bool Prefix(Vector3 i_Viewport, ID i_PlayerID, CrosshairWindow __instance) { //DemulShooter_Plugin.MyLogger.LogMessage("---- CrosshairWindow.CrosshairMove(), i_PlayerID=" + i_PlayerID.ToString() + ", i_Viewport=" + i_Viewport.ToString()); if (SBK.SceneSingleton.Exists()) { for (int i = 0; i < __instance.m_Crosshairs.Length; i++) { if (__instance.m_Crosshairs[i].m_ID == i_PlayerID) { Vector3 v = DemulShooter_Plugin.PluginControllers[i].GetAimingPosition(); v.x = v.x / (float)Screen.width * 1920.0f; v.y = v.y / (float)Screen.height * 1080.0f; __instance.m_Crosshairs[i].m_CrosshairTr.anchoredPosition = v; if (!DemulShooter_Plugin.CrossHairVisibility) __instance.m_Crosshairs[i].m_CrosshairTr.localScale = new Vector3(); else __instance.m_Crosshairs[i].m_CrosshairTr.localScale = new Vector3(1.0f, 1.0f, 0); break; } } return false; } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mInputManager.cs ================================================ using HarmonyLib; using SBK; using SBK.Audio; using TRA.Arcade.Manager; using UnityEngine; namespace BepInEx_DemulShooter_Plugin { class mInputManager { [HarmonyPatch(typeof(InputManager), "MouseControl")] class MouseControl { static bool Prefix(bool ___m_AreCrosshairsEnabled, bool[] ___m_HoldTrigger, ID[] ___m_IDResolution) { Vector3 i_Arg = new Vector3(); Player[] playerList = Singleton.Instance.m_PlayerList; if (!Singleton.Instance.IsDemo) { for (int i = 0; i < playerList.Length; i++) { i_Arg = DemulShooter_Plugin.PluginControllers[i].GetAimingPosition(); i_Arg.x = i_Arg.x / (float)Screen.width; i_Arg.y = i_Arg.y / (float)Screen.height; /*if (i == 0) { if ((Singleton.Instance.GunEnabled(i) && playerList[i].IsPlaying()) || Singleton.Instance.GetWindow(WindowID.ID.MainMenuWindow)) { Singleton.Instance.PlayerMoveCrosshair(i_Arg, ID.One); } } else { if (Singleton.Instance.GunEnabled(i) && playerList[i].IsPlaying()) { Singleton.Instance.PlayerMoveCrosshair(i_Arg, (ID)i); } }*/ //Enabling selection screen for every player at the same time, if not : only P1 can choose level/mode if ((Singleton.Instance.GunEnabled(i) && playerList[i].IsPlaying()) || Singleton.Instance.GetWindow(WindowID.ID.MainMenuWindow)) { Singleton.Instance.PlayerMoveCrosshair(i_Arg, (ID)i); } } } for (int i = 0; i < 4; i++) { i_Arg = DemulShooter_Plugin.PluginControllers[i].GetAimingPosition(); i_Arg.x = i_Arg.x / (float)Screen.width; i_Arg.y = i_Arg.y / (float)Screen.height; if (Singleton.Instance.GunEnabled(i)) { if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger) && Singleton.Instance.GetPlayerByID((ID)i).Alive) { Singleton.Instance.PlayerPressTrigger(i_Arg, (ID)i); if (___m_AreCrosshairsEnabled) { Singleton.Instance.PlayerShoot(i_Arg, (ID)i, ___m_HoldTrigger[i], true); if (!___m_HoldTrigger[i]) { ___m_HoldTrigger[i] = true; } } else if (!___m_HoldTrigger[i]) { ___m_HoldTrigger[i] = true; if (Singleton.Instance.CurrentState == GameplayManager.State.PlayState && !Singleton.Instance.IsPlaying) { Singleton.Instance.PlayPlayerSound(AudioID_Global.player_cantshoot, ___m_IDResolution[i]); } } } if (DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Reload)) { Singleton.Instance.PlayerReload((ID)i); } } if (Singleton.Instance.GunEnabled(i) && !DemulShooter_Plugin.PluginControllers[i].GetButton(UnityPlugin_BepInEx_Core.PluginController.MyInputButtons.Trigger)) { if (___m_AreCrosshairsEnabled) { Singleton.Instance.PlayerTriggerRelease((ID)i); } ___m_HoldTrigger[i] = false; } if (!___m_HoldTrigger[i]) { Singleton.Instance.StopHoldingFire((ID)i); } } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mKaboomAdrioFxPlusFourFeeders.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mKaboomAdrioFxPlusFourFeeders { /// /// Prevent the Lib to search and write to some devices on some computers /// May cause the game to hang /// [HarmonyPatch(typeof(KaboomAdrioFxPlusFourFeeders), "_Initialize")] class _Initialize { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("KaboomAdrioFxPlusFourFeeders._Initialize()"); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mKaboomManager.cs ================================================ using System.Diagnostics; using System.Runtime.CompilerServices; using HarmonyLib; using KaboomButton; using UnityEngine; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin { class mKaboomManager { /// /// Use this funtion to generate DemulShooter custom recoil output /// [HarmonyPatch(typeof(KaboomManager), "StartMotorPwm")] class StartMotorPwm { static bool Prefix(KaboomOutput.MotorPwmId MotorId, byte un8PwmDutyCycle, ushort iMsDuration = 144) { DemulShooter_Plugin.MyLogger.LogMessage("KaboomManager.StartMotorPwm(): MotorId=" + MotorId.ToString()); if (MotorId.ToString().Contains("P1")) DemulShooter_Plugin.OutputData.Recoil[0] = 1; else if (MotorId.ToString().Contains("P2")) DemulShooter_Plugin.OutputData.Recoil[1] = 1; else if (MotorId.ToString().Contains("P3")) DemulShooter_Plugin.OutputData.Recoil[2] = 1; else if (MotorId.ToString().Contains("P4")) DemulShooter_Plugin.OutputData.Recoil[3] = 1; return true; } } /// /// Changing Key Inputs /// [HarmonyPatch(typeof(KaboomManager), "Update")] class Update { static bool Prefix(KaboomAdrioFxPlusTwoFeeders ___m_KaboomAdrioFxPlusTwoFeeders, KaboomAdrioFxPlusFourFeeders ___m_KaboomAdrioFxPlusFourFeeders, KaboomManager __instance) { if (Input.GetKeyDown(KeyCode.Escape)) { Application.Quit(); mQuit.Quit(__instance); Process.GetCurrentProcess().Kill(); } //COINS if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode)) { mKaboomOnCoinEvent.KaboomOnCoinEvent(__instance, 1, 0, 0, 0); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode)) { mKaboomOnCoinEvent.KaboomOnCoinEvent(__instance, 0, 1, 0, 0); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[2].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode)) { mKaboomOnCoinEvent.KaboomOnCoinEvent(__instance, 0, 0, 1, 0); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[3].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode)) { mKaboomOnCoinEvent.KaboomOnCoinEvent(__instance, 0, 0, 0, 1); } //TEST MENU if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.Test_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_OPERATOR_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.Test_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_OPERATOR_RELEASE); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.MenuUp_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_UP_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.MenuUp_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_UP_RELEASE); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.MenuSelect_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_SELECT_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.MenuSelect_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_SELECT_RELEASE); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.MenuDown_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_DOWN_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.MenuDown_Key.KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_DOWN_RELEASE); } //START if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER1_B1_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.PluginControllers[0].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER1_B1_RELEASE); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER2_B1_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.PluginControllers[1].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER2_B1_RELEASE); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[2].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER3_B1_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.PluginControllers[2].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER3_B1_RELEASE); } if (Input.GetKeyDown((KeyCode)DemulShooter_Plugin.PluginControllers[3].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER4_B1_PRESS); } else if (Input.GetKeyUp((KeyCode)DemulShooter_Plugin.PluginControllers[3].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode)) { mKaboomOnKeyboardEvent.KaboomOnKeyboardEvent(__instance, Button.BUTTON_PLAYER4_B1_RELEASE); } /*if (Input.GetKeyDown(KeyCode.F1)) { mGiveTicketsToFeeder.GiveTicketsToFeeder(__instance, 0, 12, true); } if (Input.GetKeyDown(KeyCode.F2)) { mGiveTicketsToFeeder.GiveTicketsToFeeder(__instance, 1, 12, true); }*/ if (___m_KaboomAdrioFxPlusTwoFeeders != null) { ___m_KaboomAdrioFxPlusTwoFeeders.Update(); } else if (___m_KaboomAdrioFxPlusFourFeeders != null) { ___m_KaboomAdrioFxPlusFourFeeders.Update(); } return false; } } /*[HarmonyPatch(typeof(KaboomManager), "GiveTicketsToFeeder")] class mGiveTicketsToFeeder { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void GiveTicketsToFeeder(object instance, ushort iFeederID, ushort iTicket = 0, bool b_finalize = false) { //Used to call the private method } }*/ [HarmonyPatch(typeof(KaboomManager), "KaboomOnCoinEvent")] class mKaboomOnCoinEvent { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void KaboomOnCoinEvent(object instance, ushort aUn16CoinCount1, ushort aUn16CoinCount2, ushort aUn16CoinCount3, ushort aUn16CoinCount4) { //Used to call the private method } } [HarmonyPatch(typeof(KaboomManager), "KaboomOnKeyboardEvent")] class mKaboomOnKeyboardEvent { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void KaboomOnKeyboardEvent(object instance, KaboomButton.Button aKey) { //Used to call the private method } } [HarmonyPatch(typeof(KaboomManager), "Quit")] class mQuit { [HarmonyReversePatch] [MethodImpl(MethodImplOptions.NoInlining)] public static void Quit(object instance) { //Used to call the private method } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mLEDManager.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { /// /// Prevent connection search on COM port /// internal class mLEDManager { [HarmonyPatch(typeof(LEDManager), "Awake")] class Awake { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("LEDManager.Awake()"); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mLanguageLocalizer.cs ================================================ using System; using System.Collections.Generic; using HarmonyLib; using static SBK.Attribute.TimeSpanAttribute; namespace BepInEx_DemulShooter_Plugin { class mLanguageLocalizer { /// /// Available Languages : EN / FR / JA / ZH /// [HarmonyPatch(typeof(SBK.Localization.LanguageLocalizer), "SwitchLanguage")] class SwitchLanguage { static bool Prefix(ref SBK.Localization.LanguageCode i_Code, List ___m_AvailableLanguages) { DemulShooter_Plugin.MyLogger.LogMessage("LanguageLocalizer.SwitchLanguage(), i_Code=" + i_Code); DemulShooter_Plugin.MyLogger.LogMessage("LanguageLocalizer.SwitchLanguage(), Available Languages :"); foreach (String s in ___m_AvailableLanguages) { DemulShooter_Plugin.MyLogger.LogMessage(s); } switch (DemulShooter_Plugin.GameLanguage) { case "EN": { i_Code = SBK.Localization.LanguageCode.EN; } break; case "FR": { i_Code = SBK.Localization.LanguageCode.FR; } break; case "JA": { i_Code = SBK.Localization.LanguageCode.JA; } break; case "ZH": { i_Code = SBK.Localization.LanguageCode.ZH; } break; default: { i_Code = SBK.Localization.LanguageCode.EN; } break; } DemulShooter_Plugin.MyLogger.LogMessage("LanguageLocalizer.SwitchLanguage() : Succesfully sets Language to " + i_Code.ToString()); return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mOperatorMenu.cs ================================================ using System; using System.Collections.Generic; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin { class mOperatorMenu { [HarmonyPatch(typeof(OperatorMenu.OperatorMenuSaveInfo), "ResetData")] class ResetData { static void Postfix(OperatorMenu.OperatorMenuSaveInfo __instance) { DemulShooter_Plugin.MyLogger.LogMessage("OperatorMenu.ResetData() : Trying to read custom config file: " + BepInEx.Paths.GameRootPath + "TombRaider_Operator.conf"); DemulShooter_Plugin.PrintStackTrace(); // try // { // String[] lines = System.IO.File.ReadAllLines(BepInEx.Paths.GameRootPath + @"\TombRaider_Operator.conf"); // foreach (String line in lines) // { // String[] buffer = line.Split(':'); // String Key = buffer[0]; // String Value = buffer[1]; // switch (Key) // { // case "CreditsPerGame": // { // __instance.m_CreditsPerGame = int.Parse(Value); // } break; // case "GameAudioVolume": // { // __instance.m_GameAudioVolume = int.Parse(Value); // } break; // case "AttractAudioVolume": // { // __instance.m_AttractAudioVolume = int.Parse(Value); // } break; // case "PaymentType": // { // __instance.m_PaymentType = int.Parse(Value); // } break; // case "CredtitsSharing": // { // __instance.m_CanShareCredits = int.Parse(Value); // } break; // case "Difficulty": // { // __instance.m_Difficulty = int.Parse(Value); // } break; // case "RedemptionMode": // { // __instance.m_RedemptionMode = int.Parse(Value); // } break; // case "MinimumTicket": // { // __instance.m_MinimumTicket = int.Parse(Value); // } break; // case "TicketValue": // { // __instance.m_TicketValue = int.Parse(Value); // } break; // case "PointPerTicket": // { // __instance.m_PointPerTicket = int.Parse(Value); // } break; // case "GunFrequency": // { // __instance.m_GunFrequence = int.Parse(Value); // } break; // case "GunP1": // { // if (int.Parse(Value) == 0) // __instance.m_GunEnable[0] = false; // else // __instance.m_GunEnable[0] = true; // } break; // case "GunP2": // { // if (int.Parse(Value) == 0) // __instance.m_GunEnable[1] = false; // else // __instance.m_GunEnable[1] = true; // } break; // case "GunP3": // { // if (int.Parse(Value) == 0) // __instance.m_GunEnable[2] = false; // else // __instance.m_GunEnable[2] = true; // } break; // case "GunP4": // { // if (int.Parse(Value) == 0) // __instance.m_GunEnable[3] = false; // else // __instance.m_GunEnable[3] = true; // } break; // default: break; // } // } // DemulShooter_Plugin.MyLogger.LogMessage("mOperatorMenu.ResetData() : Succes !"); // } // catch (Exception Ex) // { // DemulShooter_Plugin.MyLogger.LogMessage("mOperatorMenu.ResetData() : Can't read config data, using custom default values."); // DemulShooter_Plugin.MyLogger.LogMessage("mOperatorMenu.ResetData() : " + Ex.Message.ToString()); // __instance.m_CreditsPerGame = 0; //0 to 20 // __instance.m_GameAudioVolume = 20; //0 to 20 // __instance.m_AttractAudioVolume = 20; //0 to 20 // __instance.m_PaymentType = 0; //0 = Credits, 1=Card // __instance.m_Difficulty = 1; //1 to 5 // __instance.m_RedemptionMode = 0; //0 = OFF, 1 = ON // __instance.m_MinimumTicket = 5; //0 to 10 // __instance.m_TicketValue = 2; //1 or 2 // __instance.m_PointPerTicket = 1000; //500 to 5000 // __instance.m_ContinueCost = 4; // __instance.m_ContinueAmount = 2; // //__instance.m_StartGameFailSafeTimer = 15; // __instance.m_CanShareCredits = 0; //0 = OFF, 1 = ON // /*__instance.m_LT_Games = 0; // __instance.m_LT_Credits = 0; // __instance.m_LR_Games = 0; // __instance.m_LR_Credits = 0; // __instance.m_LR_Day = DateTime.Now.Day; // __instance.m_LR_Month = DateTime.Now.Month; // __instance.m_LR_Year = DateTime.Now.Year; // __instance.m_LT_ContinuesTakenTotal = 0; // __instance.m_LR_ContinuesTakenTotal = 0; // __instance.m_LT_ContinuesOfferedTotal = 0; // __instance.m_LR_ContinuesOfferedTotal = 0; // __instance.m_LT_ContinuesTakenSingle = 0; // __instance.m_LR_ContinuesTakenSingle = 0; // __instance.m_LT_ContinuesOfferedSingle = 0; // __instance.m_LR_ContinuesOfferedSingle = 0; // __instance.m_LT_ContinuesTakenMulti = 0; // __instance.m_LR_ContinuesTakenMulti = 0; // __instance.m_LT_ContinuesOfferedMulti = 0; // __instance.m_LR_ContinuesOfferedMulti = 0; // __instance.m_LT_Bonus = 0; // __instance.m_FailCounterPerLevel = new Dictionary(); // __instance.m_NbOfGamesPlayedPerLevel = new Dictionary(); // __instance.m_WinCounterPerLevel = new Dictionary(); // __instance.CheckDictionaries();*/ // __instance.m_GunEnable = new List(4) //{ // true, // true, // true, // true //}; // __instance.m_GunFrequence = 1; // //__instance.m_LastLogs = new List(); // } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mPlayer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using HarmonyLib; using SBK; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mPlayer { /// /// Intercept call to get Damage outputs /// [HarmonyPatch(typeof(Player), "ReceiveDamage")] class ReceiveDamage { static bool Prefix(Player __instance, float ___m_Health, int ___m_ID) { if (___m_Health <= 0f || __instance.IsInvicible || Singleton.Instance.GodMode) { return true; } DemulShooter_Plugin.OutputData.Damaged[___m_ID] = 1; return true; } } [HarmonyPatch(typeof(Player), "Update")] class Update { static bool Prefix(Player __instance, ref float ___m_Health, ref float ___m_InvincibilityTimer) { if (UnityEngine.Input.GetKeyDown(UnityEngine.KeyCode.M)) { ___m_InvincibilityTimer = 0; } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mPlayerManager.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mPlayerManager { //Removes Cheats KEYS [HarmonyPatch(typeof(PlayerManager), "Update")] class Update { static bool Prefix(Player[] ___m_PlayerList) { for (int i = 0; i < ___m_PlayerList.Length; i++) { if (___m_PlayerList[i].IsPlaying()) { ___m_PlayerList[i].Update(); } } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Patch/mSBKInputManager.cs ================================================ using HarmonyLib; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mSBKInputManager { /// /// Override START keys at start screen /// [HarmonyPatch(typeof(SBKInputManager), "GetPlayerKeyDown")] class GetPlayerKeyDown { static bool Prefix(ref bool __result, ID i_ID) { __result = DemulShooter_Plugin.PluginControllers[(int)i_ID].GetButtonDown((int)PluginController.MyInputButtons.Start); return false; } } /// /// Not used by the game ??? /// /*[HarmonyPatch(typeof(SBKInputManager), "GetLeftKeyDown")] class GetLeftKeyDown { static bool Prefix(ref bool __result) { __result = DemulShooter_Plugin.MenuUp_Key.GetButtonDown(); return false; } } [HarmonyPatch(typeof(SBKInputManager), "GetLeftKeyUp")] class GetLeftKeyUp { static bool Prefix(ref bool __result) { __result = DemulShooter_Plugin.MenuUp_Key.GetButtonUp(); return false; } } [HarmonyPatch(typeof(SBKInputManager), "GetRightKeyDown")] class GetRightKeyDown { static bool Prefix(ref bool __result) { __result = DemulShooter_Plugin.MenuDown_Key.GetButtonDown(); return false; } } [HarmonyPatch(typeof(SBKInputManager), "GetRightKeyUp")] class GetRightKeyUp { static bool Prefix(ref bool __result) { __result = DemulShooter_Plugin.MenuDown_Key.GetButtonUp(); return false; } } [HarmonyPatch(typeof(SBKInputManager), "GetSelectKeyDown")] class GetSelectKeyDown { static bool Prefix(ref bool __result) { __result = DemulShooter_Plugin.MenuSelect_Key.GetButtonUp(); return false; } }*/ } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("TombRaider_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("TombRaider_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage("TcpOutputData.Ctor() : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError("TcpOutputData.Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Reload = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public float[] Life = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Ammo = null; public int[] Credits = null; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/TombRaider_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 P3_START=51 P4_START=52 P1_COIN=53 P2_COIN=54 P3_COIN=55 P4_COIN=56 TEST=48 EXIT=27 MENU_UP=273 MENU_DOWN=274 MENU_SELECT=13 [SYSTEM] ;Default data folder is in "%userProfile%\AppData\LocalLow\Sarbakan\SquareEnix_TombRaider\" ;Set this to "1" to save data in "\NVRAM\" folder from game root path. SAVE_TO_GAME_FOLDER=1 ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.CoreModule.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/UnityPlugin_BepInEx_TombRaider.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin TombRaider_BepInEx_DemulShooter_Plugin v3.5 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll UnityLibs\UnityEngine.CoreModule.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_TRA/UnityPlugin_BepInEx_TombRaider.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_TombRaider", "UnityPlugin_BepInEx_TombRaider.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Demulshooter_Plugin.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Threading; using BepInEx; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; using UnityPlugin_BepInEx_IniFile; namespace BepInEx_DemulShooter_Plugin { [BepInPlugin(pluginGuid, pluginName, pluginVersion)] public class DemulShooter_Plugin : BaseUnityPlugin { public const String pluginGuid = "argonlefou.demulshooter.wws"; public const String pluginName = "WildWestShootout_BepInEx_DemulShooter_Plugin"; public const String pluginVersion = "17.0.0.0"; public const String pluginConfigFile = "WildWestShootout_BepInEx_DemulShooter_Plugin.ini"; public static BepInEx.Logging.ManualLogSource MyLogger; public static DemulShooter_Plugin Instance = null; public static readonly int MAX_PLAYERS = 2; //custom Input Data public static PluginController[] PluginControllers = new PluginController[MAX_PLAYERS]; public static bool EnableInputHack = false; //By default, no input hack on the plugin. Enabled once DemulShooter is connected (without -noinput flag) //Using custom Button as Input.GetKeyDown is not correctly detected in non-unity Thread public static PluginControllerButton Exit_Key = new PluginControllerButton((int)KeyCode.Escape); public static PluginControllerButton Test_Key = new PluginControllerButton((int) KeyCode.Alpha0); public static PluginControllerButton MenuSelect_Key = new PluginControllerButton((int) KeyCode.Return); //TCP server data for Inputs/Outputs private TcpListener _TcpListener; private Thread _TcpListenerThread; private TcpClient _TcpClient; private int _TcpPort = 33610; private static NetworkStream _TcpStream; public static TcpOutputData OutputData; private TcpOutputData _OutputDataBefore; private TcpInputData _InputData; public static bool CrossHairVisibility = true; //Custom resolution public static int ScreenWidth = 1920; public static int ScreenHeight = 1080; public static bool Fullscreen = true; public static bool ForceResolution = false; //Byte payload for Input buttons byte value public static byte InputGunByte_Payload = 0x00; public static byte InputGunByte_Payload_Before = 0x00; public void Awake() { Instance = this; MyLogger = Logger; MyLogger.LogMessage("Plugin Loaded"); Harmony harmony = new Harmony(pluginGuid); OutputData = new TcpOutputData(MAX_PLAYERS); _OutputDataBefore = new TcpOutputData(MAX_PLAYERS); _InputData = new TcpInputData(MAX_PLAYERS); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i] = new PluginController(i); } // Start TcpServer _TcpListenerThread = new Thread(new ThreadStart(TcpClientThreadLoop)); _TcpListenerThread.IsBackground = true; _TcpListenerThread.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Loading custom config : " + BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); INIFile Plugin_IniFile = new INIFile(BepInEx.Paths.PluginPath + @"/" + pluginConfigFile); if (File.Exists(Plugin_IniFile.FInfo.FullName)) { try { Plugin_IniFile.IniReadIntValue("VIDEO", "WIDTH", ref ScreenWidth); Plugin_IniFile.IniReadIntValue("VIDEO", "HEIGHT", ref ScreenHeight); Plugin_IniFile.IniReadBoolValue("VIDEO", "FULLSCREEN", ref Fullscreen); Plugin_IniFile.IniReadBoolValue("VIDEO", "FORCE_RESOLUTION", ref ForceResolution); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_START")); PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "P" + (i + 1).ToString() + "_COIN")); } Exit_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "EXIT")); Test_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "TEST")); MenuSelect_Key.SetKeyCode(Plugin_IniFile.IniReadValue("INPUT_KEYS", "MENU_SELECT")); } catch (Exception Ex) { MyLogger.LogError(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Error reading config file : " + Plugin_IniFile.FInfo.FullName); MyLogger.LogError(Ex.Message.ToString()); } } else { MyLogger.LogWarning(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "():" + Plugin_IniFile.FInfo.FullName + " not found"); } if (ForceResolution) Screen.SetResolution(ScreenWidth, ScreenHeight, Fullscreen); MyLogger.LogMessage("Graphics Engine: " + SystemInfo.graphicsDeviceVersion); harmony.PatchAll(); } public void Start() {} public void Update() { //Custom Button handling Exit_Key.SetButton(Input.GetKey((KeyCode)Exit_Key.KeyCode)); Test_Key.SetButton(Input.GetKey((KeyCode)Test_Key.KeyCode)); MenuSelect_Key.SetButton(Input.GetKey((KeyCode)MenuSelect_Key.KeyCode)); for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetButton(PluginController.MyInputButtons.Start, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Start].KeyCode) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Coin, Input.GetKey((KeyCode)PluginControllers[i].InputButtons[(int)PluginController.MyInputButtons.Coin].KeyCode) ? (byte)1 : (byte)0); if (!EnableInputHack) { PluginControllers[i].SetAimingValues(Input.mousePosition); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, Input.GetMouseButton(0) ? (byte)1 : (byte)0); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, Input.GetMouseButton(1) ? (byte)1 : (byte)0); } } //Quit if (Exit_Key.GetButtonDown()) UnityEngine.Application.Quit(); //Getting Outputs Values for (int i = 0; i < MAX_PLAYERS; i++) { PlayerData Pdata = GameData.GetPlayerData(i); if (Pdata != null) { OutputData.IsPlaying[i] = Pdata.PlayGaming() ? (byte)1 : (byte)0; OutputData.Credits[i] = Pdata.coins; OutputData.Life[i] = Pdata.life; } } //Checking for a change in output to send or not byte[] bOutputData = OutputData.ToByteArray(); byte[] bOutputDataBefore = _OutputDataBefore.ToByteArray(); for (int i = 0; i < bOutputData.Length; i++) { if (bOutputData[i] != bOutputDataBefore[i]) { SendOutputs(); break; } } //Save current state _OutputDataBefore.Update(bOutputData); } public void OnDestroy() { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "()"); _TcpListener.Server.Close(); } private void HarmonyPatch(Harmony hHarmony, Type OriginalClass, String OriginalMethod, Type ReplacementClass, String ReplacementMethod) { MethodInfo original = AccessTools.Method(OriginalClass, OriginalMethod); MethodInfo patch = AccessTools.Method(ReplacementClass, ReplacementMethod); hHarmony.Patch(original, new HarmonyMethod(patch)); } /// /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// private void TcpClientThreadLoop() { try { // Create listener on localhost port 8052. _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), _TcpPort); _TcpListener.Start(); MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Server is listening on Port " + _TcpPort); Byte[] bytes = new Byte[1024]; while (true) { using (_TcpClient = _TcpListener.AcceptTcpClient()) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): TCP Client connected !"); using (_TcpStream = _TcpClient.GetStream()) { //Send outputs at connection, if DemulShooter connects during game, between events SendOutputs(); while (true) { int Length = 0; try { Length = _TcpStream.Read(bytes, 0, bytes.Length); //If Tcpclient gets disconnected, Read should return 0 bytes, so we can handle disconnection to allow a new connection if (Length == 0) break; byte[] InputBuffer = new byte[Length]; Array.Copy(bytes, 0, InputBuffer, 0, Length); _InputData.Update(InputBuffer); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): client message received as: " + _InputData.ToString()); //- Debug ONLY //lock (MutexLocker_Inputs) //{ for (int i = 0; i < MAX_PLAYERS; i++) { PluginControllers[i].SetAimingValues(new Vector3(_InputData.Axis_X[i], _InputData.Axis_Y[i])); PluginControllers[i].SetButton(PluginController.MyInputButtons.Trigger, _InputData.Trigger[i]); PluginControllers[i].SetButton(PluginController.MyInputButtons.Reload, _InputData.Reload[i]); } CrossHairVisibility = _InputData.HideCrosshairs == 1 ? false : true; EnableInputHack = _InputData.EnableInputsHack == 1 ? true : false; //} } catch { //Connnection Error ? break; } } } } } } catch (SocketException socketException) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): SocketException " + socketException.ToString()); } } /// /// Send output data over the TCP connection /// When TcpClient is disconnected, _TcpClient is Disposed and can't be acces to check if null or not => need Try/Catch /// public static void SendOutputs() { try { if (Instance._TcpClient == null) return; if (_TcpStream == null) return; // Get a stream object for writing. if (_TcpStream.CanWrite) { TcpPacket p = new TcpPacket(OutputData.ToByteArray(), TcpPacket.PacketHeader.Outputs); byte[] Buffer = p.GetFullPacket(); //- Debug ONLY //MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Sending data : " + p.ToString()); //- Debug ONLY //lock (MutexLocker_Outputs) //{ //Resetting event flags for next packets for (int i = 0; i < MAX_PLAYERS; i++) { OutputData.Recoil[i] = 0; OutputData.Damaged[i] = 0; } //} _TcpStream.Write(Buffer, 0, Buffer.Length); } } catch (Exception Ex) { MyLogger.LogMessage(Instance.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + "(): Socket exception: " + Ex); } } /// /// For deebug, printing StackTrace to trace calls to API we're looking for /// public static void PrintStackTrace() { MyLogger.LogMessage(System.Environment.StackTrace); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/INIFile.cs ================================================ using System.Diagnostics.Eventing.Reader; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace UnityPlugin_BepInEx_IniFile { public class INIFile { private string _RelativePath = string.Empty; public FileInfo FInfo { get; private set; } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); public INIFile(string INIPath) { _RelativePath = INIPath; FInfo = new FileInfo(_RelativePath); } public long IniWriteValue(string Section, string Key, string Value) { return WritePrivateProfileString(Section, Key, Value, this._RelativePath); } public string IniReadValue(string Section, string Key) { StringBuilder temp = new StringBuilder(255); int i = GetPrivateProfileString(Section, Key, "", temp, 255, this._RelativePath); return temp.ToString(); } public void IniReadBoolValue(string section, string key, ref bool TargetSetting) { int iBuffer = 0; try { iBuffer = int.Parse(IniReadValue(section, key)); if (iBuffer == 0) TargetSetting = false; else TargetSetting = true; } catch { } } public void IniReadIntValue(string section, string key, ref int TargetSetting) { try { TargetSetting = int.Parse(IniReadValue(section, key)); } catch { } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/MVSDK/mBaseGun.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mBaseGun { /// /// Returning shoot point coordinates for InGame /// Coordinates are in range [-1.0f ; 1.0f]; /// [HarmonyPatch(typeof(MVSDK.BaseGun), "GetGunPos")] class GetGunPos { static bool Prefix(MVSDK.BaseGun __instance, ref Vector2 __result, int gun_id) { if (gun_id >= 0 && gun_id < __instance.MaxGun) { Vector2 v = DemulShooter_Plugin.PluginControllers[gun_id].GetAimingPosition(); v.x = v.x / (float)Screen.width; v.y = v.y / (float)Screen.height; __result = v; } else __result = MVSDK.BaseGun.InvalidPoint; return false; } } /// /// Returning shoot point coordinates for InGame /// Coordinates are in range [WindowWidth ; WindowHeight] /// UnityEngine.Screen is returning Window size /// [HarmonyPatch(typeof(MVSDK.BaseGun), "GetGunRealPixPos")] class GetGunRealPixPos { static bool Prefix(MVSDK.BaseGun __instance, ref Vector2 __result, int gun_id) { if (gun_id >= 0 && gun_id < __instance.MaxGun) { __result = DemulShooter_Plugin.PluginControllers[gun_id].GetAimingPosition(); } else __result = MVSDK.BaseGun.InvalidPoint; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/_Unity.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class _Unity { /// /// Change data/save folder location to game folder /// [HarmonyPatch(typeof(UnityEngine.Application), "persistentDataPath", MethodType.Getter)] class persistentDataPath { static void Postfix(ref string __result) { if (DemulShooter_Plugin.SaveToGameFolder) { DemulShooter_Plugin.MyLogger.LogMessage("UnityEngine.Application.persistentDataPath: " + __result); __result = BepInEx.Paths.GameRootPath + "/NVRAM"; } } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mBaseCamera.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { /// /// This class is using calls from a DLL not present in the game release, causing multiple ERROR /// "Blanking" calls from this class allow the Guns ti run "normally" /// class mBaseCamera { [HarmonyPatch(typeof(MVSDK.BaseCamera), "CaptureThreadProc")] class CaptureThreadProc { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.CaptureThreadProc()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "Close")] class Close { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.Close()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "GetFrameData")] class GetFrameData { static bool Prefix(ref bool __result) { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.GetFrameData()"); __result = true; return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "GetImageResolution")] class GetImageResolution { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.GetImageResolution()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "InitCamera")] class InitCamera { static bool Prefix(ref bool __result) { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.InitCamera()"); __result = true; return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "LoadDefaultConfig")] class LoadDefaultConfig { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.LoadDefaultConfig()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "Pause")] class Pause { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.Pause()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "Play")] class Play { static bool Prefix(ref bool __result) { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.Play()"); __result = true; return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "SetExposureState")] class SetExposureState { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.SetExposureState()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "SetImageResolution", new[] { typeof(int), typeof(int), typeof(int), typeof(int) })] class SetImageResolution { static bool Prefix(ref MVSDK.CameraSdkStatus __result) { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.SetImageResolution()"); __result = MVSDK.CameraSdkStatus.CAMERA_STATUS_SUCCESS; return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "SetMonochrome")] class SetMonochrome { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.SetMonochrome()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera), "UpdateExposure")] class UpdateExposure { static bool Prefix() { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.UpdateExposure()"); return false; } } [HarmonyPatch(typeof(MVSDK.BaseCamera))] [HarmonyPatch("AnalogGain", MethodType.Setter)] class set_AnalogGain { static bool Prefix(float value, float ___m_fAnalogGain) { //DemulShooter_Plugin.MyLogger.LogMessage("mBaseCamera.set_AnalogGain()"); ___m_fAnalogGain = value; return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mBaseCom.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mBaseCom { /// /// Removing the call to the dog_winfows_ dll causing a crash when it's called with admin rights (netdll broken messagebox) /// [HarmonyPatch(typeof(BaseCom), "CheckDog")] class CheckDog { static bool Prefix() { DemulShooter_Plugin.MyLogger.LogMessage("BaseCom.CheckDog()"); return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mGameBeginUIController.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { class mGameBeginUIController { /// /// Removing mouse click on Title Screen to start game /// [HarmonyPatch(typeof(GameBeginUIController), "Update")] class Update { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mGameChooseUI2.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mGameChooseUI2 { /// /// Disabling mouse input on Level Select Screen /// [HarmonyPatch(typeof(GameChooseUI2), "Update")] class Update { static bool Prefix() { return false; } } /// /// Getting recoil in Level-Select screen /// [HarmonyPatch(typeof(GameChooseUI2), "HitAction")] class HitAction { static bool Prefix(Vector3 shootPoint, PlayerType playerType) { //DemulShooter_Plugin.MyLogger.LogWarning("GameChooseUI2.HitAction(): playerType=" + playerType); DemulShooter_Plugin.OutputData.Recoil[(int)playerType] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mGameStart.cs ================================================ namespace BepInEx_DemulShooter_Plugin.Patch { class mGameStart { ///// ///// Replacing this method force the game to start without waiting for the "C" button ///// //[HarmonyPatch(typeof(GameStart), "FixedUpdate")] //class FixedUpdate //{ // static bool Prefix(GameStart __instance) // { // mEnterGame.EnterGame(__instance); // return false; // } //} //[HarmonyPatch(typeof(GameStart), "EnterGame")] //class mEnterGame //{ // [HarmonyReversePatch] // [MethodImpl(MethodImplOptions.NoInlining)] // public static void EnterGame(object instance) // { // //Used to call the private method GameStart.EnterGame() // } //} } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mGameUIController.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mGameUIController { /// /// Displaying cursors InGame /// [HarmonyPatch(typeof(GameUIController), "Update")] class Update { static bool Prefix(GameUIController __instance, bool ___isLoadEnd) { for (int i = 0; i < __instance.gunPoint.Length; ++i) { if (GameData.GetPlayerData(i).CanShoot() && !___isLoadEnd && DemulShooter_Plugin.CrossHairVisibility) { Vector2 v = DemulShooter_Plugin.PluginControllers[i].GetAimingPosition(); float fRatio = (float)Screen.width / Screen.height; v.x = v.x / (float)Screen.width * 1920.0f; v.y = v.y / (float)Screen.height * 1920.0f / fRatio; __instance.gunPoint[i].mRectTF.anchoredPosition = v; } else __instance.gunPoint[i].transform.localPosition = new Vector3(-1000f, -600f); } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mInputController.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mInputController { /// /// Remove Keyboard/Mouse enable /// [HarmonyPatch(typeof(InputController), "Update")] class Update { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mLSerialPort.cs ================================================ using System; using System.Collections.Generic; using HarmonyLib; using UnityEngine; using UnityPlugin_BepInEx_Core; namespace BepInEx_DemulShooter_Plugin.Patch { class mLSerialPort { static String ByteArrayToString(byte[] bArray) { string s = ""; for (int i = 0; i < bArray.Length; i++) { s += bArray[i].ToString("X2") + " "; } return s; } /// /// Simulate COM connection OK /// [HarmonyPatch(typeof(LSerialPort), "Start")] class Start { static bool Prefix(bool ___m_com_opened, bool ___m_loop_thread) { DemulShooter_Plugin.MyLogger.LogMessage("mLSerialPort.Start()"); ___m_loop_thread = true; ___m_com_opened = true; return false; } } /// /// Replacing the COM port parsing by Shared Memory Parsing /// [HarmonyPatch(typeof(LSerialPort), "GetAllMsg")] class GetAllMsg { static bool Prefix(ref List msg_list, List ___m_recv_pack) { //Entering TEST mode // Moving down in Test Mode if (DemulShooter_Plugin.Test_Key.GetButtonDown()) DemulShooter_Plugin.InputGunByte_Payload |= 0x10; else if (DemulShooter_Plugin.Test_Key.GetButtonUp()) DemulShooter_Plugin.InputGunByte_Payload &= 0xEF; //Select in TEST mode if (DemulShooter_Plugin.MenuSelect_Key.GetButtonDown()) DemulShooter_Plugin.InputGunByte_Payload |= 0x80; else if (DemulShooter_Plugin.MenuSelect_Key.GetButtonUp()) DemulShooter_Plugin.InputGunByte_Payload &= 0x7F; //Move Up TEST mode /*if (Input.GetKeyDown(KeyCode.UpArrow)) DemulShooter_Plugin.InputGunByte_Payload |= 0x20; else if (Input.GetKeyUp(KeyCode.UpArrow)) DemulShooter_Plugin.InputGunByte_Payload &= 0xDF;*/ if (DemulShooter_Plugin.InputGunByte_Payload != DemulShooter_Plugin.InputGunByte_Payload_Before) { byte[] b = new Byte[7]; b[0] = 0xDD; b[1] = DemulShooter_Plugin.InputGunByte_Payload; b[2] = 0; b[3] = 0; b[4] = 0; b[5] = 0; b[6] = 0; ___m_recv_pack.Add(b); DemulShooter_Plugin.InputGunByte_Payload_Before = DemulShooter_Plugin.InputGunByte_Payload; } //Trigger bool bFlag = false; for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { if (DemulShooter_Plugin.PluginControllers[i].GetButtonDown(PluginController.MyInputButtons.Trigger) || DemulShooter_Plugin.PluginControllers[i].GetButtonUp(PluginController.MyInputButtons.Trigger)) { bFlag = true; break; } } if (bFlag) { byte[] b = new Byte[7]; b[0] = 0xDD; for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { b[i + 1] = DemulShooter_Plugin.PluginControllers[i].GetButton(PluginController.MyInputButtons.Trigger) ? (byte)1 : (byte)0; } ___m_recv_pack.Add(b); } //COIN for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { if (DemulShooter_Plugin.PluginControllers[i].GetButtonDown(PluginController.MyInputButtons.Coin)) { UInt32 ibuffer = 1; //COIN to add byte[] b = new Byte[7]; b[0] = 0xCC; b[1] = (byte)( i + 1); //PLAYER ID b[2] = (byte)((ibuffer >> 12) & 0x0F); b[3] = (byte)((ibuffer >> 8) & 0x0F); b[4] = (byte)((ibuffer >> 4) & 0x0F); b[5] = (byte)(ibuffer & 0x0F); b[6] = 0; ___m_recv_pack.Add(b); } } //Reload command for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { if (DemulShooter_Plugin.PluginControllers[i].GetButtonDown(PluginController.MyInputButtons.Reload)) { try { if (GameUIController.Instance.playerUI[i] != null) GameUIController.Instance.playerUI[i].ReLoadBullets(); } catch { } } } //Putting results into BaseCom array given as parameter for further actions for (int index = 0; index < ___m_recv_pack.Count; ++index) { //DemulShooter_Plugin.MyLogger.LogMessage("Received Data Packet = " + ByteArrayToString(___m_recv_pack[index])); msg_list.Add(___m_recv_pack[index]); } ___m_recv_pack.Clear(); return false; } } /// /// Only used by BaseCom.SendGunSignal() /// ??? Purpose ??? /// Frame = FF F8 [P1] [P2] [P3] [P4] etc... /// [HarmonyPatch(typeof(LSerialPort), "DiretSend")] class DiretSend { static bool Prefix(byte[] buf) { //DemulShooter_Plugin.MyLogger.LogMessage("mLSerialPort.DirectSend(byte[]) => data_buf = " + ByteArrayToString(buf)); return false; } } [HarmonyPatch(typeof(LSerialPort), "SendData", new[] { typeof(byte []), typeof(int) })] class SendData { static bool Prefix(List ___m_recv_pack, byte[] data_buf, int send_count = 1) { byte[] destinationArray = new byte[8]; destinationArray[0] = byte.MaxValue; Array.Copy((Array) data_buf, 0, (Array) destinationArray, 1, data_buf.Length); //DemulShooter_Plugin.MyLogger.LogMessage("mLSerialPort.SendData(byte[]) => data_buf = " + ByteArrayToString(destinationArray)); if (destinationArray[1] == 0x5A) //CheckIO Board { ___m_recv_pack.Add(data_buf); //reply with same packet } else { if (destinationArray[1] == 0xF9) //SendOpen() { //for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) //{ // DemulShooter_Plugin.OutputData.GunOpened[i] = destinationArray[2 + i]; //} } else if (destinationArray[1] == 0xD6) //SendTestGun() but only activate recoil on repeated shoot (trigger maintained) { /*for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) { DemulShooter_Plugin.OutputData.Recoil[i]= destinationArray[2 + i]; }*/ } else if (destinationArray[1] == 0xD9) //SendState() { //for (int i = 0; i < DemulShooter_Plugin.MAX_PLAYERS; i++) //{ // DemulShooter_Plugin.OutputData.GunState[i] = destinationArray[2 + i]; //} } else if (destinationArray[1] == 0xBB) //Clear Lack Ticket { } else if (destinationArray[1] == 0xD0) //Open Click Verify { //Do nothing, Just sent once at start } else if (destinationArray[1] == 0xEE) //Send Tickets { } } return false; } } /// /// This one seems to be unused /// [HarmonyPatch(typeof(LSerialPort), "SendData", new[] { typeof(List), typeof(int) })] class SendData_1 { static bool Prefix(List data_buf, int send_count = 1) { DemulShooter_Plugin.MyLogger.LogMessage("mLSerialPort.SendData(List)"); byte[] destinationArray = new byte[data_buf.Count]; Array.Copy((Array)data_buf.ToArray(), 0, (Array)destinationArray, 0, data_buf.Count); //DemulShooter_Plugin.MyLogger.LogMessage("data_buf = " + ByteArrayToString(destinationArray)); /*this.frame_ready.WaitOne(); this.frame_ready.Reset(); for (int index = 0; index < send_count; ++index) this.m_send_pack.Add(destinationArray);*/ return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mMVSettings.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mMVSettings { /// /// Lot of Input.GetKeyDown() calls for Debug ? /// [HarmonyPatch(typeof(MVSettings), "Update")] class Update { static bool Prefix() { return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mPlayerData.cs ================================================ using HarmonyLib; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mPlayerData { /// /// Get Damaged Output info /// [HarmonyPatch(typeof(PlayerData), "DelLife")] class Start { static bool Prefix(PlayerData __instance, int delNum) { //DemulShooter_Plugin.MyLogger.LogMessage("PlayerData.DelLife(): delNum=" + delNum); DemulShooter_Plugin.OutputData.Damaged[(int)__instance.playerTpye] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mPlayerUIController.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mPlayerUIController { /// /// Send Ammo to DemulShooter /// [HarmonyPatch(typeof(PlayerUIController), "Update")] class Update { static bool Prefix(PlayerData ___playerData, int ___maxBullets, int ___bulletIndex) { try { DemulShooter_Plugin.OutputData.Ammo[(int)___playerData.playerTpye] = (___maxBullets - ___bulletIndex); } catch { } return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mSettingController.cs ================================================ using System; using HarmonyLib; using MVSDK; using UnityEngine; using UnityEngine.UI; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mSettingController { /// /// Remove Keyboard /// [HarmonyPatch(typeof(SettingController), "Update")] class Update { static bool Prefix(Text ___dateTxt, bool ___ShowRealPointCount, ref float ___lastCountTime, ref int ___realPointCount) { ___dateTxt.text = DateTime.Now.AddSeconds(GameData.DateTimeOffset).ToString("yyyy-MM-dd HH:mm:ss"); if (___ShowRealPointCount) { if (Time.time - ___lastCountTime <= 2f) { if (BaseGun.Instance != null && BaseGun.Instance.GetGunRealPixPos(0) == BaseGun.InvalidPoint) { ___realPointCount++; } } else { if (MVSettings.Instance != null) { MVSettings.Instance.textOut.text = ___realPointCount.ToString(); } if (LightColorController.HadInstance()) { LightColorController.Instance().realCountTxt.text = ___realPointCount.ToString(); } ___lastCountTime = Time.time; ___realPointCount = 0; } } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mShootController.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { internal class mShootController { /// /// Geting In-Game recoil /// [HarmonyPatch(typeof(ShootController), "HitDown")] class HitDown { static bool Prefix(Vector3 hitPoint, PlayerType playerType, bool delBulletFlag = true) { //DemulShooter_Plugin.MyLogger.LogWarning("ShootController.HitDown(): playerType=" + playerType); if (GameData.GetPlayerData(playerType).PlayGaming() && GameUIController.Instance.CanShoot(playerType) && GameUIController.Instance.begin) DemulShooter_Plugin.OutputData.Recoil[(int)playerType] = 1; return true; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Patch/mShowGamePointController.cs ================================================ using HarmonyLib; using UnityEngine; namespace BepInEx_DemulShooter_Plugin.Patch { class mShowGamePointController { /// /// Displaying cursors on Level Select screen /// Looks like X must be in range [0-1920] whatever the resolution is /// And Y in range [0-Ymax], Ymax depending on the original screen ratio (based on 1920x1080) /// [HarmonyPatch(typeof(ShowGamePointController), "Update")] class Update { static bool Prefix(ShowGamePointController __instance) { for (int index = 0; index < __instance.gunPoint.Length; ++index) { if (GameData.GetPlayerData(index).CanShoot() && DemulShooter_Plugin.CrossHairVisibility) { Vector2 v = DemulShooter_Plugin.PluginControllers[index].GetAimingPosition(); float fRatio = (float)Screen.width / Screen.height; v.x = v.x / (float)Screen.width * 1920.0f; v.y = v.y / (float)Screen.height * 1920.0f / fRatio; __instance.gunPoint[index].mRectTF.anchoredPosition = v; } else __instance.gunPoint[index].transform.localPosition = new Vector3(-1000f, -1000f); } return false; } } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/PluginController.cs ================================================ using UnityEngine; namespace UnityPlugin_BepInEx_Core { public class PluginController { private const int INPUTBUTTONS_LENGTH = 5; public enum MyInputButtons { Start = 0, Trigger, Action, Reload, Coin } private int _ID = 0; public int ID { get { return _ID; } } public PluginControllerButton[] InputButtons; public float Axis_X { get; private set; } public float Axis_Y { get; private set; } public PluginController(int ID) { _ID = ID; InputButtons = new PluginControllerButton[INPUTBUTTONS_LENGTH]; for (int i = 0; i < INPUTBUTTONS_LENGTH; i++) { InputButtons[i] = new PluginControllerButton(); } InputButtons[(int)MyInputButtons.Start].SetKeyCode(49 + ID); InputButtons[(int)MyInputButtons.Coin].SetKeyCode(53 + ID); } public void SetAimingValues(Vector3 Position) { Axis_X = Position.x; Axis_Y = Position.y; } public void SetButton(MyInputButtons ButtonId, byte Value) { InputButtons[(int)ButtonId].SetButton(Value == 1 ? true : false); } public bool GetButton(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButton(); } public bool GetButtonDown(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonDown(); } public bool GetButtonUp(MyInputButtons ButtonId) { return InputButtons[(int)ButtonId].GetButtonUp(); } public Vector3 GetAimingPosition() { return new Vector3(Axis_X, Axis_Y); } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/PluginControllerButton.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class PluginControllerButton { public bool ButtonState { get; private set; } public bool PreviousButtonState { get; private set; } private bool _FlagButtonDown; private bool _FlagButtonUp; public int KeyCode { get; private set; } public PluginControllerButton(int iKeyCode = 0) { KeyCode = iKeyCode; } public void SetKeyCode(string StrValue) { int iResult; if (int.TryParse(StrValue, out iResult)) KeyCode = iResult; } public void SetKeyCode(int iValue) { KeyCode = iValue; } public void SetButton(bool Value) { //Setting Up/Down events if (ButtonState != Value) { if (Value) { _FlagButtonDown = true; _FlagButtonUp = false; } else { _FlagButtonDown = false; _FlagButtonUp = true; } } ButtonState = Value; } public bool GetButton() { return ButtonState; } public bool GetButtonDown() { if (_FlagButtonDown) { _FlagButtonDown = false; return true; } return false; } public bool GetButtonUp() { if (_FlagButtonUp) { _FlagButtonUp = false; return true; } return false; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. [assembly: AssemblyTitle("WWS_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("WWS_BepInEx_DemulShooter_Plugin")] [assembly: AssemblyCopyright("Argonlefou © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de // COM, affectez la valeur true à l'attribut ComVisible sur ce type. [assembly: ComVisible(false)] // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM [assembly: Guid("be701bda-57e2-4631-b834-07f500f00d74")] // Les informations de version pour un assembly se composent des quatre valeurs suivantes : // // Version principale // Version secondaire // Numéro de build // Révision // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("17.0.0.0")] [assembly: AssemblyFileVersion("17.0.0.0")] ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/TcpData.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx_DemulShooter_Plugin; namespace UnityPlugin_BepInEx_Core { [StructLayout(LayoutKind.Sequential)] public class TcpData { private BinaryWriter _Writer; private BinaryReader _Reader; private MemoryStream _MStream; private FieldInfo[] _DataFields; /// /// Usign Reflexion to automatically init data array to desired length /// public TcpData(int PlayerNumber) { try { //GetFields does not guarantee a fix order for the list of fields //To ensure a known order, adding a reordering condition to get the fields sorting by the MetadataToken property (= Declaration Order) or Alphabetical order //But it's better to get it by Name to be 100% sure of the order (member inheritance, etc...) //See https://github.com/dotnet/runtime/issues/19732 _DataFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(field => field.Name).ToArray(); foreach (FieldInfo fi in _DataFields) { //Only creating Player-based array. IF An array is already existing and initialized, do not iverwrite it if (fi.FieldType.IsArray && fi.GetValue(this) == null) { object o = Array.CreateInstance(fi.FieldType.GetElementType(), PlayerNumber); fi.SetValue(this, o); DemulShooter_Plugin.MyLogger.LogMessage(this.GetType().ToString() + " : Creating " + fi.FieldType.GetElementType().ToString() + " [" + PlayerNumber + "] " + fi.Name); } } } catch (Exception ex) { DemulShooter_Plugin.MyLogger.LogError("TcpOutputData.Ctor(): " + ex.Message.ToString()); } } /// /// Serialize Class fields to Byte array /// Using Reflexion to convert each field to Byte[] /// /// public byte[] ToByteArray() { byte[] bResult = new byte[1]; using (_MStream = new MemoryStream()) { using (_Writer = new BinaryWriter(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { WriteBytes(ElementType, a.GetValue(i)); } } else { WriteBytes(fi.FieldType, fi.GetValue(this)); } } } bResult = _MStream.ToArray(); } return bResult; } /// /// Filling the Fields from a byte array, using reflexion to get the fields in a known order /// public void Update(byte[] ReceivedData) { using (_MStream = new MemoryStream(ReceivedData)) { using (_Reader = new BinaryReader(_MStream)) { foreach (FieldInfo fi in _DataFields) { if (fi.FieldType.IsArray) { Type ElementType = fi.FieldType.GetElementType(); Array a = (Array)fi.GetValue(this); for (int i = 0; i < a.Length; i++) { a.SetValue(ReadBytes(ElementType), i); } } else { fi.SetValue(this, ReadBytes(fi.FieldType)); } } } } } /// /// BinaryStream.Write() need a known Type to work /// private void WriteBytes(Type t, Object o) { if (t == typeof(bool)) _Writer.Write((bool)o); else if (t == typeof(byte)) _Writer.Write((byte)o); else if (t == typeof(char)) _Writer.Write((char)o); else if (t == typeof(decimal)) _Writer.Write((decimal)o); else if (t == typeof(double)) _Writer.Write((double)o); else if (t == typeof(float)) _Writer.Write((float)o); /*else if (t == typeof(nint)) _Writer.Write((nint)o); else if (t == typeof(nuint)) _Writer.Write((nuint)o);*/ else if (t == typeof(long)) _Writer.Write((long)o); else if (t == typeof(sbyte)) _Writer.Write((sbyte)o); else if (t == typeof(short)) _Writer.Write((short)o); else if (t == typeof(uint)) _Writer.Write((uint)o); else if (t == typeof(ulong)) _Writer.Write((ulong)o); else if (t == typeof(ushort)) _Writer.Write((ushort)o); else if (t == typeof(int)) _Writer.Write((int)o); } /// /// BinaryStream.Read() need a known Type to work /// private object ReadBytes(Type t) { if (t == typeof(bool)) return _Reader.ReadBoolean(); else if (t == typeof(byte)) return _Reader.ReadByte(); else if (t == typeof(char)) return _Reader.ReadChar(); else if (t == typeof(decimal)) return _Reader.ReadDecimal(); else if (t == typeof(double)) return _Reader.ReadDouble(); else if (t == typeof(float)) return _Reader.ReadSingle(); /*else if (t == typeof(nint)) return _Reader. else if (t == typeof(nuint)) return _Reader.*/ else if (t == typeof(long)) return _Reader.ReadInt64(); else if (t == typeof(sbyte)) return _Reader.ReadSByte(); else if (t == typeof(short)) return _Reader.ReadInt16(); else if (t == typeof(uint)) return _Reader.ReadUInt32(); else if (t == typeof(ulong)) return _Reader.ReadUInt64(); else if (t == typeof(ushort)) return _Reader.ReadUInt16(); else if (t == typeof(int)) return _Reader.ReadInt32(); else return null; } public override string ToString() { string s = string.Empty; byte[] b = this.ToByteArray(); for (int i = 0; i < b.Length; i++) { s += "0x" + b[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/TcpInputData.cs ================================================ namespace UnityPlugin_BepInEx_Core { public class TcpInputData : TcpData { //Game Inputs public float[] Axis_X = null; public float[] Axis_Y = null; public byte[] Trigger = null; public byte[] Reload = null; //Generic Inputs public byte HideCrosshairs = 0; public byte EnableInputsHack = 0; public TcpInputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/TcpOutputData.cs ================================================ using System; namespace UnityPlugin_BepInEx_Core { public class TcpOutputData : TcpData { public byte[] IsPlaying = null; public int[] Ammo = null; public int[] Life = null; public byte[] Recoil = null; public byte[] Damaged = null; public int[] Credits = null; public TcpOutputData(int PlayerNumer) : base(PlayerNumer) { } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/TcpPacket.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace UnityPlugin_BepInEx_Core { class TcpPacket { #region Variables private PacketHeader _Header; private byte[] _Payload; private int _PayloadLenght; /// /// Packet type /// /// public enum PacketHeader : byte { /// /// Packets received from Demulshooteer with Input data to control the Game /// /// Inputs = 1, /// /// Packets sent to Demulshooter with Outputs status to control LEDs and Recoils /// /// Outputs } #endregion #region Constructors public TcpPacket(PacketHeader Header) : this(new byte[0], Header) { } public TcpPacket(byte[] Payload, PacketHeader Header) { _Payload = Payload; _Header = Header; _PayloadLenght = _Payload.Length; } #endregion /// /// Return only the Header /// /// public PacketHeader GetHeader() { return _Header; } /// /// Return only the Payload /// /// public byte[] GetPayload() { return _Payload; } /// /// Return the full message Packets containier Lenght + Header + Payload /// /// public byte[] GetFullPacket() { List FormatedPacket = new List(); FormatedPacket.AddRange(BitConverter.GetBytes(_PayloadLenght + 1)); FormatedPacket.Add((byte)_Header); FormatedPacket.AddRange(_Payload); return FormatedPacket.ToArray(); } public override string ToString() { string s = string.Empty; for (int i = 0; i < _PayloadLenght; i++) { s += "0x" + _Payload[i].ToString("X2") + " "; } return s; } } } ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/UnityLibs/List_of_dll_to_copy_here.txt ================================================ Copy the following files from the original game : Assembly-CSharp.dll UnityEngine.UI.dll UnityEngine.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/UnityPlugin_BepInEx_WWS.csproj ================================================ Release AnyCPU 8.0.30703 2.0 {E449E30F-D61F-428B-BEC1-D716E716EB6D} Library Properties BepInEx_DemulShooter_Plugin WildWestShootout_BepInEx_DemulShooter_Plugin v3.5 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false UnityLibs\0Harmony.dll UnityLibs\Assembly-CSharp.dll False UnityLibs\BepInEx.dll UnityLibs\UnityEngine.dll False UnityLibs\UnityEngine.UI.dll ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/UnityPlugin_BepInEx_WWS.sln ================================================ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityPlugin_BepInEx_WWS", "UnityPlugin_BepInEx_WWS.csproj", "{E449E30F-D61F-428B-BEC1-D716E716EB6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Debug|Any CPU.Build.0 = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E449E30F-D61F-428B-BEC1-D716E716EB6D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: UnityPlugins/UnityPlugin_BepInEx_WWS/WildWestShootout_BepInEx_DemulShooter_Plugin.ini ================================================ [VIDEO] FORCE_RESOLUTION=0 WIDTH=1920 HEIGHT=1080 FULLSCREEN=1 [INPUT_KEYS] ;Set Keycode (decimal value) for each key. See following link for the value list: ;https://gist.github.com/Extremelyd1/4bcd495e21453ed9e1dffa27f6ba5f69 P1_START=49 P2_START=50 P1_COIN=53 P2_COIN=54 TEST=48 MENU_SELECT=13 EXIT=27