From 13c287c805f2afa8623d5ecdec58d9c0b5b988c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1l=20Gezergen?= Date: Thu, 1 Apr 2021 00:28:01 +0300 Subject: [PATCH] v3.0.0.1337 + Added an option to fix damage. * Replaced "Opponents" option with "Racers" and handled it properly. * Tournament option now gets added to the menu instead of replacing Free Run mode. + Added an option to show special vinyls. + Added Unlock All Things. (F5) + Added garage camera hacks. + Added debug options. + Added an option to adjust the game speed. + Added an option to change the game region. + Added an option to set starting cash. + Added an option to set starting style points. + Added an option to choose EA Trax skin. + Added options to disable sound/music. + Added Freeze Camera. (Pause/Break) + Added Auto Drive. (F6) + Added an option to show language select screen. + Added an option to skip movies. + Added an option to allow multiple instances of the game to be ran at the same time. * Organized the code. --- NFSUExtraOptions/CodeCaves.h | 281 +++++++++++++++ NFSUExtraOptions/ExtraOptionsStuff.h | 282 +++++++++++++++ NFSUExtraOptions/GlobalVariables.h | 30 ++ NFSUExtraOptions/HotkeyStuff.h | 124 +++++++ NFSUExtraOptions/InGameFunctions.h | 49 +++ NFSUExtraOptions/NFSUExtraOptions.rc | Bin 0 -> 4722 bytes NFSUExtraOptions/NFSUExtraOptions.vcxproj | 49 ++- NFSUExtraOptions/PreRaceStatsScreen.h | 137 ++++++++ NFSUExtraOptions/QROptionsScreen.h | 45 +++ NFSUExtraOptions/UserCalls.h | 157 +++++++++ NFSUExtraOptions/dllmain.cpp | 252 +------------- NFSUExtraOptions/resource.h | 14 + NFSUExtraOptionsSettings.ini | 50 ++- README.md | 5 + README.txt | 66 ---- includes/IniReader.h | 398 ++++++++++++++++------ includes/ini_parser.hpp | 327 ++++++++++++++++++ includes/injector/assembly.hpp | 6 +- includes/injector/calling.hpp | 1 + includes/injector/gvm/gvm.hpp | 9 +- includes/injector/injector.hpp | 3 +- 21 files changed, 1843 insertions(+), 442 deletions(-) create mode 100644 NFSUExtraOptions/CodeCaves.h create mode 100644 NFSUExtraOptions/ExtraOptionsStuff.h create mode 100644 NFSUExtraOptions/GlobalVariables.h create mode 100644 NFSUExtraOptions/HotkeyStuff.h create mode 100644 NFSUExtraOptions/InGameFunctions.h create mode 100644 NFSUExtraOptions/NFSUExtraOptions.rc create mode 100644 NFSUExtraOptions/PreRaceStatsScreen.h create mode 100644 NFSUExtraOptions/QROptionsScreen.h create mode 100644 NFSUExtraOptions/UserCalls.h create mode 100644 NFSUExtraOptions/resource.h create mode 100644 README.md delete mode 100644 README.txt create mode 100644 includes/ini_parser.hpp diff --git a/NFSUExtraOptions/CodeCaves.h b/NFSUExtraOptions/CodeCaves.h new file mode 100644 index 0000000..ed32c7b --- /dev/null +++ b/NFSUExtraOptions/CodeCaves.h @@ -0,0 +1,281 @@ +#include "stdio.h" +#include +#include "..\includes\injector\injector.hpp" +#include "..\includes\IniReader.h" + +char* TakeoverString = "NFSU Extra Options - © 2021 ExOpts Team. No rights reserved."; +bool ShouldShowLapPopup; + +// 0x4DF385 +void __declspec(naked) SplashScreenCodeCave() +{ + _asm + { + call FEngSetVisible // edi = package, edx = object + mov eax, [ebp + 0xC] // package name + mov edx, 0x6C3A3C // "LicenseBlurb" + push TakeoverString + call FEngPrintf // eax = package, edx = object, push fmt + add esp,4 + push 0x4DF38A + retn + } +} + +void __declspec(naked) StartingCashCodeCave() +{ + _asm + { + push ebx + mov ebx, StartingCash + mov dword ptr ds : [esi + 0x1374] , ebx + pop ebx + push 0x5A0A45 + retn + } +} + +void __declspec(naked) TournamentCodeCave() +{ + _asm + { + call QRModeSelectScreen_MaybeMakeGameModeAvailable + + newcode : + mov ebx, [esp + 0x38] + mov al, byte ptr ds : [0x7589FE] + test al, al + jz caveexit + cmp dword ptr ds : [0x777B4C] , 04 + jz caveexit + + mov edx, dword ptr ds : [esp + 0x0C] + inc edx + push edx + push 0x6C5F64 //"Mode_%d" + push 0x20 + lea esi, [esp + 0x20] + mov dword ptr ds : [esp + 0x18] , edx + call FEngSNPrintf + mov esi, dword ptr ds : [ebx + 0x0C] + add esp, 0x0C + lea edx, [esp + 0x14] + call FEHashUpper + test esi, esi + mov edi, eax + je loc_4B7988 + mov eax, esi + call FEngFindPackage + test eax, eax + je loc_4B7988 + mov ecx, edi + mov edx, eax + call FEPackage_FindObjectByHash + test eax, eax + jne loc_4B79A1 + + loc_4B7988 : + mov ebx, dword ptr ds : [0x73578C] + test ebx, ebx + je loc_4B79B7 + mov eax, [ebx + 8] + push edi + mov ecx, esi + call FEngine_FindObject + test eax, eax + je loc_4B79B1 + + loc_4B79A1 : + cmp dword ptr ds : [eax + 0x18] , 2 + jne loc_4B79B1 + mov ebx, dword ptr ds : [0x73578C] + mov esi, eax + jmp loc_4B79B9 + + loc_4B79B1 : + mov ebx, dword ptr ds : [0x73578C] + + loc_4B79B7 : + xor esi, esi + + loc_4B79B9 : + push esi + call FEngSetVisible_obj + add esp, 4 + test ebx, ebx + je loc_4B79E0 + push 1 + lea edx, [esp + 0x18] + call FEHashUpper + mov edi, eax + mov eax, [esp + 0x3C] + mov eax, [eax + 0x0C] + push ebx + call cFEng_SetButtonState + + loc_4B79E0 : + mov edx, 0x6C5F04 //"GENERICPLACEHOLDER" + or ecx, -1 + mov al, 0x47 + lea ebx, [ebx + 0] + + loc_4B79F0 : + imul ecx, ecx, 0x21 + movzx eax, al + add ecx, eax + mov al, [edx + 01] + inc edx + test al, al + jne loc_4B79F0 + push ecx + push 0xCDA558A7 // "TOURNAMENT" + push 0x80E29CC5 // desc + mov edx, 0x6C5EDC + call FEHashUpper + mov ecx, [esp + 0x44] + push eax + push 0 + push ecx + mov ebx, 4 + call QRModeSelectScreen_MaybeMakeGameModeAvailable + + + caveexit : + push 0x4B7927 + retn + } +} + +// 0x4BB3C4 +void __declspec(naked) LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage() +{ + _asm + { + mov al, ShouldShowLapPopup + test al,al + jz originalcode + + showlappopup: + mov eax,[ebp+0x0C] + push 255 + mov edi,0x6C210C // "MU_QuickRaceLapPopup_PC.fng" + call FEngQueuePackagePush + add esp,04 + mov ShouldShowLapPopup,0 + jmp caveexit + + originalcode: + mov eax, [ebp + 0x0C] + push 255 + mov edi, 0x6C2588 // "MU_QuickRaceOptions_PC.fng" + call FEngQueuePackagePush + add esp, 04 + + caveexit: + push 0x4BB3D9 + retn + } +} + +//0x4BB41A +void __declspec(naked) LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage_2() +{ + _asm + { + mov ecx, dword ptr ds : [0x777CC8] // CurrentRaceMode + cmp ecx, 0 // Circuit + je ShowLapPopup + cmp ecx, 2 // Lap KO + je ShowLapPopup + cmp ecx, 4 // Tournament + je ShowLapPopup + cmp ecx, 6 // Drift + je ShowLapPopup + + DontShowLapPopup : + mov ShouldShowLapPopup, 0 + jmp caveexit + + ShowLapPopup : + mov ShouldShowLapPopup, 1 + + caveexit : + push 0x4BB420 + retn + } +} + +//0x4B7B88 +void __declspec(naked) LapPopupCodeCave_QRModeSelectScreen_NotificationMessage() +{ + _asm + { + mov dword ptr ds:[0x777CC8], edx // CurrentRaceMode + cmp edx, 0 // Circuit + je ShowLapPopup + cmp edx, 2 // Lap KO + je ShowLapPopup + cmp edx, 4 // Tournament + je ShowLapPopup + cmp edx, 6 // Drift + je ShowLapPopup + + DontShowLapPopup: + mov ShouldShowLapPopup, 0 + jmp caveexit + + ShowLapPopup: + mov ShouldShowLapPopup, 1 + + caveexit: + push 0x4B7B8E + retn + } +} + +//0x4BD748 +void __declspec(naked) LapPopupCodeCave_QRLapPopupScreen_NotificationMessage() +{ + _asm + { + mov eax, [esi + 0x0C] + push 255 + mov edi, 0x6C2588 // "MU_QuickRaceOptions_PC.fng" + call FEngQueuePackagePush + add esp, 4 + pop edi + pop esi + retn 0x10 + } +} + +//0x4BD740 +void __declspec(naked) LapPopupCodeCave_QRLapPopupScreen_NotificationMessage_2() +{ + _asm + { + mov ShouldShowLapPopup, 1 + add esp, 4 + pop edi + pop esi + retn 0x10 + } +} + +void __declspec(naked) SkipMovieCodeCave() +{ + _asm + { + mov eax, [_SkipMovies] + test eax, eax + jz loc_5A4A5D + push 0xC3960EB9 + mov edx, _gFEPackageManager + call FEPackageManager_BroadcastMessage + retn + + loc_5A4A5D: + push 0x5A4A5D + retn + } +} diff --git a/NFSUExtraOptions/ExtraOptionsStuff.h b/NFSUExtraOptions/ExtraOptionsStuff.h new file mode 100644 index 0000000..325ac38 --- /dev/null +++ b/NFSUExtraOptions/ExtraOptionsStuff.h @@ -0,0 +1,282 @@ +#include "stdio.h" +#include +#include "..\includes\injector\injector.hpp" +#include "..\includes\IniReader.h" + +#include "GlobalVariables.h" +#include "InGameFunctions.h" +#include "UserCalls.h" + +int hotkeyUnlockAllThings, hotkeyAnyTrackInAnyMode, hotkeyFreezeCamera, hotkeyAutoDrive, GameRegion, StartingCash, StartingStylePoints, MinLaps, MaxLaps, MinRacers, MaxRacers, EATraxSkin, WindowedMode, WindowedModeX, WindowedModeY; +bool ShowMoreRaceOptions, ShowLanguageSelectScreen, AnyTrackInAnyMode, MorePaintTypes, ShowSpecialVinyls, ExOptsTeamTakeOver, EnableHiddenCameras, UnlockAllThings, ShowTournament, GarageZoom, GarageRotate, ShowDebugOptionsMenu, SkipMovies, EnableSound, EnableMusic, AllowMultipleInstances, once1, once2, WheelFix, DamageFix, EnablePreRaceStats; +float SplashScreenTimeLimit, GameSpeed, MiniWorldCarSpeedCruising, MiniWorldCarSpeedFast, MiniWorldCarSpeedSlow; + +#include "QROptionsScreen.h" +#include "PreRaceStatsScreen.h" + +#include "CodeCaves.h" +#include "HotkeyStuff.h" + +void Init() +{ + CIniReader iniReader("NFSUExtraOptionsSettings.ini"); + + // Hotkeys + hotkeyAnyTrackInAnyMode = iniReader.ReadInteger("Hotkeys", "AnyTrackInAnyMode", 36); // HOME + hotkeyFreezeCamera = iniReader.ReadInteger("Hotkeys", "FreezeCamera", 19); // Pause/Break + hotkeyUnlockAllThings = iniReader.ReadInteger("Hotkeys", "UnlockAllThings", 116); //F5 + hotkeyAutoDrive = iniReader.ReadInteger("Hotkeys", "AutoDrive", 117); //F6 + + // LapControllers + MinLaps = iniReader.ReadInteger("LapControllers", "Minimum", 0); + MaxLaps = iniReader.ReadInteger("LapControllers", "Maximum", 127); + + // RacerControllers + MinRacers = iniReader.ReadInteger("RacerControllers", "Minimum", 1); + MaxRacers = iniReader.ReadInteger("RacerControllers", "Maximum", 4); + + // Menu + ShowMoreRaceOptions = iniReader.ReadInteger("Menu", "ShowMoreRaceOptions", 1) != 0; + ShowLanguageSelectScreen = iniReader.ReadInteger("Menu", "ShowLanguageSelectScreen", 0) != 0; + ShowSpecialVinyls = iniReader.ReadInteger("Menu", "ShowSpecialVinyls", 1) != 0; + ShowDebugOptionsMenu = iniReader.ReadInteger("Menu", "ShowDebugOptionsMenu", 0) != 0; + ShowTournament = iniReader.ReadInteger("Menu", "ShowTournament", 0) != 0; + EATraxSkin = iniReader.ReadInteger("Menu", "EATraxSkin", -1); + AnyTrackInAnyMode = iniReader.ReadInteger("Menu", "AnyTrackInAnyRaceMode", 0) != 0; + SplashScreenTimeLimit = iniReader.ReadFloat("Menu", "SplashScreenTimeLimit", 30.0f); + MiniWorldCarSpeedFast = iniReader.ReadFloat("Menu", "MiniWorldCarSpeedFast", 50.0f); + MiniWorldCarSpeedCruising = iniReader.ReadFloat("Menu", "MiniWorldCarSpeedCruising", 20.0f); + MiniWorldCarSpeedSlow = iniReader.ReadFloat("Menu", "MiniWorldCarSpeedSlow", 1.0f); + GarageZoom = iniReader.ReadInteger("Menu", "ShowcaseCamInfiniteZoom", 0) != 0; + GarageRotate = iniReader.ReadInteger("Menu", "ShowcaseCamInfiniteRotation", 0) != 0; + ExOptsTeamTakeOver = iniReader.ReadInteger("Menu", "DisableTakeover", 0) == 0; + + // Gameplay + EnableHiddenCameras = iniReader.ReadInteger("Gameplay", "EnableHiddenCameraModes", 0) != 0; + EnablePreRaceStats = iniReader.ReadInteger("Gameplay", "EnablePreRaceStats", 0) != 0; + GameSpeed = iniReader.ReadFloat("Gameplay", "GameSpeed", 1.0f); + GameRegion = iniReader.ReadInteger("Gameplay", "GameRegion", 0); + StartingCash = iniReader.ReadInteger("Gameplay", "StartingCash", 0); + StartingStylePoints = iniReader.ReadInteger("Gameplay", "StartingStylePoints", 0); + UnlockAllThings = iniReader.ReadInteger("Gameplay", "UnlockAllThings", 0) != 0; + + // Fixes + WheelFix = iniReader.ReadInteger("Fixes", "DisappearingWheelsFix", 1) != 0; + DamageFix = iniReader.ReadInteger("Fixes", "DamageFix", 0) != 0; + + // Misc + WindowedMode = iniReader.ReadInteger("Misc", "WindowedMode", 0); + SkipMovies = iniReader.ReadInteger("Misc", "SkipMovies", 0) != 0; + EnableSound = iniReader.ReadInteger("Misc", "EnableSound", 1) != 0; + EnableMusic = iniReader.ReadInteger("Misc", "EnableMusic", 1) != 0; + AllowMultipleInstances = iniReader.ReadInteger("Misc", "AllowMultipleInstances", 0) == 1; + + // Restrictions + MinLaps %= 128; + MaxLaps %= 128; + MinRacers = (MinRacers - 1) % 4 + 1; + MaxRacers = (MaxRacers - 1) % 4 + 1; + + // Allow Multiple Instances + if (AllowMultipleInstances) + { + injector::WriteMemory(0x4472F1, 0xEB, true); + } + + // Set Lap Controllers + // QROptionsScreen::ScrollLaps + injector::WriteMemory(0x4B85AB, MinLaps, true); // Minimum Lap Controller for Increment + injector::WriteMemory(0x4B8587, MinLaps, true); // Minimum Lap Controller for Decrement + injector::WriteMemory(0x4B85A7, MaxLaps, true); // Maximum Lap Controller for Increment + injector::WriteMemory(0x4B858B, MaxLaps, true); // Maximum Lap Controller for Decrement + // QRLapPopupScreen::ScrollLaps + injector::WriteMemory(0x4BD4B3, MinLaps, true); // Minimum Lap Controller for Decrement + injector::WriteMemory(0x4BD4D9, MinLaps, true); // Minimum Lap Controller for Increment + injector::WriteMemory(0x4BD4BB, MaxLaps, true); // Maximum Lap Controller for Decrement + injector::WriteMemory(0x4BD4D5, MaxLaps, true); // Maximum Lap Controller for Increment + + // Enable Drift Camera Mode For All + if (EnableHiddenCameras) + { + injector::WriteMemory(0x42E091, 0xEB, true); // Player::CycleDriveCameraPovType + injector::MakeNOP(0x4BE78B, 2, true); + injector::MakeNOP(0x4BE749, 2, true); + } + + // Enable Pre-Race Stats from PS2 Demo + if (EnablePreRaceStats) + { + //injector::MakeNOP(0x495905, 2, true); // PreRaceStatsScreen::PreRaceStatsScreen + injector::WriteMemory(0x6C7A08, &PreRaceStatsScreen_NotificationMessage, true); // PreRaceStatsScreen::vtable + } + + // Splash Screen Time Limit + injector::WriteMemory(0x6ccac0, SplashScreenTimeLimit, true); + + // Any Track In Any Mode + if (AnyTrackInAnyMode) + { + injector::MakeNOP(0x4BA16C, 2, true); + injector::MakeNOP(0x4BA202, 2, true); + } + + // Show Language Select Screen + if (ShowLanguageSelectScreen) + { + injector::WriteMemory(0x6FA1A4, 0x6C2368, true); // BootFlowNTSC - "LS_LangSelect.fng" + injector::WriteMemory(0x6FA1BC, 0x6C2368, true); // BootFlowPAL - "LS_LangSelect.fng" + injector::MakeJMP(0x4DEEDA, 0x4DEF81, true); // Always show more languages + } + + // Show Tournament in Quick Race Menu + if (ShowTournament) + { + injector::MakeJMP(0x4B7922, TournamentCodeCave, true); // QRModeSelectScreen::BuildModeList + } + + // Fix racer counts (Old ExOpts fix) + //injector::WriteMemory(0x758980, 4, true); // Circuit + //injector::WriteMemory(0x7589A0, 4, true); // Sprint + //injector::WriteMemory(0x7589C0, 4, true); // LapKnockout + //injector::WriteMemory(0x7589E0, 1, true); // FreeRun + //injector::WriteMemory(0x758A04, 4, true); // Tournament + //injector::WriteMemory(0x758A88, 4, true); // Drag + //injector::WriteMemory(0x758AB4, 1, true); // Drift + + // Add Opponents option to Race Options menu + if (ShowMoreRaceOptions) + { + // Show lap popup + injector::MakeJMP(0x4BB3C4, LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage, true); + injector::MakeJMP(0x4BB41A, LapPopupCodeCave_QRTrackSelectScreen_NotificationMessage_2, true); + injector::MakeJMP(0x4B7B88, LapPopupCodeCave_QRModeSelectScreen_NotificationMessage, true); + injector::MakeJMP(0x4BD748, LapPopupCodeCave_QRLapPopupScreen_NotificationMessage, true); // Switch from lap popup to race options when pressed enter + injector::MakeJMP(0x4BD740, LapPopupCodeCave_QRLapPopupScreen_NotificationMessage_2, true); // Reactivate lap popup when pressed back + + // Fix Lap Controller for Lap KO - RaceStarter::SetupLapKO + injector::WriteMemory(0x4B4eb5, 0x10, true); // Read normal lap count instead of KO lap + injector::MakeNOP(0x4B4ebc, 5, true); // Don't fix it to 3 + + // TEMP - Overlap options 4 and 5 for Tournament Menu (to fix invisible 5th option text and data) + injector::WriteMemory(0x4B9108, 4, true); + + // Make Circuit, Sprint, Drag and KO use the same options screen + injector::MakeCALL(0x4B89BE, QROptionsScreen_SetupCircuit, true); // Sprint + injector::MakeCALL(0x4b89c9, QROptionsScreen_SetupCircuit, true); // KO + injector::MakeCALL(0x4B8A17, QROptionsScreen_SetupCircuit, true); // Drag + + // Show Racers Option + injector::WriteMemory(0x4B8EDF, QROptionsScreen_ScrollNumRacers, true); // QROptionsScreen::SetupCircuit + injector::MakeCALL(0x4B8EE3, QROptionsScreen_DrawNumRacers, true); // QROptionsScreen::SetupCircuit + injector::WriteMemory(0x4B90A5, QROptionsScreen_ScrollNumRacers, true); // QROptionsScreen::SetupTournament + injector::MakeCALL(0x4B90A9, QROptionsScreen_DrawNumRacers, true); // QROptionsScreen::SetupTournament + } + + // Show Special Vinyls + if (ShowSpecialVinyls) + { + // Unlock all vinyls + injector::WriteMemory(0x4E27D7, 0, true); + injector::WriteMemory(0x4AAA6D, 0xEB, true); + injector::MakeNOP(0x5A3577, 2, true); + + // No Restrictions + injector::WriteMemory(0x4E4791, 0x3AEB, true); // EB 3A - jmp 0x4E47CD + injector::MakeRangedNOP(0x4E46C9, 0x4E46E0, true); + injector::WriteMemory(0x4E46C9, 0xB8, true); // mov eax,0x00000000 + injector::WriteMemory(0x4E46CA, 0, true); + injector::WriteMemory(0x4E46CE, 0xB9, true); // mov ecx,0x00000000 + injector::WriteMemory(0x4E46CF, 0, true); + injector::WriteMemory(0x4E46D4, 0xBE, true); // mov esi,0x00000000 + injector::WriteMemory(0x4E46D5, 0, true); + injector::WriteMemory(0x4E46DA, 0xBB, true); // mov ebx,0x00000000 + injector::WriteMemory(0x4E46DB, 0, true); + } + + // Copyright text + if (ExOptsTeamTakeOver) + { + injector::MakeJMP(0x4DF385, SplashScreenCodeCave, true); + } + + // Fix Invisible Wheels + if (WheelFix) + { + injector::WriteMemory(0x5696E4, 0x01, true); // CarPartCuller::CullParts + } + + // Damage Fix + if (DamageFix) + { + injector::WriteMemory(0x465DA4, 0xEB, true); // Collision::CheckForWildCollision + } + + // Garage Hacks + if (GarageZoom) + { + injector::WriteMemory(0x48721D, 0xEB, true); + injector::WriteMemory(0x487236, 0xEB, true); + } + if (GarageRotate) + { + injector::WriteMemory(0x4871F2, 0xEB, true); + injector::WriteMemory(0x487209, 0xEB, true); + } + + // Game Speed + injector::WriteMemory(_Tweak_GameSpeed, GameSpeed, true); + + // Debug Menu + if (ShowDebugOptionsMenu) + { + injector::WriteMemory(0x006C56E4, 0x00000000, true); + } + + // EA Trax Skin + if (EATraxSkin != -1) + { + EATraxSkin %= 4; + injector::MakeNOP(0x4AA212, 3, true); // Don't zero it at boot + injector::WriteMemory(_ChyronSkin, EATraxSkin, true); + } + + // Game Region + injector::WriteMemory(_BuildRegion, GameRegion, true); + + // Starting Cash + injector::MakeRangedNOP(0x5A0A3F, 0x5A0A45, true); + injector::MakeJMP(0x5A0A3F, StartingCashCodeCave, true); + + // Starting Style Points + injector::WriteMemory(0x41CF8E, StartingStylePoints, true); + + // Unlock all things (Load preference) + if (UnlockAllThings) + { + injector::WriteMemory(_UnlockEverythingThatCanBeLocked, 1, true); + } + + // Garage Car Speed + injector::WriteMemory(0x6B6B28, MiniWorldCarSpeedFast, true); // While changing cars + injector::WriteMemory(0x6B6B2C, MiniWorldCarSpeedCruising, true); // Normal car speed + injector::WriteMemory(0x6B6B30, MiniWorldCarSpeedSlow, true); // Used on rim tuning and paint + + // Windowed Mode + if (WindowedMode > 0) + { + injector::WriteMemory(_WindowedMode, 1, true); // Vanilla Windowed Mode + } + + // Skip movies + if (SkipMovies) + { + injector::WriteMemory(_SkipMovies, 1, true); // Vanilla Skip Movies + injector::MakeJMP(0x5A4A50, SkipMovieCodeCave, true); // MovieStart (fix blank screen when skipped a movie) + } + + if (!EnableSound) injector::WriteMemory(_IsSoundEnabled, 0, true); // Enable sound + if (!EnableMusic) injector::WriteMemory(_IsAudioStreamingEnabled, 0, true); // Enable music + + // Other Things + injector::MakeCALL(0x4479BE, Thing, true); +} \ No newline at end of file diff --git a/NFSUExtraOptions/GlobalVariables.h b/NFSUExtraOptions/GlobalVariables.h new file mode 100644 index 0000000..58aea7c --- /dev/null +++ b/NFSUExtraOptions/GlobalVariables.h @@ -0,0 +1,30 @@ +#pragma once + +#include "stdio.h" +#include + +// Global variables +#define _TheGameFlowManager 0x77A920 +#define _gFEPackageManager 0x746104 +#define _WindowedMode 0x73637C +#define _hWnd 0x736380 +#define _IsLostFocus 0x7363B6 +#define _ResX 0x701034 +#define _ResY 0x701038 +#define _UnlockEverythingThatCanBeLocked 0x735EC1 +#define _FEDatabase 0x748F70 +#define _BuildRegion 0x734998 +#define _ChyronSkin 0x758928 +#define _Tweak_GameSpeed 0x6B7994 +#define _SkipMovies 0x73496C +#define _IsSoundEnabled 0x6F1DD8 +#define _IsAudioStreamingEnabled 0x6F1DDC +#define _pPreRaceScreenManager 0x735ED8 +#define _pRaceCoordinator 0x7361F0 +#define _bIsDragRace 0x7361A8 +#define _unk_78A344 0x78A344 +#define _NumberOfPlayers 0x78A320 +#define _PlayersByIndex 0x7361BC +#define _PlayersByNumber 0x7361B4 +#define _pCurrentRace 0x73619C +#define _Camera_StopUpdating 0x735F5C diff --git a/NFSUExtraOptions/HotkeyStuff.h b/NFSUExtraOptions/HotkeyStuff.h new file mode 100644 index 0000000..aa31848 --- /dev/null +++ b/NFSUExtraOptions/HotkeyStuff.h @@ -0,0 +1,124 @@ +#include "stdio.h" +#include +#include "..\includes\injector\injector.hpp" +#include "..\includes\IniReader.h" + +bool once3, IsOnFocus, AutoDrive; +DWORD TheGameFlowManager; +HWND windowHandle; + +void Thing() +{ + UpdateCameraMovers(); // Hijacked call from main loop + + _asm pushad; + + TheGameFlowManager = *(DWORD*)_TheGameFlowManager; // 3 = FE, 4&5 = Loading screen, 6 = Gameplay + windowHandle = *(HWND*)_hWnd; + IsOnFocus = !(*(bool*)_IsLostFocus); + + // Windowed Mode Related Fixes (Center and Resize) + if (WindowedMode && windowHandle && !once3) + { + RECT o_cRect, n_cRect, n_wRect; + GetClientRect(windowHandle, &o_cRect); + + DWORD wStyle = GetWindowLongPtr(windowHandle, GWL_STYLE); + + switch (WindowedMode) + { + case 1: wStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU); break; + case 2: default: wStyle |= (WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_OVERLAPPEDWINDOW); break; + } + + SetWindowLongPtr(windowHandle, GWL_STYLE, wStyle); + + // make window change style + SetWindowPos(windowHandle, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME); + + //GetWindowRect(windowHandle, &n_wRect); + GetClientRect(windowHandle, &n_cRect); + int n_wWidth = *(int*)_ResX; + int n_wHeight = *(int*)_ResY; + int dif_wWidth = o_cRect.right - n_cRect.right; + int dif_wHeight = o_cRect.bottom - n_cRect.bottom; + int newWidth = n_wWidth + dif_wWidth; + int newHeight = n_wHeight + dif_wHeight; + + HWND hDesktop = GetDesktopWindow(); + RECT desktop; + GetWindowRect(hDesktop, &desktop); + + int newXPos = ((desktop.right - desktop.left) - newWidth) / 2; + int newYPos = ((desktop.bottom - desktop.top) - newHeight) / 2; + + SetWindowPos(windowHandle, NULL, newXPos, newYPos, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + once3 = 1; + } + + // Lock EA Trax Skin + if (EATraxSkin != -1) + { + injector::WriteMemory(_ChyronSkin, EATraxSkin, true); + } + + // Any Track in Any Mode + if ((GetAsyncKeyState(hotkeyAnyTrackInAnyMode) & 1)) + { + CIniReader iniReader("NFSUExtraOptionsSettings.ini"); + AnyTrackInAnyMode = !AnyTrackInAnyMode; + iniReader.WriteInteger("Menu", "AnyTrackInAnyRaceMode", AnyTrackInAnyMode); + + if (AnyTrackInAnyMode) + { + injector::MakeNOP(0x4BA16C, 2, true); + injector::MakeNOP(0x4BA202, 2, true); + } + + else + { + injector::WriteMemory(0x4BA16C, 0x1D74, true); + injector::WriteMemory(0x4BA202, 0x2C74, true); + } + } + + // Drunk Driver + if ((GetAsyncKeyState(hotkeyAutoDrive) & 1) && (TheGameFlowManager == 6) && IsOnFocus) + { + AutoDrive = !AutoDrive; + + DWORD PlayerThing = *(DWORD*)_PlayersByIndex; + + + if (PlayerThing) + { + if (AutoDrive) + { + Player_AutoPilotOn((DWORD*)PlayerThing); + } + else + { + MakeUserCall(Player_AutoPilotOff, 1, edi, PlayerThing); + } + } + } + + // Freeze Camera + if ((GetAsyncKeyState(hotkeyFreezeCamera) & 1) && IsOnFocus) + { + *(bool*)_Camera_StopUpdating = !(*(bool*)_Camera_StopUpdating); + } + + // Unlock All Things + if ((GetAsyncKeyState(hotkeyUnlockAllThings) & 1)) + { + UnlockAllThings = !UnlockAllThings; + CIniReader iniReader("NFSUExtraOptionsSettings.ini"); + iniReader.WriteInteger("Gameplay", "UnlockAllThings", UnlockAllThings); + + injector::WriteMemory(_UnlockEverythingThatCanBeLocked, UnlockAllThings, true); + } + + _asm popad; +} \ No newline at end of file diff --git a/NFSUExtraOptions/InGameFunctions.h b/NFSUExtraOptions/InGameFunctions.h new file mode 100644 index 0000000..440b2ec --- /dev/null +++ b/NFSUExtraOptions/InGameFunctions.h @@ -0,0 +1,49 @@ +#pragma once + +#include "stdio.h" +#include + +// Function pointers +int(__stdcall* Player_AutoPilotOn)(DWORD* _Player) = (int(__stdcall*)(DWORD*))0x42E230; +char*(__stdcall* Player_GetName)(DWORD* _Player) = (char* (__stdcall*)(DWORD*))0x431D30; +void(*UpdateCameraMovers)() = (void(*)())0x48E8B0; +void(*MovieStop)() = (void(*)())0x5A4C80; +void(*feDisableStartRaceScreens)() = (void(*)())0x495E90; +int(__thiscall* QRLapPopupScreen_DrawLaps)(DWORD* QRLapPopupScreen) = (int(__thiscall*)(DWORD*))0x4B81F0; +int(__thiscall* QROptionsScreen_DrawNumRaces)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8280; +int(__thiscall* QROptionsScreen_DrawCatchUp)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8310; +int(__thiscall* QROptionsScreen_DrawTraffic)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B83C0; +int(__thiscall* QROptionsScreen_DrawAISkill)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B84A0; +int(__thiscall* QROptionsScreen_ScrollLaps)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8560; +int(__thiscall* QROptionsScreen_ScrollNumRaces)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B85C0; +int(__thiscall* QROptionsScreen_ScrollCatchUp)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8650; +int(__thiscall* QROptionsScreen_ScrollTraffic)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B8690; +int(__thiscall* QROptionsScreen_ScrollAISkill)(DWORD* QROptionsScreen) = (int(__thiscall*)(DWORD*))0x4B86F0; +int(__fastcall* cFrontendDatabase_GetOptions)(DWORD* cFrontendDatabase, int eRaceType) = (int(__fastcall*)(DWORD*, int))0x4AB510; +int(__cdecl* FEngSetVisible_obj)(void* FEObject) = (int(__cdecl*)(void*))0x4F6970; +int(__stdcall* RaceCoordinator_RCPostMessage)(DWORD* RaceCoordinator, int RCMessage, int unk) = (int(__stdcall*)(DWORD*, int, int))0x421C00; + +// Functions which has odd calling conventions (using NFSUCalls.h to wrap them) +int(*Player_AutoPilotOff)(DWORD* EDI_Player) = (int(*)(DWORD*))0x42E400; +char*(*DriverInfo_GetName)(DWORD* EAX_DriverInfo) = (char*(*)(DWORD*))0x580AF0; +int(*QROptionsScreen_ShowSingleOption)(DWORD* QROptionsScreen, int a2) = (int(*)(DWORD*, int))0x4B8D50; +int(*FEHashUpper)(char const* EDX_StringToHash) = (int(*)(char const*))0x4FD230; +int(*FEngPrintf_obj)(DWORD* EAX_obj, const char* format, ...) = (int(*)(DWORD*, const char*, ...))0x4F68A0; +int(*FEngPrintf_H)(const char* EAX_pkg, int EDI_hash, const char* format, ...) = (int(*)(const char*, int, const char*, ...))0x4F6850; +int(*FEngPrintf)(const char* EAX_pkg, const char* EDX_obj_name, const char* format, ...) = (int(*)(const char*, const char*, const char*, ...))0x4F67F0; +int(*FEngSNPrintf)(const char* ESI_buf, int len, const char* format, ...) = (int(*)(const char*, int, const char*, ...))0x4F68C0; +int(*cFEng_SetButtonState)(DWORD* cFeng, char const* EAX_pkg, int EDI_hash, bool state) = (int(*)(DWORD*, char const*, int, bool))0x4F5F80; +int(*FEngQueuePackagePush)(char const* EAX_pkg, char const* EDI_pkg2, int state) = (int(*)(char const*, char const*, int))0x4F63E0; +int(*FEngSwitchPackages)(char const* pkg, char const* EDI_pkg2) = (int(*)(char const*, char const*))0x4F6360; +int(*FEngFindPackage)(char const* EAX_pkg) = (int(*)(char const*))0x4F65D0; +int(*FEPackage_FindObjectByHash)(DWORD* EDX_pkg, int ECX_hash) = (int(*)(DWORD*, int))0x4FFB70; +int(*FEPackageManager_BroadcastMessage)(DWORD* EDX_FEPackageManager, int msgHash) = (int(*)(DWORD*, int))0x4F3DC0; +int(*FEngine_FindObject)(DWORD* EAX_FEngine, unsigned int hash, const char* ECX_pkg) = (int(*)(DWORD*, unsigned int, char const*))0x4EF050; +int(*QROptionsScreen_SetupCircuit)(DWORD* EAX_QROptionsScreen) = (int(*)(DWORD*))0x4B8E60; +int(*FEngSetScript_obj)(DWORD* EDI_feObj, char const* script, bool unk) = (int(*)(DWORD*, char const*, bool))0x4F6B70; +int(*FEngSetScript_H)(char const* ESI_pkg, int EAX_hash, char const* script, bool unk) = (int(*)(char const*, int, char const*, bool))0x4F6C30; +int(*FEngSetScript)(char const* EAX_pkg, char const* EDX_obj_name, char const* script, bool unk) = (int(*)(char const*, char const*, char const*, bool))0x4F6BD0; +int(*PreRaceStatsScreen_SetVisibility)(DWORD* PreRaceStatsScreen, bool EAX_visible) = (int(*)(DWORD*, bool))0x4959D0; +bool(*FEngIsScriptRunning)(char const* EAX_pkg, int EDI_hash, char const* script) = (bool(*)(char const*, int, char const*))0x4F6C80; +int(*FEngSetVisible)(char const* EDI_pkg,char const* EDX_obj) = (int(*)(char const*, char const*))0x414A60; +DWORD QRModeSelectScreen_MaybeMakeGameModeAvailable = 0x4B6FE0; \ No newline at end of file diff --git a/NFSUExtraOptions/NFSUExtraOptions.rc b/NFSUExtraOptions/NFSUExtraOptions.rc new file mode 100644 index 0000000000000000000000000000000000000000..0a0947922ec70dd6d164733867e1b490319198e4 GIT binary patch literal 4722 zcmd6rYfl4T!Ni7(_)+YVG{S!^w=bd3- zmtAbYn2^mfyEAic@43ub{{Gpp4U6p9x^`s)>)VjKV(AjcDLQ$T#7z#pDpKhi*n8F z>o_{`-m_DyTf>@GvO_zu3p=wivn8HuR<)1J)cHNOeP*gW2l>)r^{${@7$d8I%Phtn zQ0m)P>>q)SaA;ul2YwH+x`>^^qs;s^_pC?$?}P6SUr(3BfAtUx8u{qB2P;KV2fLSx zjevaMyINz=P9Fb_XTR7l=l}J|d)Iz2^E$)t!&nw|9YU`X+0?dmFgqZQd=}Nom?rsD zB$6(Orz)5D{^W}0Qa0dmM$}%Q(U~1VA)Twt)|hQFR$OF;8SRslBVxC1@9izT>&)y= zXe7IgUVF?|*)4v$RXsY7cw4#9!iFQLOO9f-d>Pj!_%)34GCHhl)V&2-^KxJ7``BJ_ zRY7mj_{y%b=e9Ynv}KU1!yWEmM*)jenF+LE(#BKe%m^>u!#7pr7Bn zgO0j(#xzsp8uT%zAn5mPr`CPdOzsMjv+ICr>+8@$!UvTJd7I?wt=V-Jed0Hn<5QZfIu%yK*uszMZG=G%z10KV(R=nd^JI;p+4>C)+Mu?G)+b|b$`T6}zsQY!PXyW4>m4U8kB(pZu9JuV+}cV_&VFA+C+=jobH1dQhlcXjwHXu1D30WLtGnaIKM0IQJ?g0QTod}?RRnd2Yg{U - + Debug @@ -13,20 +13,21 @@ {3C558AD9-5F9C-4A14-8F07-800F46C132C7} Win32Proj - QRHiddenOptions + NFSUExtraOptions NFSUExtraOptions + 10.0 DynamicLibrary true - v140 + v142 NotSet DynamicLibrary false - v140 + v142 true MultiByte @@ -57,11 +58,22 @@ Level3 Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + UninitializedLocalUsageCheck + + + + + EditAndContinue Windows true + + copy "$(TargetDir)*.asi" "G:\Oyunlar\Need for Speed Underground\scripts" /y +copy "$(TargetDir)*.pdb" "G:\Oyunlar\Need for Speed Underground\scripts" /y + @@ -72,6 +84,11 @@ true WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreaded + Default + + + + Windows @@ -79,25 +96,33 @@ true true + + copy "$(TargetDir)*.asi" "G:\Oyunlar\Need for Speed Underground\scripts" /y + + + + + + + + + + - - false - - - false - - - + Create Create + + + diff --git a/NFSUExtraOptions/PreRaceStatsScreen.h b/NFSUExtraOptions/PreRaceStatsScreen.h new file mode 100644 index 0000000..7d7b35d --- /dev/null +++ b/NFSUExtraOptions/PreRaceStatsScreen.h @@ -0,0 +1,137 @@ +void __fastcall PreRaceStatsScreen_SetInitScript(DWORD* _PreRaceStatsScreen, void* EDX_Unused) +{ + int NumPlayers = _PreRaceStatsScreen[52]; + + for (int i = 0; i < NumPlayers; i++) + { + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 16], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 20], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 24], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 28], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 32], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 36], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 40], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 44], esp, "Init", esp, 1); + MakeUserCall(FEngSetScript_obj, 3, edi, _PreRaceStatsScreen[i + 48], esp, "Init", esp, 1); + } +} + +void __fastcall PreRaceStatsScreen_DrawCars(DWORD* _PreRaceStatsScreen, void* EDX_Unused) +{ + int NumPlayers = _PreRaceStatsScreen[52]; + DWORD* PCurrentRace = *(DWORD**)_pCurrentRace; + DWORD* CarPtr; + char* CarName; + + for (int i = 0; i < NumPlayers; i++) + { + if (i >= PCurrentRace[9]) CarPtr = 0; + else CarPtr = (DWORD*)PCurrentRace[328 + i]; + + if (CarPtr) + { + CarPtr = (DWORD*)CarPtr[0]; + if (CarPtr) + { + CarName = (char*)CarPtr[3]; + if (CarName) MakeUserCall(FEngPrintf_obj, 2, eax, _PreRaceStatsScreen[i + 16], esp, CarName); + } + } + } +} + +void __fastcall PreRaceStatsScreen_DrawDrivers(DWORD* _PreRaceStatsScreen, void* EDX_Unused) +{ + int NumPlayers = _PreRaceStatsScreen[52]; + DWORD* PCurrentRace = *(DWORD**)_pCurrentRace; + DWORD* Player = *(DWORD**)_PlayersByNumber; + DWORD* Player2 = *(DWORD**)(_PlayersByNumber + 4); + DWORD* CarPtr, *DriverInfo; + char* DriverName; + + for (int i = 0; i < NumPlayers; i++) + { + if (i >= PCurrentRace[9]) CarPtr = 0; + else CarPtr = (DWORD*)PCurrentRace[328 + i]; + + if (CarPtr) + { + CarPtr = (DWORD*)CarPtr[0]; + if (CarPtr) + { + DriverInfo = (DWORD*)CarPtr[5]; + if (DriverInfo) + { + // Check if Player's car + if (Player && (CarPtr == *(DWORD**)Player[1])) DriverName = Player_GetName(Player); + else if (Player2 && (CarPtr == *(DWORD**)Player2[1])) DriverName = Player_GetName(Player2); + else DriverName = (char*)MakeUserCall(DriverInfo_GetName, 1, eax, DriverInfo); + + MakeUserCall(FEngPrintf_obj, 4, eax, _PreRaceStatsScreen[i + 20], esp, "0%d: %s", esp, i + 1, esp, DriverName); + } + } + } + } +} + +void __fastcall PreRaceStatsScreen_NotificationMessage(DWORD* _PreRaceStatsScreen, void* EDX_Unused, DWORD msg, DWORD* FEObj, int arg1, int arg2) +{ + DWORD* PreRaceScreenManager; // eax + char* RaceHUDPkg; + + switch (msg) + { + case 0xB5AF2461: // PAD_START + RaceCoordinator_RCPostMessage(*(DWORD**)_pRaceCoordinator, 4, 0); + break; + case 0xC98356BA: + PreRaceScreenManager = *(DWORD**)_pPreRaceScreenManager; + + if (PreRaceScreenManager[1] && !_PreRaceStatsScreen[53]) + { + _PreRaceStatsScreen[53] = 1; + + PreRaceStatsScreen_SetInitScript(_PreRaceStatsScreen, EDX_Unused); + MakeUserCall(PreRaceStatsScreen_SetVisibility, 2, esp, _PreRaceStatsScreen, eax, 1); + PreRaceStatsScreen_DrawCars(_PreRaceStatsScreen, EDX_Unused); + PreRaceStatsScreen_DrawDrivers(_PreRaceStatsScreen, EDX_Unused); + + if (*(bool*)_unk_78A344 || *(bool*)_bIsDragRace) + { + RaceHUDPkg = "drag_HUD_test.fng"; + } + else if (*(int*)_NumberOfPlayers == 2) + { + RaceHUDPkg = "2PHudBot.fng"; + } + else + { + RaceHUDPkg = "HUD_SingleRace.fng"; + } + MakeUserCall(FEngSetScript, 4, eax, RaceHUDPkg, edx, "GaugeCluster", esp, "Fade", esp, 1); + } + + if (_PreRaceStatsScreen[49]) + { + if (_PreRaceStatsScreen[50]) + { + if (!(bool)MakeUserCall(FEngIsScriptRunning, 3, eax, _PreRaceStatsScreen[3], edi, 0x798C7EFF, esp, "FadeOut")) + { + if (_PreRaceStatsScreen[51]) RaceCoordinator_RCPostMessage(*(DWORD**)_pRaceCoordinator, 4, 0); + else PreRaceScreenManager[0] = 1; + } + } + else + { + _PreRaceStatsScreen[50] = 1; + MakeUserCall(FEngSetScript_H, 4, esi, _PreRaceStatsScreen[3], eax, 0x798C7EFF, esp, "FadeOut", esp, 1); + } + } + + if (PreRaceScreenManager[0]) feDisableStartRaceScreens(); // ReadyToSkip + break; + case 0xD8692111: + _PreRaceStatsScreen[49] = 1; + break; + } +} \ No newline at end of file diff --git a/NFSUExtraOptions/QROptionsScreen.h b/NFSUExtraOptions/QROptionsScreen.h new file mode 100644 index 0000000..48471e0 --- /dev/null +++ b/NFSUExtraOptions/QROptionsScreen.h @@ -0,0 +1,45 @@ +#pragma once + +#define _CurrentRaceMode 0x777CC8 + +int __fastcall QROptionsScreen_DrawNumRacers(DWORD* QROptionsScreen, void* EDX_Unused) +{ + const char* FEPkg; + int PtrNumRacers; + char buf[32]; + + int CurrentRaceMode = *(int*)_CurrentRaceMode; + int OptionNumber = QROptionsScreen[33]; + + MakeUserCall(FEngSNPrintf, 4, esi, buf, esp, 32, esp, "Option%1dTextString", esp, OptionNumber); + FEPkg = (const char*)*((DWORD*)QROptionsScreen + 3); + MakeUserCall(FEngPrintf, 3, eax, FEPkg, edx, buf, esp, "Racers:"); + MakeUserCall(FEngSNPrintf, 4, esi, buf, esp, 32, esp, "Option%1dData", esp, OptionNumber); + PtrNumRacers = cFrontendDatabase_GetOptions((DWORD*)_FEDatabase, CurrentRaceMode); + return (int)MakeUserCall(FEngPrintf, 4, eax, FEPkg, edx, buf, esp, "%d", esp, *(DWORD*)(PtrNumRacers)); +} + +int __fastcall QROptionsScreen_ScrollNumRacers(DWORD* QROptionsScreen, void* EDX_Unused, int Action) +{ + int* PtrNumRacers; // ecx + int NumRacers; // eax + + int CurrentRaceMode = *(int*)_CurrentRaceMode; + + PtrNumRacers = (int*)(cFrontendDatabase_GetOptions((DWORD*)_FEDatabase, CurrentRaceMode)); + NumRacers = *PtrNumRacers; + if (Action == 0x9120409E) // PAD_LEFT + { + if (--NumRacers < MinRacers) + { + *PtrNumRacers = MaxRacers; + return QROptionsScreen_DrawNumRacers(QROptionsScreen, EDX_Unused); + } + } + else if (Action == 0xB5971BF1 && ++NumRacers > MaxRacers) // PAD_RIGHT + { + NumRacers = MinRacers; + } + *PtrNumRacers = NumRacers; + return QROptionsScreen_DrawNumRacers(QROptionsScreen, EDX_Unused); +} \ No newline at end of file diff --git a/NFSUExtraOptions/UserCalls.h b/NFSUExtraOptions/UserCalls.h new file mode 100644 index 0000000..729187f --- /dev/null +++ b/NFSUExtraOptions/UserCalls.h @@ -0,0 +1,157 @@ +#pragma once + +#include "stdio.h" +#include + +enum reg : int +{ + eax, + ebx, + ecx, + edx, + edi, + esi, + esp +}; + +typedef struct +{ + reg reg; + void* val; +} FuncArg; + +typedef struct +{ + void* _eax; + void* _ebx; + void* _ecx; + void* _edx; + void* _edi; + void* _esi; +} Regs; + +/* A wrapper to call the functions which doesn't fit any calling convention. + * This is usually caused by the "Link-Time Code Generation". + * + * Usage: After number of arguments, you need to push the register name (eax, ebx, etc.) and the argument itself. + * If argument is pushed regularly, use "esp" as the register name. + * Stack difference will get calculated by the function automatically. + * + * For example: MakeUserCall(FEngPrintf, 3, eax, FEPkg, edx, buf, esp, str); + * // It's int by default. Don't use "" in here as it will give you a compile-time error. + * FEngPrintf // The name of the function we want to call. + * 3 // Number of arguments taken by the function. + * eax, FEPkg // "FEPkg" will get passed through "eax" register. (ASM: mov eax, FEPkg) + * edx, buf // "buf" will get passed through "edx" register. (ASM: mov edx, buf) + * esp, str // "str" will get passed through a regular "push" instruction. (ASM: push str) +*/ +template +R MakeUserCall(R _func, int _numArgs, ...) +{ + R _retval = 0; + std::vector _args; + std::vector _pushArgs; + FuncArg _anArg; + Regs _regs, _regs_backup; + va_list ap; + + // Get current registers + _asm + { + mov _regs._eax, eax + mov _regs._ebx, ebx + mov _regs._ecx, ecx + mov _regs._edx, edx + mov _regs._edi, edi + mov _regs._esi, esi + } + + // backup regs + _regs_backup = _regs; + + va_start(ap, _numArgs); + + for (int i = 0; i < _numArgs; i ++) + { + _anArg.reg = va_arg(ap, reg); + _anArg.val = va_arg(ap, void*); + + switch (_anArg.reg) + { + case esp: + _pushArgs.push_back(_anArg); + break; + default: + _args.push_back(_anArg); + break; + } + } + + va_end(ap); + + // get stack difference + int _stack = _pushArgs.size() * 4; + + // move args to the register struct + for (FuncArg i : _args) + { + switch (i.reg) + { + case eax: + _regs._eax = i.val; + break; + case ebx: + _regs._ebx = i.val; + break; + case ecx: + _regs._ecx = i.val; + break; + case edx: + _regs._edx = i.val; + break; + case edi: + _regs._edi = i.val; + break; + case esi: + _regs._esi = i.val; + break; + } + } + + // push args in reverse order + reverse(_pushArgs.begin(), _pushArgs.end()); + + for (FuncArg s : _pushArgs) + { + _asm push s.val; + } + + // Set registers before call + _asm + { + mov eax, _regs._eax + mov ebx, _regs._ebx + mov ecx, _regs._ecx + mov edx, _regs._edx + mov edi, _regs._edi + mov esi, _regs._esi + + // invoke the call + call _func; + add esp, _stack; + mov _retval, eax; + } + + // Restore regs + _asm + { + //mov eax, _regs_backup._eax + mov ebx, _regs_backup._ebx + mov ecx, _regs_backup._ecx + mov edx, _regs_backup._edx + mov edi, _regs_backup._edi + mov esi, _regs_backup._esi + } + + return _retval; +} diff --git a/NFSUExtraOptions/dllmain.cpp b/NFSUExtraOptions/dllmain.cpp index 1ac5eff..cd54af9 100644 --- a/NFSUExtraOptions/dllmain.cpp +++ b/NFSUExtraOptions/dllmain.cpp @@ -1,253 +1,8 @@ #include "stdafx.h" #include "stdio.h" +#include #include -#include "..\includes\injector\injector.hpp" -#include -#include "..\includes\IniReader.h" - -DWORD Strings; -int hotkeyUnlockAllTracks, hotkeyAnyTrackInAnyMode; -unsigned char MinLaps, MaxLaps; -bool ShowOpponents, AnyTrackInAnyMode, MorePaintTypes, ShowSpecialVinyls, nlgzrgnTakeover, EnableHiddenCameras, UnlockAllTracks, ReplaceTournamentWithFreeRun; -bool once1 = 0, once2 = 0; -float SplashScreenTimeLimit; - -DWORD WINAPI Thing(LPVOID); - -DWORD AddOptionToTrackOptsMenu = 0x004B8D50; -DWORD TrackOptionsLaps = 0x4B81F0; -DWORD TrackOptionsOpponents = 0x4B8280; -DWORD TrackOptionsCatchUp = 0x4B8310; -DWORD TrackOptionsTraffic = 0x4B83C0; -DWORD TrackOptionsDifficulty = 0x4B84A0; - -void __declspec(naked) OpponentsCodeCaveSprint() -{ - _asm - { - xor eax, eax - push esi - lea esp, dword ptr ds : [esp + 0x00] - - menuelementloop: - lea esi, [eax + 0x01] - push esi - call AddOptionToTrackOptsMenu - mov eax, esi - cmp eax, 4 - jnge menuelementloop - - //mov ecx,ebx - //mov [ebx+00000084],00000001 - //mov [ebx+60],SpeedVer14Org.exe+B8560 // Laps - //call TrackOptionsLaps - mov ecx, ebx - mov dword ptr ds : [ebx + 0x90], 1 - mov dword ptr ds : [ebx + 0x60], 0x4B85C0 // Opponents - call TrackOptionsOpponents - - mov ecx, ebx - mov dword ptr ds : [ebx + 0x98], 2 - mov dword ptr ds : [ebx + 0x64], 0x4B8650 // Catch Up - call TrackOptionsCatchUp - - mov ecx, ebx - mov dword ptr ds : [ebx + 0x9C], 3 - mov dword ptr ds : [ebx + 0x68], 0x4B8690 // Traffic - call TrackOptionsTraffic - - pop esi - - mov ecx, ebx - mov dword ptr ds : [ebx + 0xA0], 4 - mov dword ptr ds : [ebx + 0x6C], 0x4B86F0 // Difficulty - - pop ebx - jmp TrackOptionsDifficulty - - } -} - -void __declspec(naked) OpponentsCodeCaveDrag() -{ - _asm - { - - push 1 - call AddOptionToTrackOptsMenu - mov ecx, ebx - mov dword ptr ds: [ebx + 0x90], 1 - mov dword ptr ds : [ebx + 0x60], 0x4B85C0 // Opponents - call TrackOptionsOpponents - - mov ecx, ebx - mov dword ptr ds : [ebx + 0x98], 2 - mov dword ptr ds : [ebx + 0x64], 0x4B8650 // Catch Up - call TrackOptionsCatchUp - push 2 - call AddOptionToTrackOptsMenu - - mov ecx, ebx - mov dword ptr ds : [ebx + 0x9C], 3 - mov dword ptr ds : [ebx + 0x68], 0x4B8690 // Traffic - call TrackOptionsTraffic - push 3 - call AddOptionToTrackOptsMenu - - mov ecx, ebx - mov dword ptr ds : [ebx + 0xA0], 4 - mov dword ptr ds : [ebx + 0x6C], 0x4B86F0 // Difficulty - call TrackOptionsDifficulty - push 4 - call AddOptionToTrackOptsMenu - - pop ebx - ret - } -} - -void Init() -{ - CIniReader iniReader("NFSUExtraOptionsSettings.ini"); - - // Hotkeys - hotkeyAnyTrackInAnyMode = iniReader.ReadInteger("Hotkeys", "AnyTrackInAnyMode", 36); // HOME - - // LapControllers - MinLaps = iniReader.ReadInteger("LapControllers", "Minimum", 0); - MaxLaps = iniReader.ReadInteger("LapControllers", "Maximum", 127); - - // Menu - ShowOpponents = iniReader.ReadInteger("Menu", "ShowOpponents", 1) == 1; - ReplaceTournamentWithFreeRun = iniReader.ReadInteger("Menu", "ReplaceTournamentWithFreeRun", 0) == 1; - AnyTrackInAnyMode = iniReader.ReadInteger("Menu", "AnyTrackInAnyRaceMode", 0) == 1; - SplashScreenTimeLimit = iniReader.ReadFloat("Menu", "SplashScreenTimeLimit", 30.0f); - nlgzrgnTakeover = iniReader.ReadInteger("Menu", "DisableTakeover", 0) == 0; - - // Gameplay - EnableHiddenCameras = iniReader.ReadInteger("Gameplay", "EnableHiddenCameraModes", 0) == 1; - - // Restrictions - if (MinLaps < 0 || MinLaps > 127) MinLaps = 1; - if (MaxLaps < 0 || MaxLaps > 127) MaxLaps = 10; - - // Set Lap Controllers - injector::WriteMemory(0x4B85AB, MinLaps, true); // Minimum Lap Controller for Increment - injector::WriteMemory(0x4B8587, MinLaps, true); // Minimum Lap Controller for Decrement - injector::WriteMemory(0x4B85A7, MaxLaps, true); // Maximum Lap Controller for Increment - injector::WriteMemory(0x4B858B, MaxLaps, true); // Maximum Lap Controller for Decrement - - // Enable Drift Camera Mode For All - if (EnableHiddenCameras) - { - injector::WriteMemory(0x42E091, 0xEB, true); - injector::MakeNOP(0x4BE78B, 2, true); - injector::MakeNOP(0x4BE749, 2, true); - } - - // Splash Screen Time Limit - injector::WriteMemory(0x6ccac0, SplashScreenTimeLimit, true); - - // Any Track In Any Mode - if (AnyTrackInAnyMode) - { - injector::MakeNOP(0x4BA16C, 2, true); - injector::MakeNOP(0x4BA202, 2, true); - } - - // Replace Tournament With Free Run - if (ReplaceTournamentWithFreeRun) - { - injector::WriteMemory(0x4b7a02, 0xCDA558A7, true); - injector::WriteMemory(0x4B7a07, 0x80E29CC5, true); - injector::WriteMemory(0x4B7A1E, 0x04, true); - } - - // Add Opponents option to Race Options menu - if (ShowOpponents) - { - // Use unused option for opponents - injector::WriteMemory(0x4b85de, 0x08, true); // Change address to read?? - injector::WriteMemory(0x4b85df, 0x90, true); - - injector::WriteMemory(0x4b82f1, 0x10, true); // Change address to write?? - injector::WriteMemory(0x4b82f2, 0x90, true); - - injector::MakeNOP(0x4b55f3, 1, true); // Disable decrement to use it as opponent count. - injector::MakeNOP(0x4b4a12, 1, true); - injector::MakeNOP(0x4b4e92, 1, true); - injector::MakeNOP(0x4b4c62, 1, true); - - injector::WriteMemory(0x4b85e7, 0, true); // Set controllers - injector::WriteMemory(0x4b8600, 0, true); - injector::WriteMemory(0x4b85eb, 3, true); - injector::WriteMemory(0x4b85fc, 3, true); - - injector::WriteMemory(0x4b9108, 4, true); // Make 5th element overlap 4th (Otherwise it won't appear) - - // Fix Lap Controller for Lap KO - injector::WriteMemory(0x4B4eb5, 0x10, true); // Read normal lap count instead of KO lap - injector::MakeNOP(0x4B4ebc, 5, true); // Don't fix it to 3 - - injector::MakeCALL(0x4b89b3, 0x4B9070, true); // Circuit - injector::MakeJMP(0x4B8F4A, OpponentsCodeCaveSprint, true); // Sprint - injector::MakeJMP(0x4B913A, OpponentsCodeCaveDrag, true); // Drag - injector::MakeCALL(0x4b89c9, 0x4B9070, true); // KO - } - - - // Other Things - CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&Thing, NULL, 0, NULL); -} - -DWORD WINAPI Thing(LPVOID) -{ - while (true) - { - Sleep(1); - Strings = *(DWORD*)0x734434; - - if ((GetAsyncKeyState(hotkeyAnyTrackInAnyMode) & 1)) // When pressed "Any Track in Any Mode" key - { - CIniReader iniReader("NFSUExtraOptionsSettings.ini"); - AnyTrackInAnyMode = !AnyTrackInAnyMode; - iniReader.WriteInteger("Menu", "AnyTrackInAnyRaceMode", AnyTrackInAnyMode); - - if (AnyTrackInAnyMode) - { - injector::MakeNOP(0x4BA16C, 2, true); - injector::MakeNOP(0x4BA202, 2, true); - } - - else - { - injector::WriteMemory(0x4BA16C, 0x1D74, true); - injector::WriteMemory(0x4BA202, 0x2C74, true); - } - - while ((GetAsyncKeyState(hotkeyAnyTrackInAnyMode) & 0x8000) > 0) { Sleep(0); } - } - - if (Strings && ShowOpponents && !once1) // Indicate if ExOpts is Loaded at splash screen (If nlgzrgnTakeOver enabled) - { - DWORD NumberOfRacesAddr = (Strings + 0x190c); - char* OpponentsString = "Opponents:"; - injector::WriteMemory(NumberOfRacesAddr, (DWORD)OpponentsString, true); - once1 = 1; - } - - if (Strings && nlgzrgnTakeover && !once2) // Indicate if ExOpts is Loaded at splash screen (If nlgzrgnTakeOver enabled) - { - DWORD CopyrightAddr = (Strings + 0x58cc); - char* Copyright = *(char**)CopyrightAddr; - char* Append = "^NFSU Extra Options - © 2016 nlgzrgn. No rights reserved."; - char* Tookover = strcat(Copyright, Append); - injector::WriteMemory(CopyrightAddr, (DWORD)Tookover, true); - once2 = 1; - } - } - return 0; -} +#include "ExtraOptionsStuff.h" BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/) { @@ -268,5 +23,4 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/) } return TRUE; -} - +} \ No newline at end of file diff --git a/NFSUExtraOptions/resource.h b/NFSUExtraOptions/resource.h new file mode 100644 index 0000000..86e7385 --- /dev/null +++ b/NFSUExtraOptions/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by NFSUExtraOptions.rc + +// Yeni nesneler için sonraki varsayılan değerler +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/NFSUExtraOptionsSettings.ini b/NFSUExtraOptionsSettings.ini index 8a6cf71..b35b5e9 100644 --- a/NFSUExtraOptionsSettings.ini +++ b/NFSUExtraOptionsSettings.ini @@ -1,16 +1,48 @@ [Hotkeys] // Look at http://cherrytree.at/misc/vk.htm for key values (decimal) -AnyTrackInAnyMode = 36 // Shows all race tracks in every game mode. Your latest preference will be saved. (Default = 36 = Home Key) +AnyTrackInAnyMode = 36 // Shows all race tracks in every game mode. Your latest preference will be saved. (Default = 36 = Home Key) +FreezeCamera = 19 // Freezes/unfreezes camera when pressed. (Default = 19 = Pause/Break key) +UnlockAllThings = 116 // Unlocks everything in game. (Default = 116 = F5 key) +AutoDrive = 117 // Enables Auto Drive (Drunk Driver) hack. (Default = 117 = F6 key) [LapControllers] -Minimum = 0 // Minimum number of laps can be selected at Quick Race options screen. (Default = 0) (0/127) -Maximum = 127 // Maximum number of laps can be selected at Quick Race options screen. (Default = 127) (0/127) +Minimum = 0 // Minimum number of laps can be selected at Quick Race options screen. (Default = 0) (0/127) +Maximum = 127 // Maximum number of laps can be selected at Quick Race options screen. (Default = 127) (0/127) + +[RacerControllers] // Requires ShowMoreRaceOptions to be enabled. +Minimum = 1 // Minimum number of racers can be selected at Quick Race options screen. (Default = 1) (1/4) +Maximum = 4 // Maximum number of racers can be selected at Quick Race options screen. (Default = 4) (1/4) [Menu] -ShowOpponents = 1 // Adds an opponents option to Track Options menu. (0 = False, 1 = True (Default)) -ReplaceTournamentWithFreeRun = 0 // Replaces Free Run mode with hidden Tournament mode on Quick Race Menu. (0 = False (Default), 1 = True) -AnyTrackInAnyRaceMode = 0 // Current status of "Any Track In Any Mode" hack. (0 = False (Default), 1 = True) -SplashScreenTimeLimit = 30 // Duration of Splash Screen. (Default = 30) -DisableTakeover = 0 // Removes "Extra Options by nlgzrgn" text from Copyright area. (0 = False (Default), 1 = True) +ShowMoreRaceOptions = 1 // Adds a racers option to Track Options menu, moves Laps option to a seperate pop-up. (0 = False, 1 = True (Default)) +ShowLanguageSelectScreen = 0 // Shows Language Select Screen when game starts. (0 = False (Default), 1 = True) +ShowSpecialVinyls = 1 // Shows special vinyls in Unique category. (0 = False, 1 = True (Default)) +ShowDebugOptionsMenu = 0 // Shows the hidden Debug menu option in Pause Options screen. (0 = False (Default), 1 = True) +ShowTournament = 0 // Adds hidden Tournament mode to Quick Race Menu. (0 = False (Default), 1 = True) +EATraxSkin = -1 // Changes your EA Trax Menu skin. Chyron (EA Trax info box) may not be shown if not -1 nor 0. It also gets saved in the save game. (-1 = Not Set (Default), 0 = Vanilla, 1 = Alpine, 2 = Kenwood, 3 = Audiobahn) +AnyTrackInAnyRaceMode = 0 // Current status of "Any Track In Any Mode" hack. (0 = False (Default), 1 = True) +SplashScreenTimeLimit = 30 // Duration of Splash Screen. (Default = 30.0) +MiniWorldCarSpeedFast = 50 // Speed of the cars at the infinite tunnel on menus while changing between them. (Default = 50.0) +MiniWorldCarSpeedCruising = 20 // Speed of the cars at the infinite tunnel on menus. (Default = 20.0) +MiniWorldCarSpeedSlow = 1 // Speed of the cars at the infinite tunnel on rim tuning and paint. (Default = 1.0) +ShowcaseCamInfiniteZoom = 0 // Zoom hack for Showcase Camera mode. (0 = False (Default), 1 = True) +ShowcaseCamInfiniteRotation = 0 // Rotation hack for Showcase Camera mode. (0 = False (Default), 1 = True) +DisableTakeover = 0 // Removes "Extra Options by ExOpts Team" text from Copyright area. (0 = False (Default), 1 = True) [Gameplay] -EnableHiddenCameraModes = 0 // Enables hidden camera modes. (0 = False (Default), 1 = True) +EnableHiddenCameraModes = 0 // Enables hidden camera modes. (0 = False (Default), 1 = True) +GameSpeed = 1 // (ONLY FOR EXPERTS) Speed modifier for races. (Default = 1.0) +GameRegion = 0 // Sets the region for the game. (0 = NTSC-U (Default), 1+ = PAL/NTSC-J) +StartingCash = 0 // Starts the game with specified amount of cash. 10000 from prologue race is not included. (Default = 0) +StartingStylePoints = 0 // Starts the game with specified amount of style points. (Default = 0) +UnlockAllThings = 0 // Current status of UnlockAllThings hack. (0 = False (Default), 1 = True) + +[Fixes] +DisappearingWheelsFix = 1 // Fixes disappearing wheels when lost focus. Useful for open-wheel cars. (0 = False, 1 = True (Default)) +DamageFix = 0 // Makes mirrors and plate drop, glass crack when taken damage. You also need to reset your car before moving. (0 = False (Default), 1 = True) + +[Misc] +WindowedMode = 0 // Runs the game in a window instead of full screen. (0 = False (Default), 1 = True, 2 = Bordered) +SkipMovies = 0 // Skips cutscenes. (0 = False (Default), 1 = True) +EnableSound = 1 // Enables in-game sounds. (0 = False, 1 = True (Default)) +EnableMusic = 1 // Enables in-game music. (0 = False, 1 = True (Default)) +AllowMultipleInstances = 0 // Allows running more than 1 instance of the game. Also lets the game run together with Most Wanted. (0 = False (Default), 1 = True) diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c17396 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# NFSU Extra Options +Extra Options is a script mod which improves your gaming experience by many ways. It's developed by ExOpts Team. + +# Download +You can [download Extra Options](https://github.com/nlgzrgn/NFSUExOpts/releases) from Releases page. diff --git a/README.txt b/README.txt deleted file mode 100644 index fae8fc8..0000000 --- a/README.txt +++ /dev/null @@ -1,66 +0,0 @@ -NFS Underground - Extra Options - Build 1; Rev.01 -Made by nlgzrgn ------------------------------------------------------------------------------------------------------------- -Features: -(Almost every option can be edited from .ini file.) - --- Lap count restrictions are modified. -> You can have from 0 (which makes the race unlimited) to 127 (Really long races) laps. - --- Opponent count can now be selected from Track Options menu. -> The game supports from 0 (forever alone) to 3 (regular race) opponents. -! I've tried my best to avoid crashes. :P -! Some menus have 5 options. The last updated option (4th or 5th) will appear over 4th option. - --- You can play the hidden Tournament mode from Quick Race menu. -> This mod is fully functional. I don't know why EA cut it from the final game. -! This option will replace Free Run mode. - --- You can have every track on every game mode. -> This will make the list too long. But you can select the races safely. -! This option will also unlock Test Track. -> You can control it with a hotkey. Enable it when you want. - --- You can change the time limit of Splash Screen. -> 30 seconds aren't enough? Make it a minute, an hour, maybe a day! - --- You can enable Drift camera mode everywhere! -> Birds eye? Yeah, it's here! - ------------------------------------------------------------------------------------------------------------- -Installation: - -1) Open "Main Files" folder. -2) Select everything. -3) Copy them to your NFSU Installation Folder. (Use Ctrl+C, Ctrl+V / Cmd+C, Cmd+V / Strg+C, Strg+V; Right-Click > Copy; via xcopy command on CMD; or how do you like it.) -4) (Optional) Edit the NFSUExtraOptionsSettings.ini file. -5) Run NFSU. -6) And do whatever you want! - ------------------------------------------------------------------------------------------------------------- -Changelog: (+ Addition, * Change, ! Attention, - Deletion) - -Build 1; Rev.01: -+ Initial release. ------------------------------------------------------------------------------------------------------------- -Thanks to: - -379Felipe; for his great help, testing and Portuguese (Brazil) translation. -Speedyheart; for his discovery about Tournament mode. -ThirteenAG; for Universal ASI Loader, and .ini Reader. -thelink2012; for injector. -Me; for hacks & this mod. - -And who I’ve forgotten to thank here. ------------------------------------------------------------------------------------------------------------- -Final Notes: - -This mod is licensed under GNU/GPLv3. So, you can use the source code from it and improve it as I stay as the original creator. -You can also send your improvements from GitHub as well. -Don’t try to steal my mod in any way. -If you will upload this archive somewhere else; you can add a read me and customize the .ini comments (Texts after “//â€) for your language. -But please don’t delete any file from this download. -------------------------------------------------------------------------------------------------------------- - -See ya! -©2016 nlgzrgn - No rights reserved. ;) diff --git a/includes/IniReader.h b/includes/IniReader.h index 1ca2d4d..f9b188a 100644 --- a/includes/IniReader.h +++ b/includes/IniReader.h @@ -1,103 +1,311 @@ #ifndef INIREADER_H #define INIREADER_H - -#include "stdafx.h" -#include +#include "ini_parser.hpp" +#include #include -using namespace std; -#pragma warning(disable:4996) -EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +/* +* String comparision functions, with case sensitive option +*/ + +using std::strcmp; + +inline int strcmp(const char* str1, const char* str2, bool csensitive) +{ + return (csensitive ? ::strcmp(str1, str2) : ::_stricmp(str1, str2)); +} + +inline int strcmp(const char* str1, const char* str2, size_t num, bool csensitive) +{ + return (csensitive ? ::strncmp(str1, str2, num) : ::_strnicmp(str1, str2, num)); +} + +inline int compare(const std::string& str1, const std::string& str2, bool case_sensitive) +{ + if (str1.length() == str2.length()) + return strcmp(str1.c_str(), str2.c_str(), case_sensitive); + return (str1.length() < str2.length() ? -1 : 1); +} + +inline int compare(const std::string& str1, const std::string& str2, size_t num, bool case_sensitive) +{ + if (str1.length() == str2.length()) + return strcmp(str1.c_str(), str2.c_str(), num, case_sensitive); + return (str1.length() < str2.length() ? -1 : 1); +} + +inline int compare(const char* str1, const char* str2, bool case_sensitive) +{ + return strcmp(str1, str2, case_sensitive); +} + +inline int compare(const char* str1, const char* str2, size_t num, bool case_sensitive) +{ + return strcmp(str1, str2, num, case_sensitive); +} + +inline bool starts_with(const char* str, const char* prefix, bool case_sensitive) +{ + while (*prefix) + { + bool equal; + if (case_sensitive) + equal = (*str++ == *prefix++); + else + equal = (::tolower(*str++) == ::tolower(*prefix++)); + + if (!equal) return false; + } + return true; +} + +inline bool ends_with(const char* str, const char* prefix, bool case_sensitive) +{ + auto str2 = &str[strlen(str) - 1]; + auto prefix2 = &prefix[strlen(prefix) - 1]; + + while (prefix2 >= prefix) + { + bool equal; + if (case_sensitive) + equal = (*str2-- == *prefix2--); + else + equal = (::tolower(*str2--) == ::tolower(*prefix2--)); + + if (!equal) return false; + } + return true; +} class CIniReader { -public: - CIniReader(char* szFileName) - { - char moduleName[MAX_PATH]; - char dllPath[MAX_PATH]; - char iniName[MAX_PATH]; - char* tempPointer; - - GetModuleFileName((HINSTANCE)&__ImageBase, moduleName, MAX_PATH); - tempPointer = strrchr(moduleName, '.'); - *tempPointer = '\0'; - tempPointer = strrchr(moduleName, '\\'); - strncpy(iniName, tempPointer + 1, 255); - strcat(iniName, ".ini"); - strncpy(dllPath, moduleName, (tempPointer - moduleName + 1)); - dllPath[tempPointer - moduleName + 1] = '\0'; - if (strcmp(szFileName, "") == 0) - { - strcat(dllPath, iniName); - } - else { - strcat(dllPath, szFileName); - } - - memset(m_szFileName, 0x00, 255); - memcpy(m_szFileName, dllPath, strlen(dllPath)); - } - int ReadInteger(char* szSection, char* szKey, int iDefaultValue) - { - int iResult = GetPrivateProfileInt(szSection, szKey, iDefaultValue, m_szFileName); - return iResult; - } - float ReadFloat(char* szSection, char* szKey, float fltDefaultValue) - { - char szResult[255]; - char szDefault[255]; - float fltResult; - _snprintf(szDefault, 255, "%f", fltDefaultValue); - GetPrivateProfileString(szSection, szKey, szDefault, szResult, 255, m_szFileName); - fltResult = (float)atof(szResult); - return fltResult; - } - bool ReadBoolean(char* szSection, char* szKey, bool bolDefaultValue) - { - char szResult[255]; - char szDefault[255]; - bool bolResult; - _snprintf(szDefault, 255, "%s", bolDefaultValue ? "True" : "False"); - GetPrivateProfileString(szSection, szKey, szDefault, szResult, 255, m_szFileName); - bolResult = (strcmp(szResult, "True") == 0 || - strcmp(szResult, "true") == 0) ? true : false; - return bolResult; - } - char* ReadString(char* szSection, char* szKey, const char* szDefaultValue) - { - char* szResult = new char[255]; - memset(szResult, 0x00, 255); - GetPrivateProfileString(szSection, szKey, - szDefaultValue, szResult, 255, m_szFileName); - return szResult; - } - void WriteInteger(char* szSection, char* szKey, int iValue) - { - char szValue[255]; - _snprintf(szValue, 255, "%s%d", " ", iValue); - WritePrivateProfileString(szSection, szKey, szValue, m_szFileName); - } - void WriteFloat(char* szSection, char* szKey, float fltValue) - { - char szValue[255]; - _snprintf(szValue, 255, "%s%f", " ", fltValue); - WritePrivateProfileString(szSection, szKey, szValue, m_szFileName); - } - void WriteBoolean(char* szSection, char* szKey, bool bolValue) - { - char szValue[255]; - _snprintf(szValue, 255, "%s%s", " ", bolValue ? "True" : "False"); - WritePrivateProfileString(szSection, szKey, szValue, m_szFileName); - } - void WriteString(char* szSection, char* szKey, char* szValue) - { - WritePrivateProfileString(szSection, szKey, szValue, m_szFileName); - } - char* GetIniPath() - { - return m_szFileName; - } private: - char m_szFileName[MAX_PATH]; + std::string m_szFileName; + +public: + linb::ini data; + + CIniReader() + { + SetIniPath(""); + } + + CIniReader(char* szFileName) + { + SetIniPath(szFileName); + } + + CIniReader(const char* szFileName) + { + SetIniPath((char*)szFileName); + } + + CIniReader(std::stringstream& ini_mem) + { + data.load_file(ini_mem); + } + + bool operator==(CIniReader& ir) + { + if (data.size() != ir.data.size()) + return false; + + for (auto& section : data) + { + for (auto& key : data[section.first]) + { + if (key.second != ir.data[section.first][key.first]) + return false; + } + } + return true; + } + + bool operator!=(CIniReader& ir) + { + return !(*this == ir); + } + + bool CompareBySections(CIniReader& ir) + { + if (data.size() != ir.data.size()) + return false; + + for (auto& section : data) + { + if (ir.data.find(section.first) == ir.data.end()) + return false; + + if (section.second.size() != ir.data.find(section.first)->second.size()) + return false; + + if (section.first != ir.data.find(section.first)->first) + return false; + } + return true; + } + + bool CompareByValues(CIniReader& ir) + { + return *this == ir; + } + + const std::string& GetIniPath() + { + return m_szFileName; + } + + void SetIniPath() + { + SetIniPath(""); + } + + void SetIniPath(char* szFileName) + { + char buffer[MAX_PATH]; + HMODULE hm = NULL; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)&ends_with, &hm); + GetModuleFileNameA(hm, buffer, sizeof(buffer)); + std::string modulePath = buffer; + + if (strchr(szFileName, ':') != NULL) + { + m_szFileName = szFileName; + } + else if (std::string(szFileName).length() == 0) + { + m_szFileName = modulePath.substr(0, modulePath.find_last_of('.')) + ".ini"; + } + else + { + m_szFileName = modulePath.substr(0, modulePath.rfind('\\') + 1) + szFileName; + } + + data.load_file(m_szFileName); + } + + int ReadInteger(char* szSection, char* szKey, int iDefaultValue) + { + try { + auto str = data.get(szSection, szKey, std::to_string(iDefaultValue)); + return std::stoi(str, nullptr, starts_with(str.c_str(), "0x", false) ? 16 : 10); + } + catch (...) { + return iDefaultValue; + } + } + + float ReadFloat(char* szSection, char* szKey, float fltDefaultValue) + { + try { + return (float)atof(data.get(szSection, szKey, std::to_string(fltDefaultValue)).c_str()); + } + catch (...) { + return fltDefaultValue; + } + } + + bool ReadBoolean(char* szSection, char* szKey, bool bolDefaultValue) + { + try { + auto& config = data[szSection]; + if (config.count(szKey)) + { + if (config[szKey].size() == 1) return config[szKey].front() != '0'; + return !!compare(config[szKey], "false", false); + } + } + catch (...) { + return bolDefaultValue; + } + } + + char* ReadString(char* szSection, char* szKey, const char* szDefaultValue) + { + char* szResult = new char[255]; + try { + auto& config = data[szSection]; + if (config.count(szKey)) + { + if (config[szKey].at(0) == '\"' || config[szKey].at(0) == '\'') + config[szKey].erase(0, 1); + + if (config[szKey].at(config[szKey].size() - 1) == '\"' || config[szKey].at(config[szKey].size() - 1) == '\'') + config[szKey].erase(config[szKey].size() - 1); + + strcpy(szResult, config[szKey].c_str()); + return szResult; + } + } + catch (...) { } + strcpy(szResult, szDefaultValue); + return szResult; + } + + std::string ReadString(char* szSection, char* szKey, std::string szDefaultValue) + { + char* str = ReadString(szSection, szKey, szDefaultValue.c_str()); + std::string* szResult = new std::string(str); + return *szResult; + } + + void WriteInteger(char* szSection, char* szKey, int iValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, std::to_string(iValue)); + data.write_file(m_szFileName); + } + else + { + char szValue[255]; + _snprintf_s(szValue, 255, "%s%d", " ", iValue); + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } + + void WriteFloat(char* szSection, char* szKey, float fltValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, std::to_string(fltValue)); + data.write_file(m_szFileName); + } + else + { + char szValue[255]; + _snprintf_s(szValue, 255, "%s%f", " ", fltValue); + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } + + void WriteBoolean(char* szSection, char* szKey, bool bolValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, std::to_string(bolValue)); + data.write_file(m_szFileName); + } + else + { + char szValue[255]; + _snprintf_s(szValue, 255, "%s%s", " ", bolValue ? "True" : "False"); + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } + + void WriteString(char* szSection, char* szKey, char* szValue, bool useparser = false) + { + if (useparser) + { + data.set(szSection, szKey, szValue); + data.write_file(m_szFileName); + } + else + { + WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); + } + } }; -#endif//INIREADER_H \ No newline at end of file + +#endif //INIREADER_H diff --git a/includes/ini_parser.hpp b/includes/ini_parser.hpp new file mode 100644 index 0000000..a3a0f53 --- /dev/null +++ b/includes/ini_parser.hpp @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2013-2015 Denilson das Mercês Amorim + * Copyright (c) 2017 ThirteenAG + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#ifndef LINB_INI_PARSER_HPP +#define LINB_INI_PARSER_HPP + +/* + * STL-like INI Container + */ + +#include // for std::string +#include // for std::map +#include // for std::FILE +#include // for std::find_if +#include // for std::function +#include // for std::vector +#include +#include + +namespace linb +{ + template< + class CharT = char, /* Not compatible with other type here, since we're using C streams */ + class StringType = std::basic_string, + class KeyContainer = std::map, + class SectionContainer = std::map + > class basic_ini + { + public: + typedef CharT char_type; + typedef StringType string_type; + typedef KeyContainer key_container; + typedef SectionContainer section_container; + + // Typedef container values types + typedef typename section_container::value_type value_type; + typedef typename section_container::key_type key_type; + typedef typename section_container::mapped_type mapped_type; + + // Typedef common types + typedef typename section_container::size_type size_type; + typedef typename section_container::difference_type difference_type; + + // Typedef iterators + typedef typename section_container::iterator iterator; + typedef typename section_container::const_iterator const_iterator; + typedef typename section_container::reverse_iterator reverse_iterator; + typedef typename section_container::const_reverse_iterator const_reverse_iterator; + + // typedef References and pointers + typedef typename section_container::reference reference; + typedef typename section_container::const_reference const_reference; + typedef typename section_container::pointer pointer; + typedef typename section_container::const_pointer const_pointer; + + private: + section_container data; + + public: + + basic_ini() + { } + + basic_ini(const char_type* filename) + { this->read_file(filename); } + + /* Iterator methods */ + iterator begin() + { return data.begin(); } + const_iterator begin() const + { return data.begin(); } + iterator end() + { return data.end(); } + const_iterator end() const + { return data.end(); } + const_iterator cbegin() const + { return data.cbegin(); } + const_iterator cend() const + { return data.cend(); } + + /* Reverse iterator methods */ + reverse_iterator rbegin() + { return data.rbegin(); } + const_reverse_iterator rbegin() const + { return data.rbegin(); } + reverse_iterator rend() + { return data.rend(); } + const_reverse_iterator rend() const + { return data.rend(); } + const_reverse_iterator crbegin() const + { return data.crbegin(); } + const_reverse_iterator crend() const + { return data.crend(); } + + /* Acessing index methods */ + mapped_type& operator[](const string_type& sect) + { return data[sect]; } + mapped_type& operator[](string_type&& sect) + { return data[std::forward(sect)]; } + mapped_type& at( const string_type& sect) + { return data.at(sect); } + const mapped_type& at(const string_type& sect) const + { return data.at(sect); } + + /* Capacity information */ + bool empty() const + { return data.empty(); } + size_type size() const + { return data.size(); } + size_type max_size() const + { return data.max_size(); } + + /* Modifiers */ + void clear() + { return data.clear(); } + + /* Lookup */ + size_type count(const string_type& sect) + { return data.count(sect); } + iterator find(const string_type& sect) + { return data.find(sect); } + + /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */ + string_type get(const string_type& sect, const key_type& key, const string_type& default_value) + { + auto it = this->find(sect); + if(it != this->end()) + { + auto itv = it->second.find(key); + if(itv != it->second.end()) + return itv->second; + } + return default_value; + } + + /* Sets the value of a value in the ini */ + void set(const string_type& sect, const key_type& key, const string_type& value) + { + (*this)[sect][key] = value; // no emplace since overwrite! + } + + /* Too lazy to continue this container... If you need more methods, just add it */ + + + bool read_file(std::stringstream& ini_mem) + { + if(ini_mem.rdbuf()->in_avail()) + { + key_container* keys = nullptr; + string_type line; + string_type key; + string_type value; + string_type null_string; + size_type pos; + + // Trims an string + auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type& + { + if(s.size()) + { + // Ignore UTF-8 BOM + while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF)) + s.erase(s.begin(), s.begin() + 3); + + if(trimLeft) + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function(::isspace)))); + if(trimRight) + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function(::isspace))).base(), s.end()); + } + return s; + }; + + // Start parsing + while (std::getline(ini_mem, line)) + { + // Find comment and remove anything after it from the line + if((pos = line.find_first_of(';')) != line.npos) + line.erase(pos); + + if ((pos = line.rfind(" //")) != line.npos) + line.erase(pos); + + // Trim the string, and if it gets empty, skip this line + if(trim(line, true, true).empty()) + continue; + + // Find section name + if(line.front() == '[' && line.back() == ']') + { + pos = line.length() - 1; //line.find_first_of(']'); + if(pos != line.npos) + { + trim(key.assign(line, 1, pos-1), true, true); + keys = &data[std::move(key)]; // Create section + } + else + keys = nullptr; + } + else + { + // Find key and value positions + pos = line.find_first_of('='); + if(pos == line.npos) + { + // There's only the key + key = line; // No need for trim, line is already trimmed + value.clear(); + } + else + { + // There's the key and the value + trim(key.assign(line, 0, pos), false, true); // trim the right + trim(value.assign(line, pos + 1, line.npos), true, false); // trim the left + } + + // Put the key/value into the current keys object, or into the section "" if no section has been found + #if __cplusplus >= 201103L || _MSC_VER >= 1800 + (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value)); + #else + (keys ? *keys : data[null_string])[key] = value; + key.clear(); value.clear(); + #endif + } + } + + return true; + } + return false; + } + + bool read_file(const char_type* filename) + { + std::ifstream file(filename, std::ios::in); + if (file.is_open()) + { + std::stringstream ss; + ss << file.rdbuf(); + file.close(); + return read_file(ss); + } + return false; + } + + /* + * Dumps the content of this container into an ini file + */ + bool write_file(const char_type* filename) + { + if(FILE* f = fopen(filename, "w")) + { + bool first = true; + for(auto& sec : this->data) + { + fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str()); + first = false; + for(auto& kv : sec.second) + { + if(kv.second.empty()) + fprintf(f, "%s\n", kv.first.c_str()); + else + fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str()); + } + } + fclose(f); + return true; + } + return false; + } + + + /* + */ + bool load_file(const char_type* filename) + { + return read_file(filename); + } + + bool load_file(const StringType& filename) + { + return load_file(filename.c_str()); + } + + bool load_file(std::stringstream& filename) + { + return read_file(filename); + } + + bool write_file(const StringType& filename) + { + return write_file(filename.c_str()); + } + }; + + + /* Use default basic_ini + * + * Limitations: + * * Not unicode aware + * * Case sensitive + * * Sections must have unique keys + */ + typedef basic_ini<> ini; +} + +#endif + diff --git a/includes/injector/assembly.hpp b/includes/injector/assembly.hpp index 622f57e..69d418a 100644 --- a/includes/injector/assembly.hpp +++ b/includes/injector/assembly.hpp @@ -152,14 +152,14 @@ namespace injector template void MakeInline(FuncT func) { - static FuncT static_func = func; // Stores the func object - static_func = func; // + static std::unique_ptr static_func; + static_func.reset(new FuncT(std::move(func))); // Encapsulates the call to static_func struct Caps { void operator()(reg_pack& regs) - { static_func(regs); } + { (*static_func)(regs); } }; // Does the actual MakeInline diff --git a/includes/injector/calling.hpp b/includes/injector/calling.hpp index ebf3bdf..0a1dd62 100644 --- a/includes/injector/calling.hpp +++ b/includes/injector/calling.hpp @@ -26,6 +26,7 @@ #pragma once #include "injector.hpp" #include +#include #if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 #else diff --git a/includes/injector/gvm/gvm.hpp b/includes/injector/gvm/gvm.hpp index a957929..27a0562 100644 --- a/includes/injector/gvm/gvm.hpp +++ b/includes/injector/gvm/gvm.hpp @@ -55,7 +55,7 @@ class game_version_manager #ifdef INJECTOR_GVM_PLUGIN_NAME PluginName = INJECTOR_GVM_PLUGIN_NAME; #else - PluginName = "LODLights.asi"; + PluginName = "Unknown Plugin Name"; #endif this->Clear(); @@ -65,7 +65,7 @@ class game_version_manager // Clear any information about game version void Clear() { - game = region = major = minor = cracker = steam = 0; + game = region = major = minor = majorRevision = minorRevision = cracker = steam = 0; } // Checks if I don't know the game we are attached to @@ -110,10 +110,7 @@ class game_version_manager const char* g = this->IsIII() ? "III" : this->IsVC() ? "VC" : this->IsSA() ? "SA" : this->IsIV() ? "IV" : this->IsEFLC() ? "EFLC" : "UNK"; const char* r = this->IsUS()? "US" : this->IsEU()? "EURO" : "UNK_REGION"; const char* s = this->IsSteam()? "Steam" : ""; - if (this->IsIII() || this->IsVC() || this->IsSA()) - sprintf(buffer, "GTA %s %d.%d %s%s", g, major, minor, r, s); - else - sprintf(buffer, "GTA %s %d.%d.%d.%d %s%s", g, major, minor, majorRevision, minorRevision, r, s); + sprintf(buffer, "GTA %s %d.%d.%d.%d %s%s", g, major, minor, majorRevision, minorRevision, r, s); return buffer; } diff --git a/includes/injector/injector.hpp b/includes/injector/injector.hpp index ffa3a02..ed13fcc 100644 --- a/includes/injector/injector.hpp +++ b/includes/injector/injector.hpp @@ -2,7 +2,6 @@ * Injectors - Base Header * * Copyright (C) 2012-2014 LINK/2012 - * Copyright (C) 2014 Deji * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -673,7 +672,7 @@ inline bool game_version_manager::Detect() // Look for game and version thought the entry-point // Thanks to Silent for many of the entry point offsets - switch (base + nt->OptionalHeader.AddressOfEntryPoint + 0x400000 - (DWORD)GetModuleHandle(NULL)) + switch (base + nt->OptionalHeader.AddressOfEntryPoint + (0x400000 - base)) { case 0x5C1E70: // GTA III 1.0 game = '3', major = 1, minor = 0, region = 0, steam = false;