Merge pull request #314 from Zerocker/patch-textsaves

Fix parsing and warnings for text savefiles
This commit is contained in:
Zerocker 2020-06-07 22:23:08 +09:00 committed by GitHub
commit 5121172e65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 74 deletions

View File

@ -391,7 +391,6 @@ BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex) {
void save_file_load_all(void) { void save_file_load_all(void) {
s32 file; s32 file;
s32 validSlots;
gMainMenuDataModified = FALSE; gMainMenuDataModified = FALSE;
gSaveFileModified = FALSE; gSaveFileModified = FALSE;
@ -405,6 +404,7 @@ void save_file_load_all(void) {
gSaveFileModified = TRUE; gSaveFileModified = TRUE;
gMainMenuDataModified = TRUE; gMainMenuDataModified = TRUE;
#else #else
s32 validSlots;
read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer)); read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer));
if (save_file_need_bswap(&gSaveBuffer)) if (save_file_need_bswap(&gSaveBuffer))

View File

@ -5,43 +5,38 @@
#include "pc/ini.h" #include "pc/ini.h"
#include "pc/platform.h" #include "pc/platform.h"
#define FILENAME_FORMAT "%s/save_file_%d.sav" #define FILENAME_FORMAT "%s\\sm64_save_file_%d.sav"
#define NUM_COURSES 15 #define NUM_COURSES 15
#define NUM_BONUS_COURSES 10 #define NUM_BONUS_COURSES 10
#define NUM_FLAGS 21 #define NUM_FLAGS 21
#define NUM_CAP_ON 4 #define NUM_CAP_ON 4
/* Flag keys */
const char *sav_flags[NUM_FLAGS] = { const char *sav_flags[NUM_FLAGS] = {
"file_exists", "wing_cap", "metal_cap", "vanish_cap", "key_1", "key_2", "file_exists", "wing_cap", "metal_cap", "vanish_cap", "key_1", "key_2",
"basement_door", "upstairs_door", "ddd_moved_back", "moat_drained", "basement_door", "upstairs_door", "ddd_moved_back", "moat_drained",
"pps_door", "wf_door", "ccm_door", "jrb_door", "bitdw_door", "pps_door", "wf_door", "ccm_door", "jrb_door", "bitdw_door",
"bitfs_door", "", "", "", "", "50star_door" "bitfs_door", "", "", "", "", "50star_door" // 4 Cap flags are processed in their own section
}; };
/* Main course keys */
const char *sav_courses[NUM_COURSES] = { const char *sav_courses[NUM_COURSES] = {
"bob", "wf", "jrb", "ccm", "bbh", "hmc", "lll", "bob", "wf", "jrb", "ccm", "bbh", "hmc", "lll",
"ssl", "ddd", "sl", "wdw", "ttm", "thi", "ttc", "rr" "ssl", "ddd", "sl", "wdw", "ttm", "thi", "ttc", "rr"
}; };
/* Bonus courses keys (including Castle Course) */
const char *sav_bonus_courses[NUM_BONUS_COURSES] = { const char *sav_bonus_courses[NUM_BONUS_COURSES] = {
"hub", "bitdw", "bitfs", "bits", "pss", "cotmc", "bitdw", "bitfs", "bits", "pss", "cotmc",
"totwc", "vcutm", "wmotr", "sa", "totwc", "vcutm", "wmotr", "sa", "hub" // hub is Castle Grounds
}; };
/* Mario's cap type keys */
const char *cap_on_types[NUM_CAP_ON] = { const char *cap_on_types[NUM_CAP_ON] = {
"ground", "klepto", "ukiki", "mrblizzard" "ground", "klepto", "ukiki", "mrblizzard"
}; };
/* Sound modes */
const char *sound_modes[3] = { const char *sound_modes[3] = {
"stereo", "mono", "headset" "stereo", "mono", "headset"
}; };
/* Get current timestamp */ /* Get current timestamp string */
static void get_timestamp(char* buffer) { static void get_timestamp(char* buffer) {
time_t timer; time_t timer;
struct tm* tm_info; struct tm* tm_info;
@ -77,104 +72,107 @@ static u32 int_to_bin(u32 n) {
} }
/** /**
* Write SaveFile and MainMenuSaveData structs to a text-based savefile. * Write SaveFile and MainMenuSaveData structs to a text-based savefile
*/ */
static s32 write_text_save(s32 fileIndex) { static s32 write_text_save(s32 fileIndex) {
FILE* file; FILE* file;
struct SaveFile *savedata; struct SaveFile *savedata;
struct MainMenuSaveData *menudata; struct MainMenuSaveData *menudata;
char filename[SYS_MAX_PATH] = { 0 }; char filename[SYS_MAX_PATH] = { 0 };
char value[SYS_MAX_PATH] = { 0 }; char *value;
u32 i, bit, flags, coins, stars, starFlags; u32 i, bit, flags, coins, stars, starFlags;
/* Define savefile's name */
if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, sys_save_path(), fileIndex) < 0) if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, sys_save_path(), fileIndex) < 0)
return -1; return -1;
file = fopen(filename, "wt"); file = fopen(filename, "wt");
if (file == NULL) if (file == NULL) {
printf("Savefile '%s' not found!\n", filename);
return -1; return -1;
else } else
printf("Updating savefile in '%s'\n", filename); printf("Saving updated progress to '%s'\n", filename);
/* Write header */
fprintf(file, "# Super Mario 64 save file\n"); fprintf(file, "# Super Mario 64 save file\n");
fprintf(file, "# Comment starts with #\n"); fprintf(file, "# Comment starts with #\n");
fprintf(file, "# True = 1, False = 0\n"); fprintf(file, "# True = 1, False = 0\n");
/* Write current timestamp */
get_timestamp(value); get_timestamp(value);
fprintf(file, "# %s\n", value); fprintf(file, "# %s\n", value);
/* Write MainMenuSaveData info */
menudata = &gSaveBuffer.menuData[0]; menudata = &gSaveBuffer.menuData[0];
fprintf(file, "\n[menu]\n"); fprintf(file, "\n[menu]\n");
fprintf(file, "coin_score_age = %d\n", menudata->coinScoreAges[fileIndex]); fprintf(file, "coin_score_age = %d\n", menudata->coinScoreAges[fileIndex]);
/* Sound mode */
if (menudata->soundMode == 0) { if (menudata->soundMode == 0) {
fprintf(file, "sound_mode = %s\n", sound_modes[0]); // stereo fprintf(file, "sound_mode = %s\n", sound_modes[0]); // stereo
} }
else if (menudata->soundMode == 3) { else if (menudata->soundMode == 3) {
fprintf(file, "sound_mode = %s\n", sound_modes[1]); // mono fprintf(file, "sound_mode = %s\n", sound_modes[1]); // mono
} }
else if (menudata->soundMode == 1) { else if (menudata->soundMode == 1) {
fprintf(file, "sound_mode = %s\n", sound_modes[2]); // headset fprintf(file, "sound_mode = %s\n", sound_modes[2]); // headset
} }
else { else {
printf("Undefined sound mode!"); printf("Undefined sound mode!");
return -1; return -1;
} }
/* Write all flags */
fprintf(file, "\n[flags]\n"); fprintf(file, "\n[flags]\n");
for (i = 1; i < NUM_FLAGS; i++) { for (i = 1; i < NUM_FLAGS; i++) {
if (strcmp(sav_flags[i], "")) { if (strcmp(sav_flags[i], "")) {
flags = save_file_get_flags(); flags = save_file_get_flags();
flags = (flags & (1 << i)); /* Get a specific bit */ flags = (flags & (1 << i)); // Get 'star' flag bit
flags = (flags) ? 1 : 0; /* Determine if bit is set or not */ flags = (flags) ? 1 : 0;
fprintf(file, "%s = %d\n", sav_flags[i], flags); fprintf(file, "%s = %d\n", sav_flags[i], flags);
} }
} }
/* Write coin count and star flags from each course (except Castle Grounds) */
fprintf(file, "\n[courses]\n"); fprintf(file, "\n[courses]\n");
for (i = 0; i < NUM_COURSES; i++) { for (i = 0; i < NUM_COURSES; i++) {
stars = save_file_get_star_flags(fileIndex, i); stars = save_file_get_star_flags(fileIndex, i);
coins = save_file_get_course_coin_score(fileIndex, i); coins = save_file_get_course_coin_score(fileIndex, i);
starFlags = int_to_bin(stars); /* 63 -> 111111 */ starFlags = int_to_bin(stars); // 63 -> 111111
fprintf(file, "%s = \"%d, %07d\"\n", sav_courses[i], coins, starFlags); fprintf(file, "%s = \"%d, %07d\"\n", sav_courses[i], coins, starFlags);
} }
/* Write star flags from each bonus cource (including Castle Grounds) */
fprintf(file, "\n[bonus]\n"); fprintf(file, "\n[bonus]\n");
for (i = 0; i < NUM_BONUS_COURSES; i++) { for (i = 0; i < NUM_BONUS_COURSES; i++) {
if (i == 0) { char *format;
stars = save_file_get_star_flags(fileIndex, -1);
} else {
stars = save_file_get_star_flags(fileIndex, i+14);
}
starFlags = int_to_bin(stars);
fprintf(file, "%s = %d\n", sav_bonus_courses[i], starFlags); 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);
} }
/* Write who steal Mario's cap */
fprintf(file, "\n[cap]\n"); fprintf(file, "\n[cap]\n");
for (i = 0; i < NUM_CAP_ON; i++) { for (i = 0; i < NUM_CAP_ON; i++) {
flags = save_file_get_flags(); // Read all flags flags = save_file_get_flags();
bit = (1 << (i+16)); // Determine current flag bit = (1 << (i+16)); // Determine current flag
flags = (flags & bit); // Get `cap` flag flags = (flags & bit); // Get 'cap' flag bit
flags = (flags) ? 1 : 0; // Determine if bit is set or not flags = (flags) ? 1 : 0;
if (flags) { if (flags) {
fprintf(file, "type = %s\n", cap_on_types[i]); fprintf(file, "type = %s\n", cap_on_types[i]);
break; break;
} }
} }
/* Write in what course and area Mario losted its cap, and cap's position */
savedata = &gSaveBuffer.files[fileIndex][0]; savedata = &gSaveBuffer.files[fileIndex][0];
switch(savedata->capLevel) { switch(savedata->capLevel) {
case COURSE_SSL: case COURSE_SSL:
@ -187,12 +185,13 @@ static s32 write_text_save(s32 fileIndex) {
fprintf(file, "level = %s\n", "ttm"); fprintf(file, "level = %s\n", "ttm");
break; break;
default: default:
fprintf(file, "level = %s\n", "none");
break; break;
} }
fprintf(file, "area = %d\n", savedata->capArea); if (savedata->capLevel) {
fprintf(file, "area = %d\n", savedata->capArea);
}
/* Update a backup */ // Backup is nessecary for saving recent progress after gameover
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]));
@ -201,22 +200,19 @@ static s32 write_text_save(s32 fileIndex) {
} }
/** /**
* Read gSaveBuffer data from a text-based savefile. * Read gSaveBuffer data from a text-based savefile
*/ */
static s32 read_text_save(s32 fileIndex) { static s32 read_text_save(s32 fileIndex) {
char filename[SYS_MAX_PATH] = { 0 }; char filename[SYS_MAX_PATH] = { 0 };
char temp[SYS_MAX_PATH] = { 0 };
const char *value; const char *value;
ini_t *savedata; ini_t *savedata;
u32 i, flag, coins, stars, starFlags; u32 i, flag, coins, stars, starFlags;
u32 capArea; u32 capArea;
/* Define savefile's name */
if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, sys_save_path(), fileIndex) < 0) if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, sys_save_path(), fileIndex) < 0)
return -1; return -1;
/* Try to open the file */
savedata = ini_load(filename); savedata = ini_load(filename);
if (savedata == NULL) { if (savedata == NULL) {
return -1; return -1;
@ -224,7 +220,6 @@ static s32 read_text_save(s32 fileIndex) {
printf("Loading savefile from '%s'\n", filename); printf("Loading savefile from '%s'\n", filename);
} }
/* Read coin score age for selected file and sound mode */
ini_sget(savedata, "menu", "coin_score_age", "%d", ini_sget(savedata, "menu", "coin_score_age", "%d",
&gSaveBuffer.menuData[0].coinScoreAges[fileIndex]); &gSaveBuffer.menuData[0].coinScoreAges[fileIndex]);
@ -245,32 +240,28 @@ static s32 read_text_save(s32 fileIndex) {
return -1; return -1;
} }
/* Parse main flags */
for (i = 1; i < NUM_FLAGS; i++) { for (i = 1; i < NUM_FLAGS; i++) {
value = ini_get(savedata, "flags", sav_flags[i]); value = ini_get(savedata, "flags", sav_flags[i]);
if (value) { if (value) {
flag = strtol(value, &temp, 10); flag = value[0] - '0'; // Flag should be 0 or 1
if (flag) { if (flag) {
flag = 1 << i; /* Look #define in header.. */ flag = 1 << i; // Flags defined in 'save_file' header
gSaveBuffer.files[fileIndex][0].flags |= flag; gSaveBuffer.files[fileIndex][0].flags |= flag;
} }
} }
} }
/* Parse coin and star values for each main course */
for (i = 0; i < NUM_COURSES; i++) { for (i = 0; i < NUM_COURSES; i++) {
value = ini_get(savedata, "courses", sav_courses[i]); value = ini_get(savedata, "courses", sav_courses[i]);
if (value) { if (value) {
sscanf(value, "%d, %d", &coins, &stars); sscanf(value, "%d, %d", &coins, &stars);
starFlags = bin_to_int(stars); /* 111111 -> 63 */ starFlags = bin_to_int(stars); // 111111 -> 63
save_file_set_star_flags(fileIndex, i, starFlags); save_file_set_star_flags(fileIndex, i, starFlags);
gSaveBuffer.files[fileIndex][0].courseCoinScores[i] = coins; gSaveBuffer.files[fileIndex][0].courseCoinScores[i] = coins;
} }
} }
/* Parse star values for each bonus course */
for (i = 0; i < NUM_BONUS_COURSES; i++) { for (i = 0; i < NUM_BONUS_COURSES; i++) {
value = ini_get(savedata, "bonus", sav_bonus_courses[i]); value = ini_get(savedata, "bonus", sav_bonus_courses[i]);
if (value) { if (value) {
@ -278,19 +269,18 @@ static s32 read_text_save(s32 fileIndex) {
starFlags = bin_to_int(stars); starFlags = bin_to_int(stars);
if (strlen(value) == 5) { if (strlen(value) == 5) {
/* Process Castle Grounds */ // Process Castle Grounds
save_file_set_star_flags(fileIndex, -1, starFlags); save_file_set_star_flags(fileIndex, -1, starFlags);
} else if (strlen(value) == 2) { } else if (strlen(value) == 2) {
/* Process Princess's Secret Slide */ // Process Princess's Secret Slide
save_file_set_star_flags(fileIndex, COURSE_PSS, starFlags); save_file_set_star_flags(fileIndex, 18, starFlags);
} else { } else {
/* Process another shitty bonus course */ // Process bonus courses
save_file_set_star_flags(fileIndex, i+15, starFlags); save_file_set_star_flags(fileIndex, i+15, starFlags);
} }
} }
} }
/* Find, who steal Mario's cap ... */
for (i = 0; i < NUM_CAP_ON; i++) { for (i = 0; i < NUM_CAP_ON; i++) {
value = ini_get(savedata, "cap", "type"); value = ini_get(savedata, "cap", "type");
if (value) { if (value) {
@ -302,20 +292,16 @@ static s32 read_text_save(s32 fileIndex) {
} }
} }
/* ... it's level ... */
value = ini_get(savedata, "cap", "level"); value = ini_get(savedata, "cap", "level");
if (value) { if (value) {
if (strcmp(value, "ssl") == 0) { if (strcmp(value, "ssl") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 8; // ssl gSaveBuffer.files[fileIndex][0].capLevel = COURSE_SSL; // ssl
} }
else if (strcmp(value, "sl") == 0) { else if (strcmp(value, "sl") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 10; // sl gSaveBuffer.files[fileIndex][0].capLevel = COURSE_SL; // sl
} }
else if (strcmp(value, "ttm") == 0) { else if (strcmp(value, "ttm") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 12; // ttm gSaveBuffer.files[fileIndex][0].capLevel = COURSE_TTM; // ttm
}
else if (strcmp(value, "none") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 0;
} }
else { else {
printf("Invalid 'cap:level' flag!\n"); printf("Invalid 'cap:level' flag!\n");
@ -323,7 +309,6 @@ static s32 read_text_save(s32 fileIndex) {
} }
} }
/* ... and it's area */
value = ini_get(savedata, "cap", "area"); value = ini_get(savedata, "cap", "area");
if (value) { if (value) {
sscanf(value, "%d", &capArea); sscanf(value, "%d", &capArea);
@ -336,14 +321,13 @@ static s32 read_text_save(s32 fileIndex) {
} }
} }
/* Good, file exists for gSaveBuffer */ // Good, file exists for gSaveBuffer
gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS; gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS;
/* Make a backup */ // Backup is nessecary for saving recent progress after gameover
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]));
/* Cleaning up after ourselves */
ini_free(savedata); ini_free(savedata);
return 0; return 0;
} }