1- // Include necessary headers
2- #include " stdafx.h"
3- #include " helper.hpp"
4-
5- #include < spdlog/spdlog.h>
6- #include < spdlog/sinks/basic_file_sink.h>
7- #include < inipp/inipp.h>
8- #include < safetyhook.hpp>
9- #include < vector>
10- #include < map>
11- #include < windows.h>
12- #include < psapi.h> // For GetModuleInformation
13- #include < fstream>
14- #include < filesystem>
15- #include < sstream>
16- #include < cstring>
17- #include < iomanip>
18- #include < cstdint>
19- #include < iostream>
20-
21- #define spdlog_confparse (var ) spdlog::info(" Config Parse: {}: {}" , #var, var)
22-
23- HMODULE exeModule = GetModuleHandle(NULL );
24- HMODULE dllModule = nullptr ;
25- HMODULE thisModule;
26-
27- // Fix details
28- std::string sFixName = " 102DalmatiansPuppiesToTheRescueFOVFix" ;
29- std::string sFixVersion = " 1.0" ;
30- std::filesystem::path sFixPath ;
31-
32- // Ini
33- inipp::Ini<char > ini;
34- std::string sConfigFile = sFixName + " .ini" ;
35-
36- // Logger
37- std::shared_ptr<spdlog::logger> logger;
38- std::string sLogFile = sFixName + " .log" ;
39- std::filesystem::path sExePath ;
40- std::string sExeName ;
41-
42- // Constants
43- constexpr float fOldAspectRatio = 4 .0f / 3 .0f ;
44- constexpr uint16_t iOriginalCameraHFOV = 4096 ;
45-
46- // Ini variables
47- bool bFixActive;
48- int iCurrentResX;
49- int iCurrentResY;
50-
51- // Variables
52- float fNewAspectRatio ;
53- float fAspectRatioScale ;
54- uint16_t iNewCameraHFOV;
55-
56- // Game detection
57- enum class Game
58- {
59- DPTTR ,
60- Unknown
61- };
1+ #include " ..\..\common\FixBase.hpp"
622
63- struct GameInfo
3+ class DalmatiansFix final : public FixBase
644{
65- std::string GameTitle;
66- std::string ExeName;
67- };
68-
69- const std::map<Game, GameInfo> kGames = {
70- {Game::DPTTR , {" 102 Dalmatians: Puppies to the Rescue" , " Pcdogs.exe" }},
71- };
72-
73- const GameInfo* game = nullptr ;
74- Game eGameType = Game::Unknown;
75-
76- void Logging ()
77- {
78- // Get path to DLL
79- WCHAR dllPath[_MAX_PATH] = { 0 };
80- GetModuleFileNameW (thisModule, dllPath, MAX_PATH );
81- sFixPath = dllPath;
82- sFixPath = sFixPath .remove_filename ();
83-
84- // Get game name and exe path
85- WCHAR exePathW[_MAX_PATH] = { 0 };
86- GetModuleFileNameW (dllModule, exePathW, MAX_PATH );
87- sExePath = exePathW;
88- sExeName = sExePath .filename ().string ();
89- sExePath = sExePath .remove_filename ();
90-
91- // Spdlog initialization
92- try
5+ public:
6+ explicit DalmatiansFix (HMODULE selfModule) : FixBase(selfModule)
937 {
94- logger = spdlog::basic_logger_st (sFixName .c_str (), sExePath .string () + " \\ " + sLogFile , true );
95- spdlog::set_default_logger (logger);
96- spdlog::flush_on (spdlog::level::debug);
97- spdlog::set_level (spdlog::level::debug); // Enable debug level logging
98-
99- spdlog::info (" ----------" );
100- spdlog::info (" {:s} v{:s} loaded." , sFixName .c_str (), sFixVersion .c_str ());
101- spdlog::info (" ----------" );
102- spdlog::info (" Log file: {}" , sExePath .string () + " \\ " + sLogFile );
103- spdlog::info (" ----------" );
104- spdlog::info (" Module Name: {0:s}" , sExeName .c_str ());
105- spdlog::info (" Module Path: {0:s}" , sExePath .string ());
106- spdlog::info (" Module Address: 0x{0:X}" , (uintptr_t )dllModule);
107- spdlog::info (" ----------" );
108- spdlog::info (" DLL has been successfully loaded." );
8+ s_instance_ = this ;
1099 }
110- catch (const spdlog::spdlog_ex& ex)
10+
11+ ~DalmatiansFix () override
11112 {
112- AllocConsole ();
113- FILE * dummy;
114- freopen_s (&dummy, " CONOUT$" , " w" , stdout);
115- std::cout << " Log initialization failed: " << ex.what () << std::endl;
116- FreeLibraryAndExitThread (thisModule, 1 );
13+ if (s_instance_ == this )
14+ {
15+ s_instance_ = nullptr ;
16+ }
11717 }
118- }
11918
120- void Configuration ()
121- {
122- // Inipp initialization
123- std::ifstream iniFile (sFixPath .string () + " \\ " + sConfigFile );
124- if (!iniFile)
19+ protected:
20+ const char * FixName () const override
12521 {
126- AllocConsole ();
127- FILE * dummy;
128- freopen_s (&dummy, " CONOUT$" , " w" , stdout);
129- std::cout << sFixName .c_str () << " v" << sFixVersion .c_str () << " loaded." << std::endl;
130- std::cout << " ERROR: Could not locate config file." << std::endl;
131- std::cout << " ERROR: Make sure " << sConfigFile .c_str () << " is located in " << sFixPath .string ().c_str () << std::endl;
132- spdlog::shutdown ();
133- FreeLibraryAndExitThread (thisModule, 1 );
22+ return " 102DalmatiansPuppiesToTheRescueFOVFix" ;
13423 }
135- else
24+
25+ const char * FixVersion () const override
13626 {
137- spdlog::info (" Config file: {}" , sFixPath .string () + " \\ " + sConfigFile );
138- ini.parse (iniFile);
27+ return " 1.1" ;
13928 }
14029
141- // Parse config
142- ini.strip_trailing_comments ();
143- spdlog::info (" ----------" );
144-
145- // Load settings from ini
146- inipp::get_value (ini.sections [" FOVFix" ], " Enabled" , bFixActive);
147- spdlog_confparse (bFixActive);
148-
149- // Load resolution from ini
150- inipp::get_value (ini.sections [" Settings" ], " Width" , iCurrentResX);
151- inipp::get_value (ini.sections [" Settings" ], " Height" , iCurrentResY);
152- spdlog_confparse (iCurrentResX);
153- spdlog_confparse (iCurrentResY);
154-
155- // If resolution not specified, use desktop resolution
156- if (iCurrentResX <= 0 || iCurrentResY <= 0 )
30+ const char * TargetName () const override
15731 {
158- spdlog::info (" Resolution not specified in ini file. Using desktop resolution." );
159- // Implement Util::GetPhysicalDesktopDimensions() accordingly
160- auto desktopDimensions = Util::GetPhysicalDesktopDimensions ();
161- iCurrentResX = desktopDimensions.first ;
162- iCurrentResY = desktopDimensions.second ;
163- spdlog_confparse (iCurrentResX);
164- spdlog_confparse (iCurrentResY);
32+ return " 102 Dalmatians: Puppies to the Rescue" ;
16533 }
16634
167- spdlog::info (" ----------" );
168- }
169-
170- bool DetectGame ()
171- {
172- for (const auto & [type, info] : kGames )
35+ InitMode GetInitMode () const override
17336 {
174- if (Util::stringcmp_caseless (info.ExeName , sExeName ))
175- {
176- spdlog::info (" Detected game: {:s} ({:s})" , info.GameTitle , sExeName );
177- spdlog::info (" ----------" );
178- eGameType = type;
179- game = &info;
180- return true ;
181- }
37+ // return InitMode::Direct;
38+ return InitMode::WorkerThread;
39+ // return InitMode::ExportedOnly;
18240 }
18341
184- spdlog::error (" Failed to detect supported game, {:s} isn't supported by the fix." , sExeName );
185- return false ;
186- }
42+ bool IsCompatibleExecutable (const std::string& exeName) const override
43+ {
44+ return Util::stringcmp_caseless (exeName, " Pcdogs.exe" );
45+ }
18746
188- void FOVFix ()
189- {
190- if (eGameType == Game::DPTTR && bFixActive == true )
47+ void ParseFixConfig (inipp::Ini<char >& ini) override
19148 {
192- fNewAspectRatio = static_cast <float >(iCurrentResX) / static_cast <float >(iCurrentResY);
49+ inipp::get_value (ini.sections [" Settings" ], " Width" , m_newResX);
50+ inipp::get_value (ini.sections [" Settings" ], " Height" , m_newResY);
19351
194- fAspectRatioScale = fNewAspectRatio / fOldAspectRatio ;
52+ FallbackToDesktopResolution (m_newResX, m_newResY) ;
19553
196- std::uint8_t * AspectRatioInstructionScanResult = Memory::PatternScan (exeModule, " 0D 00 00 00 0F 00 00 00 10 00 00 00 FF 00 00 00 00 10 00 00 00 00 00 00 00 10 00 00 00" );
197- if (AspectRatioInstructionScanResult)
198- {
199- spdlog::info (" Aspect Ratio Instruction: Address is {:s}+{:x}" , sExeName .c_str (), AspectRatioInstructionScanResult + 16 - (std::uint8_t *)exeModule);
54+ spdlog_confparse (m_newResX);
55+ spdlog_confparse (m_newResY);
56+ }
20057
201- iNewCameraHFOV = (int )(iOriginalCameraHFOV / fAspectRatioScale );
58+ void ApplyFix () override
59+ {
60+ m_newAspectRatio = static_cast <float >(m_newResX) / static_cast <float >(m_newResY);
61+ m_aspectRatioScale = m_newAspectRatio / m_oldAspectRatio;
20262
203- Memory::Write (AspectRatioInstructionScanResult + 16 , iNewCameraHFOV);
204- }
205- else
63+ auto AspectRatioScanResult = Memory::PatternScan (ExeModule (), " 0D 00 00 00 0F 00 00 00 10 00 00 00 FF 00 00 00 00 10 00 00 00 00 00 00 00 10 00 00 00" );
64+ if (!AspectRatioScanResult)
20665 {
207- spdlog::error (" Failed to locate aspect ratio instruction memory address." );
66+ spdlog::error (" Failed to locate aspect ratio memory address." );
20867 return ;
20968 }
210- }
211- }
21269
213- DWORD __stdcall Main (void *)
214- {
215- Logging ();
216- Configuration ();
217- if (DetectGame ())
218- {
219- FOVFix ();
70+ spdlog::info (" Aspect Ratio: Address is {}+{:x}" , ExeName ().c_str (), AspectRatioScanResult + 16 - reinterpret_cast <std::uint8_t *>(ExeModule ()));
71+
72+ iNewAspectRatio = (int )(iOriginalAspectRatio / m_aspectRatioScale);
73+
74+ Memory::Write (AspectRatioScanResult + 16 , iNewAspectRatio);
22075 }
221- return TRUE ;
222- }
76+
77+ private:
78+ static constexpr float m_oldAspectRatio = 4 .0f / 3 .0f ;
79+ static constexpr uint16_t iOriginalAspectRatio = 4096 ;
80+ static inline int iNewAspectRatio;
81+
82+ inline static DalmatiansFix* s_instance_ = nullptr ;
83+ };
84+
85+ static std::unique_ptr<DalmatiansFix> g_fix;
22386
22487BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
22588{
89+ UNREFERENCED_PARAMETER (lpReserved);
90+
22691 switch (ul_reason_for_call)
22792 {
22893 case DLL_PROCESS_ATTACH :
22994 {
230- thisModule = hModule;
231- HANDLE mainHandle = CreateThread (NULL , 0 , Main, 0 , NULL , 0 );
232- if (mainHandle)
233- {
234- SetThreadPriority (mainHandle, THREAD_PRIORITY_HIGHEST );
235- CloseHandle (mainHandle);
236- }
95+ DisableThreadLibraryCalls (hModule);
96+
97+ g_fix = std::make_unique<DalmatiansFix>(hModule);
98+
99+ g_fix->Start ();
100+
101+ break ;
102+ }
103+
104+ case DLL_PROCESS_DETACH :
105+ {
106+ g_fix->Shutdown ();
107+ g_fix.reset ();
237108 break ;
238109 }
110+
239111 case DLL_THREAD_ATTACH :
240112 case DLL_THREAD_DETACH :
241- case DLL_PROCESS_DETACH :
113+ default :
242114 break ;
243115 }
116+
244117 return TRUE ;
245- }
118+ }
0 commit comments