Allow coop mods to use the backup save slot independently of the normal one

This commit is contained in:
MysterD 2022-04-10 00:30:47 -07:00
parent 653ab58a5d
commit 3db42f1700
16 changed files with 156 additions and 462 deletions

View file

@ -269,6 +269,10 @@ function SOUND_ARG_LOAD(bank, soundID, priority, flags)
return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING
end end
-------------
-- courses --
-------------
--- @type integer --- @type integer
COURSE_NONE = 0 COURSE_NONE = 0
--- @type integer --- @type integer
@ -320,4 +324,12 @@ COURSE_WMOTR = 23
--- @type integer --- @type integer
COURSE_SA = 24 COURSE_SA = 24
--- @type integer --- @type integer
COURSE_CAKE_END = 25 COURSE_CAKE_END = 25
--- @type integer
COURSE_END = 26
--- @type integer
COURSE_MAX = 25
--- @type integer
COURSE_COUNT = 25
--- @type integer
COURSE_MIN = 1

View file

@ -271,6 +271,10 @@ function SOUND_ARG_LOAD(bank, soundID, priority, flags)
return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING
end end
-------------
-- courses --
-------------
--- @type integer --- @type integer
COURSE_NONE = 0 COURSE_NONE = 0
--- @type integer --- @type integer
@ -323,6 +327,14 @@ COURSE_WMOTR = 23
COURSE_SA = 24 COURSE_SA = 24
--- @type integer --- @type integer
COURSE_CAKE_END = 25 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 --- @class BehaviorId

View file

@ -7107,6 +7107,12 @@ function movtexqc_register(name, level, area, type)
-- ... -- ...
end end
--- @param usingBackupSlot boolean
--- @return nil
function save_file_set_using_backup_slot(usingBackupSlot)
-- ...
end
--- @param index integer --- @param index integer
--- @param value integer --- @param value integer
--- @return nil --- @return nil

View file

@ -34,63 +34,6 @@ gLevelValues = {}
--- @type BehaviorValues --- @type BehaviorValues
gBehaviorValues = {} 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 -- -- hooks --
----------- -----------

View file

@ -45,7 +45,7 @@ void DynOS_Lvl_Activate(s32 modIndex, const SysPath &aPackFolder, const char *aL
sDynosCustomLevelScripts.Add({ levelName, _Node }); sDynosCustomLevelScripts.Add({ levelName, _Node });
// Override vanilla script // Override vanilla script
auto& newScripts = _Node->mLevelScripts; // DO NOT COMMIT auto& newScripts = _Node->mLevelScripts;
auto& newScriptNode = newScripts[newScripts.Count() - 1]; auto& newScriptNode = newScripts[newScripts.Count() - 1];
const void* originalScript = DynOS_Builtin_ScriptPtr_GetFromName(newScriptNode->mName.begin()); const void* originalScript = DynOS_Builtin_ScriptPtr_GetFromName(newScriptNode->mName.begin());
if (originalScript == NULL) { if (originalScript == NULL) {

View file

@ -1321,6 +1321,7 @@
- [hud_hide](#hud_hide) - [hud_hide](#hud_hide)
- [hud_show](#hud_show) - [hud_show](#hud_show)
- [movtexqc_register](#movtexqc_register) - [movtexqc_register](#movtexqc_register)
- [save_file_set_using_backup_slot](#save_file_set_using_backup_slot)
- [set_environment_region](#set_environment_region) - [set_environment_region](#set_environment_region)
- [warp_exit_level](#warp_exit_level) - [warp_exit_level](#warp_exit_level)
- [warp_restart_level](#warp_restart_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 /> <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) ## [set_environment_region](#set_environment_region)
### Lua Example ### Lua Example

View file

@ -14,9 +14,6 @@
#include "pc/ini.h" #include "pc/ini.h"
#include "pc/network/network.h" #include "pc/network/network.h"
// note: force-disable TEXTSAVES until it's synchronized
#undef TEXTSAVES
#define MENU_DATA_MAGIC 0x4849 #define MENU_DATA_MAGIC 0x4849
#define SAVE_FILE_MAGIC 0x4441 #define SAVE_FILE_MAGIC 0x4441
@ -36,6 +33,7 @@ u8 gGotFileCoinHiScore = FALSE;
u8 gCurrCourseStarFlags = 0; u8 gCurrCourseStarFlags = 0;
u8 gSpecialTripleJump = FALSE; u8 gSpecialTripleJump = FALSE;
u8 gSaveFileUsingBackupSlot = FALSE;
#define STUB_LEVEL(_0, _1, courseenum, _3, _4, _5, _6, _7, _8) courseenum, #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, #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, STATIC_ASSERT(ARRAY_COUNT(gLevelToCourseNumTable) == LEVEL_COUNT - 1,
"change this array if you are adding levels"); "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 // This was probably used to set progress to 100% for debugging, but
// it was removed from the release ROM. // it was removed from the release ROM.
static void stub_save_file_1(void) { 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) if (fileIndex < 0 || fileIndex >= NUM_SAVE_FILES)
return; return;
#ifdef TEXTSAVES
if (gSaveFileModified && gNetworkType != NT_CLIENT) {
// Write to text file
write_text_save(fileIndex);
gSaveFileModified = FALSE;
gMainMenuDataModified = FALSE;
return;
}
#endif
if (gSaveFileModified) { if (gSaveFileModified) {
// Compute checksum // Compute checksum
add_save_block_signature(&gSaveBuffer.files[fileIndex][0], add_save_block_signature(&gSaveBuffer.files[fileIndex][0],
sizeof(gSaveBuffer.files[fileIndex][0]), SAVE_FILE_MAGIC); sizeof(gSaveBuffer.files[fileIndex][0]), SAVE_FILE_MAGIC);
// Copy to backup slot // Copy to backup slot
bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1], //bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1],
sizeof(gSaveBuffer.files[fileIndex][1])); //sizeof(gSaveBuffer.files[fileIndex][1]));
// Write to EEPROM // Write to EEPROM
write_eeprom_savefile(fileIndex, 0, 2); write_eeprom_savefile(fileIndex, 0, 2);
@ -387,6 +369,7 @@ void save_file_erase(s32 fileIndex) {
touch_high_score_ages(fileIndex); touch_high_score_ages(fileIndex);
bzero(&gSaveBuffer.files[fileIndex][0], sizeof(gSaveBuffer.files[fileIndex][0])); bzero(&gSaveBuffer.files[fileIndex][0], sizeof(gSaveBuffer.files[fileIndex][0]));
bzero(&gSaveBuffer.files[fileIndex][1], sizeof(gSaveBuffer.files[fileIndex][1]));
gSaveFileModified = TRUE; gSaveFileModified = TRUE;
save_file_do_save(fileIndex, 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); touch_high_score_ages(destFileIndex);
bcopy(&gSaveBuffer.files[srcFileIndex][0], &gSaveBuffer.files[destFileIndex][0], bcopy(&gSaveBuffer.files[srcFileIndex][0], &gSaveBuffer.files[destFileIndex][0],
sizeof(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; gSaveFileModified = TRUE;
save_file_do_save(destFileIndex, 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) { void save_file_load_all(UNUSED u8 reload) {
s32 file; //s32 file;
gMainMenuDataModified = FALSE; gMainMenuDataModified = FALSE;
gSaveFileModified = FALSE; gSaveFileModified = FALSE;
@ -430,6 +404,7 @@ void save_file_load_all(UNUSED u8 reload) {
save_file_bswap(&gSaveBuffer); save_file_bswap(&gSaveBuffer);
// Verify the main menu data and create a backup copy if only one of the slots is valid. // 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; s32 validSlots;
validSlots = verify_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC); 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; 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; break;
} }
} }
*/
stub_save_file_1(); stub_save_file_1();
} }
@ -471,11 +447,11 @@ void save_file_load_all(UNUSED u8 reload) {
*/ */
void save_file_reload(void) { void save_file_reload(void) {
// Copy save file data from backup // 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])); sizeof(gSaveBuffer.files[gCurrSaveFileNum - 1][0]));
// Copy main menu data from backup // 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; gMainMenuDataModified = FALSE;
gSaveFileModified = 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)) { 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); touch_coin_score_age(fileIndex, courseIndex);
gGotFileCoinHiScore = TRUE; 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); 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; } 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; gSaveFileModified = TRUE;
network_send_save_set_flag(gCurrSaveFileNum - 1, 0, 0, (flags | SAVE_FLAG_FILE_EXISTS)); network_send_save_set_flag(gCurrSaveFileNum - 1, 0, 0, (flags | SAVE_FLAG_FILE_EXISTS));
} }
void save_file_clear_flags(u32 flags) { void save_file_clear_flags(u32 flags) {
gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags &= ~flags; gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags &= ~flags;
gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags |= SAVE_FLAG_FILE_EXISTS; gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags |= SAVE_FLAG_FILE_EXISTS;
gSaveFileModified = TRUE; gSaveFileModified = TRUE;
} }
@ -617,7 +593,7 @@ u32 save_file_get_flags(void) {
if (gCurrCreditsEntry != NULL || gCurrDemoInput != NULL) { if (gCurrCreditsEntry != NULL || gCurrDemoInput != NULL) {
return 0; 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; u32 starFlags;
if (courseIndex == -1) { 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 { } else {
starFlags = gSaveBuffer.files[fileIndex][0].courseStars[courseIndex] & 0x7F; starFlags = gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].courseStars[courseIndex] & 0x7F;
} }
return starFlags; 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) { void save_file_set_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlags) {
if (courseIndex == -1) { 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)); network_send_save_set_flag(fileIndex, courseIndex, 0, (STAR_FLAG_TO_SAVE_FLAG(starFlags) | SAVE_FLAG_FILE_EXISTS));
} else { } 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); 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; gSaveFileModified = TRUE;
} }
s32 save_file_get_course_coin_score(s32 fileIndex, s32 courseIndex) { 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. * Return TRUE if the cannon is unlocked in the current course.
*/ */
s32 save_file_is_cannon_unlocked(void) { 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. * Sets the cannon status to unlocked in the current course.
*/ */
void save_file_set_cannon_unlocked(void) { void save_file_set_cannon_unlocked(void) {
gSaveBuffer.files[gCurrSaveFileNum - 1][0].courseStars[gCurrCourseNum] |= 0x80; gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].courseStars[gCurrCourseNum] |= 0x80;
gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags |= SAVE_FLAG_FILE_EXISTS; gSaveBuffer.files[gCurrSaveFileNum - 1][gSaveFileUsingBackupSlot].flags |= SAVE_FLAG_FILE_EXISTS;
gSaveFileModified = TRUE; gSaveFileModified = TRUE;
network_send_save_set_flag(gCurrSaveFileNum - 1, gCurrCourseNum, 0x80, SAVE_FLAG_FILE_EXISTS); 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) { 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->capLevel = gCurrLevelNum;
saveFile->capArea = gCurrAreaIndex; 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) { 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(); s32 flags = save_file_get_flags();
if (saveFile->capLevel == gCurrLevelNum && saveFile->capArea == gCurrAreaIndex 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) { void save_file_move_cap_to_default_location(void) {
if (save_file_get_flags() & SAVE_FLAG_CAP_ON_GROUND) { 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: case LEVEL_SSL:
save_file_set_flags(SAVE_FLAG_CAP_ON_KLEPTO); save_file_set_flags(SAVE_FLAG_CAP_ON_KLEPTO);
break; break;

View file

@ -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;
}

View file

@ -244,6 +244,9 @@ char gSmluaConstants[] = ""
" if flags == nil then flags = 0 end\n" " if flags == nil then flags = 0 end\n"
" return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING\n" " return (bank << 28) | (soundID << 16) | (priority << 8) | flags | SOUND_STATUS_WAITING\n"
"end\n" "end\n"
"-------------\n"
"-- courses --\n"
"-------------\n"
"--- @type integer\n" "--- @type integer\n"
"COURSE_NONE = 0\n" "COURSE_NONE = 0\n"
"--- @type integer\n" "--- @type integer\n"
@ -296,6 +299,14 @@ char gSmluaConstants[] = ""
"COURSE_SA = 24\n" "COURSE_SA = 24\n"
"--- @type integer\n" "--- @type integer\n"
"COURSE_CAKE_END = 25\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_bhv1Up = 0\n"
"id_bhv1upJumpOnApproach = 1\n" "id_bhv1upJumpOnApproach = 1\n"
"id_bhv1upRunningAway = 2\n" "id_bhv1upRunningAway = 2\n"

View file

@ -14771,6 +14771,17 @@ int smlua_func_movtexqc_register(lua_State* L) {
return 1; 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) { int smlua_func_set_environment_region(lua_State* L) {
if(!smlua_functions_valid_param_count(L, 2)) { return 0; } 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_hide", smlua_func_hud_hide);
smlua_bind_function(L, "hud_show", smlua_func_hud_show); smlua_bind_function(L, "hud_show", smlua_func_hud_show);
smlua_bind_function(L, "movtexqc_register", smlua_func_movtexqc_register); 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, "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_exit_level", smlua_func_warp_exit_level);
smlua_bind_function(L, "warp_restart_level", smlua_func_warp_restart_level); smlua_bind_function(L, "warp_restart_level", smlua_func_warp_restart_level);

View file

@ -87,6 +87,11 @@ s16 get_current_save_file_num(void) {
return gCurrSaveFileNum; 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) { void movtexqc_register(const char* name, s16 level, s16 area, s16 type) {

View file

@ -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); f32 get_hand_foot_pos_z(struct MarioState* m, u8 index);
s16 get_current_save_file_num(void); 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); void movtexqc_register(const char* name, s16 level, s16 area, s16 type);
f32 get_environment_region(u8 index); f32 get_environment_region(u8 index);

View file

@ -109,6 +109,9 @@ bool network_init(enum NetworkType inNetworkType) {
gNetworkType = inNetworkType; gNetworkType = inNetworkType;
if (gNetworkType == NT_SERVER) { if (gNetworkType == NT_SERVER) {
extern s16 gCurrSaveFileNum;
gCurrSaveFileNum = configHostSaveSlot;
mods_activate(&gLocalMods); mods_activate(&gLocalMods);
smlua_init(); smlua_init();

View file

@ -12,6 +12,7 @@
extern s16 gCurrSaveFileNum; extern s16 gCurrSaveFileNum;
extern s16 gCurrCourseNum; extern s16 gCurrCourseNum;
extern u8 gSaveFileUsingBackupSlot;
static f32 dist_to_pos(struct Object* o, f32* pos) { static f32 dist_to_pos(struct Object* o, f32* pos) {
f32 x = o->oPosX - pos[0]; x *= x; 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, &behaviorId, sizeof(u32));
packet_write(&p, &coinScore, sizeof(s16)); packet_write(&p, &coinScore, sizeof(s16));
packet_write(&p, &starIndex, sizeof(s16)); packet_write(&p, &starIndex, sizeof(s16));
packet_write(&p, &gSaveFileUsingBackupSlot, sizeof(u8));
network_send(&p); network_send(&p);
} }
@ -71,6 +73,7 @@ void network_receive_collect_star(struct Packet* p) {
s16 lastStarActNum = gCurrActStarNum; s16 lastStarActNum = gCurrActStarNum;
s16 lastLevelNum = gCurrLevelNum; s16 lastLevelNum = gCurrLevelNum;
s16 lastAreaIndex = gCurrAreaIndex; s16 lastAreaIndex = gCurrAreaIndex;
u8 lastBackupSlot = gSaveFileUsingBackupSlot;
packet_read(p, &gCurrSaveFileNum, sizeof(s16)); packet_read(p, &gCurrSaveFileNum, sizeof(s16));
packet_read(p, &gCurrCourseNum, 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, &behaviorId, sizeof(u32));
packet_read(p, &coinScore, sizeof(s16)); packet_read(p, &coinScore, sizeof(s16));
packet_read(p, &starIndex, 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); const void* behavior = get_behavior_from_id(behaviorId);
save_file_collect_star_or_key(coinScore, starIndex, 1); 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; struct NetworkPlayer* np = gNetworkPlayerLocal;
bool levelAreaMismatch = ((np == NULL) bool levelAreaMismatch = ((np == NULL)
|| np->currCourseNum != gCurrCourseNum || np->currCourseNum != gCurrCourseNum
@ -98,11 +98,17 @@ void network_receive_collect_star(struct Packet* p) {
|| np->currLevelNum != gCurrLevelNum || np->currLevelNum != gCurrLevelNum
|| np->currAreaIndex != gCurrAreaIndex); || np->currAreaIndex != gCurrAreaIndex);
gCurrSaveFileNum = lastSaveFileNum; gCurrSaveFileNum = lastSaveFileNum;
gCurrCourseNum = lastCourseNum; gCurrCourseNum = lastCourseNum;
gCurrActStarNum = lastStarActNum; gCurrActStarNum = lastStarActNum;
gCurrLevelNum = lastLevelNum; gCurrLevelNum = lastLevelNum;
gCurrAreaIndex = lastAreaIndex; 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) { if (!levelAreaMismatch) {
struct Object* star = find_nearest_star(behavior, pos, 500); struct Object* star = find_nearest_star(behavior, pos, 500);

View file

@ -3,18 +3,28 @@
#include "game/save_file.h" #include "game/save_file.h"
#include "pc/debuglog.h" #include "pc/debuglog.h"
extern u8 gSaveFileUsingBackupSlot;
void network_send_save_file(s32 fileIndex) { void network_send_save_file(s32 fileIndex) {
if (gNetworkPlayerServer == NULL) { return; } if (gNetworkPlayerServer == NULL) { return; }
SOFT_ASSERT(gNetworkType == NT_CLIENT); SOFT_ASSERT(gNetworkType == NT_CLIENT);
struct Packet p = { 0 }; struct Packet p = { 0 };
packet_init(&p, PACKET_SAVE_FILE, true, PLMT_NONE); 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); network_send_to(gNetworkPlayerServer->localIndex, &p);
} }
void network_receive_save_file(struct Packet* p) { void network_receive_save_file(struct Packet* p) {
if (gNetworkType != NT_SERVER) { return; } if (gNetworkType != NT_SERVER) { return; }
s32 fileIndex = 0; s32 fileIndex = 0;
u8 lastBackupSlot = gSaveFileUsingBackupSlot;
packet_read(p, &fileIndex, sizeof(s32)); packet_read(p, &fileIndex, sizeof(s32));
packet_read(p, &gSaveFileUsingBackupSlot, sizeof(u8));
if (gSaveFileUsingBackupSlot != 0) { gSaveFileUsingBackupSlot = 1; }
save_file_do_save(fileIndex, FALSE); save_file_do_save(fileIndex, FALSE);
gSaveFileUsingBackupSlot = lastBackupSlot;
} }

View file

@ -4,13 +4,16 @@
#include "buffers/buffers.h" #include "buffers/buffers.h"
#include "pc/debuglog.h" #include "pc/debuglog.h"
extern u8 gSaveFileUsingBackupSlot;
void network_send_save_set_flag(s32 fileIndex, s32 courseIndex, u8 courseStars, u32 flags) { void network_send_save_set_flag(s32 fileIndex, s32 courseIndex, u8 courseStars, u32 flags) {
struct Packet p = { 0 }; struct Packet p = { 0 };
packet_init(&p, PACKET_SAVE_SET_FLAG, true, PLMT_NONE); packet_init(&p, PACKET_SAVE_SET_FLAG, true, PLMT_NONE);
packet_write(&p, &fileIndex, sizeof(s32)); packet_write(&p, &fileIndex, sizeof(s32));
packet_write(&p, &courseIndex, sizeof(s32)); packet_write(&p, &courseIndex, sizeof(s32));
packet_write(&p, &courseStars, sizeof(u8)); packet_write(&p, &courseStars, sizeof(u8));
packet_write(&p, &flags, sizeof(u32)); packet_write(&p, &flags, sizeof(u32));
packet_write(&p, &gSaveFileUsingBackupSlot, sizeof(u8));
network_send(&p); network_send(&p);
} }
@ -19,10 +22,12 @@ void network_receive_save_set_flag(struct Packet* p) {
s32 courseIndex; s32 courseIndex;
u8 courseStars; u8 courseStars;
u32 flags; u32 flags;
u8 backupSlot;
packet_read(p, &fileIndex, sizeof(s32)); packet_read(p, &fileIndex, sizeof(s32));
packet_read(p, &courseIndex, sizeof(s32)); packet_read(p, &courseIndex, sizeof(s32));
packet_read(p, &courseStars, sizeof(u8)); packet_read(p, &courseStars, sizeof(u8));
packet_read(p, &flags, sizeof(u32)); packet_read(p, &flags, sizeof(u32));
packet_read(p, &backupSlot, sizeof(u8));
if (fileIndex >= NUM_SAVE_FILES) { if (fileIndex >= NUM_SAVE_FILES) {
LOG_ERROR("Invalid fileIndex: %d", fileIndex); LOG_ERROR("Invalid fileIndex: %d", fileIndex);
@ -34,7 +39,12 @@ void network_receive_save_set_flag(struct Packet* p) {
return; return;
} }
gSaveBuffer.files[fileIndex][0].courseStars[courseIndex] |= courseStars; if (backupSlot > 1) {
gSaveBuffer.files[fileIndex][0].flags |= flags; LOG_ERROR("Invalid backupSlot: %d", backupSlot);
return;
}
gSaveBuffer.files[fileIndex][backupSlot].courseStars[courseIndex] |= courseStars;
gSaveBuffer.files[fileIndex][backupSlot].flags |= flags;
gSaveFileModified = TRUE; gSaveFileModified = TRUE;
} }