diff --git a/autogen/lua_constants/built-in.lua b/autogen/lua_constants/built-in.lua
index 687307d2..ad0af270 100644
--- a/autogen/lua_constants/built-in.lua
+++ b/autogen/lua_constants/built-in.lua
@@ -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
@@ -320,4 +324,12 @@ COURSE_WMOTR = 23
--- @type integer
COURSE_SA = 24
--- @type integer
-COURSE_CAKE_END = 25
\ No newline at end of file
+COURSE_CAKE_END = 25
+--- @type integer
+COURSE_END = 26
+--- @type integer
+COURSE_MAX = 25
+--- @type integer
+COURSE_COUNT = 25
+--- @type integer
+COURSE_MIN = 1
\ No newline at end of file
diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index ed9a9f54..f13105b5 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -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
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 799a3d29..58425312 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -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
diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua
index c1778e72..dc6e94b3 100644
--- a/autogen/lua_definitions/manual.lua
+++ b/autogen/lua_definitions/manual.lua
@@ -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 --
-----------
diff --git a/data/dynos_mgr_lvl.cpp b/data/dynos_mgr_lvl.cpp
index cce61b5f..28b0c7b7 100644
--- a/data/dynos_mgr_lvl.cpp
+++ b/data/dynos_mgr_lvl.cpp
@@ -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) {
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 6969e1d1..ab1f9159 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -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
+## [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:](#)
+
+
+
## [set_environment_region](#set_environment_region)
### Lua Example
diff --git a/src/game/save_file.c b/src/game/save_file.c
index a02901ea..a51e7e8c 100644
--- a/src/game/save_file.c
+++ b/src/game/save_file.c
@@ -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;
diff --git a/src/game/text_save.inc.h b/src/game/text_save.inc.h
deleted file mode 100644
index a9ce6c2a..00000000
--- a/src/game/text_save.inc.h
+++ /dev/null
@@ -1,334 +0,0 @@
-#include
-#include
-#include
-#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;
-}
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index 39970956..fcd938b2 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -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"
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index d358f4a8..be9a1271 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -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);
diff --git a/src/pc/lua/utils/smlua_misc_utils.c b/src/pc/lua/utils/smlua_misc_utils.c
index 6b3f55ed..7173cc4a 100644
--- a/src/pc/lua/utils/smlua_misc_utils.c
+++ b/src/pc/lua/utils/smlua_misc_utils.c
@@ -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) {
diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h
index c1fc65a9..d72f349f 100644
--- a/src/pc/lua/utils/smlua_misc_utils.h
+++ b/src/pc/lua/utils/smlua_misc_utils.h
@@ -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);
diff --git a/src/pc/network/network.c b/src/pc/network/network.c
index f6ad16ec..d4412299 100644
--- a/src/pc/network/network.c
+++ b/src/pc/network/network.c
@@ -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();
diff --git a/src/pc/network/packets/packet_collect_star.c b/src/pc/network/packets/packet_collect_star.c
index 0ac3047d..a0fc2c45 100644
--- a/src/pc/network/packets/packet_collect_star.c
+++ b/src/pc/network/packets/packet_collect_star.c
@@ -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
@@ -98,11 +98,17 @@ void network_receive_collect_star(struct Packet* p) {
|| np->currLevelNum != gCurrLevelNum
|| np->currAreaIndex != gCurrAreaIndex);
- gCurrSaveFileNum = lastSaveFileNum;
- gCurrCourseNum = lastCourseNum;
- gCurrActStarNum = lastStarActNum;
- gCurrLevelNum = lastLevelNum;
- gCurrAreaIndex = lastAreaIndex;
+ gCurrSaveFileNum = lastSaveFileNum;
+ gCurrCourseNum = lastCourseNum;
+ 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);
diff --git a/src/pc/network/packets/packet_save_file.c b/src/pc/network/packets/packet_save_file.c
index 394c56ef..36c94d5d 100644
--- a/src/pc/network/packets/packet_save_file.c
+++ b/src/pc/network/packets/packet_save_file.c
@@ -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, &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;
}
diff --git a/src/pc/network/packets/packet_save_set_flag.c b/src/pc/network/packets/packet_save_set_flag.c
index 2384cf7a..a1a67d53 100644
--- a/src/pc/network/packets/packet_save_set_flag.c
+++ b/src/pc/network/packets/packet_save_set_flag.c
@@ -4,13 +4,16 @@
#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);
- packet_write(&p, &fileIndex, sizeof(s32));
- packet_write(&p, &courseIndex, sizeof(s32));
- packet_write(&p, &courseStars, sizeof(u8));
- packet_write(&p, &flags, sizeof(u32));
+ packet_write(&p, &fileIndex, sizeof(s32));
+ 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;
}