Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Rubberduckycooly
GitHub Repository: Rubberduckycooly/RSDKv5-Decompilation
Path: blob/master/RSDKv5/RSDK/Core/ModAPI.cpp
1162 views
1
#include "RSDK/Core/RetroEngine.hpp"
2
3
#if RETRO_USE_MOD_LOADER
4
5
using namespace RSDK;
6
7
#if RETRO_REV0U
8
#include "Legacy/ModAPILegacy.cpp"
9
#endif
10
11
#include <filesystem>
12
#include <stdexcept>
13
#include <functional>
14
15
#if RETRO_PLATFORM != RETRO_ANDROID
16
namespace fs = std::filesystem;
17
#else
18
bool fs::exists(fs::path path)
19
{
20
auto *jni = GetJNISetup();
21
jbyteArray array = jni->env->NewByteArray(path.string().length());
22
jni->env->SetByteArrayRegion(array, 0, path.string().length(), (jbyte *)path.string().c_str());
23
return jni->env->CallBooleanMethod(jni->thiz, fsExists, array);
24
}
25
26
bool fs::is_directory(fs::path path)
27
{
28
auto *jni = GetJNISetup();
29
jbyteArray array = jni->env->NewByteArray(path.string().length());
30
jni->env->SetByteArrayRegion(array, 0, path.string().length(), (jbyte *)path.string().c_str());
31
return jni->env->CallBooleanMethod(jni->thiz, fsIsDir, array);
32
}
33
34
fs::path_list fs::directory_iterator(fs::path path)
35
{
36
auto *jni = GetJNISetup();
37
jbyteArray array = jni->env->NewByteArray(path.string().length());
38
jni->env->SetByteArrayRegion(array, 0, path.string().length(), (jbyte *)path.string().c_str());
39
return fs::path_list((jobjectArray)jni->env->CallObjectMethod(jni->thiz, fsDirIter, array));
40
}
41
#endif
42
43
#include "iniparser/iniparser.h"
44
45
int32 RSDK::currentObjectID = 0;
46
std::vector<ObjectClass *> allocatedInherits;
47
48
// this helps later on just trust me
49
#define MODAPI_ENDS_WITH(str) buf.length() > strlen(str) && !buf.compare(buf.length() - strlen(str), strlen(str), std::string(str))
50
51
ModSettings RSDK::modSettings;
52
std::vector<ModInfo> RSDK::modList;
53
std::vector<ModCallbackSTD> RSDK::modCallbackList[MODCB_MAX];
54
std::vector<StateHook> RSDK::stateHookList;
55
std::vector<ObjectHook> RSDK::objectHookList;
56
ModVersionInfo RSDK::targetModVersion = { RETRO_REVISION, 0, RETRO_MOD_LOADER_VER };
57
58
char RSDK::customUserFileDir[0x100];
59
60
RSDK::ModInfo *RSDK::currentMod;
61
62
std::vector<RSDK::ModPublicFunctionInfo> gamePublicFuncs;
63
64
void *RSDK::modFunctionTable[RSDK::ModTable_Count];
65
66
std::map<uint32, uint32> RSDK::superLevels;
67
int32 RSDK::inheritLevel = 0;
68
69
#define ADD_MOD_FUNCTION(id, func) modFunctionTable[id] = (void *)func;
70
71
// https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
72
std::string trim(const std::string &s)
73
{
74
auto start = s.begin();
75
while (start != s.end() && std::isspace(*start)) {
76
start++;
77
}
78
79
auto end = s.end();
80
do {
81
end--;
82
} while (std::distance(start, end) > 0 && std::isspace(*end));
83
84
return std::string(start, end + 1);
85
}
86
87
void RSDK::InitModAPI(bool32 getVersion)
88
{
89
memset(modFunctionTable, 0, sizeof(modFunctionTable));
90
91
// ============================
92
// Mod Function Table
93
// ============================
94
95
// Registration & Core
96
ADD_MOD_FUNCTION(ModTable_RegisterGlobals, ModRegisterGlobalVariables);
97
ADD_MOD_FUNCTION(ModTable_RegisterObject, ModRegisterObject);
98
ADD_MOD_FUNCTION(ModTable_RegisterObjectSTD, ModRegisterObject_STD);
99
ADD_MOD_FUNCTION(ModTable_RegisterObjectHook, ModRegisterObjectHook);
100
ADD_MOD_FUNCTION(ModTable_FindObject, ModFindObject);
101
ADD_MOD_FUNCTION(ModTable_GetGlobals, GetGlobals);
102
ADD_MOD_FUNCTION(ModTable_Super, Super);
103
104
// Mod Info
105
ADD_MOD_FUNCTION(ModTable_LoadModInfo, LoadModInfo);
106
ADD_MOD_FUNCTION(ModTable_GetModPath, GetModPath);
107
ADD_MOD_FUNCTION(ModTable_GetModCount, GetModCount);
108
ADD_MOD_FUNCTION(ModTable_GetModIDByIndex, GetModIDByIndex);
109
ADD_MOD_FUNCTION(ModTable_ForeachModID, ForeachModID);
110
111
// Mod Callbacks & Public Functions
112
ADD_MOD_FUNCTION(ModTable_AddModCallback, AddModCallback);
113
ADD_MOD_FUNCTION(ModTable_AddModCallbackSTD, AddModCallback_STD);
114
ADD_MOD_FUNCTION(ModTable_AddPublicFunction, AddPublicFunction);
115
ADD_MOD_FUNCTION(ModTable_GetPublicFunction, GetPublicFunction);
116
117
// Mod Settings
118
ADD_MOD_FUNCTION(ModTable_GetSettingsBool, GetSettingsBool);
119
ADD_MOD_FUNCTION(ModTable_GetSettingsInt, GetSettingsInteger);
120
ADD_MOD_FUNCTION(ModTable_GetSettingsFloat, GetSettingsFloat);
121
ADD_MOD_FUNCTION(ModTable_GetSettingsString, GetSettingsString);
122
ADD_MOD_FUNCTION(ModTable_SetSettingsBool, SetSettingsBool);
123
ADD_MOD_FUNCTION(ModTable_SetSettingsInt, SetSettingsInteger);
124
ADD_MOD_FUNCTION(ModTable_SetSettingsFloat, SetSettingsFloat);
125
ADD_MOD_FUNCTION(ModTable_SetSettingsString, SetSettingsString);
126
ADD_MOD_FUNCTION(ModTable_SaveSettings, SaveSettings);
127
128
// Config
129
ADD_MOD_FUNCTION(ModTable_GetConfigBool, GetConfigBool);
130
ADD_MOD_FUNCTION(ModTable_GetConfigInt, GetConfigInteger);
131
ADD_MOD_FUNCTION(ModTable_GetConfigFloat, GetConfigFloat);
132
ADD_MOD_FUNCTION(ModTable_GetConfigString, GetConfigString);
133
ADD_MOD_FUNCTION(ModTable_ForeachConfig, ForeachConfig);
134
ADD_MOD_FUNCTION(ModTable_ForeachConfigCategory, ForeachConfigCategory);
135
136
// Achievements
137
ADD_MOD_FUNCTION(ModTable_RegisterAchievement, RegisterAchievement);
138
ADD_MOD_FUNCTION(ModTable_GetAchievementInfo, GetAchievementInfo);
139
ADD_MOD_FUNCTION(ModTable_GetAchievementIndexByID, GetAchievementIndexByID);
140
ADD_MOD_FUNCTION(ModTable_GetAchievementCount, GetAchievementCount);
141
142
// Shaders
143
ADD_MOD_FUNCTION(ModTable_LoadShader, RenderDevice::LoadShader);
144
145
// StateMachine
146
ADD_MOD_FUNCTION(ModTable_StateMachineRun, StateMachineRun);
147
ADD_MOD_FUNCTION(ModTable_RegisterStateHook, RegisterStateHook);
148
ADD_MOD_FUNCTION(ModTable_HandleRunState_HighPriority, HandleRunState_HighPriority);
149
ADD_MOD_FUNCTION(ModTable_HandleRunState_LowPriority, HandleRunState_LowPriority);
150
151
#if RETRO_MOD_LOADER_VER >= 2
152
// Mod Settings (Part 2)
153
ADD_MOD_FUNCTION(ModTable_ForeachSetting, ForeachSetting);
154
ADD_MOD_FUNCTION(ModTable_ForeachSettingCategory, ForeachSettingCategory);
155
156
// Files
157
ADD_MOD_FUNCTION(ModTable_ExcludeFile, ExcludeFile);
158
ADD_MOD_FUNCTION(ModTable_ExcludeAllFiles, ExcludeAllFiles);
159
ADD_MOD_FUNCTION(ModTable_ReloadFile, ReloadFile);
160
ADD_MOD_FUNCTION(ModTable_ReloadAllFiles, ReloadAllFiles);
161
162
// Graphics
163
ADD_MOD_FUNCTION(ModTable_GetSpriteAnimation, GetSpriteAnimation);
164
ADD_MOD_FUNCTION(ModTable_GetSpriteSurface, GetSpriteSurface);
165
ADD_MOD_FUNCTION(ModTable_GetPaletteBank, GetPaletteBank);
166
ADD_MOD_FUNCTION(ModTable_GetActivePaletteBuffer, GetActivePaletteBuffer);
167
ADD_MOD_FUNCTION(ModTable_GetRGB32To16Buffer, GetRGB32To16Buffer);
168
ADD_MOD_FUNCTION(ModTable_GetBlendLookupTable, GetBlendLookupTable);
169
ADD_MOD_FUNCTION(ModTable_GetSubtractLookupTable, GetSubtractLookupTable);
170
ADD_MOD_FUNCTION(ModTable_GetTintLookupTable, GetTintLookupTable);
171
ADD_MOD_FUNCTION(ModTable_GetMaskColor, GetMaskColor);
172
ADD_MOD_FUNCTION(ModTable_GetScanEdgeBuffer, GetScanEdgeBuffer);
173
ADD_MOD_FUNCTION(ModTable_GetCamera, GetCamera);
174
ADD_MOD_FUNCTION(ModTable_GetShader, GetShader);
175
ADD_MOD_FUNCTION(ModTable_GetModel, GetModel);
176
ADD_MOD_FUNCTION(ModTable_GetScene3D, GetScene3D);
177
ADD_MOD_FUNCTION(ModTable_DrawDynamicAniTile, DrawDynamicAniTile);
178
179
// Audio
180
ADD_MOD_FUNCTION(ModTable_GetSfx, GetSfxEntry);
181
ADD_MOD_FUNCTION(ModTable_GetChannel, GetChannel);
182
183
// Objects/Entities
184
ADD_MOD_FUNCTION(ModTable_GetGroupEntities, GetGroupEntities);
185
186
// Collision
187
ADD_MOD_FUNCTION(ModTable_SetPathGripSensors, SetPathGripSensors);
188
ADD_MOD_FUNCTION(ModTable_FloorCollision, FloorCollision);
189
ADD_MOD_FUNCTION(ModTable_LWallCollision, LWallCollision);
190
ADD_MOD_FUNCTION(ModTable_RoofCollision, RoofCollision);
191
ADD_MOD_FUNCTION(ModTable_RWallCollision, RWallCollision);
192
ADD_MOD_FUNCTION(ModTable_FindFloorPosition, FindFloorPosition);
193
ADD_MOD_FUNCTION(ModTable_FindLWallPosition, FindLWallPosition);
194
ADD_MOD_FUNCTION(ModTable_FindRoofPosition, FindRoofPosition);
195
ADD_MOD_FUNCTION(ModTable_FindRWallPosition, FindRWallPosition);
196
ADD_MOD_FUNCTION(ModTable_CopyCollisionMask, CopyCollisionMask);
197
ADD_MOD_FUNCTION(ModTable_GetCollisionInfo, GetCollisionInfo);
198
#endif
199
200
superLevels.clear();
201
inheritLevel = 0;
202
LoadMods(false, getVersion);
203
}
204
205
void RSDK::SortMods()
206
{
207
if (ENGINE_VERSION) {
208
for (int32 m = 0; m < modList.size(); ++m) {
209
int32 targetVersion = modList[m].forceVersion ? modList[m].forceVersion : modList[m].targetVersion;
210
211
if (modList[m].active && targetVersion != -1 && targetVersion != ENGINE_VERSION) {
212
PrintLog(PRINT_NORMAL, "[MOD] Mod %s disabled due to target version mismatch", modList[m].id.c_str());
213
modList[m].active = false;
214
}
215
}
216
}
217
218
std::stable_sort(modList.begin(), modList.end(), [](const ModInfo &a, const ModInfo &b) {
219
if (!(a.active && b.active))
220
return a.active;
221
// keep it unsorted i guess
222
return false;
223
});
224
}
225
226
void RSDK::LoadModSettings()
227
{
228
customUserFileDir[0] = 0;
229
230
modSettings.redirectSaveRAM = false;
231
modSettings.disableGameLogic = false;
232
233
#if RETRO_REV0U
234
modSettings.versionOverride = 0;
235
modSettings.forceScripts = customSettings.forceScripts;
236
#endif
237
238
if (modList.empty())
239
return;
240
241
// Iterate backwards to find the last active mod in the list
242
int32 start = modList.size() - 1;
243
while ((start != -1) && !modList[start].active) {
244
--start;
245
}
246
247
// No active mod in the list
248
if (start == -1)
249
return;
250
251
for (int32 i = start; i >= 0; --i) {
252
ModInfo *mod = &modList[i];
253
254
if (mod->redirectSaveRAM) {
255
if (SKU::userFileDir[0])
256
sprintf(customUserFileDir, "%smods/%s/", SKU::userFileDir, mod->folderName.c_str());
257
else
258
sprintf(customUserFileDir, "mods/%s/", mod->folderName.c_str());
259
}
260
261
modSettings.redirectSaveRAM |= mod->redirectSaveRAM ? 1 : 0;
262
modSettings.disableGameLogic |= mod->disableGameLogic ? 1 : 0;
263
264
#if RETRO_REV0U
265
if (mod->forceVersion)
266
modSettings.versionOverride = mod->forceVersion;
267
modSettings.forceScripts |= mod->forceScripts ? 1 : 0;
268
#endif
269
}
270
}
271
272
void RSDK::ApplyModChanges()
273
{
274
#if RETRO_REV0U
275
uint32 category = sceneInfo.activeCategory;
276
uint32 scene = sceneInfo.listPos;
277
dataStorage[DATASET_SFX].usedStorage = 0;
278
RefreshModFolders(true);
279
LoadModSettings();
280
DetectEngineVersion();
281
if (!engine.version)
282
engine.version = devMenu.startingVersion;
283
284
switch (engine.version) {
285
case 5:
286
globalVarsInitCB = NULL;
287
LoadGameConfig();
288
sceneInfo.state = ENGINESTATE_DEVMENU;
289
Legacy::gameMode = Legacy::ENGINE_MAINGAME;
290
break;
291
292
case 4:
293
Legacy::v4::LoadGameConfig("Data/Game/GameConfig.bin");
294
strcpy(gameVerInfo.version, "Legacy v4 Mode");
295
296
sceneInfo.state = ENGINESTATE_NONE; // i think this is fine ??? lmk if otherwise // rmg seal of approval // WAIT THIS WAS ME
297
Legacy::gameMode = Legacy::ENGINE_DEVMENU;
298
break;
299
300
case 3:
301
Legacy::v3::LoadGameConfig("Data/Game/GameConfig.bin");
302
strcpy(gameVerInfo.version, "Legacy v3 Mode");
303
304
sceneInfo.state = ENGINESTATE_NONE;
305
Legacy::gameMode = Legacy::ENGINE_DEVMENU;
306
break;
307
}
308
if (engine.version == devMenu.startingVersion) {
309
sceneInfo.activeCategory = category;
310
sceneInfo.listPos = scene;
311
}
312
#else
313
uint32 category = sceneInfo.activeCategory;
314
uint32 scene = sceneInfo.listPos;
315
dataStorage[DATASET_SFX].usedStorage = 0;
316
RefreshModFolders(true);
317
LoadModSettings();
318
LoadGameConfig();
319
sceneInfo.activeCategory = category;
320
sceneInfo.listPos = scene;
321
#endif
322
RenderDevice::SetWindowTitle();
323
}
324
325
void DrawStatus(const char *str)
326
{
327
int32 dy = currentScreen->center.y - 32;
328
DrawRectangle(currentScreen->center.x - 128, dy + 52, 0x100, 0x8, 0x80, 0xFF, INK_NONE, true);
329
DrawDevString(str, currentScreen->center.x, dy + 52, ALIGN_CENTER, 0xFFFFFF);
330
331
RenderDevice::CopyFrameBuffer();
332
RenderDevice::FlipScreen();
333
}
334
335
#if RETRO_RENDERDEVICE_EGL
336
// egl devices are slower in I/O so render more increments
337
#define BAR_THRESHOLD (10.F)
338
#define RENDER_COUNT (200)
339
#else
340
#define BAR_THRESHOLD (100.F)
341
#define RENDER_COUNT (200)
342
#endif
343
344
bool32 RSDK::ScanModFolder(ModInfo *info, const char *targetFile, bool32 fromLoadMod, bool32 loadingBar)
345
{
346
if (!info)
347
return false;
348
349
const std::string modDir = info->path;
350
351
if (!targetFile)
352
info->fileMap.clear();
353
354
std::string targetFileStr = "";
355
if (targetFile) {
356
char pathLower[0x100];
357
memset(pathLower, 0, sizeof(char) * 0x100);
358
for (int32 c = 0; c < strlen(targetFile); ++c) pathLower[c] = tolower(targetFile[c]);
359
360
targetFileStr = std::string(pathLower);
361
}
362
363
fs::path dataPath(modDir);
364
int32 dy = currentScreen->center.y - 32;
365
int32 dx = currentScreen->center.x;
366
367
if (targetFile) {
368
if (fs::exists(fs::path(modDir + "/" + targetFileStr))) {
369
info->fileMap.insert(std::pair<std::string, std::string>(targetFileStr, modDir + "/" + targetFileStr));
370
return true;
371
}
372
else
373
return false;
374
}
375
376
if (fs::exists(dataPath) && fs::is_directory(dataPath)) {
377
try {
378
if (loadingBar) {
379
currentScreen = &screens[0];
380
DrawRectangle(dx - 0x80 + 0x10, dy + 48, 0x100 - 0x20, 0x10, 0x000000, 0xFF, INK_NONE, true);
381
DrawDevString(fromLoadMod ? "Getting count..." : ("Scanning " + info->id + "...").c_str(), currentScreen->center.x, dy + 52,
382
ALIGN_CENTER, 0xFFFFFF);
383
RenderDevice::CopyFrameBuffer();
384
RenderDevice::FlipScreen();
385
}
386
387
auto dirIterator = fs::recursive_directory_iterator(dataPath, fs::directory_options::follow_directory_symlink);
388
389
std::vector<fs::directory_entry> files;
390
391
int32 renders = 1;
392
int32 size = 0;
393
394
for (auto dirFile : dirIterator) {
395
#if RETRO_PLATFORM != RETRO_ANDROID
396
if (!dirFile.is_directory()) {
397
#endif
398
files.push_back(dirFile);
399
400
if (loadingBar && ++size >= RENDER_COUNT * renders) {
401
DrawRectangle(dx - 0x80 + 0x10, dy + 48, 0x100 - 0x20, 0x10, 0x000000, 0xFF, INK_NONE, true);
402
DrawDevString((std::to_string(size) + " files").c_str(), currentScreen->center.x, dy + 52, ALIGN_CENTER, 0xFFFFFF);
403
RenderDevice::CopyFrameBuffer();
404
RenderDevice::FlipScreen();
405
renders++;
406
}
407
#if RETRO_PLATFORM != RETRO_ANDROID
408
}
409
#endif
410
}
411
412
int32 i = 0;
413
int32 bars = 1;
414
415
for (auto dirFile : files) {
416
std::string folderPath = dirFile.path().string().substr(dataPath.string().length() + 1);
417
std::transform(folderPath.begin(), folderPath.end(), folderPath.begin(),
418
[](unsigned char c) { return c == '\\' ? '/' : std::tolower(c); });
419
420
info->fileMap.insert(std::pair<std::string, std::string>(folderPath, dirFile.path().string()));
421
if (loadingBar && (size * bars) / BAR_THRESHOLD < ++i) {
422
DrawRectangle(dx - 0x80 + 0x10, dy + 48, 0x100 - 0x20, 0x10, 0x000000, 0xFF, INK_NONE, true);
423
DrawRectangle(dx - 0x80 + 0x10 + 2, dy + 50, (int32)((0x100 - 0x20 - 4) * (i / (float)size)), 0x10 - 4, 0x00FF00, 0xFF, INK_NONE,
424
true);
425
while ((size * bars) / BAR_THRESHOLD < i) bars++;
426
DrawDevString((std::to_string(i) + "/" + std::to_string(size)).c_str(), currentScreen->center.x, dy + 52, ALIGN_CENTER, 0xFFFFFF);
427
RenderDevice::CopyFrameBuffer();
428
RenderDevice::FlipScreen();
429
}
430
}
431
} catch (fs::filesystem_error &fe) {
432
PrintLog(PRINT_ERROR, "Mod File Scanning Error: %s", fe.what());
433
}
434
}
435
436
if (loadingBar && fromLoadMod) {
437
DrawRectangle(dx - 0x80 + 0x10, dy + 48, 0x100 - 0x20, 0x10, 0x000080, 0xFF, INK_NONE, true);
438
439
RenderDevice::CopyFrameBuffer();
440
RenderDevice::FlipScreen();
441
}
442
443
return true;
444
}
445
446
void RSDK::UnloadMods()
447
{
448
for (ModInfo &mod : modList) {
449
if (mod.unloadMod)
450
mod.unloadMod();
451
452
for (Link::Handle &handle : mod.modLogicHandles) {
453
Link::Close(handle);
454
}
455
456
mod.modLogicHandles.clear();
457
}
458
459
modList.clear();
460
for (int32 c = 0; c < MODCB_MAX; ++c) modCallbackList[c].clear();
461
stateHookList.clear();
462
objectHookList.clear();
463
464
for (int32 i = 0; i < (int32)allocatedInherits.size(); ++i) {
465
ObjectClass *inherit = allocatedInherits[i];
466
if (inherit)
467
delete inherit;
468
}
469
allocatedInherits.clear();
470
471
#if RETRO_REV0U
472
memset(Legacy::modTypeNames, 0, sizeof(Legacy::modTypeNames));
473
memset(Legacy::modTypeNames, 0, sizeof(Legacy::modScriptPaths));
474
memset(Legacy::modScriptFlags, 0, sizeof(Legacy::modScriptFlags));
475
Legacy::modObjCount = 0;
476
477
memset(modSettings.playerNames, 0, sizeof(modSettings.playerNames));
478
modSettings.playerCount = 0;
479
480
modSettings.versionOverride = 0;
481
modSettings.activeMod = -1;
482
#endif
483
484
customUserFileDir[0] = 0;
485
486
// Clear storage
487
dataStorage[DATASET_STG].usedStorage = 0;
488
DefragmentAndGarbageCollectStorage(DATASET_MUS);
489
dataStorage[DATASET_SFX].usedStorage = 0;
490
dataStorage[DATASET_STR].usedStorage = 0;
491
dataStorage[DATASET_TMP].usedStorage = 0;
492
493
#if RETRO_REV02
494
// Clear out any userDBs
495
if (SKU::userDBStorage)
496
SKU::userDBStorage->ClearAllUserDBs();
497
#endif
498
}
499
500
void RSDK::LoadMods(bool newOnly, bool32 getVersion)
501
{
502
if (!newOnly) {
503
UnloadMods();
504
505
if (AudioDevice::initializedAudioChannels) {
506
// Stop all sounds
507
for (int32 c = 0; c < CHANNEL_COUNT; ++c) StopChannel(c);
508
509
// we're about to reload these, so clear anything we already have
510
ClearGlobalSfx();
511
}
512
}
513
514
using namespace std;
515
char modBuf[0x100];
516
sprintf_s(modBuf, sizeof(modBuf), "%smods", SKU::userFileDir);
517
fs::path modPath(modBuf);
518
519
if (fs::exists(modPath) && fs::is_directory(modPath)) {
520
string mod_config = modPath.string() + "/modconfig.ini";
521
FileIO *configFile = fOpen(mod_config.c_str(), "r");
522
if (configFile) {
523
fClose(configFile);
524
auto ini = iniparser_load(mod_config.c_str());
525
526
int32 c = iniparser_getsecnkeys(ini, "Mods");
527
const char **keys = new const char *[c];
528
iniparser_getseckeys(ini, "Mods", keys);
529
530
for (int32 m = 0; m < c; ++m) {
531
if (newOnly && std::find_if(modList.begin(), modList.end(), [&keys, &m](ModInfo mod) {
532
return mod.folderName == string(keys[m] + 5);
533
}) != modList.end())
534
continue;
535
ModInfo info = {};
536
bool32 active = iniparser_getboolean(ini, keys[m], false);
537
bool32 loaded = LoadMod(&info, modPath.string(), string(keys[m] + 5), active, getVersion);
538
if (info.id.empty()) {
539
PrintLog(PRINT_NORMAL, "[MOD] Mod %s doesn't exist!", keys[m] + 5);
540
continue;
541
}
542
else if (!loaded) {
543
PrintLog(PRINT_NORMAL, "[MOD] Failed to load mod %s.", info.id.c_str(), active ? "Y" : "N");
544
info.active = false;
545
}
546
else
547
PrintLog(PRINT_NORMAL, "[MOD] Loaded mod %s! Active: %s", info.id.c_str(), active ? "Y" : "N");
548
modList.push_back(info);
549
}
550
delete[] keys;
551
iniparser_freedict(ini);
552
}
553
554
try {
555
auto rdi = fs::directory_iterator(modPath);
556
for (auto de : rdi) {
557
if (de.is_directory()) {
558
fs::path modDirPath = de.path();
559
ModInfo info = {};
560
std::string folder = modDirPath.filename().string();
561
562
if (std::find_if(modList.begin(), modList.end(), [&folder](ModInfo m) { return m.folderName == folder; }) == modList.end()) {
563
564
const std::string modDir = modPath.string() + "/" + folder;
565
566
FileIO *f = fOpen((modDir + "/mod.ini").c_str(), "r");
567
if (f) {
568
fClose(f);
569
LoadMod(&info, modPath.string(), folder, false, getVersion);
570
modList.push_back(info);
571
}
572
}
573
}
574
}
575
} catch (fs::filesystem_error &fe) {
576
PrintLog(PRINT_ERROR, "Mods folder scanning error: %s", fe.what());
577
}
578
}
579
580
int32 dy = currentScreen->center.y - 32;
581
DrawRectangle(currentScreen->center.x - 128, dy, 0x100, 0x48, 0x80, 0xFF, INK_NONE, true);
582
DrawDevString("Mod loading done!", currentScreen->center.x, dy + 28, ALIGN_CENTER, 0xFFFFFF);
583
RenderDevice::CopyFrameBuffer();
584
RenderDevice::FlipScreen();
585
586
SortMods();
587
LoadModSettings();
588
}
589
590
void loadCfg(ModInfo *info, const std::string &path)
591
{
592
FileInfo cfg;
593
InitFileInfo(&cfg);
594
cfg.externalFile = true;
595
// CFG FILE READ
596
if (LoadFile(&cfg, path.c_str(), FMODE_RB)) {
597
int32 catCount = ReadInt8(&cfg);
598
for (int32 c = 0; c < catCount; ++c) {
599
char catBuf[0x100];
600
ReadString(&cfg, catBuf);
601
int32 keyCount = ReadInt8(&cfg);
602
for (int32 k = 0; k < keyCount; ++k) {
603
// ReadString except w packing the type bit
604
uint8 size = ReadInt8(&cfg);
605
char *keyBuf = new char[size & 0x7F];
606
ReadBytes(&cfg, keyBuf, size & 0x7F);
607
keyBuf[size & 0x7F] = 0;
608
uint8 type = size & 0x80;
609
if (!type) {
610
char buf[0xFFFF];
611
ReadString(&cfg, buf);
612
info->config[catBuf][keyBuf] = buf;
613
}
614
else
615
info->config[catBuf][keyBuf] = std::to_string(ReadInt32(&cfg, false));
616
delete[] keyBuf;
617
}
618
}
619
620
CloseFile(&cfg);
621
}
622
}
623
624
bool32 RSDK::LoadMod(ModInfo *info, const std::string &modsPath, const std::string &folder, bool32 active, bool32 getVersion)
625
{
626
if (!info)
627
return false;
628
629
ModInfo *cur = currentMod;
630
631
PrintLog(PRINT_NORMAL, "[MOD] Trying to load mod %s...", folder.c_str());
632
633
info->fileMap.clear();
634
info->excludedFiles.clear();
635
info->modLogicHandles.clear();
636
info->name = "";
637
info->desc = "";
638
info->author = "";
639
info->version = "";
640
info->id = "";
641
info->active = false;
642
info->redirectSaveRAM = false;
643
info->disableGameLogic = false;
644
645
const std::string modDir = modsPath + "/" + folder;
646
647
FileIO *f = fOpen((modDir + "/mod.ini").c_str(), "r");
648
if (f) {
649
int32 dy = currentScreen->center.y - 32;
650
DrawRectangle(currentScreen->center.x - 128, dy, 0x100, 0x48, 0x80, 0xFF, INK_NONE, true);
651
652
DrawDevString("Loading mod", currentScreen->center.x, dy + 16, ALIGN_CENTER, 0xFFFFFF);
653
DrawDevString((folder + "...").c_str(), currentScreen->center.x, dy + 28, ALIGN_CENTER, 0xFFFFFF);
654
655
DrawStatus("Parsing INI...");
656
657
fClose(f);
658
auto modIni = iniparser_load((modDir + "/mod.ini").c_str());
659
660
info->path = modDir;
661
info->folderName = folder;
662
info->id = iniparser_getstring(modIni, ":ModID", folder.c_str());
663
info->active = active;
664
665
info->name = iniparser_getstring(modIni, ":Name", "Unnamed Mod");
666
info->desc = iniparser_getstring(modIni, ":Description", "");
667
info->author = iniparser_getstring(modIni, ":Author", "Unknown Author");
668
info->version = iniparser_getstring(modIni, ":Version", "1.0.0");
669
670
info->redirectSaveRAM = iniparser_getboolean(modIni, ":RedirectSaveRAM", false);
671
info->disableGameLogic = iniparser_getboolean(modIni, ":DisableGameLogic", false);
672
673
info->forceVersion = iniparser_getint(modIni, ":ForceVersion", 0);
674
if (!info->forceVersion) {
675
info->targetVersion = iniparser_getint(modIni, ":TargetVersion", 5);
676
if (info->targetVersion != -1 && ENGINE_VERSION) {
677
if (info->targetVersion < 3 || info->targetVersion > 5) {
678
PrintLog(PRINT_NORMAL, "[MOD] Invalid target version. Should be 3, 4, or 5");
679
return false;
680
}
681
else if (info->targetVersion != ENGINE_VERSION) {
682
PrintLog(PRINT_NORMAL, "[MOD] Target version does not match current engine version.");
683
return false;
684
}
685
}
686
}
687
else
688
info->targetVersion = info->forceVersion;
689
info->forceScripts = iniparser_getboolean(modIni, ":TxtScripts", false);
690
691
if (!active) {
692
iniparser_freedict(modIni);
693
return true;
694
}
695
696
// ASSETS
697
DrawStatus("Scanning mod folder...");
698
ScanModFolder(info, getVersion ? "Data/Game/GameConfig.bin" : nullptr, true);
699
700
if (!getVersion) {
701
// LOGIC
702
std::string logic(iniparser_getstring(modIni, ":LogicFile", ""));
703
if (logic.length()) {
704
std::istringstream stream(logic);
705
std::string buf;
706
while (std::getline(stream, buf, ',')) {
707
buf = trim(buf);
708
DrawStatus(("Starting logic " + buf + "...").c_str());
709
bool linked = false;
710
711
fs::path file(modDir + "/" + buf);
712
Link::Handle linkHandle = Link::Open(file.string().c_str());
713
714
if (linkHandle) {
715
modLink linkModLogic = (modLink)Link::GetSymbol(linkHandle, "LinkModLogic");
716
const ModVersionInfo *modInfo = (const ModVersionInfo *)Link::GetSymbol(linkHandle, "modInfo");
717
if (!modInfo) {
718
// PrintLog(PRINT_NORMAL, "[MOD] Failed to load mod %s...", folder.c_str());
719
PrintLog(PRINT_NORMAL, "[MOD] ERROR: Failed to find modInfo", file.string().c_str());
720
721
iniparser_freedict(modIni);
722
currentMod = cur;
723
return false;
724
}
725
726
if (modInfo->engineVer != targetModVersion.engineVer) {
727
// PrintLog(PRINT_NORMAL, "[MOD] Failed to load mod %s...", folder.c_str());
728
PrintLog(PRINT_NORMAL, "[MOD] ERROR: Logic file '%s' engineVer %d does not match expected engineVer of %d",
729
file.string().c_str(), modInfo->engineVer, targetModVersion.engineVer);
730
731
iniparser_freedict(modIni);
732
currentMod = cur;
733
return false;
734
}
735
736
if (modInfo->modLoaderVer != targetModVersion.modLoaderVer) {
737
// PrintLog(PRINT_NORMAL, "[MOD] Failed to load mod %s...", folder.c_str());
738
PrintLog(PRINT_NORMAL, "[MOD] ERROR: Logic file '%s' modLoaderVer %d does not match expected modLoaderVer of %d",
739
file.string().c_str(), modInfo->modLoaderVer, targetModVersion.modLoaderVer);
740
}
741
742
if (linkModLogic) {
743
info->linkModLogic.push_back(linkModLogic);
744
linked = true;
745
}
746
else {
747
PrintLog(PRINT_ERROR, "[MOD] ERROR: Failed to find 'LinkModLogic' -> %s", Link::GetError());
748
}
749
info->unloadMod = (void (*)())Link::GetSymbol(linkHandle, "UnloadMod");
750
info->modLogicHandles.push_back(linkHandle);
751
}
752
else {
753
PrintLog(PRINT_ERROR, "[MOD] ERROR: Failed to open mod logic file -> %s", Link::GetError());
754
}
755
756
if (!linked) {
757
// PrintLog(PRINT_NORMAL, "[MOD] Failed to load mod %s...", folder.c_str());
758
PrintLog(PRINT_NORMAL, "[MOD] ERROR: Failed to link logic '%s'", file.string().c_str());
759
760
iniparser_freedict(modIni);
761
currentMod = cur;
762
return false;
763
}
764
}
765
}
766
767
// SETTINGS
768
FileIO *set = fOpen((modDir + "/modSettings.ini").c_str(), "r");
769
if (set) {
770
DrawStatus("Reading settings...");
771
772
fClose(set);
773
using namespace std;
774
auto modSettingsIni = iniparser_load((modDir + "/modSettings.ini").c_str());
775
int32 sec = iniparser_getnsec(modSettingsIni);
776
if (sec) {
777
for (int32 i = 0; i < sec; ++i) {
778
const char *secn = iniparser_getsecname(modSettingsIni, i);
779
int32 len = iniparser_getsecnkeys(modSettingsIni, secn);
780
const char **keys = new const char *[len];
781
iniparser_getseckeys(modSettingsIni, secn, keys);
782
map<string, string> secset;
783
for (int32 j = 0; j < len; ++j)
784
secset.insert(pair<string, string>(keys[j] + strlen(secn) + 1, iniparser_getstring(modSettingsIni, keys[j], "")));
785
info->settings.insert(pair<string, map<string, string>>(secn, secset));
786
delete[] keys;
787
}
788
}
789
else {
790
// either you use categories or you don't, i don't make the rules
791
map<string, string> secset;
792
for (int32 j = 0; j < modSettingsIni->n; ++j)
793
secset.insert(pair<string, string>(modSettingsIni->key[j] + 1, modSettingsIni->val[j]));
794
info->settings.insert(pair<string, map<string, string>>("", secset));
795
}
796
iniparser_freedict(modSettingsIni);
797
}
798
// CONFIG
799
loadCfg(info, modDir + "/modConfig.cfg");
800
801
std::string cfg(iniparser_getstring(modIni, ":ConfigFile", ""));
802
bool saveCfg = false;
803
if (cfg.length() && info->active) {
804
std::istringstream stream(cfg);
805
std::string buf;
806
while (std::getline(stream, buf, ',')) {
807
buf = trim(buf);
808
DrawStatus(("Reading config " + buf + "...").c_str());
809
810
int32 mode = 0;
811
fs::path file;
812
if (MODAPI_ENDS_WITH(".ini")) {
813
file = fs::path(modDir + "/" + buf + ".ini");
814
mode = 1;
815
}
816
else if (MODAPI_ENDS_WITH(".cfg")) {
817
file = fs::path(modDir + "/" + buf + ".cfg");
818
mode = 2;
819
}
820
821
if (!mode) {
822
file = fs::path(modDir + "/" + buf + ".ini");
823
if (fs::exists(file))
824
mode = 1;
825
}
826
if (!mode) {
827
file = fs::path(modDir + "/" + buf + ".cfg");
828
if (fs::exists(file))
829
mode = 2;
830
}
831
832
// if fail just free do nothing
833
if (!mode)
834
continue;
835
836
if (mode == 1) {
837
FileIO *set = fOpen(file.string().c_str(), "r");
838
if (set) {
839
saveCfg = true;
840
fClose(set);
841
using namespace std;
842
auto cfgIni = iniparser_load(file.string().c_str());
843
int32 sec = iniparser_getnsec(cfgIni);
844
for (int32 i = 0; i < sec; ++i) {
845
const char *secn = iniparser_getsecname(cfgIni, i);
846
int32 len = iniparser_getsecnkeys(cfgIni, secn);
847
const char **keys = new const char *[len];
848
iniparser_getseckeys(cfgIni, secn, keys);
849
for (int32 j = 0; j < len; ++j)
850
info->config[secn][keys[j] + strlen(secn) + 1] = iniparser_getstring(cfgIni, keys[j], "");
851
delete[] keys;
852
}
853
iniparser_freedict(cfgIni);
854
}
855
}
856
else if (mode == 2)
857
loadCfg(info, file.string());
858
}
859
}
860
861
if (saveCfg && info->config.size()) {
862
DrawStatus("Saving config...");
863
FileIO *cfg = fOpen((modDir + "/modConfig.cfg").c_str(), "wb");
864
uint8 ct = info->config.size();
865
fWrite(&ct, 1, 1, cfg);
866
for (auto kv : info->config) {
867
if (!kv.first.length())
868
continue; // don't save no-categories
869
uint8 len = kv.first.length();
870
fWrite(&len, 1, 1, cfg);
871
WriteText(cfg, kv.first.c_str());
872
uint8 kt = kv.second.size();
873
fWrite(&kt, 1, 1, cfg);
874
for (auto kkv : kv.second) {
875
uint8 len = (uint8)(kkv.first.length()) & 0x7F;
876
bool32 isint = false;
877
int32 r = 0;
878
try {
879
r = std::stoi(kkv.second, nullptr, 0);
880
isint = true;
881
len |= 0x80;
882
} catch (...) {
883
}
884
fWrite(&len, 1, 1, cfg);
885
WriteText(cfg, kkv.first.c_str());
886
if (isint)
887
fWrite(&r, sizeof(int32), 1, cfg);
888
else {
889
uint8 len = kkv.second.length();
890
fWrite(&len, 1, 1, cfg);
891
WriteText(cfg, kkv.second.c_str());
892
}
893
}
894
}
895
fClose(cfg);
896
}
897
}
898
899
iniparser_freedict(modIni);
900
currentMod = cur;
901
return true;
902
}
903
return false;
904
}
905
906
void RSDK::SaveMods()
907
{
908
ModInfo *cur = currentMod;
909
char modBuf[0x100];
910
sprintf_s(modBuf, sizeof(modBuf), "%smods/", SKU::userFileDir);
911
fs::path modPath(modBuf);
912
913
SortMods();
914
915
PrintLog(PRINT_NORMAL, "[MOD] Saving mods...");
916
917
if (fs::exists(modPath) && fs::is_directory(modPath)) {
918
std::string mod_config = modPath.string() + "/modconfig.ini";
919
FileIO *file = fOpen(mod_config.c_str(), "w");
920
921
WriteText(file, "[Mods]\n");
922
923
for (int32 m = 0; m < modList.size(); ++m) {
924
currentMod = &modList[m];
925
SaveSettings();
926
WriteText(file, "%s=%c\n", currentMod->folderName.c_str(), currentMod->active ? 'y' : 'n');
927
}
928
fClose(file);
929
}
930
currentMod = cur;
931
}
932
933
void RSDK::RunModCallbacks(int32 callbackID, void *data)
934
{
935
if (callbackID < 0 || callbackID >= MODCB_MAX)
936
return;
937
938
for (auto &c : modCallbackList[callbackID]) {
939
if (c)
940
c(data);
941
}
942
}
943
944
// Mod API
945
bool32 RSDK::LoadModInfo(const char *id, String *name, String *description, String *version, bool32 *active)
946
{
947
if (!id) { // NULL == "Internal" Logic
948
if (name)
949
InitString(name, gameVerInfo.gameTitle, 0);
950
if (description)
951
InitString(description, gameVerInfo.gameSubtitle, 0);
952
if (version)
953
InitString(version, gameVerInfo.version, 0);
954
if (active)
955
*active = true;
956
957
return true;
958
}
959
else if (!strlen(id) && currentMod) { // "" == Current Mod
960
if (name)
961
InitString(name, currentMod->name.c_str(), 0);
962
if (description)
963
InitString(description, currentMod->desc.c_str(), 0);
964
if (version)
965
InitString(version, currentMod->version.c_str(), 0);
966
if (active)
967
*active = currentMod->active;
968
969
return true;
970
}
971
972
for (int32 m = 0; m < modList.size(); ++m) {
973
if (modList[m].id == id) {
974
if (name)
975
InitString(name, modList[m].name.c_str(), 0);
976
if (description)
977
InitString(description, modList[m].desc.c_str(), 0);
978
if (version)
979
InitString(version, modList[m].version.c_str(), 0);
980
if (active)
981
*active = modList[m].active;
982
983
return true;
984
}
985
}
986
return false;
987
}
988
989
int32 RSDK::GetModCount(bool32 active)
990
{
991
int32 c = 0;
992
for (auto &m : modList) {
993
if (++c && active && !m.active)
994
return c - 1;
995
}
996
return c;
997
}
998
999
const char *RSDK::GetModIDByIndex(uint32 index)
1000
{
1001
if (index >= modList.size())
1002
return NULL;
1003
return modList[index].id.c_str();
1004
}
1005
1006
bool32 RSDK::ForeachModID(String *id)
1007
{
1008
if (!id)
1009
return false;
1010
1011
using namespace std;
1012
1013
if (id->chars)
1014
++foreachStackPtr->id;
1015
else {
1016
++foreachStackPtr;
1017
foreachStackPtr->id = 0;
1018
}
1019
1020
if (foreachStackPtr->id >= modList.size()) {
1021
foreachStackPtr--;
1022
return false;
1023
}
1024
string set = modList[foreachStackPtr->id].id;
1025
InitString(id, set.c_str(), 0);
1026
return true;
1027
}
1028
1029
void RSDK::AddModCallback(int32 callbackID, ModCallback callback) { return AddModCallback_STD(callbackID, callback); }
1030
1031
void RSDK::AddModCallback_STD(int32 callbackID, ModCallbackSTD callback)
1032
{
1033
if (callbackID < 0 || callbackID >= MODCB_MAX)
1034
return;
1035
1036
modCallbackList[callbackID].push_back(callback);
1037
}
1038
1039
void RSDK::AddPublicFunction(const char *functionName, void *functionPtr)
1040
{
1041
if (!currentMod)
1042
return gamePublicFuncs.push_back({ functionName, functionPtr });
1043
if (!currentMod->active)
1044
return;
1045
currentMod->functionList.push_back({ functionName, functionPtr });
1046
}
1047
1048
void *RSDK::GetPublicFunction(const char *id, const char *functionName)
1049
{
1050
if (!id) {
1051
for (auto &f : gamePublicFuncs) {
1052
if (f.name == functionName)
1053
return f.ptr;
1054
}
1055
1056
return NULL;
1057
}
1058
1059
if (!strlen(id) && currentMod)
1060
id = currentMod->id.c_str();
1061
1062
for (ModInfo &m : modList) {
1063
if (m.active && m.id == id) {
1064
for (auto &f : m.functionList) {
1065
if (f.name == functionName)
1066
return f.ptr;
1067
}
1068
1069
return NULL;
1070
}
1071
}
1072
1073
return NULL;
1074
}
1075
1076
std::string GetModPath_i(const char *id)
1077
{
1078
int32 m;
1079
for (m = 0; m < modList.size(); ++m) {
1080
if (modList[m].active && modList[m].id == id)
1081
break;
1082
}
1083
1084
if (m == modList.size())
1085
return std::string();
1086
1087
return modList[m].path;
1088
}
1089
1090
void RSDK::GetModPath(const char *id, String *result)
1091
{
1092
std::string modPath = GetModPath_i(id);
1093
1094
if (modPath.empty())
1095
return;
1096
1097
InitString(result, modPath.c_str(), 0);
1098
}
1099
1100
std::string GetModSettingsValue(const char *id, const char *key)
1101
{
1102
std::string skey(key);
1103
if (!strchr(key, ':'))
1104
skey = std::string(":") + key;
1105
1106
std::string cat = skey.substr(0, skey.find(":"));
1107
std::string rkey = skey.substr(skey.find(":") + 1);
1108
1109
for (ModInfo &m : modList) {
1110
if (m.active && m.id == id) {
1111
try {
1112
return m.settings.at(cat).at(rkey);
1113
} catch (std::out_of_range) {
1114
return std::string();
1115
}
1116
}
1117
}
1118
return std::string();
1119
}
1120
1121
bool32 RSDK::GetSettingsBool(const char *id, const char *key, bool32 fallback)
1122
{
1123
if (!id) {
1124
// TODO: allow user to get values from settings.ini?
1125
}
1126
else if (!strlen(id)) {
1127
if (!currentMod)
1128
return fallback;
1129
1130
id = currentMod->id.c_str();
1131
}
1132
1133
std::string v = GetModSettingsValue(id, key);
1134
1135
if (!v.length()) {
1136
if (currentMod->id == id)
1137
SetSettingsBool(key, fallback);
1138
return fallback;
1139
}
1140
char first = v.at(0);
1141
if (first == 'y' || first == 'Y' || first == 't' || first == 'T' || (first = GetSettingsInteger(id, key, 0)))
1142
return true;
1143
if (first == 'n' || first == 'N' || first == 'f' || first == 'F' || !first)
1144
return false;
1145
if (currentMod->id == id)
1146
SetSettingsBool(key, fallback);
1147
return fallback;
1148
}
1149
1150
int32 RSDK::GetSettingsInteger(const char *id, const char *key, int32 fallback)
1151
{
1152
if (!id) {
1153
// TODO: allow user to get values from settings.ini?
1154
}
1155
else if (!strlen(id)) {
1156
if (!currentMod)
1157
return fallback;
1158
1159
id = currentMod->id.c_str();
1160
}
1161
1162
std::string v = GetModSettingsValue(id, key);
1163
1164
if (!v.length()) {
1165
if (currentMod->id == id)
1166
SetSettingsInteger(key, fallback);
1167
return fallback;
1168
}
1169
try {
1170
return std::stoi(v, nullptr, 0);
1171
} catch (...) {
1172
if (currentMod->id == id)
1173
SetSettingsInteger(key, fallback);
1174
return fallback;
1175
}
1176
}
1177
1178
float RSDK::GetSettingsFloat(const char *id, const char *key, float fallback)
1179
{
1180
if (!id) {
1181
// TODO: allow user to get values from settings.ini?
1182
}
1183
else if (!strlen(id)) {
1184
if (!currentMod)
1185
return fallback;
1186
1187
id = currentMod->id.c_str();
1188
}
1189
1190
std::string v = GetModSettingsValue(id, key);
1191
1192
if (!v.length()) {
1193
if (currentMod->id == id)
1194
SetSettingsFloat(key, fallback);
1195
return fallback;
1196
}
1197
try {
1198
return std::stof(v, nullptr);
1199
} catch (...) {
1200
if (currentMod->id == id)
1201
SetSettingsFloat(key, fallback);
1202
return fallback;
1203
}
1204
}
1205
1206
void RSDK::GetSettingsString(const char *id, const char *key, String *result, const char *fallback)
1207
{
1208
if (!id) {
1209
// TODO: allow user to get values from settings.ini?
1210
}
1211
else if (!strlen(id)) {
1212
if (!currentMod) {
1213
InitString(result, fallback, 0);
1214
return;
1215
}
1216
1217
id = currentMod->id.c_str();
1218
}
1219
1220
std::string v = GetModSettingsValue(id, key);
1221
if (!v.length()) {
1222
InitString(result, fallback, 0);
1223
if (currentMod->id == id)
1224
SetSettingsString(key, result);
1225
return;
1226
}
1227
InitString(result, v.c_str(), 0);
1228
}
1229
1230
std::string GetNidConfigValue(const char *key)
1231
{
1232
if (!currentMod || !currentMod->active)
1233
return std::string();
1234
std::string skey(key);
1235
if (!strchr(key, ':'))
1236
skey = std::string(":") + key;
1237
1238
std::string cat = skey.substr(0, skey.find(":"));
1239
std::string rkey = skey.substr(skey.find(":") + 1);
1240
1241
try {
1242
return currentMod->config.at(cat).at(rkey);
1243
} catch (std::out_of_range) {
1244
return std::string();
1245
}
1246
return std::string();
1247
}
1248
1249
bool32 RSDK::GetConfigBool(const char *key, bool32 fallback)
1250
{
1251
std::string v = GetNidConfigValue(key);
1252
if (!v.length())
1253
return fallback;
1254
char first = v.at(0);
1255
if (first == 'y' || first == 'Y' || first == 't' || first == 'T' || (first = GetConfigInteger(key, 0)))
1256
return true;
1257
if (first == 'n' || first == 'N' || first == 'f' || first == 'F' || !first)
1258
return false;
1259
return fallback;
1260
}
1261
1262
int32 RSDK::GetConfigInteger(const char *key, int32 fallback)
1263
{
1264
std::string v = GetNidConfigValue(key);
1265
if (!v.length())
1266
return fallback;
1267
try {
1268
return std::stoi(v, nullptr, 0);
1269
} catch (...) {
1270
return fallback;
1271
}
1272
}
1273
1274
float RSDK::GetConfigFloat(const char *key, float fallback)
1275
{
1276
std::string v = GetNidConfigValue(key);
1277
if (!v.length())
1278
return fallback;
1279
try {
1280
return std::stof(v, nullptr);
1281
} catch (...) {
1282
return fallback;
1283
}
1284
}
1285
1286
void RSDK::GetConfigString(const char *key, String *result, const char *fallback)
1287
{
1288
std::string v = GetNidConfigValue(key);
1289
if (!v.length()) {
1290
InitString(result, fallback, 0);
1291
return;
1292
}
1293
InitString(result, v.c_str(), 0);
1294
}
1295
1296
bool32 RSDK::ForeachConfigCategory(String *category)
1297
{
1298
if (!category || !currentMod)
1299
return false;
1300
1301
using namespace std;
1302
if (!currentMod->config.size())
1303
return false;
1304
1305
if (category->chars)
1306
++foreachStackPtr->id;
1307
else {
1308
++foreachStackPtr;
1309
foreachStackPtr->id = 0;
1310
}
1311
int32 sid = 0;
1312
string cat;
1313
bool32 set = false;
1314
if (currentMod->config[""].size() && foreachStackPtr->id == sid++) {
1315
set = true;
1316
cat = "";
1317
}
1318
if (!set) {
1319
for (pair<string, map<string, string>> kv : currentMod->config) {
1320
if (!kv.first.length())
1321
continue;
1322
if (kv.second.size() && foreachStackPtr->id == sid++) {
1323
set = true;
1324
cat = kv.first;
1325
break;
1326
}
1327
}
1328
}
1329
if (!set) {
1330
foreachStackPtr--;
1331
return false;
1332
}
1333
InitString(category, cat.c_str(), 0);
1334
return true;
1335
}
1336
1337
bool32 RSDK::ForeachConfig(String *config)
1338
{
1339
if (!config || !currentMod)
1340
return false;
1341
using namespace std;
1342
if (!currentMod->config.size())
1343
return false;
1344
1345
if (config->chars)
1346
++foreachStackPtr->id;
1347
else {
1348
++foreachStackPtr;
1349
foreachStackPtr->id = 0;
1350
}
1351
int32 sid = 0;
1352
string key, cat;
1353
if (currentMod->config[""].size()) {
1354
for (pair<string, string> pair : currentMod->config[""]) {
1355
if (foreachStackPtr->id == sid++) {
1356
cat = "";
1357
key = pair.first;
1358
break;
1359
}
1360
}
1361
}
1362
if (!key.length()) {
1363
for (pair<string, map<string, string>> kv : currentMod->config) {
1364
if (!kv.first.length())
1365
continue;
1366
for (pair<string, string> pair : kv.second) {
1367
if (foreachStackPtr->id == sid++) {
1368
cat = kv.first;
1369
key = pair.first;
1370
break;
1371
}
1372
}
1373
}
1374
}
1375
if (!key.length()) {
1376
foreachStackPtr--;
1377
return false;
1378
}
1379
string r = cat + ":" + key;
1380
InitString(config, r.c_str(), 0);
1381
return true;
1382
}
1383
1384
#if RETRO_MOD_LOADER_VER >= 2
1385
bool32 RSDK::ForeachSettingCategory(const char *id, String *category)
1386
{
1387
if (!id) {
1388
// TODO: allow user to get values from settings.ini?
1389
}
1390
else if (!strlen(id)) {
1391
if (!currentMod)
1392
return false;
1393
1394
id = currentMod->id.c_str();
1395
}
1396
1397
if (!category)
1398
return false;
1399
1400
int32 m;
1401
for (m = 0; m < modList.size(); ++m) {
1402
if (modList[m].active && modList[m].id == id)
1403
break;
1404
}
1405
1406
if (m == modList.size())
1407
return false;
1408
1409
ModInfo *mod = &modList[m];
1410
1411
using namespace std;
1412
if (!mod->settings.size())
1413
return false;
1414
1415
if (category->chars)
1416
++foreachStackPtr->id;
1417
else {
1418
++foreachStackPtr;
1419
foreachStackPtr->id = 0;
1420
}
1421
int32 sid = 0;
1422
string cat;
1423
bool32 set = false;
1424
if (mod->settings[""].size() && foreachStackPtr->id == sid++) {
1425
set = true;
1426
cat = "";
1427
}
1428
if (!set) {
1429
for (pair<string, map<string, string>> kv : mod->settings) {
1430
if (!kv.first.length())
1431
continue;
1432
if (kv.second.size() && foreachStackPtr->id == sid++) {
1433
set = true;
1434
cat = kv.first;
1435
break;
1436
}
1437
}
1438
}
1439
if (!set) {
1440
foreachStackPtr--;
1441
return false;
1442
}
1443
InitString(category, cat.c_str(), 0);
1444
return true;
1445
}
1446
1447
bool32 RSDK::ForeachSetting(const char *id, String *setting)
1448
{
1449
if (!id) {
1450
// TODO: allow user to get values from settings.ini?
1451
}
1452
else if (!strlen(id)) {
1453
if (!currentMod)
1454
return false;
1455
1456
id = currentMod->id.c_str();
1457
}
1458
1459
if (!setting)
1460
return false;
1461
using namespace std;
1462
if (!currentMod->settings.size())
1463
return false;
1464
1465
int32 m;
1466
for (m = 0; m < modList.size(); ++m) {
1467
if (modList[m].active && modList[m].id == id)
1468
break;
1469
}
1470
1471
if (m == modList.size())
1472
return false;
1473
1474
ModInfo *mod = &modList[m];
1475
1476
if (setting->chars)
1477
++foreachStackPtr->id;
1478
else {
1479
++foreachStackPtr;
1480
foreachStackPtr->id = 0;
1481
}
1482
int32 sid = 0;
1483
string key, cat;
1484
if (mod->settings[""].size()) {
1485
for (pair<string, string> pair : mod->settings[""]) {
1486
if (foreachStackPtr->id == sid++) {
1487
cat = "";
1488
key = pair.first;
1489
break;
1490
}
1491
}
1492
}
1493
if (!key.length()) {
1494
for (pair<string, map<string, string>> kv : mod->settings) {
1495
if (!kv.first.length())
1496
continue;
1497
for (pair<string, string> pair : kv.second) {
1498
if (foreachStackPtr->id == sid++) {
1499
cat = kv.first;
1500
key = pair.first;
1501
break;
1502
}
1503
}
1504
}
1505
}
1506
if (!key.length()) {
1507
foreachStackPtr--;
1508
return false;
1509
}
1510
string r = cat + ":" + key;
1511
InitString(setting, r.c_str(), 0);
1512
return true;
1513
}
1514
#endif
1515
1516
void SetModSettingsValue(const char *key, const std::string &val)
1517
{
1518
if (!currentMod)
1519
return;
1520
std::string skey(key);
1521
if (!strchr(key, ':'))
1522
skey = std::string(":") + key;
1523
1524
std::string cat = skey.substr(0, skey.find(":"));
1525
std::string rkey = skey.substr(skey.find(":") + 1);
1526
1527
currentMod->settings[cat][rkey] = val;
1528
}
1529
1530
void RSDK::SetSettingsBool(const char *key, bool32 val) { SetModSettingsValue(key, val ? "Y" : "N"); }
1531
void RSDK::SetSettingsInteger(const char *key, int32 val) { SetModSettingsValue(key, std::to_string(val)); }
1532
void RSDK::SetSettingsFloat(const char *key, float val) { SetModSettingsValue(key, std::to_string(val)); }
1533
void RSDK::SetSettingsString(const char *key, String *val)
1534
{
1535
char *buf = new char[val->length + 1]; // Take into account '\0'
1536
GetCString(buf, val);
1537
SetModSettingsValue(key, buf);
1538
delete[] buf;
1539
}
1540
1541
void RSDK::SaveSettings()
1542
{
1543
using namespace std;
1544
if (!currentMod || !currentMod->settings.size() || !currentMod->active)
1545
return;
1546
1547
FileIO *file = fOpen((GetModPath_i(currentMod->id.c_str()) + "/modSettings.ini").c_str(), "w");
1548
1549
if (currentMod->settings[""].size()) {
1550
for (pair<string, string> pair : currentMod->settings[""]) WriteText(file, "%s = %s\n", pair.first.c_str(), pair.second.c_str());
1551
}
1552
for (pair<string, map<string, string>> kv : currentMod->settings) {
1553
if (!kv.first.length())
1554
continue;
1555
WriteText(file, "\n[%s]\n", kv.first.c_str());
1556
for (pair<string, string> pair : kv.second) WriteText(file, "%s = %s\n", pair.first.c_str(), pair.second.c_str());
1557
}
1558
fClose(file);
1559
PrintLog(PRINT_NORMAL, "[MOD] Saved mod settings for mod %s", currentMod->id.c_str());
1560
return;
1561
}
1562
1563
// i'm going to hell for this
1564
// nvm im actually so proud of this func yall have no idea i'm insane
1565
void SuperInternal(RSDK::ObjectClass *super, RSDK::ModSuper callback, void *data)
1566
{
1567
using namespace RSDK;
1568
1569
ModInfo *curMod = currentMod;
1570
bool32 override = false;
1571
if (!super->inherited)
1572
return; // Mod.Super on an object that's literally an original object why did you do this
1573
++superLevels[inheritLevel];
1574
if (HASH_MATCH_MD5(super->hash, super->inherited->hash)) {
1575
// entity override
1576
override = true;
1577
for (int32 i = 0; i < superLevels[inheritLevel]; i++) {
1578
if (!super->inherited)
1579
break; // *do not* cap superLevel because if we do we'll break things even more than what we had to do to get here
1580
super = super->inherited;
1581
}
1582
}
1583
else {
1584
// basic entity inherit
1585
inheritLevel++;
1586
super = super->inherited;
1587
}
1588
1589
switch (callback) {
1590
case SUPER_UPDATE:
1591
if (super->update)
1592
super->update();
1593
break;
1594
1595
case SUPER_LATEUPDATE:
1596
if (super->lateUpdate)
1597
super->lateUpdate();
1598
break;
1599
1600
case SUPER_STATICUPDATE:
1601
if (super->staticUpdate)
1602
super->staticUpdate();
1603
break;
1604
1605
case SUPER_DRAW:
1606
if (super->draw)
1607
super->draw();
1608
break;
1609
1610
case SUPER_CREATE:
1611
if (super->create)
1612
super->create(data);
1613
break;
1614
1615
case SUPER_STAGELOAD:
1616
if (super->stageLoad)
1617
super->stageLoad();
1618
break;
1619
1620
case SUPER_EDITORLOAD:
1621
if (super->editorLoad)
1622
super->editorLoad();
1623
break;
1624
1625
case SUPER_EDITORDRAW:
1626
if (super->editorDraw)
1627
super->editorDraw();
1628
break;
1629
1630
case SUPER_SERIALIZE:
1631
if (super->serialize)
1632
super->serialize();
1633
break;
1634
}
1635
1636
if (!override)
1637
inheritLevel--;
1638
superLevels[inheritLevel]--;
1639
currentMod = curMod;
1640
}
1641
1642
void RSDK::Super(int32 objectID, ModSuper callback, void *data) { return SuperInternal(&objectClassList[stageObjectIDs[objectID]], callback, data); }
1643
1644
void *RSDK::GetGlobals() { return (void *)globalVarsPtr; }
1645
1646
void RSDK::ModRegisterGlobalVariables(const char *globalsPath, void **globals, uint32 size)
1647
{
1648
AllocateStorage(globals, size, DATASET_STG, true);
1649
FileInfo info;
1650
InitFileInfo(&info);
1651
1652
int32 *varPtr = *(int32 **)globals;
1653
if (LoadFile(&info, globalsPath, FMODE_RB)) {
1654
uint8 varCount = ReadInt8(&info);
1655
for (int32 i = 0; i < varCount && globalVarsPtr; ++i) {
1656
int32 offset = ReadInt32(&info, false);
1657
int32 count = ReadInt32(&info, false);
1658
for (int32 v = 0; v < count; ++v) {
1659
varPtr[offset + v] = ReadInt32(&info, false);
1660
}
1661
}
1662
1663
CloseFile(&info);
1664
}
1665
}
1666
1667
#if RETRO_REV0U
1668
void RSDK::ModRegisterObject(Object **staticVars, Object **modStaticVars, const char *name, uint32 entityClassSize, uint32 staticClassSize,
1669
uint32 modClassSize, void (*update)(), void (*lateUpdate)(), void (*staticUpdate)(), void (*draw)(),
1670
void (*create)(void *), void (*stageLoad)(), void (*editorLoad)(), void (*editorDraw)(), void (*serialize)(),
1671
void (*staticLoad)(Object *), const char *inherited)
1672
{
1673
return ModRegisterObject_STD(staticVars, modStaticVars, name, entityClassSize, staticClassSize, modClassSize, update, lateUpdate, staticUpdate,
1674
draw, create, stageLoad, editorLoad, editorDraw, serialize, staticLoad, inherited);
1675
}
1676
1677
void RSDK::ModRegisterObject_STD(Object **staticVars, Object **modStaticVars, const char *name, uint32 entityClassSize, uint32 staticClassSize,
1678
uint32 modClassSize, std::function<void()> update, std::function<void()> lateUpdate,
1679
std::function<void()> staticUpdate, std::function<void()> draw, std::function<void(void *)> create,
1680
std::function<void()> stageLoad, std::function<void()> editorLoad, std::function<void()> editorDraw,
1681
std::function<void()> serialize, std::function<void(Object *)> staticLoad, const char *inherited)
1682
#else
1683
1684
void RSDK::ModRegisterObject(Object **staticVars, Object **modStaticVars, const char *name, uint32 entityClassSize, uint32 staticClassSize,
1685
uint32 modClassSize, void (*update)(), void (*lateUpdate)(), void (*staticUpdate)(), void (*draw)(),
1686
void (*create)(void *), void (*stageLoad)(), void (*editorLoad)(), void (*editorDraw)(), void (*serialize)(),
1687
const char *inherited)
1688
{
1689
return ModRegisterObject_STD(staticVars, modStaticVars, name, entityClassSize, staticClassSize, modClassSize, update, lateUpdate, staticUpdate,
1690
draw, create, stageLoad, editorLoad, editorDraw, serialize, inherited);
1691
}
1692
1693
void RSDK::ModRegisterObject_STD(Object **staticVars, Object **modStaticVars, const char *name, uint32 entityClassSize, uint32 staticClassSize,
1694
uint32 modClassSize, std::function<void()> update, std::function<void()> lateUpdate,
1695
std::function<void()> staticUpdate, std::function<void()> draw, std::function<void(void *)> create,
1696
std::function<void()> stageLoad, std::function<void()> editorLoad, std::function<void()> editorDraw,
1697
std::function<void()> serialize, const char *inherited)
1698
#endif
1699
{
1700
ModInfo *curMod = currentMod;
1701
int32 preCount = objectClassCount + 1;
1702
RETRO_HASH_MD5(hash);
1703
GEN_HASH_MD5(name, hash);
1704
1705
ObjectClass *inherit = NULL;
1706
for (int32 i = 0; i < objectClassCount; ++i) {
1707
if (HASH_MATCH_MD5(objectClassList[i].hash, hash)) {
1708
objectClassCount = i;
1709
inherit = new ObjectClass(objectClassList[i]);
1710
allocatedInherits.push_back(inherit);
1711
--preCount;
1712
if (!inherited)
1713
inherited = name;
1714
break;
1715
}
1716
}
1717
1718
if (inherited) {
1719
RETRO_HASH_MD5(hash);
1720
GEN_HASH_MD5(inherited, hash);
1721
if (!inherit) {
1722
for (int32 i = 0; i < preCount; ++i) {
1723
if (HASH_MATCH_MD5(objectClassList[i].hash, hash)) {
1724
inherit = new ObjectClass(objectClassList[i]);
1725
allocatedInherits.push_back(inherit);
1726
break;
1727
}
1728
}
1729
}
1730
1731
if (!inherit)
1732
inherited = NULL;
1733
}
1734
1735
if (inherited) {
1736
if (inherit->entityClassSize > entityClassSize)
1737
entityClassSize = inherit->entityClassSize;
1738
}
1739
1740
#if RETRO_REV0U
1741
RegisterObject_STD(staticVars, name, entityClassSize, staticClassSize, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1742
nullptr, nullptr);
1743
#else
1744
RegisterObject_STD(staticVars, name, entityClassSize, staticClassSize, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1745
nullptr);
1746
#endif
1747
1748
ObjectClass *info = &objectClassList[objectClassCount - 1];
1749
1750
// clang-format off
1751
if (update) info->update = [curMod, update]() { currentMod = curMod; update(); currentMod = NULL; };
1752
if (lateUpdate) info->lateUpdate = [curMod, lateUpdate]() { currentMod = curMod; lateUpdate(); currentMod = NULL; };
1753
if (staticUpdate) info->staticUpdate = [curMod, staticUpdate]() { currentMod = curMod; staticUpdate(); currentMod = NULL; };
1754
if (draw) info->draw = [curMod, draw]() { currentMod = curMod; draw(); currentMod = NULL; };
1755
if (create) info->create = [curMod, create](void* data) { currentMod = curMod; create(data); currentMod = NULL; };
1756
if (stageLoad) info->stageLoad = [curMod, stageLoad]() { currentMod = curMod; stageLoad(); currentMod = NULL; };
1757
#if RETRO_REV0U
1758
if (staticLoad) info->staticLoad = [curMod, staticLoad](Object *staticVars) { currentMod = curMod; staticLoad(staticVars); currentMod = NULL; };
1759
#endif
1760
if (editorLoad) info->editorLoad = [curMod, editorLoad]() { currentMod = curMod; editorLoad(); currentMod = NULL; };
1761
if (editorDraw) info->editorDraw = [curMod, editorDraw]() { currentMod = curMod; editorDraw(); currentMod = NULL; };
1762
if (serialize) info->serialize = [curMod, serialize]() { currentMod = curMod; serialize(); currentMod = NULL; };
1763
// clang-format on
1764
1765
if (inherited) {
1766
info->inherited = inherit;
1767
1768
if (HASH_MATCH_MD5(info->hash, inherit->hash)) {
1769
// we override an obj and lets set staticVars
1770
info->staticVars = inherit->staticVars;
1771
info->staticClassSize = inherit->staticClassSize;
1772
if (staticVars) {
1773
// give them a hook
1774
ModRegisterObjectHook(staticVars, name);
1775
}
1776
// lets also setup mod static vars
1777
if (modStaticVars && modClassSize) {
1778
curMod->staticVars[info->hash] = { curMod->id + "_" + name, modStaticVars, modClassSize };
1779
}
1780
}
1781
1782
// clang-format off
1783
if (!update) info->update = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_UPDATE, NULL); currentMod = NULL; };
1784
if (!lateUpdate) info->lateUpdate = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_LATEUPDATE, NULL); currentMod = NULL; };
1785
if (!staticUpdate) info->staticUpdate = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_STATICUPDATE, NULL); currentMod = NULL; };
1786
if (!draw) info->draw = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_DRAW, NULL); currentMod = NULL; };
1787
if (!create) info->create = [curMod, info](void* data) { currentMod = curMod; SuperInternal(info, SUPER_CREATE, data); currentMod = NULL; };
1788
if (!stageLoad) info->stageLoad = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_STAGELOAD, NULL); currentMod = NULL; };
1789
#if RETRO_REV0U
1790
// Don't inherit staticLoad, that should be per-struct
1791
// if (!staticLoad) info->staticLoad = [curMod, info](Object *staticVars) { currentMod = curMod; SuperInternal(info, SUPER_STATICLOAD, staticVars); currentMod = NULL; };
1792
#endif
1793
if (!editorLoad) info->editorLoad = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_EDITORLOAD, NULL); currentMod = NULL; };
1794
if (!editorDraw) info->editorDraw = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_EDITORDRAW, NULL); currentMod = NULL; };
1795
if (!serialize) info->serialize = [curMod, info]() { currentMod = curMod; SuperInternal(info, SUPER_SERIALIZE, NULL); currentMod = NULL; };
1796
// clang-format on
1797
}
1798
1799
objectClassCount = preCount;
1800
}
1801
1802
void RSDK::ModRegisterObjectHook(Object **staticVars, const char *staticName)
1803
{
1804
if (!staticVars || !staticName)
1805
return;
1806
1807
ObjectHook hook;
1808
GEN_HASH_MD5(staticName, hook.hash);
1809
hook.staticVars = staticVars;
1810
1811
objectHookList.push_back(hook);
1812
}
1813
1814
Object *RSDK::ModFindObject(const char *name)
1815
{
1816
if (int32 o = FindObject(name))
1817
return *objectClassList[stageObjectIDs[o]].staticVars;
1818
1819
return NULL;
1820
}
1821
1822
void RSDK::GetAchievementInfo(uint32 id, String *name, String *description, String *identifer, bool32 *achieved)
1823
{
1824
if (id >= achievementList.size())
1825
return;
1826
1827
if (name)
1828
InitString(name, achievementList[id].name.c_str(), 0);
1829
1830
if (description)
1831
InitString(description, achievementList[id].description.c_str(), 0);
1832
1833
if (identifer)
1834
InitString(identifer, achievementList[id].identifier.c_str(), 0);
1835
1836
if (achieved)
1837
*achieved = achievementList[id].achieved;
1838
}
1839
1840
int32 RSDK::GetAchievementIndexByID(const char *id)
1841
{
1842
for (int32 i = 0; i < achievementList.size(); ++i) {
1843
if (achievementList[i].identifier == std::string(id))
1844
return i;
1845
}
1846
1847
return -1;
1848
}
1849
int32 RSDK::GetAchievementCount() { return (int32)achievementList.size(); }
1850
1851
void RSDK::StateMachineRun(void (*state)(void))
1852
{
1853
bool32 skipState = false;
1854
1855
for (int32 h = 0; h < (int32)stateHookList.size(); ++h) {
1856
if (stateHookList[h].priority && stateHookList[h].state == state && stateHookList[h].hook)
1857
skipState |= stateHookList[h].hook(skipState);
1858
}
1859
1860
if (!skipState && state)
1861
state();
1862
1863
for (int32 h = 0; h < (int32)stateHookList.size(); ++h) {
1864
if (!stateHookList[h].priority && stateHookList[h].state == state && stateHookList[h].hook)
1865
stateHookList[h].hook(skipState);
1866
}
1867
}
1868
1869
bool32 RSDK::HandleRunState_HighPriority(void (*state)(void))
1870
{
1871
bool32 skipState = false;
1872
1873
for (int32 h = 0; h < (int32)stateHookList.size(); ++h) {
1874
if (stateHookList[h].priority && stateHookList[h].state == state && stateHookList[h].hook)
1875
skipState |= stateHookList[h].hook(skipState);
1876
}
1877
1878
return skipState;
1879
}
1880
1881
void RSDK::HandleRunState_LowPriority(void (*state)(void), bool32 skipState)
1882
{
1883
for (int32 h = 0; h < (int32)stateHookList.size(); ++h) {
1884
if (!stateHookList[h].priority && stateHookList[h].state == state && stateHookList[h].hook)
1885
stateHookList[h].hook(skipState);
1886
}
1887
}
1888
1889
void RSDK::RegisterStateHook(void (*state)(void), bool32 (*hook)(bool32 skippedState), bool32 priority)
1890
{
1891
if (!state)
1892
return;
1893
1894
StateHook stateHook;
1895
stateHook.state = state;
1896
stateHook.hook = hook;
1897
stateHook.priority = priority;
1898
1899
stateHookList.push_back(stateHook);
1900
}
1901
1902
#if RETRO_MOD_LOADER_VER >= 2
1903
1904
// Files
1905
bool32 RSDK::ExcludeFile(const char *id, const char *path)
1906
{
1907
if (!id)
1908
return false;
1909
1910
if (!strlen(id) && currentMod)
1911
id = currentMod->id.c_str();
1912
1913
int32 m;
1914
for (m = 0; m < modList.size(); ++m) {
1915
if (modList[m].active && modList[m].id == id)
1916
break;
1917
}
1918
1919
if (m == modList.size())
1920
return false;
1921
1922
char pathLower[0x100];
1923
memset(pathLower, 0, sizeof(pathLower));
1924
for (int32 c = 0; c < strlen(path); ++c) pathLower[c] = tolower(path[c]);
1925
1926
auto &excludeList = modList[m].excludedFiles;
1927
if (std::find(excludeList.begin(), excludeList.end(), pathLower) == excludeList.end()) {
1928
excludeList.push_back(std::string(pathLower));
1929
1930
return true;
1931
}
1932
1933
return false;
1934
}
1935
bool32 RSDK::ExcludeAllFiles(const char *id)
1936
{
1937
if (!id)
1938
return false;
1939
1940
if (!strlen(id) && currentMod)
1941
id = currentMod->id.c_str();
1942
1943
int32 m;
1944
for (m = 0; m < modList.size(); ++m) {
1945
if (modList[m].active && modList[m].id == id)
1946
break;
1947
}
1948
1949
if (m == modList.size())
1950
return false;
1951
1952
auto &excludeList = modList[m].excludedFiles;
1953
for (auto file : modList[m].fileMap) {
1954
excludeList.push_back(file.first);
1955
}
1956
1957
modList[m].fileMap.clear();
1958
1959
return true;
1960
}
1961
bool32 RSDK::ReloadFile(const char *id, const char *path)
1962
{
1963
if (!id)
1964
return false;
1965
1966
if (!strlen(id) && currentMod)
1967
id = currentMod->id.c_str();
1968
1969
int32 m;
1970
for (m = 0; m < modList.size(); ++m) {
1971
if (modList[m].active && modList[m].id == id)
1972
break;
1973
}
1974
1975
if (m == modList.size())
1976
return false;
1977
1978
char pathLower[0x100];
1979
memset(pathLower, 0, sizeof(pathLower));
1980
for (int32 c = 0; c < strlen(path); ++c) pathLower[c] = tolower(path[c]);
1981
1982
auto &excludeList = modList[m].excludedFiles;
1983
if (std::find(excludeList.begin(), excludeList.end(), pathLower) != excludeList.end()) {
1984
excludeList.erase(std::remove(excludeList.begin(), excludeList.end(), pathLower), excludeList.end());
1985
1986
return true;
1987
}
1988
1989
ScanModFolder(&modList[m], path);
1990
1991
return true;
1992
}
1993
bool32 RSDK::ReloadAllFiles(const char *id)
1994
{
1995
if (!id)
1996
return false;
1997
1998
if (!strlen(id) && currentMod)
1999
id = currentMod->id.c_str();
2000
2001
int32 m;
2002
for (m = 0; m < modList.size(); ++m) {
2003
if (modList[m].active && modList[m].id == id)
2004
break;
2005
}
2006
2007
if (m == modList.size())
2008
return false;
2009
2010
modList[m].excludedFiles.clear();
2011
ScanModFolder(&modList[m]);
2012
2013
return true;
2014
}
2015
2016
// Objects & Entities
2017
bool32 RSDK::GetGroupEntities(uint16 group, void **entity)
2018
{
2019
if (group >= TYPEGROUP_COUNT)
2020
return false;
2021
2022
if (!entity)
2023
return false;
2024
2025
if (*entity) {
2026
++foreachStackPtr->id;
2027
}
2028
else {
2029
foreachStackPtr++;
2030
foreachStackPtr->id = 0;
2031
}
2032
2033
for (Entity *nextEntity = &objectEntityList[typeGroups[group].entries[foreachStackPtr->id]]; foreachStackPtr->id < typeGroups[group].entryCount;
2034
++foreachStackPtr->id, nextEntity = &objectEntityList[typeGroups[group].entries[foreachStackPtr->id]]) {
2035
if (nextEntity->group == group) {
2036
*entity = nextEntity;
2037
return true;
2038
}
2039
}
2040
2041
foreachStackPtr--;
2042
2043
return false;
2044
}
2045
2046
#endif
2047
2048
#endif
2049
2050