@@ -35,15 +35,70 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
3535 return TRUE ;
3636}
3737
38+ void Loader::InitFromEntryPoint ()
39+ {
40+ typedef function_hooker<0x53ECBD , void (int )> ridle_hook;
41+ typedef function_hooker<0x53ECCB , void (int )> rfidle_hook; // Actually void() but... meh
42+
43+ // This may be called more than once due to the cinit/WinMain trick at Patch().
44+ static bool has_patches = false ;
45+ if (has_patches) return ;
46+ has_patches = true ;
47+
48+ auto & gvm = injector::address_manager::singleton ();
49+
50+ #ifndef NDEBUG
51+ if (!gvm.IsSA ())
52+ {
53+ static int & gGameState = *mem_ptr (0xC8D4C0 ).get <int >();
54+
55+ gGameState = 5 ; // skip intro
56+ if (gvm.IsIII ())
57+ MakeNOP (raw_ptr (0x5811F8 ), 10 );
58+ else if (gvm.IsVC ())
59+ MakeNOP (raw_ptr (0x601B3B ), 10 );
60+
61+ if (gvm.IsIII ())
62+ MakeJMP (raw_ptr (0x405DB0 ), &Loader::Log);
63+ else if (gvm.IsVC ())
64+ MakeJMP (raw_ptr (0x401000 ), &Loader::Log);
65+
66+ // Debugger does not attach properly in III/VC DxWnd, so we need to do it manually.
67+ LaunchDebugger ();
68+ }
69+ #endif
70+
71+ // Install exception filter to log crashes
72+ InstallExceptionCatcher ([](const char * buffer)
73+ {
74+ Log (" \n\n " );
75+ LogGameVersion ();
76+ Log (buffer);
77+ loader.Shutdown ();
78+ });
79+
80+ // To be called each frame
81+ auto CallTick = [this ](ridle_hook::func_type Idle, int & i)
82+ {
83+ this ->Tick ();
84+ return Idle (i);
85+ };
86+
87+ // Do tick hook only if possible
88+ if (try_address (ridle_hook::addr)) make_static_hook<ridle_hook>(CallTick);
89+ if (try_address (rfidle_hook::addr)) make_static_hook<rfidle_hook>(CallTick);
90+
91+ this ->Startup ();
92+ }
93+
3894/*
3995 * Loader::Patch
4096 * Patches the game code to run the loader
4197 */
4298bool Loader::Patch ()
4399{
44100 typedef function_hooker_stdcall<0x8246EC , int (HINSTANCE , HINSTANCE , LPSTR , int )> winmain_hook;
45- typedef function_hooker<0x53ECBD , void (int )> ridle_hook;
46- typedef function_hooker<0x53ECCB , void (int )> rfidle_hook; // Actually void() but... meh
101+ typedef function_hooker<0x8246AC , int ()> cinit_hook;
47102
48103 auto & gvm = injector::address_manager::singleton ();
49104 gvm.set_name (" Mod Loader" );
@@ -54,74 +109,39 @@ bool Loader::Patch()
54109 if (injector::ReadMemory<int >(0xC920E8 )) // RwInitialized -- should enter only when using the mss32 loader on III/VC
55110 {
56111 const char * buf = " You installed Mod Loader wrongly!\n\n "
57- " You need Ultimate ASI Loader, and modloader.asi must be in the 'scripts/' directory.\n\n "
58- " Please refer to the 'Readme.txt' or 'Leia-me.txt' for more information." ;
112+ " You need Ultimate ASI Loader, and modloader.asi must be in the 'scripts/' directory.\n\n "
113+ " Please refer to the 'Readme.txt' or 'Leia-me.txt' for more information." ;
59114 this ->Error (buf);
60115 return false ;
61116 }
62117
63118 // Hook WinMain to run mod loader
64119 injector::make_static_hook<winmain_hook>([this ](winmain_hook::func_type WinMain,
65- HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
120+ HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
66121 {
67- // Avoind circular looping forever
122+ // Avoid circular looping forever
68123 static bool bRan = false ;
69124 if (bRan) return WinMain (hInstance, hPrevInstance, lpCmdLine, nCmdShow);
70125 bRan = true ;
71126
72- #ifndef NDEBUG
73- auto & gvm = injector::address_manager::singleton ();
74- if (!gvm.IsSA ())
75- {
76- static int & gGameState = *mem_ptr (0xC8D4C0 ).get <int >();
77-
78- gGameState = 5 ; // skip intro
79- if (gvm.IsIII ())
80- MakeNOP (raw_ptr (0x5811F8 ), 10 );
81- else if (gvm.IsVC ())
82- MakeNOP (raw_ptr (0x601B3B ), 10 );
83-
84- if (gvm.IsIII ())
85- MakeJMP (raw_ptr (0x405DB0 ), &Loader::Log);
86- else if (gvm.IsVC ())
87- MakeJMP (raw_ptr (0x401000 ), &Loader::Log);
88-
89- // Debugger does not attach properly in III/VC DxWnd, so we need to do it manually.
90- LaunchDebugger ();
91- }
92- #endif
93-
94- // Install exception filter to log crashes
95- InstallExceptionCatcher ([](const char * buffer)
96- {
97- Log (" \n\n " );
98- LogGameVersion ();
99- Log (buffer);
100- loader.Shutdown ();
101- });
102-
103- // To be called each frame
104- auto CallTick = [this ](ridle_hook::func_type Idle, int & i)
105- {
106- this ->Tick ();
107- return Idle (i);
108- };
109-
110- // Do tick hook only if possible
111- if (try_address (ridle_hook::addr)) make_static_hook<ridle_hook>(CallTick);
112- if (try_address (rfidle_hook::addr)) make_static_hook<rfidle_hook>(CallTick);
113-
114- // Startup the loader and call WinMain, Shutdown the loader after WinMain.
115- // If any mod hooked WinMain at Startup, no conflict will happen, we're takin' care of that
116- {
117- this ->Startup ();
118- auto WinMain = (winmain_hook::func_type_raw) ReadRelativeOffset (winmain_hook::addr + 1 ).get ();
119- auto result = WinMain (hInstance, hPrevInstance, lpCmdLine, nCmdShow);
127+ this ->InitFromEntryPoint ();
128+ auto WinMainNext = (winmain_hook::func_type_raw)ReadRelativeOffset (winmain_hook::addr + 1 ).get ();
129+ auto result = WinMainNext (hInstance, hPrevInstance, lpCmdLine, nCmdShow);
120130 this ->Shutdown ();
121131 return result;
122- }
123132 });
124133
134+ // If possible, startup before WinMain.
135+ // This is important for certain mods (e.g. FLA) that need to change offsets of
136+ // functions that run pre-WinMain (e.g. global constructors).
137+ if (try_address (cinit_hook::addr))
138+ {
139+ injector::make_static_hook<cinit_hook>([this ](cinit_hook::func_type cinit) {
140+ this ->InitFromEntryPoint ();
141+ return cinit ();
142+ });
143+ }
144+
125145 return true ;
126146 }
127147 else
0 commit comments