mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-28 15:03:01 +00:00
Allow coop mods to use the backup save slot independently of the normal one
This commit is contained in:
parent
653ab58a5d
commit
3db42f1700
16 changed files with 156 additions and 462 deletions
|
@ -269,6 +269,10 @@ function SOUND_ARG_LOAD(bank, soundID, priority, flags)
|
|||
return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING
|
||||
end
|
||||
|
||||
-------------
|
||||
-- courses --
|
||||
-------------
|
||||
|
||||
--- @type integer
|
||||
COURSE_NONE = 0
|
||||
--- @type integer
|
||||
|
@ -321,3 +325,11 @@ COURSE_WMOTR = 23
|
|||
COURSE_SA = 24
|
||||
--- @type integer
|
||||
COURSE_CAKE_END = 25
|
||||
--- @type integer
|
||||
COURSE_END = 26
|
||||
--- @type integer
|
||||
COURSE_MAX = 25
|
||||
--- @type integer
|
||||
COURSE_COUNT = 25
|
||||
--- @type integer
|
||||
COURSE_MIN = 1
|
|
@ -271,6 +271,10 @@ function SOUND_ARG_LOAD(bank, soundID, priority, flags)
|
|||
return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING
|
||||
end
|
||||
|
||||
-------------
|
||||
-- courses --
|
||||
-------------
|
||||
|
||||
--- @type integer
|
||||
COURSE_NONE = 0
|
||||
--- @type integer
|
||||
|
@ -323,6 +327,14 @@ COURSE_WMOTR = 23
|
|||
COURSE_SA = 24
|
||||
--- @type integer
|
||||
COURSE_CAKE_END = 25
|
||||
--- @type integer
|
||||
COURSE_END = 26
|
||||
--- @type integer
|
||||
COURSE_MAX = 25
|
||||
--- @type integer
|
||||
COURSE_COUNT = 25
|
||||
--- @type integer
|
||||
COURSE_MIN = 1
|
||||
|
||||
--- @class BehaviorId
|
||||
|
||||
|
|
|
@ -7107,6 +7107,12 @@ function movtexqc_register(name, level, area, type)
|
|||
-- ...
|
||||
end
|
||||
|
||||
--- @param usingBackupSlot boolean
|
||||
--- @return nil
|
||||
function save_file_set_using_backup_slot(usingBackupSlot)
|
||||
-- ...
|
||||
end
|
||||
|
||||
--- @param index integer
|
||||
--- @param value integer
|
||||
--- @return nil
|
||||
|
|
|
@ -34,63 +34,6 @@ gLevelValues = {}
|
|||
--- @type BehaviorValues
|
||||
gBehaviorValues = {}
|
||||
|
||||
---------------
|
||||
-- constants --
|
||||
---------------
|
||||
|
||||
--- @type integer
|
||||
COURSE_NONE = 0
|
||||
--- @type integer
|
||||
COURSE_BOB = 1
|
||||
--- @type integer
|
||||
COURSE_WF = 2
|
||||
--- @type integer
|
||||
COURSE_JRB = 3
|
||||
--- @type integer
|
||||
COURSE_CCM = 4
|
||||
--- @type integer
|
||||
COURSE_BBH = 5
|
||||
--- @type integer
|
||||
COURSE_HMC = 6
|
||||
--- @type integer
|
||||
COURSE_LLL = 7
|
||||
--- @type integer
|
||||
COURSE_SSL = 8
|
||||
--- @type integer
|
||||
COURSE_DDD = 9
|
||||
--- @type integer
|
||||
COURSE_SL = 10
|
||||
--- @type integer
|
||||
COURSE_WDW = 11
|
||||
--- @type integer
|
||||
COURSE_TTM = 12
|
||||
--- @type integer
|
||||
COURSE_THI = 13
|
||||
--- @type integer
|
||||
COURSE_TTC = 14
|
||||
--- @type integer
|
||||
COURSE_RR = 15
|
||||
--- @type integer
|
||||
COURSE_BITDW = 16
|
||||
--- @type integer
|
||||
COURSE_BITFS = 17
|
||||
--- @type integer
|
||||
COURSE_BITS = 18
|
||||
--- @type integer
|
||||
COURSE_PSS = 19
|
||||
--- @type integer
|
||||
COURSE_COTMC = 20
|
||||
--- @type integer
|
||||
COURSE_TOTWC = 21
|
||||
--- @type integer
|
||||
COURSE_VCUTM = 22
|
||||
--- @type integer
|
||||
COURSE_WMOTR = 23
|
||||
--- @type integer
|
||||
COURSE_SA = 24
|
||||
--- @type integer
|
||||
COURSE_CAKE_END = 25
|
||||
|
||||
-----------
|
||||
-- hooks --
|
||||
-----------
|
||||
|
|
|
@ -45,7 +45,7 @@ void DynOS_Lvl_Activate(s32 modIndex, const SysPath &aPackFolder, const char *aL
|
|||
sDynosCustomLevelScripts.Add({ levelName, _Node });
|
||||
|
||||
// Override vanilla script
|
||||
auto& newScripts = _Node->mLevelScripts; // DO NOT COMMIT
|
||||
auto& newScripts = _Node->mLevelScripts;
|
||||
auto& newScriptNode = newScripts[newScripts.Count() - 1];
|
||||
const void* originalScript = DynOS_Builtin_ScriptPtr_GetFromName(newScriptNode->mName.begin());
|
||||
if (originalScript == NULL) {
|
||||
|
|
|
@ -1321,6 +1321,7 @@
|
|||
- [hud_hide](#hud_hide)
|
||||
- [hud_show](#hud_show)
|
||||
- [movtexqc_register](#movtexqc_register)
|
||||
- [save_file_set_using_backup_slot](#save_file_set_using_backup_slot)
|
||||
- [set_environment_region](#set_environment_region)
|
||||
- [warp_exit_level](#warp_exit_level)
|
||||
- [warp_restart_level](#warp_restart_level)
|
||||
|
@ -24841,6 +24842,26 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
|
|||
|
||||
<br />
|
||||
|
||||
## [save_file_set_using_backup_slot](#save_file_set_using_backup_slot)
|
||||
|
||||
### Lua Example
|
||||
`save_file_set_using_backup_slot(usingBackupSlot)`
|
||||
|
||||
### Parameters
|
||||
| Field | Type |
|
||||
| ----- | ---- |
|
||||
| usingBackupSlot | `boolean` |
|
||||
|
||||
### Returns
|
||||
- None
|
||||
|
||||
### C Prototype
|
||||
`void save_file_set_using_backup_slot(bool usingBackupSlot);`
|
||||
|
||||
[:arrow_up_small:](#)
|
||||
|
||||
<br />
|
||||
|
||||
## [set_environment_region](#set_environment_region)
|
||||
|
||||
### Lua Example
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
#include "pc/ini.h"
|
||||
#include "pc/network/network.h"
|
||||
|
||||
// note: force-disable TEXTSAVES until it's synchronized
|
||||
#undef TEXTSAVES
|
||||
|
||||
#define MENU_DATA_MAGIC 0x4849
|
||||
#define SAVE_FILE_MAGIC 0x4441
|
||||
|
||||
|
@ -36,6 +33,7 @@ u8 gGotFileCoinHiScore = FALSE;
|
|||
u8 gCurrCourseStarFlags = 0;
|
||||
|
||||
u8 gSpecialTripleJump = FALSE;
|
||||
u8 gSaveFileUsingBackupSlot = FALSE;
|
||||
|
||||
#define STUB_LEVEL(_0, _1, courseenum, _3, _4, _5, _6, _7, _8) courseenum,
|
||||
#define DEFINE_LEVEL(_0, _1, courseenum, _3, _4, _5, _6, _7, _8, _9, _10) courseenum,
|
||||
|
@ -49,12 +47,6 @@ s8 gLevelToCourseNumTable[] = {
|
|||
STATIC_ASSERT(ARRAY_COUNT(gLevelToCourseNumTable) == LEVEL_COUNT - 1,
|
||||
"change this array if you are adding levels");
|
||||
|
||||
#ifdef TEXTSAVES
|
||||
|
||||
#include "text_save.inc.h"
|
||||
|
||||
#endif
|
||||
|
||||
// This was probably used to set progress to 100% for debugging, but
|
||||
// it was removed from the release ROM.
|
||||
static void stub_save_file_1(void) {
|
||||
|
@ -354,24 +346,14 @@ void save_file_do_save(s32 fileIndex, s8 forceSave) {
|
|||
if (fileIndex < 0 || fileIndex >= NUM_SAVE_FILES)
|
||||
return;
|
||||
|
||||
#ifdef TEXTSAVES
|
||||
if (gSaveFileModified && gNetworkType != NT_CLIENT) {
|
||||
// Write to text file
|
||||
write_text_save(fileIndex);
|
||||
gSaveFileModified = FALSE;
|
||||
gMainMenuDataModified = FALSE;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gSaveFileModified) {
|
||||
// Compute checksum
|
||||
add_save_block_signature(&gSaveBuffer.files[fileIndex][0],
|
||||
sizeof(gSaveBuffer.files[fileIndex][0]), SAVE_FILE_MAGIC);
|
||||
|
||||
// Copy to backup slot
|
||||
bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1],
|
||||
sizeof(gSaveBuffer.files[fileIndex][1]));
|
||||
//bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1],
|
||||
//sizeof(gSaveBuffer.files[fileIndex][1]));
|
||||
|
||||
// Write to EEPROM
|
||||
write_eeprom_savefile(fileIndex, 0, 2);
|
||||
|
@ -387,6 +369,7 @@ void save_file_erase(s32 fileIndex) {
|
|||
|
||||
touch_high_score_ages(fileIndex);
|
||||
bzero(&gSaveBuffer.files[fileIndex][0], sizeof(gSaveBuffer.files[fileIndex][0]));
|
||||
bzero(&gSaveBuffer.files[fileIndex][1], sizeof(gSaveBuffer.files[fileIndex][1]));
|
||||
|
||||
gSaveFileModified = TRUE;
|
||||
save_file_do_save(fileIndex, TRUE);
|
||||
|
@ -400,24 +383,15 @@ BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex) {
|
|||
touch_high_score_ages(destFileIndex);
|
||||
bcopy(&gSaveBuffer.files[srcFileIndex][0], &gSaveBuffer.files[destFileIndex][0],
|
||||
sizeof(gSaveBuffer.files[destFileIndex][0]));
|
||||
bcopy(&gSaveBuffer.files[srcFileIndex][1], &gSaveBuffer.files[destFileIndex][1],
|
||||
sizeof(gSaveBuffer.files[destFileIndex][1]));
|
||||
|
||||
gSaveFileModified = TRUE;
|
||||
save_file_do_save(destFileIndex, TRUE);
|
||||
}
|
||||
|
||||
#ifdef TEXTSAVES
|
||||
static void save_file_load_textsaves(void) {
|
||||
for (s32 file = 0; file < NUM_SAVE_FILES; file++) {
|
||||
read_text_save(file);
|
||||
}
|
||||
gSaveFileModified = TRUE;
|
||||
gMainMenuDataModified = TRUE;
|
||||
stub_save_file_1();
|
||||
}
|
||||
#endif
|
||||
|
||||
void save_file_load_all(UNUSED u8 reload) {
|
||||
s32 file;
|
||||
//s32 file;
|
||||
|
||||
gMainMenuDataModified = FALSE;
|
||||
gSaveFileModified = FALSE;
|
||||
|
@ -430,6 +404,7 @@ void save_file_load_all(UNUSED u8 reload) {
|
|||
save_file_bswap(&gSaveBuffer);
|
||||
|
||||
// Verify the main menu data and create a backup copy if only one of the slots is valid.
|
||||
/* Disable this so the 'backup' slot can be used
|
||||
s32 validSlots;
|
||||
validSlots = verify_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC);
|
||||
validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]),MENU_DATA_MAGIC) << 1;
|
||||
|
@ -461,6 +436,7 @@ void save_file_load_all(UNUSED u8 reload) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
stub_save_file_1();
|
||||
}
|
||||
|
||||
|
@ -471,11 +447,11 @@ void save_file_load_all(UNUSED u8 reload) {
|
|||
*/
|
||||
void save_file_reload(void) {
|
||||
// Copy save file data from backup
|
||||
bcopy(&gSaveBuffer.files[gCurrSaveFileNum - 1][1], &gSaveBuffer.files[gCurrSaveFileNum - 1][0],
|
||||
/*bcopy(&gSaveBuffer.files[gCurrSaveFileNum - 1][1], &gSaveBuffer.files[gCurrSaveFileNum - 1][0],
|
||||
sizeof(gSaveBuffer.files[gCurrSaveFileNum - 1][0]));
|
||||
|
||||
// Copy main menu data from backup
|
||||
bcopy(&gSaveBuffer.menuData[1], &gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]));
|
||||
bcopy(&gSaveBuffer.menuData[1], &gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]));*/
|
||||
|
||||
gMainMenuDataModified = FALSE;
|
||||
gSaveFileModified = FALSE;
|
||||
|
@ -509,7 +485,7 @@ void save_file_collect_star_or_key(s16 coinScore, s16 starIndex, u8 fromNetwork)
|
|||
}
|
||||
|
||||
if (coinScore > save_file_get_course_coin_score(fileIndex, courseIndex)) {
|
||||
gSaveBuffer.files[fileIndex][0].courseCoinScores[courseIndex] = coinScore;
|
||||
gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].courseCoinScores[courseIndex] = coinScore;
|
||||
touch_coin_score_age(fileIndex, courseIndex);
|
||||
|
||||
gGotFileCoinHiScore = TRUE;
|
||||
|
@ -602,14 +578,14 @@ void save_file_set_flags(u32 flags) {
|
|||
flags &= ~(SAVE_FLAG_CAP_ON_GROUND | SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_MR_BLIZZARD | SAVE_FLAG_CAP_ON_UKIKI);
|
||||
if (flags == 0) { return; }
|
||||
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags |= (flags | SAVE_FLAG_FILE_EXISTS);
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags |= (flags | SAVE_FLAG_FILE_EXISTS);
|
||||
gSaveFileModified = TRUE;
|
||||
network_send_save_set_flag(gCurrSaveFileNum - 1, 0, 0, (flags | SAVE_FLAG_FILE_EXISTS));
|
||||
}
|
||||
|
||||
void save_file_clear_flags(u32 flags) {
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags &= ~flags;
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags |= SAVE_FLAG_FILE_EXISTS;
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags &= ~flags;
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags |= SAVE_FLAG_FILE_EXISTS;
|
||||
gSaveFileModified = TRUE;
|
||||
}
|
||||
|
||||
|
@ -617,7 +593,7 @@ u32 save_file_get_flags(void) {
|
|||
if (gCurrCreditsEntry != NULL || gCurrDemoInput != NULL) {
|
||||
return 0;
|
||||
}
|
||||
return gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags;
|
||||
return gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -628,9 +604,9 @@ u32 save_file_get_star_flags(s32 fileIndex, s32 courseIndex) {
|
|||
u32 starFlags;
|
||||
|
||||
if (courseIndex == -1) {
|
||||
starFlags = SAVE_FLAG_TO_STAR_FLAG(gSaveBuffer.files[fileIndex][0].flags);
|
||||
starFlags = SAVE_FLAG_TO_STAR_FLAG(gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].flags);
|
||||
} else {
|
||||
starFlags = gSaveBuffer.files[fileIndex][0].courseStars[courseIndex] & 0x7F;
|
||||
starFlags = gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].courseStars[courseIndex] & 0x7F;
|
||||
}
|
||||
|
||||
return starFlags;
|
||||
|
@ -642,40 +618,40 @@ u32 save_file_get_star_flags(s32 fileIndex, s32 courseIndex) {
|
|||
*/
|
||||
void save_file_set_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlags) {
|
||||
if (courseIndex == -1) {
|
||||
gSaveBuffer.files[fileIndex][0].flags |= STAR_FLAG_TO_SAVE_FLAG(starFlags);
|
||||
gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].flags |= STAR_FLAG_TO_SAVE_FLAG(starFlags);
|
||||
network_send_save_set_flag(fileIndex, courseIndex, 0, (STAR_FLAG_TO_SAVE_FLAG(starFlags) | SAVE_FLAG_FILE_EXISTS));
|
||||
} else {
|
||||
gSaveBuffer.files[fileIndex][0].courseStars[courseIndex] |= starFlags;
|
||||
gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].courseStars[courseIndex] |= starFlags;
|
||||
network_send_save_set_flag(fileIndex, courseIndex, starFlags, SAVE_FLAG_FILE_EXISTS);
|
||||
}
|
||||
|
||||
gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS;
|
||||
gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].flags |= SAVE_FLAG_FILE_EXISTS;
|
||||
gSaveFileModified = TRUE;
|
||||
}
|
||||
|
||||
s32 save_file_get_course_coin_score(s32 fileIndex, s32 courseIndex) {
|
||||
return gSaveBuffer.files[fileIndex][0].courseCoinScores[courseIndex];
|
||||
return gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].courseCoinScores[courseIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return TRUE if the cannon is unlocked in the current course.
|
||||
*/
|
||||
s32 save_file_is_cannon_unlocked(void) {
|
||||
return (gSaveBuffer.files[gCurrSaveFileNum - 1][0].courseStars[gCurrCourseNum] & 0x80) != 0;
|
||||
return (gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].courseStars[gCurrCourseNum] & 0x80) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cannon status to unlocked in the current course.
|
||||
*/
|
||||
void save_file_set_cannon_unlocked(void) {
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][0].courseStars[gCurrCourseNum] |= 0x80;
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags |= SAVE_FLAG_FILE_EXISTS;
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].courseStars[gCurrCourseNum] |= 0x80;
|
||||
gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags |= SAVE_FLAG_FILE_EXISTS;
|
||||
gSaveFileModified = TRUE;
|
||||
network_send_save_set_flag(gCurrSaveFileNum - 1, gCurrCourseNum, 0x80, SAVE_FLAG_FILE_EXISTS);
|
||||
}
|
||||
|
||||
void save_file_set_cap_pos(s16 x, s16 y, s16 z) {
|
||||
struct SaveFile *saveFile = &gSaveBuffer.files[gCurrSaveFileNum - 1][0];
|
||||
struct SaveFile *saveFile = &gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot];
|
||||
|
||||
saveFile->capLevel = gCurrLevelNum;
|
||||
saveFile->capArea = gCurrAreaIndex;
|
||||
|
@ -684,7 +660,7 @@ void save_file_set_cap_pos(s16 x, s16 y, s16 z) {
|
|||
}
|
||||
|
||||
s32 save_file_get_cap_pos(Vec3s capPos) {
|
||||
struct SaveFile *saveFile = &gSaveBuffer.files[gCurrSaveFileNum - 1][0];
|
||||
struct SaveFile *saveFile = &gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot];
|
||||
s32 flags = save_file_get_flags();
|
||||
|
||||
if (saveFile->capLevel == gCurrLevelNum && saveFile->capArea == gCurrAreaIndex
|
||||
|
@ -709,7 +685,7 @@ u16 save_file_get_sound_mode(void) {
|
|||
|
||||
void save_file_move_cap_to_default_location(void) {
|
||||
if (save_file_get_flags() & SAVE_FLAG_CAP_ON_GROUND) {
|
||||
switch (gSaveBuffer.files[gCurrSaveFileNum - 1][0].capLevel) {
|
||||
switch (gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].capLevel) {
|
||||
case LEVEL_SSL:
|
||||
save_file_set_flags(SAVE_FLAG_CAP_ON_KLEPTO);
|
||||
break;
|
||||
|
|
|
@ -1,334 +0,0 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "course_table.h"
|
||||
#include "pc/ini.h"
|
||||
#include "pc/platform.h"
|
||||
#include "pc/fs/fs.h"
|
||||
|
||||
#define FILENAME_FORMAT "%s/sm64_save_file_%d.sav"
|
||||
#define NUM_COURSES 15
|
||||
#define NUM_BONUS_COURSES 10
|
||||
#define NUM_FLAGS 21
|
||||
#define NUM_CAP_ON 4
|
||||
|
||||
const char *sav_flags[NUM_FLAGS] = {
|
||||
"file_exists", "wing_cap", "metal_cap", "vanish_cap", "key_1", "key_2",
|
||||
"basement_door", "upstairs_door", "ddd_moved_back", "moat_drained",
|
||||
"pps_door", "wf_door", "ccm_door", "jrb_door", "bitdw_door",
|
||||
"bitfs_door", "", "", "", "", "50star_door" // 4 Cap flags are processed in their own section
|
||||
};
|
||||
|
||||
const char *sav_courses[NUM_COURSES] = {
|
||||
"bob", "wf", "jrb", "ccm", "bbh", "hmc", "lll",
|
||||
"ssl", "ddd", "sl", "wdw", "ttm", "thi", "ttc", "rr"
|
||||
};
|
||||
|
||||
const char *sav_bonus_courses[NUM_BONUS_COURSES] = {
|
||||
"bitdw", "bitfs", "bits", "pss", "cotmc",
|
||||
"totwc", "vcutm", "wmotr", "sa", "hub" // hub is Castle Grounds
|
||||
};
|
||||
|
||||
const char *cap_on_types[NUM_CAP_ON] = {
|
||||
"ground", "klepto", "ukiki", "mrblizzard"
|
||||
};
|
||||
|
||||
const char *sound_modes[3] = {
|
||||
"stereo", "mono", "headset"
|
||||
};
|
||||
|
||||
/* Get current timestamp string */
|
||||
static void get_timestamp(char* buffer) {
|
||||
time_t timer;
|
||||
struct tm* tm_info;
|
||||
|
||||
timer = time(NULL);
|
||||
tm_info = localtime(&timer);
|
||||
|
||||
strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
}
|
||||
|
||||
/* Convert 'binary' integer to decimal integer */
|
||||
static u32 bin_to_int(u32 n) {
|
||||
s32 dec = 0, i = 0, rem;
|
||||
while (n != 0) {
|
||||
rem = n % 10;
|
||||
n /= 10;
|
||||
dec += rem * (1 << i);
|
||||
++i;
|
||||
}
|
||||
return dec;
|
||||
}
|
||||
|
||||
/* Convert decimal integer to 'binary' integer */
|
||||
static u32 int_to_bin(u32 n) {
|
||||
s32 bin = 0, rem, i = 1;
|
||||
while (n != 0) {
|
||||
rem = n % 2;
|
||||
n /= 2;
|
||||
bin += rem * i;
|
||||
i *= 10;
|
||||
}
|
||||
return bin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write SaveFile and MainMenuSaveData structs to a text-based savefile
|
||||
*/
|
||||
static s32 write_text_save(s32 fileIndex) {
|
||||
FILE* file;
|
||||
struct SaveFile *savedata;
|
||||
struct MainMenuSaveData *menudata;
|
||||
char filename[SYS_MAX_PATH] = { 0 };
|
||||
char value[64];
|
||||
u32 i, bit, flags, coins, stars, starFlags;
|
||||
|
||||
if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, fs_writepath, fileIndex) < 0)
|
||||
return -1;
|
||||
|
||||
file = fopen(filename, "wt");
|
||||
if (file == NULL) {
|
||||
printf("Savefile '%s' not found!\n", filename);
|
||||
return -1;
|
||||
} else
|
||||
printf("Saving updated progress to '%s'\n", filename);
|
||||
|
||||
fprintf(file, "# Super Mario 64 save file\n");
|
||||
fprintf(file, "# Comment starts with #\n");
|
||||
fprintf(file, "# True = 1, False = 0\n");
|
||||
|
||||
get_timestamp(value);
|
||||
fprintf(file, "# %s\n", value);
|
||||
|
||||
menudata = &gSaveBuffer.menuData[0];
|
||||
fprintf(file, "\n[menu]\n");
|
||||
fprintf(file, "coin_score_age = %d\n", menudata->coinScoreAges[fileIndex]);
|
||||
|
||||
if (menudata->soundMode == 0) {
|
||||
fprintf(file, "sound_mode = %s\n", sound_modes[0]); // stereo
|
||||
}
|
||||
else if (menudata->soundMode == 3) {
|
||||
fprintf(file, "sound_mode = %s\n", sound_modes[1]); // mono
|
||||
}
|
||||
else if (menudata->soundMode == 1) {
|
||||
fprintf(file, "sound_mode = %s\n", sound_modes[2]); // headset
|
||||
}
|
||||
else {
|
||||
printf("Undefined sound mode!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(file, "\n[flags]\n");
|
||||
for (i = 1; i < NUM_FLAGS; i++) {
|
||||
if (strcmp(sav_flags[i], "")) {
|
||||
flags = save_file_get_flags();
|
||||
flags = (flags & (1 << i)); // Get 'star' flag bit
|
||||
flags = (flags) ? 1 : 0;
|
||||
|
||||
fprintf(file, "%s = %d\n", sav_flags[i], flags);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(file, "\n[courses]\n");
|
||||
for (i = 0; i < NUM_COURSES; i++) {
|
||||
stars = save_file_get_star_flags(fileIndex, i);
|
||||
coins = save_file_get_course_coin_score(fileIndex, i);
|
||||
starFlags = int_to_bin(stars); // 63 -> 111111
|
||||
|
||||
fprintf(file, "%s = \"%d, %07d\"\n", sav_courses[i], coins, starFlags);
|
||||
}
|
||||
|
||||
fprintf(file, "\n[bonus]\n");
|
||||
for (i = 0; i < NUM_BONUS_COURSES; i++) {
|
||||
char *format;
|
||||
|
||||
if (i == NUM_BONUS_COURSES-1) {
|
||||
// Process Castle Grounds
|
||||
stars = save_file_get_star_flags(fileIndex, -1);
|
||||
format = "%05d";
|
||||
} else if (i == 3) {
|
||||
// Process Princess's Secret Slide
|
||||
stars = save_file_get_star_flags(fileIndex, 18);
|
||||
format = "%02d";
|
||||
} else {
|
||||
// Process bonus courses
|
||||
stars = save_file_get_star_flags(fileIndex, i+15);
|
||||
format = "%d";
|
||||
}
|
||||
|
||||
starFlags = int_to_bin(stars);
|
||||
if (sprintf(value, format, starFlags) < 0)
|
||||
return -1;
|
||||
fprintf(file, "%s = %s\n", sav_bonus_courses[i], value);
|
||||
}
|
||||
|
||||
fprintf(file, "\n[cap]\n");
|
||||
for (i = 0; i < NUM_CAP_ON; i++) {
|
||||
flags = save_file_get_flags();
|
||||
bit = (1 << (i+16)); // Determine current flag
|
||||
flags = (flags & bit); // Get 'cap' flag bit
|
||||
flags = (flags) ? 1 : 0;
|
||||
if (flags) {
|
||||
fprintf(file, "type = %s\n", cap_on_types[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
savedata = &gSaveBuffer.files[fileIndex][0];
|
||||
switch(savedata->capLevel) {
|
||||
case COURSE_SSL:
|
||||
fprintf(file, "level = %s\n", "ssl");
|
||||
break;
|
||||
case COURSE_SL:
|
||||
fprintf(file, "level = %s\n", "sl");
|
||||
break;
|
||||
case COURSE_TTM:
|
||||
fprintf(file, "level = %s\n", "ttm");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (savedata->capLevel) {
|
||||
fprintf(file, "area = %d\n", savedata->capArea);
|
||||
}
|
||||
|
||||
// Backup is nessecary for saving recent progress after gameover
|
||||
bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1],
|
||||
sizeof(gSaveBuffer.files[fileIndex][1]));
|
||||
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read gSaveBuffer data from a text-based savefile
|
||||
*/
|
||||
static s32 read_text_save(s32 fileIndex) {
|
||||
char filename[SYS_MAX_PATH] = { 0 };
|
||||
const char *value;
|
||||
ini_t *savedata;
|
||||
|
||||
u32 i, flag, coins, stars, starFlags;
|
||||
u32 capArea;
|
||||
|
||||
if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, fs_writepath, fileIndex) < 0)
|
||||
return -1;
|
||||
|
||||
savedata = ini_load(filename);
|
||||
if (savedata == NULL) {
|
||||
return -1;
|
||||
} else {
|
||||
printf("Loading savefile from '%s'\n", filename);
|
||||
}
|
||||
|
||||
ini_sget(savedata, "menu", "coin_score_age", "%d",
|
||||
&gSaveBuffer.menuData[0].coinScoreAges[fileIndex]);
|
||||
|
||||
value = ini_get(savedata, "menu", "sound_mode");
|
||||
if (value) {
|
||||
if (strcmp(value, sound_modes[0]) == 0) {
|
||||
gSaveBuffer.menuData[0].soundMode = 0; // stereo
|
||||
}
|
||||
else if (strcmp(value, sound_modes[1]) == 0) {
|
||||
gSaveBuffer.menuData[0].soundMode = 3; // mono
|
||||
}
|
||||
else if (strcmp(value, sound_modes[2]) == 0) {
|
||||
gSaveBuffer.menuData[0].soundMode = 1; // headset
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Invalid 'menu:sound_mode' flag!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 1; i < NUM_FLAGS; i++) {
|
||||
value = ini_get(savedata, "flags", sav_flags[i]);
|
||||
if (value) {
|
||||
flag = value[0] - '0'; // Flag should be 0 or 1
|
||||
if (flag) {
|
||||
flag = 1 << i; // Flags defined in 'save_file' header
|
||||
gSaveBuffer.files[fileIndex][0].flags |= flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_COURSES; i++) {
|
||||
value = ini_get(savedata, "courses", sav_courses[i]);
|
||||
if (value) {
|
||||
sscanf(value, "%d, %d", &coins, &stars);
|
||||
starFlags = bin_to_int(stars); // 111111 -> 63
|
||||
|
||||
save_file_set_star_flags(fileIndex, i, starFlags);
|
||||
gSaveBuffer.files[fileIndex][0].courseCoinScores[i] = coins;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_BONUS_COURSES; i++) {
|
||||
value = ini_get(savedata, "bonus", sav_bonus_courses[i]);
|
||||
if (value) {
|
||||
sscanf(value, "%d", &stars);
|
||||
starFlags = bin_to_int(stars);
|
||||
|
||||
if (strlen(value) == 5) {
|
||||
// Process Castle Grounds
|
||||
save_file_set_star_flags(fileIndex, -1, starFlags);
|
||||
} else if (strlen(value) == 2) {
|
||||
// Process Princess's Secret Slide
|
||||
save_file_set_star_flags(fileIndex, 18, starFlags);
|
||||
} else {
|
||||
// Process bonus courses
|
||||
save_file_set_star_flags(fileIndex, i+15, starFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CAP_ON; i++) {
|
||||
value = ini_get(savedata, "cap", "type");
|
||||
if (value) {
|
||||
if (!strcmp(value, cap_on_types[i])) {
|
||||
flag = (1 << (16 + i));
|
||||
gSaveBuffer.files[fileIndex][0].flags |= flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = ini_get(savedata, "cap", "level");
|
||||
if (value) {
|
||||
if (strcmp(value, "ssl") == 0) {
|
||||
gSaveBuffer.files[fileIndex][0].capLevel = COURSE_SSL; // ssl
|
||||
}
|
||||
else if (strcmp(value, "sl") == 0) {
|
||||
gSaveBuffer.files[fileIndex][0].capLevel = COURSE_SL; // sl
|
||||
}
|
||||
else if (strcmp(value, "ttm") == 0) {
|
||||
gSaveBuffer.files[fileIndex][0].capLevel = COURSE_TTM; // ttm
|
||||
}
|
||||
else {
|
||||
printf("Invalid 'cap:level' flag!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
value = ini_get(savedata, "cap", "area");
|
||||
if (value) {
|
||||
sscanf(value, "%d", &capArea);
|
||||
if (capArea > 1 && capArea < 2) {
|
||||
printf("Invalid 'cap:area' flag: %d!\n", capArea);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
gSaveBuffer.files[fileIndex][0].capArea = capArea;
|
||||
}
|
||||
}
|
||||
|
||||
// Good, file exists for gSaveBuffer
|
||||
gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS;
|
||||
|
||||
// Backup is nessecary for saving recent progress after gameover
|
||||
bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1],
|
||||
sizeof(gSaveBuffer.files[fileIndex][1]));
|
||||
|
||||
ini_free(savedata);
|
||||
return 0;
|
||||
}
|
|
@ -244,6 +244,9 @@ char gSmluaConstants[] = ""
|
|||
" if flags == nil then flags = 0 end\n"
|
||||
" return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING\n"
|
||||
"end\n"
|
||||
"-------------\n"
|
||||
"-- courses --\n"
|
||||
"-------------\n"
|
||||
"--- @type integer\n"
|
||||
"COURSE_NONE = 0\n"
|
||||
"--- @type integer\n"
|
||||
|
@ -296,6 +299,14 @@ char gSmluaConstants[] = ""
|
|||
"COURSE_SA = 24\n"
|
||||
"--- @type integer\n"
|
||||
"COURSE_CAKE_END = 25\n"
|
||||
"--- @type integer\n"
|
||||
"COURSE_END = 26\n"
|
||||
"--- @type integer\n"
|
||||
"COURSE_MAX = 25\n"
|
||||
"--- @type integer\n"
|
||||
"COURSE_COUNT = 25\n"
|
||||
"--- @type integer\n"
|
||||
"COURSE_MIN = 1\n"
|
||||
"id_bhv1Up = 0\n"
|
||||
"id_bhv1upJumpOnApproach = 1\n"
|
||||
"id_bhv1upRunningAway = 2\n"
|
||||
|
|
|
@ -14771,6 +14771,17 @@ int smlua_func_movtexqc_register(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int smlua_func_save_file_set_using_backup_slot(lua_State* L) {
|
||||
if(!smlua_functions_valid_param_count(L, 1)) { return 0; }
|
||||
|
||||
bool usingBackupSlot = smlua_to_boolean(L, 1);
|
||||
if (!gSmLuaConvertSuccess) { return 0; }
|
||||
|
||||
save_file_set_using_backup_slot(usingBackupSlot);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int smlua_func_set_environment_region(lua_State* L) {
|
||||
if(!smlua_functions_valid_param_count(L, 2)) { return 0; }
|
||||
|
||||
|
@ -16925,6 +16936,7 @@ void smlua_bind_functions_autogen(void) {
|
|||
smlua_bind_function(L, "hud_hide", smlua_func_hud_hide);
|
||||
smlua_bind_function(L, "hud_show", smlua_func_hud_show);
|
||||
smlua_bind_function(L, "movtexqc_register", smlua_func_movtexqc_register);
|
||||
smlua_bind_function(L, "save_file_set_using_backup_slot", smlua_func_save_file_set_using_backup_slot);
|
||||
smlua_bind_function(L, "set_environment_region", smlua_func_set_environment_region);
|
||||
smlua_bind_function(L, "warp_exit_level", smlua_func_warp_exit_level);
|
||||
smlua_bind_function(L, "warp_restart_level", smlua_func_warp_restart_level);
|
||||
|
|
|
@ -87,6 +87,11 @@ s16 get_current_save_file_num(void) {
|
|||
return gCurrSaveFileNum;
|
||||
}
|
||||
|
||||
void save_file_set_using_backup_slot(bool usingBackupSlot) {
|
||||
extern u8 gSaveFileUsingBackupSlot;
|
||||
gSaveFileUsingBackupSlot = usingBackupSlot ? 1 : 0;
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
void movtexqc_register(const char* name, s16 level, s16 area, s16 type) {
|
||||
|
|
|
@ -23,6 +23,7 @@ f32 get_hand_foot_pos_y(struct MarioState* m, u8 index);
|
|||
f32 get_hand_foot_pos_z(struct MarioState* m, u8 index);
|
||||
|
||||
s16 get_current_save_file_num(void);
|
||||
void save_file_set_using_backup_slot(bool usingBackupSlot);
|
||||
|
||||
void movtexqc_register(const char* name, s16 level, s16 area, s16 type);
|
||||
f32 get_environment_region(u8 index);
|
||||
|
|
|
@ -109,6 +109,9 @@ bool network_init(enum NetworkType inNetworkType) {
|
|||
gNetworkType = inNetworkType;
|
||||
|
||||
if (gNetworkType == NT_SERVER) {
|
||||
extern s16 gCurrSaveFileNum;
|
||||
gCurrSaveFileNum = configHostSaveSlot;
|
||||
|
||||
mods_activate(&gLocalMods);
|
||||
smlua_init();
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
extern s16 gCurrSaveFileNum;
|
||||
extern s16 gCurrCourseNum;
|
||||
extern u8 gSaveFileUsingBackupSlot;
|
||||
|
||||
static f32 dist_to_pos(struct Object* o, f32* pos) {
|
||||
f32 x = o->oPosX - pos[0]; x *= x;
|
||||
|
@ -58,6 +59,7 @@ void network_send_collect_star(struct Object* o, s16 coinScore, s16 starIndex) {
|
|||
packet_write(&p, &behaviorId, sizeof(u32));
|
||||
packet_write(&p, &coinScore, sizeof(s16));
|
||||
packet_write(&p, &starIndex, sizeof(s16));
|
||||
packet_write(&p, &gSaveFileUsingBackupSlot, sizeof(u8));
|
||||
|
||||
network_send(&p);
|
||||
}
|
||||
|
@ -71,6 +73,7 @@ void network_receive_collect_star(struct Packet* p) {
|
|||
s16 lastStarActNum = gCurrActStarNum;
|
||||
s16 lastLevelNum = gCurrLevelNum;
|
||||
s16 lastAreaIndex = gCurrAreaIndex;
|
||||
u8 lastBackupSlot = gSaveFileUsingBackupSlot;
|
||||
|
||||
packet_read(p, &gCurrSaveFileNum, sizeof(s16));
|
||||
packet_read(p, &gCurrCourseNum, sizeof(s16));
|
||||
|
@ -81,16 +84,13 @@ void network_receive_collect_star(struct Packet* p) {
|
|||
packet_read(p, &behaviorId, sizeof(u32));
|
||||
packet_read(p, &coinScore, sizeof(s16));
|
||||
packet_read(p, &starIndex, sizeof(s16));
|
||||
packet_read(p, &gSaveFileUsingBackupSlot, sizeof(u8));
|
||||
if (gSaveFileUsingBackupSlot != 0) { gSaveFileUsingBackupSlot = 1; }
|
||||
|
||||
const void* behavior = get_behavior_from_id(behaviorId);
|
||||
|
||||
save_file_collect_star_or_key(coinScore, starIndex, 1);
|
||||
|
||||
s32 numStars = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1);
|
||||
for (s32 i = 0; i < MAX_PLAYERS; i++) {
|
||||
gMarioStates[i].numStars = numStars;
|
||||
}
|
||||
|
||||
struct NetworkPlayer* np = gNetworkPlayerLocal;
|
||||
bool levelAreaMismatch = ((np == NULL)
|
||||
|| np->currCourseNum != gCurrCourseNum
|
||||
|
@ -103,6 +103,12 @@ void network_receive_collect_star(struct Packet* p) {
|
|||
gCurrActStarNum = lastStarActNum;
|
||||
gCurrLevelNum = lastLevelNum;
|
||||
gCurrAreaIndex = lastAreaIndex;
|
||||
gSaveFileUsingBackupSlot = lastBackupSlot;
|
||||
|
||||
s32 numStars = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1);
|
||||
for (s32 i = 0; i < MAX_PLAYERS; i++) {
|
||||
gMarioStates[i].numStars = numStars;
|
||||
}
|
||||
|
||||
if (!levelAreaMismatch) {
|
||||
struct Object* star = find_nearest_star(behavior, pos, 500);
|
||||
|
|
|
@ -3,18 +3,28 @@
|
|||
#include "game/save_file.h"
|
||||
#include "pc/debuglog.h"
|
||||
|
||||
extern u8 gSaveFileUsingBackupSlot;
|
||||
|
||||
void network_send_save_file(s32 fileIndex) {
|
||||
if (gNetworkPlayerServer == NULL) { return; }
|
||||
SOFT_ASSERT(gNetworkType == NT_CLIENT);
|
||||
struct Packet p = { 0 };
|
||||
packet_init(&p, PACKET_SAVE_FILE, true, PLMT_NONE);
|
||||
packet_write(&p, &fileIndex, sizeof(s32));
|
||||
packet_write(&p, &gSaveFileUsingBackupSlot, sizeof(u8));
|
||||
network_send_to(gNetworkPlayerServer->localIndex, &p);
|
||||
}
|
||||
|
||||
void network_receive_save_file(struct Packet* p) {
|
||||
if (gNetworkType != NT_SERVER) { return; }
|
||||
s32 fileIndex = 0;
|
||||
u8 lastBackupSlot = gSaveFileUsingBackupSlot;
|
||||
packet_read(p, &fileIndex, sizeof(s32));
|
||||
packet_read(p, &gSaveFileUsingBackupSlot, sizeof(u8));
|
||||
|
||||
if (gSaveFileUsingBackupSlot != 0) { gSaveFileUsingBackupSlot = 1; }
|
||||
|
||||
save_file_do_save(fileIndex, FALSE);
|
||||
|
||||
gSaveFileUsingBackupSlot = lastBackupSlot;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "buffers/buffers.h"
|
||||
#include "pc/debuglog.h"
|
||||
|
||||
extern u8 gSaveFileUsingBackupSlot;
|
||||
|
||||
void network_send_save_set_flag(s32 fileIndex, s32 courseIndex, u8 courseStars, u32 flags) {
|
||||
struct Packet p = { 0 };
|
||||
packet_init(&p, PACKET_SAVE_SET_FLAG, true, PLMT_NONE);
|
||||
|
@ -11,6 +13,7 @@ void network_send_save_set_flag(s32 fileIndex, s32 courseIndex, u8 courseStars,
|
|||
packet_write(&p, &courseIndex, sizeof(s32));
|
||||
packet_write(&p, &courseStars, sizeof(u8));
|
||||
packet_write(&p, &flags, sizeof(u32));
|
||||
packet_write(&p, &gSaveFileUsingBackupSlot, sizeof(u8));
|
||||
network_send(&p);
|
||||
}
|
||||
|
||||
|
@ -19,10 +22,12 @@ void network_receive_save_set_flag(struct Packet* p) {
|
|||
s32 courseIndex;
|
||||
u8 courseStars;
|
||||
u32 flags;
|
||||
u8 backupSlot;
|
||||
packet_read(p, &fileIndex, sizeof(s32));
|
||||
packet_read(p, &courseIndex, sizeof(s32));
|
||||
packet_read(p, &courseStars, sizeof(u8));
|
||||
packet_read(p, &flags, sizeof(u32));
|
||||
packet_read(p, &backupSlot, sizeof(u8));
|
||||
|
||||
if (fileIndex >= NUM_SAVE_FILES) {
|
||||
LOG_ERROR("Invalid fileIndex: %d", fileIndex);
|
||||
|
@ -34,7 +39,12 @@ void network_receive_save_set_flag(struct Packet* p) {
|
|||
return;
|
||||
}
|
||||
|
||||
gSaveBuffer.files[fileIndex][0].courseStars[courseIndex] |= courseStars;
|
||||
gSaveBuffer.files[fileIndex][0].flags |= flags;
|
||||
if (backupSlot > 1) {
|
||||
LOG_ERROR("Invalid backupSlot: %d", backupSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
gSaveBuffer.files[fileIndex][backupSlot].courseStars[courseIndex] |= courseStars;
|
||||
gSaveBuffer.files[fileIndex][backupSlot].flags |= flags;
|
||||
gSaveFileModified = TRUE;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue