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