Skip to content

Commit 951d4cb

Browse files
committed
Merge branch 'develop' of https://github.com/thelink2012/modloader into develop
2 parents 0f03586 + eca1fb5 commit 951d4cb

20 files changed

Lines changed: 290 additions & 196 deletions

File tree

doc/config/modloader.ini.0

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
;
44
[Folder.Config]
55
Profile = Default ; Profile to be used to configure mods
6+
PriorityLimit = 100 ; Max allowed value in Profiles.*.Priority (minimum is 1)
67

78
[Profiles.Default.Config]
89
IgnoreAllMods = false ; Ignores all mods. This essentially disables mods loading.
910
ExcludeAllMods = false ; Excludes all mods from being loaded except the ones at IncludeMods list
1011

1112
[Profiles.Default.Priority]
1213
; Defines mods priority (which mods should go into effect if both replaces the same file)
13-
; The priority should be between 1 and 100, where 100 overrides 1, additionally priority 0 makes be ignored (just like IgnoreMods)
14-
; The default priority is 50!
14+
; The priority should be between 1 and PriorityLimit, where higher values override lower ones.
15+
; Additionally priority 0 makes the mod be ignored (just like IgnoreMods).
16+
; The default priority is 50.
1517
;MyMod=50
1618

1719
[Profiles.Default.IgnoreFiles]

src/core/config.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ void Loader::UpdateOldConfig_0115_021()
242242
for(auto& kv : newer["Priority"])
243243
{
244244
auto pr = std::stoi(kv.second);
245-
kv.second = std::to_string(pr > 0 && pr <= 100? 101 - pr : pr);
245+
auto limit = this->mods.GetPriorityLimit();
246+
kv.second = std::to_string(pr > 0 && pr <= limit? (limit + 1) - pr : pr);
246247
}
247248

248249
newer.write_file(gamePath + "modloader/" + folderConfigFilename);

src/core/extras/gta3/menu.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ void TheMenu::ModPageEvents()
610610
// Helper function to setup a integer priority entry in the menu
611611
auto SetupPriorityEntry = [this](const char* label, uint32_t& priority, std::function<void(MenuEntry&)> cb) -> std::function<bool(ActionInfo&)>
612612
{
613-
static const uint32_t min = 0, max = 100, step = 1;
613+
const uint32_t min = 0, max = loader.mods.GetPriorityLimit(), step = 1;
614614

615615
auto PriorityLabel = [](const uint32_t& priority)
616616
{
@@ -624,10 +624,14 @@ void TheMenu::ModPageEvents()
624624
entry->SetHelper(HelpLabel(label));
625625
entry->OnStateChange(cb);
626626

627-
auto fInitStates = entry->SetupStatefulEntry(std::ref(priority), std::ref(PriorityLabel), [](const uint32_t& value, ActionInfo& info)
628-
{
629-
return std::min(std::max((value + step * info.wheel), min), max);
630-
});
627+
auto fInitStates = entry->SetupStatefulEntry(
628+
std::ref(priority),
629+
std::ref(PriorityLabel),
630+
[min, max, step](const uint32_t& value, ActionInfo& info)
631+
{
632+
return std::min(std::max((value + step * info.wheel), min), max);
633+
}
634+
);
631635

632636
return fInitStates;
633637
}

src/core/folder.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ using namespace modloader;
1414
void Loader::FolderInformation::Clear()
1515
{
1616
mods.clear();
17-
profiles.clear();
18-
current_profile = nullptr;
17+
RemoveProfiles();
18+
priority_limit = default_priority_limit;
1919
}
2020

2121
/*
@@ -127,8 +127,9 @@ void Loader::FolderInformation::RemoveReferencesToProfile(Loader::Profile& rm)
127127
*/
128128
void Loader::FolderInformation::RemoveProfiles()
129129
{
130-
for(auto it = this->profiles.begin(); it != this->profiles.end(); )
131-
it = this->profiles.erase(it); // Profile destructor automatically cleans this->current_profile
130+
for(auto& prof : this->profiles)
131+
RemoveReferencesToProfile(prof);
132+
this->profiles.clear();
132133
}
133134

134135
/*
@@ -374,7 +375,13 @@ void Loader::FolderInformation::LoadConfigFromINI()
374375

375376
// First take the profiles from modloader.ini
376377
if(ini.load_file(loader.folderConfigFilename))
378+
{
379+
const auto default_limit = std::to_string(default_priority_limit);
380+
const auto priority_limit = std::strtol(ini.get("Folder.Config", "PriorityLimit", default_limit).c_str(), 0, 0);
381+
this->SetPriorityLimit(priority_limit);
382+
377383
ReadProfilesFromINI(ini, "");
384+
}
378385
else
379386
Log("Warning: Failed to load folder config file");
380387

@@ -411,6 +418,7 @@ void Loader::FolderInformation::SaveConfigForINI()
411418

412419
// Save current profile
413420
ini.set("Folder.Config", "Profile", this->Profile().GetName());
421+
ini.set("Folder.Config", "PriorityLimit", std::to_string(this->GetPriorityLimit()));
414422

415423
// Save all the profiles
416424
for(auto& profile : this->profiles)

src/core/loader.cpp

Lines changed: 75 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
4298
bool 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

src/core/loader.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ static const char* downurl = "https://github.com/thelink2012/modloader/releases"
3434
//
3535
static const char* default_profile_name = "Default";
3636

37+
static const int default_priority_limit = 100;
38+
3739
// Functor for sorting based on priority
3840
template<class T>
3941
struct SimplePriorityPred
@@ -272,7 +274,6 @@ class Loader : public modloader_t
272274
{}
273275

274276
Profile(const Profile&) = default;
275-
~Profile();
276277

277278
//
278279
FolderInformation& Parent() const { return this->parent; }
@@ -413,6 +414,9 @@ class Loader : public modloader_t
413414

414415
// Gets the path to this modfolder (relative to gamedir, normalized)
415416
const std::string& GetPath() { return path; }
417+
418+
int GetPriorityLimit() const { return this->priority_limit; }
419+
void SetPriorityLimit(int value) { this->priority_limit = std::max(value, 1); }
416420

417421
// Clears all buffers from this structure
418422
void Clear();
@@ -454,6 +458,7 @@ class Loader : public modloader_t
454458
Loader::Profile* current_profile;// Curretly selected profile from the 'profiles' list
455459
std::unique_ptr<Loader::Profile> anon_profile;// Forced temporary profile (made by command line, conditionals, etc)
456460
// The .first boolean specifies whether we have a temporary profile
461+
int priority_limit = default_priority_limit; // Max allowed priority for mods in this folder config
457462

458463
protected:
459464
void SetUnchanged() { if(status != Status::Removed) status = Status::Unchanged; }
@@ -553,6 +558,7 @@ class Loader : public modloader_t
553558
bool Patch();
554559

555560
// Start or Shutdown the loader
561+
void InitFromEntryPoint();
556562
void Startup();
557563
void Shutdown();
558564

src/core/profiles.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,6 @@ static bool MatchWildcards(const std::string& string, const Container& patterns)
3333

3434

3535

36-
/*
37-
* Profile::~Profile
38-
*/
39-
Loader::Profile::~Profile()
40-
{
41-
parent.RemoveReferencesToProfile(*this);
42-
}
43-
4436
/*
4537
* Profile::Clear
4638
* Clears this object
@@ -256,7 +248,7 @@ void Loader::Profile::SetPriority(std::string name, int priority)
256248
if(priority == default_priority)
257249
mods_priority.erase(name);
258250
else
259-
mods_priority[name] = std::max(std::min(priority, 100), 0); // clamp to 0-100
251+
mods_priority[name] = std::max(std::min(priority, this->parent.GetPriorityLimit()), 0); // clamp to 0-priorityLimit
260252
}
261253

262254
/*

src/plugins/gta3/std.asi/CsInfo.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@ using namespace modloader;
1616
* This stores information about cleo scripts
1717
*/
1818
ThePlugin::CsInfo::CsInfo(const modloader::file* file) : file(file)
19-
{
20-
// Do basic initial setup
21-
this->bIsMission = false;
22-
this->iVersion = CLEO_VER_NONE;
23-
24-
std::string path = file->filepath();
19+
{
20+
const std::string path = file->filepath();
2521

2622
// Get path used to search files when a script tries to open a file (fopen etc)
2723
if(IsFileInsideFolder(file->filedir(), true, "CLEO")) // If inside a CLEO folder, use the path before it
28-
this->folder = path.substr(0, GetLastPathComponent(path, 2));
24+
this->translationPath = plugin_ptr->loader->gamepath + path.substr(0, GetLastPathComponent(path, 2));
2925
else // Use this path
30-
this->folder = path.substr(0, GetLastPathComponent(path, 1));
26+
this->translationPath = plugin_ptr->loader->gamepath + path.substr(0, GetLastPathComponent(path, 1));
3127

3228
// Get script attributes
3329
if(file->is_ext("cm"))

0 commit comments

Comments
 (0)