Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-1-2-2013-Decompilation
Path: blob/main/RSDKv4/RetroEngine.cpp
817 views
1
#include "RetroEngine.hpp"
2
3
#if !RETRO_USE_ORIGINAL_CODE
4
bool usingCWD = false;
5
bool engineDebugMode = false;
6
#endif
7
8
#if RETRO_PLATFORM == RETRO_ANDROID
9
#include <unistd.h>
10
#endif
11
12
RetroEngine Engine = RetroEngine();
13
14
#if !RETRO_USE_ORIGINAL_CODE
15
inline int GetLowerRate(int intendRate, int targetRate)
16
{
17
int result = 0;
18
int valStore = 0;
19
20
result = targetRate;
21
if (intendRate) {
22
do {
23
valStore = result % intendRate;
24
result = intendRate;
25
intendRate = valStore;
26
} while (valStore);
27
}
28
return result;
29
}
30
#endif
31
32
bool ProcessEvents()
33
{
34
#if !RETRO_USE_ORIGINAL_CODE
35
#if RETRO_USING_SDL1 || RETRO_USING_SDL2
36
while (SDL_PollEvent(&Engine.sdlEvents)) {
37
// Main Events
38
switch (Engine.sdlEvents.type) {
39
#if RETRO_USING_SDL2
40
case SDL_WINDOWEVENT:
41
switch (Engine.sdlEvents.window.event) {
42
case SDL_WINDOWEVENT_MAXIMIZED: {
43
SDL_RestoreWindow(Engine.window);
44
SDL_SetWindowFullscreen(Engine.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
45
SDL_ShowCursor(SDL_FALSE);
46
Engine.isFullScreen = true;
47
break;
48
}
49
case SDL_WINDOWEVENT_CLOSE: return false;
50
case SDL_WINDOWEVENT_FOCUS_LOST:
51
if (Engine.gameMode == ENGINE_MAINGAME && !(disableFocusPause & 1))
52
Engine.gameMode = ENGINE_INITPAUSE;
53
#if RETRO_REV00
54
if (!(disableFocusPause & 1))
55
Engine.message = MESSAGE_LOSTFOCUS;
56
#endif
57
Engine.hasFocus = false;
58
break;
59
case SDL_WINDOWEVENT_FOCUS_GAINED: Engine.hasFocus = true; break;
60
}
61
break;
62
case SDL_CONTROLLERDEVICEADDED: controllerInit(Engine.sdlEvents.cdevice.which); break;
63
case SDL_CONTROLLERDEVICEREMOVED: controllerClose(Engine.sdlEvents.cdevice.which); break;
64
case SDL_APP_WILLENTERBACKGROUND:
65
if (Engine.gameMode == ENGINE_MAINGAME && !(disableFocusPause & 1))
66
Engine.gameMode = ENGINE_INITPAUSE;
67
#if RETRO_REV00
68
if (!(disableFocusPause & 1))
69
Engine.message = MESSAGE_LOSTFOCUS;
70
#endif
71
Engine.hasFocus = false;
72
break;
73
case SDL_APP_WILLENTERFOREGROUND: Engine.hasFocus = true; break;
74
case SDL_APP_TERMINATING: return false;
75
#endif
76
77
#if RETRO_USING_SDL2 && defined(RETRO_USING_MOUSE)
78
case SDL_MOUSEMOTION:
79
if (touches <= 1) { // Touch always takes priority over mouse
80
uint state = SDL_GetMouseState(&touchX[0], &touchY[0]);
81
82
touchXF[0] = (((touchX[0] - displaySettings.offsetX) / (float)displaySettings.width) * SCREEN_XSIZE_F) - SCREEN_CENTERX_F;
83
touchYF[0] = -(((touchY[0] / (float)displaySettings.height) * SCREEN_YSIZE_F) - SCREEN_CENTERY_F);
84
touchX[0] = ((touchX[0] - displaySettings.offsetX) / (float)displaySettings.width) * SCREEN_XSIZE;
85
touchY[0] = (touchY[0] / (float)displaySettings.height) * SCREEN_YSIZE;
86
87
touchDown[0] = state & SDL_BUTTON_LMASK;
88
if (touchDown[0])
89
touches = 1;
90
}
91
break;
92
case SDL_MOUSEBUTTONDOWN:
93
if (touches <= 1) { // Touch always takes priority over mouse
94
switch (Engine.sdlEvents.button.button) {
95
case SDL_BUTTON_LEFT: touchDown[0] = true; break;
96
}
97
touches = 1;
98
}
99
break;
100
case SDL_MOUSEBUTTONUP:
101
if (touches <= 1) { // Touch always takes priority over mouse
102
switch (Engine.sdlEvents.button.button) {
103
case SDL_BUTTON_LEFT: touchDown[0] = false; break;
104
}
105
touches = 0;
106
}
107
break;
108
#endif
109
110
#if defined(RETRO_USING_TOUCH) && RETRO_USING_SDL2
111
case SDL_FINGERMOTION:
112
case SDL_FINGERDOWN:
113
case SDL_FINGERUP: {
114
int count = SDL_GetNumTouchFingers(Engine.sdlEvents.tfinger.touchId);
115
touches = 0;
116
for (int i = 0; i < count; i++) {
117
SDL_Finger *finger = SDL_GetTouchFinger(Engine.sdlEvents.tfinger.touchId, i);
118
if (finger) {
119
touchDown[touches] = true;
120
touchX[touches] = finger->x * SCREEN_XSIZE;
121
touchY[touches] = finger->y * SCREEN_YSIZE;
122
touchXF[touches] = (finger->x * SCREEN_XSIZE_F) - SCREEN_CENTERX_F;
123
touchYF[touches] = -((finger->y * SCREEN_YSIZE_F) - SCREEN_CENTERY_F);
124
125
touchX[touches] -= displaySettings.offsetX;
126
touchXF[touches] -= displaySettings.offsetX;
127
128
touches++;
129
}
130
}
131
break;
132
}
133
#endif //! RETRO_USING_SDL2
134
135
case SDL_KEYDOWN:
136
switch (Engine.sdlEvents.key.keysym.sym) {
137
default: break;
138
case SDLK_ESCAPE:
139
if (Engine.devMenu) {
140
#if RETRO_USE_MOD_LOADER
141
// hacky patch because people can escape
142
if (Engine.gameMode == ENGINE_DEVMENU && stageMode == DEVMENU_MODMENU)
143
RefreshEngine();
144
#endif
145
ClearNativeObjects();
146
CREATE_ENTITY(RetroGameLoop);
147
if (Engine.gameDeviceType == RETRO_MOBILE)
148
CREATE_ENTITY(VirtualDPad);
149
Engine.gameMode = ENGINE_INITDEVMENU;
150
}
151
break;
152
153
case SDLK_F1:
154
if (Engine.devMenu) {
155
activeStageList = 0;
156
stageListPosition = 0;
157
stageMode = STAGEMODE_LOAD;
158
Engine.gameMode = ENGINE_MAINGAME;
159
}
160
break;
161
162
case SDLK_F2:
163
if (Engine.devMenu) {
164
stageListPosition--;
165
if (stageListPosition < 0) {
166
activeStageList--;
167
168
if (activeStageList < 0) {
169
activeStageList = 3;
170
}
171
stageListPosition = stageListCount[activeStageList] - 1;
172
}
173
stageMode = STAGEMODE_LOAD;
174
Engine.gameMode = ENGINE_MAINGAME;
175
SetGlobalVariableByName("lampPostID", 0); // For S1
176
SetGlobalVariableByName("starPostID", 0); // For S2
177
}
178
break;
179
180
case SDLK_F3:
181
if (Engine.devMenu) {
182
stageListPosition++;
183
if (stageListPosition >= stageListCount[activeStageList]) {
184
activeStageList++;
185
186
stageListPosition = 0;
187
188
if (activeStageList >= 4) {
189
activeStageList = 0;
190
}
191
}
192
stageMode = STAGEMODE_LOAD;
193
Engine.gameMode = ENGINE_MAINGAME;
194
SetGlobalVariableByName("lampPostID", 0); // For S1
195
SetGlobalVariableByName("starPostID", 0); // For S2
196
}
197
break;
198
199
case SDLK_F4:
200
Engine.isFullScreen ^= 1;
201
SetFullScreen(Engine.isFullScreen);
202
break;
203
204
case SDLK_F5:
205
if (Engine.devMenu) {
206
currentStageFolder[0] = 0; // reload all assets & scripts
207
stageMode = STAGEMODE_LOAD;
208
}
209
break;
210
211
case SDLK_F8:
212
if (Engine.devMenu)
213
showHitboxes ^= 2;
214
break;
215
216
case SDLK_F9:
217
if (Engine.devMenu)
218
showHitboxes ^= 1;
219
break;
220
221
case SDLK_F10:
222
if (Engine.devMenu)
223
Engine.showPaletteOverlay ^= 1;
224
break;
225
226
case SDLK_BACKSPACE:
227
if (Engine.devMenu)
228
Engine.gameSpeed = Engine.fastForwardSpeed;
229
break;
230
231
#if RETRO_PLATFORM == RETRO_OSX
232
case SDLK_F6:
233
if (Engine.masterPaused)
234
Engine.frameStep = true;
235
break;
236
237
case SDLK_F7:
238
if (Engine.devMenu)
239
Engine.masterPaused ^= 1;
240
break;
241
#else
242
case SDLK_F11:
243
case SDLK_INSERT:
244
if (Engine.masterPaused)
245
Engine.frameStep = true;
246
break;
247
248
case SDLK_F12:
249
case SDLK_PAUSE:
250
if (Engine.devMenu)
251
Engine.masterPaused ^= 1;
252
break;
253
#endif
254
}
255
256
#if RETRO_USING_SDL1
257
keyState[Engine.sdlEvents.key.keysym.sym] = 1;
258
#endif
259
break;
260
case SDL_KEYUP:
261
switch (Engine.sdlEvents.key.keysym.sym) {
262
default: break;
263
case SDLK_BACKSPACE: Engine.gameSpeed = 1; break;
264
}
265
#if RETRO_USING_SDL1
266
keyState[Engine.sdlEvents.key.keysym.sym] = 0;
267
#endif
268
break;
269
case SDL_QUIT: return false;
270
}
271
}
272
#endif
273
#endif
274
return true;
275
}
276
277
void RetroEngine::Init()
278
{
279
CalculateTrigAngles();
280
GenerateBlendLookupTable();
281
282
CloseRSDKContainers(); // Clears files
283
284
Engine.usingDataFile = false;
285
Engine.usingBytecode = false;
286
287
#if !RETRO_USE_ORIGINAL_CODE
288
InitUserdata();
289
#if RETRO_USE_MOD_LOADER
290
InitMods();
291
#endif
292
#if RETRO_USE_NETWORKING
293
InitNetwork();
294
#endif
295
296
char dest[0x200];
297
#if RETRO_PLATFORM == RETRO_UWP
298
static char resourcePath[256] = { 0 };
299
300
if (strlen(resourcePath) == 0) {
301
auto folder = winrt::Windows::Storage::ApplicationData::Current().LocalFolder();
302
auto path = to_string(folder.Path());
303
304
std::copy(path.begin(), path.end(), resourcePath);
305
}
306
307
strcpy(dest, resourcePath);
308
strcat(dest, "\\");
309
strcat(dest, Engine.dataFile);
310
#elif RETRO_PLATFORM == RETRO_ANDROID
311
StrCopy(dest, gamePath);
312
StrAdd(dest, Engine.dataFile[0]);
313
disableFocusPause = 0; // focus pause is ALWAYS enabled.
314
#else
315
316
StrCopy(dest, BASE_PATH);
317
StrAdd(dest, Engine.dataFile[0]);
318
#endif
319
CheckRSDKFile(dest);
320
#else
321
CheckRSDKFile("Data.rsdk");
322
#endif
323
324
#if !RETRO_USE_ORIGINAL_CODE
325
for (int i = 1; i < RETRO_PACK_COUNT; ++i) {
326
if (!StrComp(Engine.dataFile[i], "")) {
327
StrCopy(dest, BASE_PATH);
328
StrAdd(dest, Engine.dataFile[i]);
329
CheckRSDKFile(dest);
330
}
331
}
332
#endif
333
334
gameMode = ENGINE_MAINGAME;
335
running = false;
336
#if !RETRO_USE_ORIGINAL_CODE
337
bool skipStart = skipStartMenu;
338
#endif
339
SaveGame *saveGame = (SaveGame *)saveRAM;
340
341
if (LoadGameConfig("Data/Game/GameConfig.bin")) {
342
if (InitRenderDevice()) {
343
if (InitAudioPlayback()) {
344
InitFirstStage();
345
ClearScriptData();
346
initialised = true;
347
running = true;
348
349
#if !RETRO_USE_ORIGINAL_CODE
350
if ((startList_Game != 0xFF && startList_Game) || (startStage_Game != 0xFF && startStage_Game) || startPlayer != 0xFF) {
351
skipStart = true;
352
InitStartingStage(startList_Game == 0xFF ? STAGELIST_PRESENTATION : startList_Game, startStage_Game == 0xFF ? 0 : startStage_Game,
353
startPlayer == 0xFF ? 0 : startPlayer);
354
}
355
else if (startSave != 0xFF && startSave <= 4) {
356
if (startSave == 0) {
357
SetGlobalVariableByName("options.saveSlot", 0);
358
SetGlobalVariableByName("options.gameMode", 0);
359
360
SetGlobalVariableByName("options.stageSelectFlag", 0);
361
SetGlobalVariableByName("player.lives", 3);
362
SetGlobalVariableByName("player.score", 0);
363
SetGlobalVariableByName("player.scoreBonus", 50000);
364
SetGlobalVariableByName("specialStage.emeralds", 0);
365
SetGlobalVariableByName("specialStage.listPos", 0);
366
SetGlobalVariableByName("stage.player2Enabled", 0);
367
SetGlobalVariableByName("lampPostID", 0); // For S1
368
SetGlobalVariableByName("starPostID", 0); // For S2
369
SetGlobalVariableByName("options.vsMode", 0);
370
371
SetGlobalVariableByName("specialStage.nextZone", 0);
372
InitStartingStage(STAGELIST_REGULAR, 0, 0);
373
}
374
else {
375
SetGlobalVariableByName("options.saveSlot", startSave);
376
SetGlobalVariableByName("options.gameMode", 1);
377
int slot = (startSave - 1) << 3;
378
379
SetGlobalVariableByName("options.stageSelectFlag", false);
380
SetGlobalVariableByName("player.lives", saveGame->files[slot].lives);
381
SetGlobalVariableByName("player.score", saveGame->files[slot].score);
382
SetGlobalVariableByName("player.scoreBonus", saveGame->files[slot].scoreBonus);
383
SetGlobalVariableByName("specialStage.emeralds", saveGame->files[slot].emeralds);
384
SetGlobalVariableByName("specialStage.listPos", saveGame->files[slot].specialStageID);
385
SetGlobalVariableByName("stage.player2Enabled", saveGame->files[slot].characterID == 3);
386
SetGlobalVariableByName("lampPostID", 0); // For S1
387
SetGlobalVariableByName("starPostID", 0); // For S2
388
SetGlobalVariableByName("options.vsMode", 0);
389
390
int nextStage = saveGame->files[slot].stageID;
391
if (nextStage >= 0x80) {
392
SetGlobalVariableByName("specialStage.nextZone", nextStage - 0x81);
393
InitStartingStage(STAGELIST_SPECIAL, saveGame->files[slot].specialStageID, saveGame->files[slot].characterID);
394
}
395
else if (nextStage >= 1) {
396
SetGlobalVariableByName("specialStage.nextZone", nextStage - 1);
397
InitStartingStage(STAGELIST_REGULAR, nextStage - 1, saveGame->files[slot].characterID);
398
}
399
else {
400
saveGame->files[slot].characterID = 0;
401
saveGame->files[slot].lives = 3;
402
saveGame->files[slot].score = 0;
403
saveGame->files[slot].scoreBonus = 50000;
404
saveGame->files[slot].stageID = 0;
405
saveGame->files[slot].emeralds = 0;
406
saveGame->files[slot].specialStageID = 0;
407
saveGame->files[slot].unused = 0;
408
409
SetGlobalVariableByName("specialStage.nextZone", 0);
410
InitStartingStage(STAGELIST_REGULAR, 0, 0);
411
}
412
}
413
skipStart = true;
414
}
415
#endif
416
}
417
}
418
}
419
420
#if !RETRO_USE_ORIGINAL_CODE
421
gameType = GAME_SONIC2;
422
#if RETRO_USE_MOD_LOADER
423
if (strstr(gameWindowText, "Sonic 1") || forceSonic1) {
424
#else
425
if (strstr(gameWindowText, "Sonic 1")) {
426
#endif
427
gameType = GAME_SONIC1;
428
}
429
#endif
430
431
#if !RETRO_USE_ORIGINAL_CODE
432
bool skipStore = skipStartMenu;
433
skipStartMenu = skipStart;
434
InitNativeObjectSystem();
435
skipStartMenu = skipStore;
436
#else
437
InitNativeObjectSystem();
438
#endif
439
440
#if !RETRO_USE_ORIGINAL_CODE
441
// Calculate Skip frame
442
int lower = GetLowerRate(targetRefreshRate, refreshRate);
443
renderFrameIndex = targetRefreshRate / lower;
444
skipFrameIndex = refreshRate / lower;
445
446
ReadSaveRAMData();
447
448
if (Engine.gameType == GAME_SONIC1) {
449
AddAchievement("Ramp Ring Acrobatics",
450
"Without touching the ground,\rcollect all the rings in a\rtrapezoid formation in Green\rHill Zone Act 1");
451
AddAchievement("Blast Processing", "Clear Green Hill Zone Act 1\rin under 30 seconds");
452
AddAchievement("Secret of Marble Zone", "Travel though a secret\rroom in Marble Zone Act 3");
453
AddAchievement("Block Buster", "Break 16 blocks in a row\rwithout stopping");
454
AddAchievement("Ring King", "Collect 200 Rings");
455
AddAchievement("Secret of Labyrinth Zone", "Activate and ride the\rhidden platform in\rLabyrinth Zone Act 1");
456
AddAchievement("Flawless Pursuit", "Clear the boss in Labyrinth\rZone without getting hurt");
457
AddAchievement("Bombs Away", "Defeat the boss in Starlight Zone\rusing only the see-saw bombs");
458
AddAchievement("Hidden Transporter", "Collect 50 Rings and take the hidden transporter path\rin Scrap Brain Act 2");
459
AddAchievement("Chaos Connoisseur", "Collect all the chaos\remeralds");
460
AddAchievement("One For the Road", "As a parting gift, land a\rfinal hit on Dr. Eggman's\rescaping Egg Mobile");
461
AddAchievement("Beat The Clock", "Clear the Time Attack\rmode in less than 45\rminutes");
462
}
463
else if (Engine.gameType == GAME_SONIC2) {
464
AddAchievement("Quick Run", "Complete Emerald Hill\rZone Act 1 in under 35\rseconds");
465
AddAchievement("100% Chemical Free", "Complete Chemical Plant\rwithout going underwater");
466
AddAchievement("Early Bird Special", "Collect all the Chaos\rEmeralds before Chemical\rPlant");
467
AddAchievement("Superstar", "Complete any Act as\rSuper Sonic");
468
AddAchievement("Hit it Big", "Get a jackpot on the Casino Night slot machines");
469
AddAchievement("Bop Non-stop", "Defeat any boss in 8\rconsecutive hits without\rtouching he ground");
470
AddAchievement("Perfectionist", "Get a Perfect Bonus by\rcollecting every Ring in an\rAct");
471
AddAchievement("A Secret Revealed", "Find and complete\rHidden Palace Zone");
472
AddAchievement("Head 2 Head", "Win a 2P Versus race\ragainst a friend");
473
AddAchievement("Metropolis Master", "Complete Any Metropolis\rZone Act without getting\rhurt");
474
AddAchievement("Scrambled Egg", "Defeat Dr. Eggman's Boss\rAttack mode in under 7\rminutes");
475
AddAchievement("Beat the Clock", "Complete the Time Attack\rmode in less than 45\rminutes");
476
}
477
478
if (skipStart)
479
Engine.gameMode = ENGINE_MAINGAME;
480
else
481
Engine.gameMode = ENGINE_WAIT;
482
483
// "error message"
484
if (!running) {
485
char rootDir[0x80];
486
char pathBuffer[0x80];
487
488
#if RETRO_PLATFORM == RETRO_UWP
489
if (!usingCWD)
490
sprintf(rootDir, "%s/", getResourcesPath());
491
else
492
sprintf(rootDir, "%s", "");
493
#elif RETRO_PLATFORM == RETRO_OSX
494
sprintf(rootDir, "%s/", gamePath);
495
#else
496
sprintf(rootDir, "%s", "");
497
#endif
498
sprintf(pathBuffer, "%s%s", rootDir, "usage.txt");
499
500
FileIO *f;
501
if ((f = fOpen(pathBuffer, "w")) == NULL) {
502
PrintLog("ERROR: Couldn't open file '%s' for writing!", "usage.txt");
503
return;
504
}
505
506
char textBuf[0x100];
507
sprintf(textBuf, "RETRO ENGINE v4 USAGE:\n");
508
fWrite(textBuf, 1, strlen(textBuf), f);
509
510
sprintf(textBuf, "- Open the asset directory '%s' in a file browser\n", !rootDir[0] ? "./" : rootDir);
511
fWrite(textBuf, 1, strlen(textBuf), f);
512
513
sprintf(textBuf, "- Place a data pack named '%s' in the asset directory\n", Engine.dataFile[0]);
514
fWrite(textBuf, 1, strlen(textBuf), f);
515
516
sprintf(textBuf, "- OR extract a data pack and place the \"Data\" & \"Bytecode\" folders in the asset directory\n");
517
fWrite(textBuf, 1, strlen(textBuf), f);
518
519
fClose(f);
520
}
521
522
#endif
523
}
524
525
void RetroEngine::Run()
526
{
527
Engine.deltaTime = 0.0f;
528
529
unsigned long long targetFreq = SDL_GetPerformanceFrequency() / Engine.refreshRate;
530
unsigned long long curTicks = 0;
531
unsigned long long prevTicks = 0;
532
533
while (running) {
534
#if !RETRO_USE_ORIGINAL_CODE
535
if (!vsync) {
536
curTicks = SDL_GetPerformanceCounter();
537
if (curTicks < prevTicks + targetFreq)
538
continue;
539
prevTicks = curTicks;
540
}
541
542
Engine.deltaTime = 1.0 / 60;
543
#endif
544
running = ProcessEvents();
545
546
// Focus Checks
547
if (!(disableFocusPause & 2)) {
548
if (!Engine.hasFocus) {
549
if (!(Engine.focusState & 1))
550
Engine.focusState = PauseSound() ? 3 : 1;
551
}
552
else if (Engine.focusState) {
553
if ((Engine.focusState & 2))
554
ResumeSound();
555
Engine.focusState = 0;
556
}
557
}
558
559
if (!(Engine.focusState & 1) || vsPlaying) {
560
#if !RETRO_USE_ORIGINAL_CODE
561
for (int s = 0; s < gameSpeed; ++s) {
562
ProcessInput();
563
#endif
564
565
#if !RETRO_USE_ORIGINAL_CODE
566
if (!masterPaused || frameStep) {
567
#endif
568
ProcessNativeObjects();
569
#if !RETRO_USE_ORIGINAL_CODE
570
}
571
#endif
572
}
573
574
#if !RETRO_USE_ORIGINAL_CODE
575
if (!masterPaused || frameStep) {
576
#endif
577
FlipScreen();
578
579
#if !RETRO_USE_ORIGINAL_CODE
580
#if RETRO_USING_OPENGL && RETRO_USING_SDL2
581
SDL_GL_SwapWindow(Engine.window);
582
#endif
583
frameStep = false;
584
}
585
#endif
586
587
#if RETRO_REV00
588
Engine.message = MESSAGE_NONE;
589
#endif
590
591
#if RETRO_USE_HAPTICS
592
int hapticID = GetHapticEffectNum();
593
if (hapticID >= 0) {
594
// playHaptics(hapticID);
595
}
596
else if (hapticID == HAPTIC_STOP) {
597
// stopHaptics();
598
}
599
#endif
600
}
601
}
602
603
ReleaseAudioDevice();
604
ReleaseRenderDevice();
605
#if !RETRO_USE_ORIGINAL_CODE
606
ReleaseInputDevices();
607
#if RETRO_USE_NETWORKING
608
DisconnectNetwork(true);
609
#endif
610
WriteSettings();
611
#if RETRO_USE_MOD_LOADER
612
SaveMods();
613
#endif
614
#endif
615
616
#if RETRO_USING_SDL1 || RETRO_USING_SDL2
617
SDL_Quit();
618
#endif
619
}
620
621
#if RETRO_USE_MOD_LOADER
622
const tinyxml2::XMLElement *FirstXMLChildElement(tinyxml2::XMLDocument *doc, const tinyxml2::XMLElement *elementPtr, const char *name)
623
{
624
if (doc) {
625
if (!elementPtr)
626
return doc->FirstChildElement(name);
627
else
628
return elementPtr->FirstChildElement(name);
629
}
630
return NULL;
631
}
632
633
const tinyxml2::XMLElement *NextXMLSiblingElement(tinyxml2::XMLDocument *doc, const tinyxml2::XMLElement *elementPtr, const char *name)
634
{
635
if (doc) {
636
if (!elementPtr)
637
return doc->NextSiblingElement(name);
638
else
639
return elementPtr->NextSiblingElement(name);
640
}
641
return NULL;
642
}
643
644
const tinyxml2::XMLAttribute *FindXMLAttribute(const tinyxml2::XMLElement *elementPtr, const char *name) { return elementPtr->FindAttribute(name); }
645
const char *GetXMLAttributeName(const tinyxml2::XMLAttribute *attributePtr) { return attributePtr->Name(); }
646
int GetXMLAttributeValueInt(const tinyxml2::XMLAttribute *attributePtr) { return attributePtr->IntValue(); }
647
bool GetXMLAttributeValueBool(const tinyxml2::XMLAttribute *attributePtr) { return attributePtr->BoolValue(); }
648
const char *GetXMLAttributeValueString(const tinyxml2::XMLAttribute *attributePtr) { return attributePtr->Value(); }
649
650
void RetroEngine::LoadXMLWindowText()
651
{
652
FileInfo info;
653
for (int m = 0; m < (int)modList.size(); ++m) {
654
if (!modList[m].active)
655
continue;
656
657
SetActiveMod(m);
658
if (LoadFile("Data/Game/Game.xml", &info)) {
659
tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
660
661
char *xmlData = new char[info.fileSize + 1];
662
FileRead(xmlData, info.fileSize);
663
xmlData[info.fileSize] = 0;
664
665
bool success = doc->Parse(xmlData) == tinyxml2::XML_SUCCESS;
666
667
if (success) {
668
const tinyxml2::XMLElement *gameElement = FirstXMLChildElement(doc, nullptr, "game");
669
const tinyxml2::XMLElement *titleElement = FirstXMLChildElement(doc, gameElement, "title");
670
if (titleElement) {
671
const tinyxml2::XMLAttribute *nameAttr = FindXMLAttribute(titleElement, "name");
672
if (nameAttr)
673
StrCopy(gameWindowText, GetXMLAttributeValueString(nameAttr));
674
}
675
}
676
677
delete[] xmlData;
678
delete doc;
679
680
CloseFile();
681
}
682
}
683
SetActiveMod(-1);
684
}
685
void RetroEngine::LoadXMLVariables()
686
{
687
FileInfo info;
688
for (int m = 0; m < (int)modList.size(); ++m) {
689
if (!modList[m].active)
690
continue;
691
692
SetActiveMod(m);
693
if (LoadFile("Data/Game/Game.xml", &info)) {
694
tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
695
696
char *xmlData = new char[info.fileSize + 1];
697
FileRead(xmlData, info.fileSize);
698
xmlData[info.fileSize] = 0;
699
700
bool success = doc->Parse(xmlData) == tinyxml2::XML_SUCCESS;
701
702
if (success) {
703
const tinyxml2::XMLElement *gameElement = FirstXMLChildElement(doc, nullptr, "game");
704
const tinyxml2::XMLElement *variablesElement = FirstXMLChildElement(doc, gameElement, "variables");
705
if (variablesElement) {
706
const tinyxml2::XMLElement *varElement = FirstXMLChildElement(doc, variablesElement, "variable");
707
if (varElement) {
708
do {
709
const tinyxml2::XMLAttribute *nameAttr = FindXMLAttribute(varElement, "name");
710
const char *varName = "unknownVariable";
711
if (nameAttr)
712
varName = GetXMLAttributeValueString(nameAttr);
713
714
const tinyxml2::XMLAttribute *valAttr = FindXMLAttribute(varElement, "value");
715
int varValue = 0;
716
if (valAttr)
717
varValue = GetXMLAttributeValueInt(valAttr);
718
719
if (globalVariablesCount >= GLOBALVAR_COUNT)
720
PrintLog("Failed to add global variable '%s' (max limit reached)", varName);
721
else if (GetGlobalVariableID(varName) == 0xFF) {
722
StrCopy(globalVariableNames[globalVariablesCount], varName);
723
globalVariables[globalVariablesCount] = varValue;
724
globalVariablesCount++;
725
}
726
} while ((varElement = NextXMLSiblingElement(doc, varElement, "variable")));
727
}
728
}
729
}
730
731
delete[] xmlData;
732
delete doc;
733
734
CloseFile();
735
}
736
}
737
SetActiveMod(-1);
738
}
739
void RetroEngine::LoadXMLPalettes()
740
{
741
FileInfo info;
742
for (int m = 0; m < (int)modList.size(); ++m) {
743
if (!modList[m].active)
744
continue;
745
746
SetActiveMod(m);
747
if (LoadFile("Data/Game/Game.xml", &info)) {
748
tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
749
750
char *xmlData = new char[info.fileSize + 1];
751
FileRead(xmlData, info.fileSize);
752
xmlData[info.fileSize] = 0;
753
754
bool success = doc->Parse(xmlData) == tinyxml2::XML_SUCCESS;
755
756
if (success) {
757
const tinyxml2::XMLElement *gameElement = FirstXMLChildElement(doc, nullptr, "game");
758
const tinyxml2::XMLElement *paletteElement = FirstXMLChildElement(doc, gameElement, "palette");
759
if (paletteElement) {
760
for (const tinyxml2::XMLElement *clrElement = paletteElement->FirstChildElement("color"); clrElement;
761
clrElement = clrElement->NextSiblingElement("color")) {
762
const tinyxml2::XMLAttribute *bankAttr = clrElement->FindAttribute("bank");
763
int clrBank = 0;
764
if (bankAttr)
765
clrBank = bankAttr->IntValue();
766
767
const tinyxml2::XMLAttribute *indAttr = clrElement->FindAttribute("index");
768
int clrInd = 0;
769
if (indAttr)
770
clrInd = indAttr->IntValue();
771
772
const tinyxml2::XMLAttribute *rAttr = clrElement->FindAttribute("r");
773
int clrR = 0;
774
if (rAttr)
775
clrR = rAttr->IntValue();
776
777
const tinyxml2::XMLAttribute *gAttr = clrElement->FindAttribute("g");
778
int clrG = 0;
779
if (gAttr)
780
clrG = gAttr->IntValue();
781
782
const tinyxml2::XMLAttribute *bAttr = clrElement->FindAttribute("b");
783
int clrB = 0;
784
if (bAttr)
785
clrB = bAttr->IntValue();
786
787
SetPaletteEntry(clrBank, clrInd, clrR, clrG, clrB);
788
}
789
790
for (const tinyxml2::XMLElement *clrsElement = paletteElement->FirstChildElement("colors"); clrsElement;
791
clrsElement = clrsElement->NextSiblingElement("colors")) {
792
const tinyxml2::XMLAttribute *bankAttr = clrsElement->FindAttribute("bank");
793
int bank = 0;
794
if (bankAttr)
795
bank = bankAttr->IntValue();
796
797
const tinyxml2::XMLAttribute *indAttr = clrsElement->FindAttribute("start");
798
int index = 0;
799
if (indAttr)
800
index = indAttr->IntValue();
801
802
std::string text = clrsElement->GetText();
803
// working: AABBFF #FFaaFF (12, 32, 34) (145 53 234)
804
std::regex search(R"((?:#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2}))|(?:\((\d+),?\s*(\d+),?\s*(\d+)\)))",
805
std::regex_constants::icase | std::regex_constants::ECMAScript);
806
std::smatch match;
807
while (std::regex_search(text, match, search)) {
808
int r, g, b;
809
int base, start;
810
if (match[1].matched) {
811
// we have hex
812
base = 16;
813
start = 1;
814
}
815
else {
816
// triplet
817
base = 10;
818
start = 4;
819
}
820
821
r = std::stoi(match[start + 0].str(), nullptr, base);
822
g = std::stoi(match[start + 1].str(), nullptr, base);
823
b = std::stoi(match[start + 2].str(), nullptr, base);
824
825
SetPaletteEntry(bank, index++, r, g, b);
826
text = match.suffix();
827
}
828
}
829
}
830
}
831
832
delete[] xmlData;
833
delete doc;
834
835
CloseFile();
836
}
837
}
838
SetActiveMod(-1);
839
}
840
void RetroEngine::LoadXMLObjects()
841
{
842
FileInfo info;
843
modObjCount = 0;
844
845
for (int m = 0; m < (int)modList.size(); ++m) {
846
if (!modList[m].active)
847
continue;
848
849
SetActiveMod(m);
850
if (LoadFile("Data/Game/Game.xml", &info)) {
851
tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
852
853
char *xmlData = new char[info.fileSize + 1];
854
FileRead(xmlData, info.fileSize);
855
xmlData[info.fileSize] = 0;
856
857
bool success = doc->Parse(xmlData) == tinyxml2::XML_SUCCESS;
858
859
if (success) {
860
const tinyxml2::XMLElement *gameElement = FirstXMLChildElement(doc, nullptr, "game");
861
const tinyxml2::XMLElement *objectsElement = FirstXMLChildElement(doc, gameElement, "objects");
862
if (objectsElement) {
863
const tinyxml2::XMLElement *objElement = FirstXMLChildElement(doc, objectsElement, "object");
864
if (objElement) {
865
do {
866
const tinyxml2::XMLAttribute *nameAttr = FindXMLAttribute(objElement, "name");
867
const char *objName = "unknownObject";
868
if (nameAttr)
869
objName = GetXMLAttributeValueString(nameAttr);
870
871
const tinyxml2::XMLAttribute *scrAttr = FindXMLAttribute(objElement, "script");
872
const char *objScript = "unknownObject.txt";
873
if (scrAttr)
874
objScript = GetXMLAttributeValueString(scrAttr);
875
876
byte flags = 0;
877
878
// forces the object to be loaded, this means the object doesn't have to be and *SHOULD NOT* be in the stage object list
879
// if it is, it'll cause issues!!!!
880
const tinyxml2::XMLAttribute *loadAttr = FindXMLAttribute(objElement, "forceLoad");
881
int objForceLoad = false;
882
if (loadAttr)
883
objForceLoad = GetXMLAttributeValueBool(loadAttr);
884
885
flags |= (objForceLoad & 1);
886
887
StrCopy(modTypeNames[modObjCount], objName);
888
StrCopy(modScriptPaths[modObjCount], objScript);
889
modScriptFlags[modObjCount] = flags;
890
modObjCount++;
891
892
} while ((objElement = NextXMLSiblingElement(doc, objElement, "object")));
893
}
894
}
895
}
896
else {
897
PrintLog("Failed to parse game.xml File!");
898
}
899
900
delete[] xmlData;
901
delete doc;
902
903
CloseFile();
904
}
905
}
906
SetActiveMod(-1);
907
}
908
void RetroEngine::LoadXMLSoundFX()
909
{
910
FileInfo info;
911
FileInfo infoStore;
912
for (int m = 0; m < (int)modList.size(); ++m) {
913
if (!modList[m].active)
914
continue;
915
916
SetActiveMod(m);
917
if (LoadFile("Data/Game/Game.xml", &info)) {
918
tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
919
920
char *xmlData = new char[info.fileSize + 1];
921
FileRead(xmlData, info.fileSize);
922
xmlData[info.fileSize] = 0;
923
924
bool success = doc->Parse(xmlData) == tinyxml2::XML_SUCCESS;
925
926
if (success) {
927
const tinyxml2::XMLElement *gameElement = FirstXMLChildElement(doc, nullptr, "game");
928
const tinyxml2::XMLElement *soundsElement = FirstXMLChildElement(doc, gameElement, "sounds");
929
if (soundsElement) {
930
const tinyxml2::XMLElement *sfxElement = FirstXMLChildElement(doc, soundsElement, "soundfx");
931
if (sfxElement) {
932
do {
933
const tinyxml2::XMLAttribute *nameAttr = FindXMLAttribute(sfxElement, "name");
934
const char *sfxName = "unknownSFX";
935
if (nameAttr)
936
sfxName = GetXMLAttributeValueString(nameAttr);
937
938
const tinyxml2::XMLAttribute *valAttr = FindXMLAttribute(sfxElement, "path");
939
const char *sfxPath = "unknownSFX.wav";
940
if (valAttr)
941
sfxPath = GetXMLAttributeValueString(valAttr);
942
943
SetSfxName(sfxName, globalSFXCount);
944
945
GetFileInfo(&infoStore);
946
CloseFile();
947
LoadSfx((char *)sfxPath, globalSFXCount);
948
SetFileInfo(&infoStore);
949
globalSFXCount++;
950
951
} while ((sfxElement = NextXMLSiblingElement(doc, sfxElement, "soundfx")));
952
}
953
}
954
}
955
else {
956
PrintLog("Failed to parse game.xml File!");
957
}
958
959
delete[] xmlData;
960
delete doc;
961
962
CloseFile();
963
}
964
}
965
SetActiveMod(-1);
966
}
967
void RetroEngine::LoadXMLPlayers(TextMenu *menu)
968
{
969
FileInfo info;
970
971
for (int m = 0; m < (int)modList.size(); ++m) {
972
if (!modList[m].active)
973
continue;
974
975
SetActiveMod(m);
976
if (LoadFile("Data/Game/Game.xml", &info)) {
977
tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
978
979
char *xmlData = new char[info.fileSize + 1];
980
FileRead(xmlData, info.fileSize);
981
xmlData[info.fileSize] = 0;
982
983
bool success = doc->Parse(xmlData) == tinyxml2::XML_SUCCESS;
984
985
if (success) {
986
const tinyxml2::XMLElement *gameElement = FirstXMLChildElement(doc, nullptr, "game");
987
const tinyxml2::XMLElement *playersElement = FirstXMLChildElement(doc, gameElement, "players");
988
if (playersElement) {
989
const tinyxml2::XMLElement *plrElement = FirstXMLChildElement(doc, playersElement, "player");
990
if (plrElement) {
991
do {
992
const tinyxml2::XMLAttribute *nameAttr = FindXMLAttribute(plrElement, "name");
993
const char *plrName = "unknownPlayer";
994
if (nameAttr)
995
plrName = GetXMLAttributeValueString(nameAttr);
996
997
if (playerCount >= PLAYER_COUNT)
998
PrintLog("Failed to add dev menu character '%s' (max limit reached)", plrName);
999
else if (menu)
1000
AddTextMenuEntry(menu, plrName);
1001
else
1002
StrCopy(playerNames[playerCount++], plrName);
1003
1004
} while ((plrElement = NextXMLSiblingElement(doc, plrElement, "player")));
1005
}
1006
}
1007
}
1008
else {
1009
PrintLog("Failed to parse game.xml File!");
1010
}
1011
1012
delete[] xmlData;
1013
delete doc;
1014
1015
CloseFile();
1016
}
1017
}
1018
SetActiveMod(-1);
1019
}
1020
void RetroEngine::LoadXMLStages(TextMenu *menu, int listNo)
1021
{
1022
FileInfo info;
1023
for (int m = 0; m < (int)modList.size(); ++m) {
1024
if (!modList[m].active)
1025
continue;
1026
1027
SetActiveMod(m);
1028
if (LoadFile("Data/Game/Game.xml", &info)) {
1029
tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
1030
1031
char *xmlData = new char[info.fileSize + 1];
1032
FileRead(xmlData, info.fileSize);
1033
xmlData[info.fileSize] = 0;
1034
1035
bool success = doc->Parse(xmlData) == tinyxml2::XML_SUCCESS;
1036
1037
if (success) {
1038
const tinyxml2::XMLElement *gameElement = FirstXMLChildElement(doc, nullptr, "game");
1039
const char *elementNames[] = { "presentationStages", "regularStages", "bonusStages", "specialStages" };
1040
1041
for (int l = 0; l < STAGELIST_MAX; ++l) {
1042
const tinyxml2::XMLElement *listElement = FirstXMLChildElement(doc, gameElement, elementNames[l]);
1043
if (listElement) {
1044
const tinyxml2::XMLElement *stgElement = FirstXMLChildElement(doc, listElement, "stage");
1045
if (stgElement) {
1046
do {
1047
const tinyxml2::XMLAttribute *nameAttr = FindXMLAttribute(stgElement, "name");
1048
const char *stgName = "unknownStage";
1049
if (nameAttr)
1050
stgName = GetXMLAttributeValueString(nameAttr);
1051
1052
const tinyxml2::XMLAttribute *folderAttr = FindXMLAttribute(stgElement, "folder");
1053
const char *stgFolder = "unknownStageFolder";
1054
if (folderAttr)
1055
stgFolder = GetXMLAttributeValueString(folderAttr);
1056
1057
const tinyxml2::XMLAttribute *idAttr = FindXMLAttribute(stgElement, "id");
1058
const char *stgID = "unknownStageID";
1059
if (idAttr)
1060
stgID = GetXMLAttributeValueString(idAttr);
1061
1062
const tinyxml2::XMLAttribute *highlightAttr = FindXMLAttribute(stgElement, "highlight");
1063
bool stgHighlighted = false;
1064
if (highlightAttr)
1065
stgHighlighted = GetXMLAttributeValueBool(highlightAttr);
1066
1067
if (menu) {
1068
if (listNo == 3 || listNo == 4) {
1069
if ((listNo == 4 && l == 2) || (listNo == 3 && l == 3)) {
1070
AddTextMenuEntry(menu, stgName);
1071
menu->entryHighlight[menu->rowCount - 1] = stgHighlighted;
1072
}
1073
}
1074
else if (listNo == l + 1) {
1075
AddTextMenuEntry(menu, stgName);
1076
menu->entryHighlight[menu->rowCount - 1] = stgHighlighted;
1077
}
1078
}
1079
else {
1080
StrCopy(stageList[l][stageListCount[l]].name, stgName);
1081
StrCopy(stageList[l][stageListCount[l]].folder, stgFolder);
1082
StrCopy(stageList[l][stageListCount[l]].id, stgID);
1083
stageList[l][stageListCount[l]].highlighted = stgHighlighted;
1084
1085
stageListCount[l]++;
1086
}
1087
1088
} while ((stgElement = NextXMLSiblingElement(doc, stgElement, "stage")));
1089
}
1090
}
1091
}
1092
}
1093
else {
1094
PrintLog("Failed to parse game.xml File!");
1095
}
1096
1097
delete[] xmlData;
1098
delete doc;
1099
1100
CloseFile();
1101
}
1102
}
1103
SetActiveMod(-1);
1104
}
1105
#endif
1106
1107
bool RetroEngine::LoadGameConfig(const char *filePath)
1108
{
1109
FileInfo info;
1110
byte fileBuffer = 0;
1111
byte fileBuffer2 = 0;
1112
char strBuffer[0x40];
1113
StrCopy(gameWindowText, "Retro-Engine"); // this is the default window name
1114
1115
globalVariablesCount = 0;
1116
#if RETRO_USE_MOD_LOADER
1117
playerCount = 0;
1118
#endif
1119
1120
bool loaded = LoadFile(filePath, &info);
1121
if (loaded) {
1122
FileRead(&fileBuffer, 1);
1123
FileRead(gameWindowText, fileBuffer);
1124
gameWindowText[fileBuffer] = 0;
1125
1126
FileRead(&fileBuffer, 1);
1127
FileRead(gameDescriptionText, fileBuffer);
1128
gameDescriptionText[fileBuffer] = 0;
1129
1130
byte buf[3];
1131
for (int c = 0; c < 0x60; ++c) {
1132
FileRead(buf, 3);
1133
SetPaletteEntry(-1, c, buf[0], buf[1], buf[2]);
1134
}
1135
1136
// Read Obect Names
1137
byte objectCount = 0;
1138
FileRead(&objectCount, 1);
1139
for (byte o = 0; o < objectCount; ++o) {
1140
FileRead(&fileBuffer, 1);
1141
FileRead(&strBuffer, fileBuffer);
1142
}
1143
1144
// Read Script Paths
1145
for (byte s = 0; s < objectCount; ++s) {
1146
FileRead(&fileBuffer, 1);
1147
FileRead(&strBuffer, fileBuffer);
1148
}
1149
1150
byte varCount = 0;
1151
FileRead(&varCount, 1);
1152
globalVariablesCount = varCount;
1153
for (int v = 0; v < varCount; ++v) {
1154
// Read Variable Name
1155
FileRead(&fileBuffer, 1);
1156
FileRead(&globalVariableNames[v], fileBuffer);
1157
globalVariableNames[v][fileBuffer] = 0;
1158
1159
// Read Variable Value
1160
FileRead(&fileBuffer2, 1);
1161
globalVariables[v] = fileBuffer2 << 0;
1162
FileRead(&fileBuffer2, 1);
1163
globalVariables[v] += fileBuffer2 << 8;
1164
FileRead(&fileBuffer2, 1);
1165
globalVariables[v] += fileBuffer2 << 16;
1166
FileRead(&fileBuffer2, 1);
1167
globalVariables[v] += fileBuffer2 << 24;
1168
}
1169
1170
// Read SFX
1171
byte globalSFXCount = 0;
1172
FileRead(&globalSFXCount, 1);
1173
for (int s = 0; s < globalSFXCount; ++s) { // SFX Names
1174
FileRead(&fileBuffer, 1);
1175
FileRead(&strBuffer, fileBuffer);
1176
strBuffer[fileBuffer] = 0;
1177
}
1178
for (byte s = 0; s < globalSFXCount; ++s) { // SFX Paths
1179
FileRead(&fileBuffer, 1);
1180
FileRead(&strBuffer, fileBuffer);
1181
strBuffer[fileBuffer] = 0;
1182
}
1183
1184
// Read Player Names
1185
byte plrCount = 0;
1186
FileRead(&plrCount, 1);
1187
#if RETRO_USE_MOD_LOADER
1188
// Check for max player limit
1189
if (plrCount >= PLAYER_COUNT) {
1190
PrintLog("WARNING: GameConfig attempted to exceed the player limit, truncating to supported limit");
1191
plrCount = PLAYER_COUNT;
1192
}
1193
#endif
1194
for (byte p = 0; p < plrCount; ++p) {
1195
FileRead(&fileBuffer, 1);
1196
FileRead(&strBuffer, fileBuffer);
1197
1198
// needed for PlayerName[] stuff in scripts
1199
#if !RETRO_USE_ORIGINAL_CODE
1200
strBuffer[fileBuffer] = 0;
1201
StrCopy(playerNames[p], strBuffer);
1202
playerCount++;
1203
#endif
1204
}
1205
1206
for (byte c = 0; c < 4; ++c) {
1207
// Special Stages are stored as cat 2 in file, but cat 3 in game :(
1208
int cat = c;
1209
if (c == 2)
1210
cat = 3;
1211
else if (c == 3)
1212
cat = 2;
1213
stageListCount[cat] = 0;
1214
FileRead(&fileBuffer, 1);
1215
stageListCount[cat] = fileBuffer;
1216
for (byte s = 0; s < stageListCount[cat]; ++s) {
1217
1218
// Read Stage Folder
1219
FileRead(&fileBuffer, 1);
1220
FileRead(&stageList[cat][s].folder, fileBuffer);
1221
stageList[cat][s].folder[fileBuffer] = 0;
1222
1223
// Read Stage ID
1224
FileRead(&fileBuffer, 1);
1225
FileRead(&stageList[cat][s].id, fileBuffer);
1226
stageList[cat][s].id[fileBuffer] = 0;
1227
1228
// Read Stage Name
1229
FileRead(&fileBuffer, 1);
1230
FileRead(&stageList[cat][s].name, fileBuffer);
1231
stageList[cat][s].name[fileBuffer] = 0;
1232
1233
// Read Stage Mode
1234
FileRead(&fileBuffer, 1);
1235
stageList[cat][s].highlighted = fileBuffer;
1236
}
1237
}
1238
1239
CloseFile();
1240
1241
#if RETRO_USE_MOD_LOADER
1242
LoadXMLWindowText();
1243
LoadXMLVariables();
1244
LoadXMLPalettes();
1245
LoadXMLObjects();
1246
LoadXMLPlayers(NULL);
1247
LoadXMLStages(NULL, 0);
1248
1249
SetGlobalVariableByName("options.devMenuFlag", devMenu ? 1 : 0);
1250
SetGlobalVariableByName("engine.standalone", 1);
1251
#endif
1252
}
1253
1254
#if RETRO_REV03
1255
SetGlobalVariableByName("game.hasPlusDLC", !RSDK_AUTOBUILD);
1256
#endif
1257
1258
// These need to be set every time its reloaded
1259
nativeFunctionCount = 0;
1260
AddNativeFunction("SetAchievement", SetAchievement);
1261
AddNativeFunction("SetLeaderboard", SetLeaderboard);
1262
#if RETRO_USE_HAPTICS
1263
AddNativeFunction("HapticEffect", HapticEffect);
1264
#endif
1265
AddNativeFunction("Connect2PVS", Connect2PVS);
1266
AddNativeFunction("Disconnect2PVS", Disconnect2PVS);
1267
AddNativeFunction("SendEntity", SendEntity);
1268
AddNativeFunction("SendValue", SendValue);
1269
AddNativeFunction("ReceiveEntity", ReceiveEntity);
1270
AddNativeFunction("ReceiveValue", ReceiveValue);
1271
AddNativeFunction("TransmitGlobal", TransmitGlobal);
1272
AddNativeFunction("ShowPromoPopup", ShowPromoPopup);
1273
1274
// Introduced in the Sega Forever versions of S1 (3.9.0) and S2 (1.7.0)
1275
AddNativeFunction("NativePlayerWaitingAds", NativePlayerWaitingAds);
1276
AddNativeFunction("NativeWaterPlayerWaitingAds", NativeWaterPlayerWaitingAds);
1277
1278
#if RETRO_REV03
1279
AddNativeFunction("NotifyCallback", NotifyCallback);
1280
#endif
1281
1282
#if RETRO_USE_NETWORKING
1283
AddNativeFunction("SetNetworkGameName", SetNetworkGameName);
1284
#endif
1285
1286
#if RETRO_USE_MOD_LOADER
1287
AddNativeFunction("ExitGame", ExitGame);
1288
AddNativeFunction("FileExists", FileExists);
1289
AddNativeFunction("OpenModMenu", OpenModMenu); // Opens the dev menu-based mod menu incase you cant be bothered or smth
1290
AddNativeFunction("AddAchievement", AddGameAchievement);
1291
AddNativeFunction("SetAchievementDescription", SetAchievementDescription);
1292
AddNativeFunction("ClearAchievements", ClearAchievements);
1293
AddNativeFunction("GetAchievementCount", GetAchievementCount);
1294
AddNativeFunction("GetAchievement", GetAchievement);
1295
AddNativeFunction("GetAchievementName", GetAchievementName);
1296
AddNativeFunction("GetAchievementDescription", GetAchievementDescription);
1297
AddNativeFunction("GetScreenWidth", GetScreenWidth);
1298
AddNativeFunction("SetScreenWidth", SetScreenWidth);
1299
AddNativeFunction("GetWindowScale", GetWindowScale);
1300
AddNativeFunction("SetWindowScale", SetWindowScale);
1301
AddNativeFunction("GetWindowScaleMode", GetWindowScaleMode);
1302
AddNativeFunction("SetWindowScaleMode", SetWindowScaleMode);
1303
AddNativeFunction("GetWindowFullScreen", GetWindowFullScreen);
1304
AddNativeFunction("SetWindowFullScreen", SetWindowFullScreen);
1305
AddNativeFunction("GetWindowBorderless", GetWindowBorderless);
1306
AddNativeFunction("SetWindowBorderless", SetWindowBorderless);
1307
AddNativeFunction("GetWindowVSync", GetWindowVSync);
1308
AddNativeFunction("SetWindowVSync", SetWindowVSync);
1309
AddNativeFunction("ApplyWindowChanges", ApplyWindowChanges); // Refresh window after changing window options
1310
AddNativeFunction("GetModCount", GetModCount);
1311
AddNativeFunction("GetModName", GetModName);
1312
AddNativeFunction("GetModDescription", GetModDescription);
1313
AddNativeFunction("GetModAuthor", GetModAuthor);
1314
AddNativeFunction("GetModVersion", GetModVersion);
1315
AddNativeFunction("GetModActive", GetModActive);
1316
AddNativeFunction("SetModActive", SetModActive);
1317
AddNativeFunction("MoveMod", MoveMod);
1318
AddNativeFunction("RefreshEngine", RefreshEngine); // Reload engine after changing mod status
1319
#endif
1320
1321
#if !RETRO_USE_ORIGINAL_CODE
1322
if (strlen(Engine.startSceneFolder) && strlen(Engine.startSceneID)) {
1323
SceneInfo *scene = &stageList[STAGELIST_BONUS][0xFE]; // slot 0xFF is used for "none" startStage
1324
strcpy(scene->name, "_RSDK_SCENE");
1325
strcpy(scene->folder, Engine.startSceneFolder);
1326
strcpy(scene->id, Engine.startSceneID);
1327
startList_Game = STAGELIST_BONUS;
1328
startStage_Game = 0xFE;
1329
}
1330
1331
#if RETRO_REV03
1332
Engine.usingOrigins = GetGlobalVariableID("game.playMode") != 0xFF;
1333
#endif
1334
#endif
1335
1336
return loaded;
1337
}
1338
1339