mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2026-04-27 04:21:42 +00:00
Merge eae34aabe6 into ba54cbd1d0
This commit is contained in:
commit
c5eccf6c17
31 changed files with 629 additions and 342 deletions
|
|
@ -89,7 +89,7 @@ override_allowed_functions = {
|
|||
"src/pc/djui/djui_popup.h": [ "create" ],
|
||||
"src/pc/djui/djui_language.h": [ "djui_language_get" ],
|
||||
"src/pc/djui/djui_panel_menu.h": [ "djui_menu_get_rainbow_string_color" ],
|
||||
"src/game/save_file.h": [ "get_level_", "save_file_get_", "save_file_set_flags", "save_file_clear_flags", "save_file_reload", "save_file_erase_current_backup_save", "save_file_set_star_flags", "save_file_is_cannon_unlocked", "save_file_set_cannon_unlocked", "touch_coin_score_age", "save_file_set_course_coin_score", "save_file_do_save", "save_file_remove_star_flags", "save_file_erase" ],
|
||||
"src/game/save_file.h": [ "get_level_", "save_file_get_", "save_file_set_flags", "save_file_clear_flags", "save_file_reload", "save_file_erase_current_backup_save", "save_file_set_star_flags", "save_file_is_cannon_unlocked", "save_file_set_cannon_unlocked", "save_file_set_course_coin_score", "save_file_do_save", "save_file_remove_star_flags", "save_file_erase" ],
|
||||
"src/pc/lua/utils/smlua_model_utils.h": [ "smlua_model_util_get_id" ],
|
||||
"src/game/object_list_processor.h": [ "set_object_respawn_info_bits" ],
|
||||
"src/game/platform_displacement.h": [ "apply_platform_displacement" ],
|
||||
|
|
@ -119,6 +119,7 @@ override_disallowed_functions = {
|
|||
"src/pc/djui/djui_console.h": [ " djui_console_create", "djui_console_message_create", "djui_console_message_dequeue" ],
|
||||
"src/pc/djui/djui_chat_message.h": [ "create_from" ],
|
||||
"src/game/interaction.h": [ "process_interaction", "_handle_" ],
|
||||
"src/game/save_file.h": [ "save_file_get_all_filenames", "save_file_get_dir", "save_file_get_first_available_index", "save_file_get_amount_of_available_indexes", "save_file_get_first_active_index", "save_file_rename_file" ],
|
||||
"src/game/sound_init.h": [ "_loop_", "thread4_", "set_sound_mode" ],
|
||||
"src/pc/network/network_utils.h": [ "network_get_player_text_color[^_]" ],
|
||||
"src/pc/network/network_player.h": [ "_init", "_connected[^_]", "_shutdown", "_disconnected", "_update", "construct_player_popup", "network_player_name_valid" ],
|
||||
|
|
|
|||
|
|
@ -6878,7 +6878,7 @@ METAL = CAP --- @type PlayerPart
|
|||
--- | `METAL`
|
||||
|
||||
--- @type integer
|
||||
NUM_SAVE_FILES = 4
|
||||
NUM_SAVE_FILES = 64
|
||||
|
||||
SAVE_FILE_A = 0 --- @type SaveFileIndex
|
||||
SAVE_FILE_B = 1 --- @type SaveFileIndex
|
||||
|
|
|
|||
|
|
@ -9971,13 +9971,6 @@ function get_level_course_num(levelNum)
|
|||
-- ...
|
||||
end
|
||||
|
||||
--- @param fileIndex integer
|
||||
--- @param courseIndex integer
|
||||
--- Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update. Useful for leaderboard tracking or displaying recent progress
|
||||
function touch_coin_score_age(fileIndex, courseIndex)
|
||||
-- ...
|
||||
end
|
||||
|
||||
--- @param fileIndex integer
|
||||
--- @param forceSave integer
|
||||
--- Saves the current state of the game into a specified save file. Includes data verification and backup management. Useful for maintaining game progress during play or when saving manually
|
||||
|
|
|
|||
|
|
@ -4805,30 +4805,6 @@ Gets the level number's corresponding course number
|
|||
|
||||
<br />
|
||||
|
||||
## [touch_coin_score_age](#touch_coin_score_age)
|
||||
|
||||
### Description
|
||||
Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update. Useful for leaderboard tracking or displaying recent progress
|
||||
|
||||
### Lua Example
|
||||
`touch_coin_score_age(fileIndex, courseIndex)`
|
||||
|
||||
### Parameters
|
||||
| Field | Type |
|
||||
| ----- | ---- |
|
||||
| fileIndex | `integer` |
|
||||
| courseIndex | `integer` |
|
||||
|
||||
### Returns
|
||||
- None
|
||||
|
||||
### C Prototype
|
||||
`void touch_coin_score_age(s32 fileIndex, s32 courseIndex);`
|
||||
|
||||
[:arrow_up_small:](#)
|
||||
|
||||
<br />
|
||||
|
||||
## [save_file_do_save](#save_file_do_save)
|
||||
|
||||
### Description
|
||||
|
|
|
|||
|
|
@ -1782,7 +1782,6 @@
|
|||
- save_file.h
|
||||
- [get_level_num_from_course_num](functions-6.md#get_level_num_from_course_num)
|
||||
- [get_level_course_num](functions-6.md#get_level_course_num)
|
||||
- [touch_coin_score_age](functions-6.md#touch_coin_score_age)
|
||||
- [save_file_do_save](functions-6.md#save_file_do_save)
|
||||
- [save_file_erase](functions-6.md#save_file_erase)
|
||||
- [save_file_erase_current_backup_save](functions-6.md#save_file_erase_current_backup_save)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
/*---------------------------------------------------------------------*
|
||||
Copyright (C) 1998 Nintendo. (Originated by SGI)
|
||||
|
||||
|
||||
$RCSfile: os_eeprom.h,v $
|
||||
$Revision: 1.1 $
|
||||
$Date: 1998/10/09 08:01:06 $
|
||||
|
|
@ -94,8 +94,9 @@ extern "C" {
|
|||
extern s32 osEepromProbe(OSMesgQueue *);
|
||||
extern s32 osEepromRead(OSMesgQueue *, u8, u8 *);
|
||||
extern s32 osEepromWrite(OSMesgQueue *, u8, u8 *);
|
||||
extern s32 osEepromLongRead(OSMesgQueue *, u8, u8 *, int);
|
||||
extern s32 osEepromLongWrite(OSMesgQueue *, u8, u8 *, int);
|
||||
extern s32 osEepromLongRead(OSMesgQueue *, u8 , u8 *, int, char *, size_t);
|
||||
extern s32 osEepromLongReadFile(OSMesgQueue *, u8, u8, u8 *, int);
|
||||
extern s32 osEepromLongWrite(OSMesgQueue *, u8, u8, u8 *, int);
|
||||
|
||||
|
||||
#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ DEBUG_FLY = "@ entered debug free-fly mode"
|
|||
IMPORT_MOD_SUCCESS = "\\#a0ffa0\\Imported mod\n\\#dcdcdc\\'@'"
|
||||
IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\Imported DynOS pack\n\\#dcdcdc\\'@'"
|
||||
IMPORT_PALETTE_SUCCESS = "\\#a0ffa0\\Imported palette preset\n\\#dcdcdc\\'@'"
|
||||
IMPORT_SAVE_SUCCESS = "\\#a0ffa0\\Imported save\n\\#dcdcdc\\'@'"
|
||||
IMPORT_FAIL = "\\#ffa0a0\\Failed to import\n\\#dcdcdc\\'@'"
|
||||
IMPORT_FAIL_INGAME = "\\#ffa0a0\\Can not import while in-game"
|
||||
COOPNET_CONNECTION_FAILED = "\\#ffa0a0\\Could not connect to CoopNet!"
|
||||
|
|
@ -223,6 +224,11 @@ ERASE = "Erase"
|
|||
EDIT = "Edit"
|
||||
EDIT_TITLE = "EDIT"
|
||||
EDIT_NAME = "Edit Save File @'s Name:"
|
||||
APPLY = "Apply"
|
||||
CREATE_SAVE = "Create Save"
|
||||
CREATE = "Create"
|
||||
CREATE_TITLE = "CREATE"
|
||||
CREATE_NAME = "Set Save File @'s Name:"
|
||||
|
||||
[HOST_SETTINGS]
|
||||
SETTINGS = "SETTINGS"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ DEBUG_FLY = "@ hat den Debug-Free-Fly-Modus aktiviert."
|
|||
IMPORT_MOD_SUCCESS = "\\#a0ffa0\\Mod erfolgreich importiert\n\\#dcdcdc\\'@'"
|
||||
IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\DynOS-Paket erfolgreich importiert\n\\#dcdcdc\\'@'"
|
||||
IMPORT_PALETTE_SUCCESS = "\\#a0ffa0\\Paletten-Voreinstellung erfolgreich importiert\n\\#dcdcdc\\'@'"
|
||||
IMPORT_SAVE_SUCCESS = "\\#a0ffa0\\Spielstand importiert:\n\\#dcdcdc\\'@'"
|
||||
IMPORT_FAIL = "\\#ffa0a0\\Importierung fehlgeschlagen\n\\#dcdcdc\\'@'"
|
||||
IMPORT_FAIL_INGAME = "\\#ffa0a0\\Ein Fehler beim Importieren ist aufgetreten."
|
||||
COOPNET_CONNECTION_FAILED = "\\#ffa0a0\\Es konnte keine Verbindung zu CoopNet hergestellt werden!"
|
||||
|
|
@ -223,6 +224,10 @@ ERASE = "Löschen"
|
|||
EDIT = "Bearbeiten"
|
||||
EDIT_TITLE = "BEARBEITEN"
|
||||
EDIT_NAME = "Bearbeite den Namen des Spielstandes @:"
|
||||
CREATE_SAVE = "Spielstand erstellen"
|
||||
CREATE = "Erstellen"
|
||||
CREATE_TITLE = "ERSTELLEN"
|
||||
CREATE_NAME = "Name von Spielstand @ festlegen:"
|
||||
|
||||
[HOST_SETTINGS]
|
||||
SETTINGS = "EINSTELLUNGEN"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ DEBUG_FLY = "@ entrou no modo de voo livre de debug"
|
|||
IMPORT_MOD_SUCCESS = "\\#dcdcdc\\'@'\n\\#a0ffa0\\Mod importado"
|
||||
IMPORT_DYNOS_SUCCESS = "\\#dcdcdc\\'@'\n\\#a0ffa0\\Pacote DynOS importado"
|
||||
IMPORT_PALETTE_SUCCESS = "\\#dcdcdc\\'@'\n\\#a0ffa0\\Paleta importada"
|
||||
IMPORT_SAVE_SUCCESS = "\\#a0ffa0\\Arquivo importado\n\\#dcdcdc\\'@'"
|
||||
IMPORT_FAIL = "\\#dcdcdc\\'@'\n\\#ffa0a0\\Erro ao importar"
|
||||
IMPORT_FAIL_INGAME = "\\#ffa0a0\\Não é possível importar\ndurante o jogo"
|
||||
COOPNET_CONNECTION_FAILED = "\\#ffa0a0\\Não foi possível se conectar à CoopNet!"
|
||||
|
|
@ -223,6 +224,10 @@ EDIT_TITLE = "EDITAR ARQUIVO"
|
|||
EDIT_NAME = "Editar nome do arquivo salvo @:"
|
||||
CONFIRM = "Quer mesmo limpar este arquivo?"
|
||||
ERASE = "Limpar"
|
||||
CREATE_SAVE = "Criar Arquivo"
|
||||
CREATE = "Criar"
|
||||
CREATE_TITLE = "CRIAR"
|
||||
CREATE_NAME = "Nome do Arquivo @:"
|
||||
|
||||
[HOST_SETTINGS]
|
||||
SETTINGS = "CONFIGURAÇÕES"
|
||||
|
|
@ -413,7 +418,7 @@ MUTE_FOCUS_LOSS = "Silenciar quando a janela estiver desfocada"
|
|||
|
||||
[LANGUAGE]
|
||||
LANGUAGE = "IDIOMA"
|
||||
Czech = "Tcheco (Čeština)"
|
||||
Czech = "Tcheco (Čeština)"
|
||||
Dutch = "Holandês (Nederlands)"
|
||||
French = "Francês (Français)"
|
||||
German = "Alemão (Deutsch)"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ DEBUG_FLY = "@ вошел в режим полета при отладке"
|
|||
IMPORT_MOD_SUCCESS = "\\#a0ffa0\\Импортирован мод\n\\#dcdcdc\\'@'"
|
||||
IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\Импортированнан пакет DynOS\n\\#dcdcdc\\'@'"
|
||||
IMPORT_PALETTE_SUCCESS = "\\#a0ffa0\\Импортированный предустановленный набор палитр\n\\#dcdcdc\\'@'"
|
||||
IMPORT_SAVE_SUCCESS = "\\#a0ffa0\\Импортировано сохранение\n\\#dcdcdc\\'@'"
|
||||
IMPORT_FAIL = "\\#ffa0a0\\Не удалось импортировать\n\\#dcdcdc\\'@'"
|
||||
IMPORT_FAIL_INGAME = "\\#ffa0a0\\Невозможно импортировать во время игры"
|
||||
COOPNET_CONNECTION_FAILED = "\\#ffa0a0\\Не удалось установить соединение с CoopNet!"
|
||||
|
|
@ -222,6 +223,10 @@ ERASE = "Удалить"
|
|||
EDIT = "Редактировать"
|
||||
EDIT_TITLE = "РЕДАКТИРОВАТЬ"
|
||||
EDIT_NAME = "Редактировать имя файла сохранения @:"
|
||||
CREATE_SAVE = "Создать сохранение"
|
||||
CREATE = "Создать"
|
||||
CREATE_TITLE = "CREATE"
|
||||
CREATE_NAME = "Изменить название сохранения @:"
|
||||
|
||||
[HOST_SETTINGS]
|
||||
SETTINGS = "SETTINGS"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ ALIGNED8 u8 gThread6Stack[0x2000];
|
|||
ALIGNED8 u8 gGfxSPTaskStack[SP_DRAM_STACK_SIZE8] = { 0 };
|
||||
// 0xc00 bytes for f3dex, 0x900 otherwise
|
||||
ALIGNED8 u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE] = { 0 };
|
||||
// 0x200 bytes
|
||||
// 0x80 bytes
|
||||
ALIGNED8 struct SaveBuffer gSaveBuffer = { 0 };
|
||||
// 0x190a0 bytes
|
||||
struct GfxPool gGfxPools[GFX_NUM_POOLS] = { 0 };
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@
|
|||
#include "macros.h"
|
||||
#include "pc/network/network.h"
|
||||
#include "pc/lua/utils/smlua_level_utils.h"
|
||||
#include "pc/mods/mod.h"
|
||||
#include "pc/mods/mods_utils.h"
|
||||
#include "pc/utils/misc.h"
|
||||
|
||||
#ifndef bcopy
|
||||
#define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0)
|
||||
#endif
|
||||
|
||||
#define MENU_DATA_MAGIC 0x4849
|
||||
#define SAVE_FILE_MAGIC 0x4441
|
||||
|
||||
#define INVALID_FILE_INDEX(_fi) ((u32)_fi >= NUM_SAVE_FILES)
|
||||
|
|
@ -29,13 +30,12 @@
|
|||
#define INVALID_COURSE_STAR_INDEX(_ci) ((u32)_ci >= COURSE_COUNT)
|
||||
#define INVALID_COURSE_COIN_INDEX(_ci) ((u32)_ci >= COURSE_STAGES_COUNT)
|
||||
|
||||
STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match");
|
||||
STATIC_ASSERT(sizeof(struct SingleSaveFile) == EEPROM_SIZE, "eeprom buffer size must match");
|
||||
|
||||
extern struct SaveBuffer gSaveBuffer;
|
||||
|
||||
struct WarpCheckpoint gWarpCheckpoint;
|
||||
|
||||
s8 gMainMenuDataModified;
|
||||
s8 gSaveFileModified;
|
||||
|
||||
u8 gLastCompletedCourseNum = COURSE_NONE;
|
||||
|
|
@ -100,12 +100,6 @@ s8 get_level_course_num(s16 levelNum) {
|
|||
return gLevelToCourseNumTable[levelNum];
|
||||
}
|
||||
|
||||
// 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) {
|
||||
UNUSED s32 pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Byteswap all multibyte fields in a SaveBlockSignature.
|
||||
*/
|
||||
|
|
@ -114,19 +108,6 @@ static inline void bswap_signature(struct SaveBlockSignature *data) {
|
|||
data->chksum = BSWAP16(data->chksum); // valid as long as the checksum is a literal sum
|
||||
}
|
||||
|
||||
/**
|
||||
* Byteswap all multibyte fields in a MainMenuSaveData.
|
||||
*/
|
||||
static inline void bswap_menudata(struct MainMenuSaveData *data) {
|
||||
for (s32 i = 0; i < NUM_SAVE_FILES; ++i)
|
||||
data->coinScoreAges[i] = BSWAP32(data->coinScoreAges[i]);
|
||||
data->soundMode = BSWAP16(data->soundMode);
|
||||
#ifdef VERSION_EU
|
||||
data->language = BSWAP16(data->language);
|
||||
#endif
|
||||
bswap_signature(&data->signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Byteswap all multibyte fields in a SaveFile.
|
||||
*/
|
||||
|
|
@ -142,19 +123,19 @@ static inline void bswap_savefile(struct SaveFile *data) {
|
|||
* Read from EEPROM to a given address.
|
||||
* The EEPROM address is computed using the offset of the destination address from gSaveBuffer.
|
||||
* Try at most 4 times, and return 0 on success. On failure, return the status returned from
|
||||
* osEepromLongRead. It also returns 0 if EEPROM isn't loaded correctly in the system.
|
||||
* osEepromLongReadFile. It also returns 0 if EEPROM isn't loaded correctly in the system.
|
||||
*/
|
||||
static s32 read_eeprom_data(void *buffer, s32 size) {
|
||||
s32 read_eeprom_data(u8 file, void *buffer, s32 size) {
|
||||
s32 status = 0;
|
||||
|
||||
if (gEepromProbe != 0) {
|
||||
s32 triesLeft = 4;
|
||||
u32 offset = (u32)((u8 *) buffer - (u8 *) &gSaveBuffer) / 8;
|
||||
u32 offset = (u32)((u8 *) buffer - (u8 *) &gSaveBuffer.files[file]) / 8;
|
||||
|
||||
do {
|
||||
block_until_rumble_pak_free();
|
||||
triesLeft--;
|
||||
status = osEepromLongRead(&gSIEventMesgQueue, offset, buffer, size);
|
||||
status = osEepromLongReadFile(&gSIEventMesgQueue, file, offset, buffer, size);
|
||||
release_rumble_pak_control();
|
||||
} while (triesLeft > 0 && status != 0);
|
||||
}
|
||||
|
|
@ -168,7 +149,7 @@ static s32 read_eeprom_data(void *buffer, s32 size) {
|
|||
* Try at most 4 times, and return 0 on success. On failure, return the status returned from
|
||||
* osEepromLongWrite. Unlike read_eeprom_data, return 1 if EEPROM isn't loaded.
|
||||
*/
|
||||
static s32 write_eeprom_data(void *buffer, s32 size, const uintptr_t baseofs) {
|
||||
s32 write_eeprom_data(u8 file, void *buffer, s32 size, const uintptr_t baseofs) {
|
||||
s32 status = 1;
|
||||
|
||||
if (gEepromProbe != 0) {
|
||||
|
|
@ -178,7 +159,7 @@ static s32 write_eeprom_data(void *buffer, s32 size, const uintptr_t baseofs) {
|
|||
do {
|
||||
block_until_rumble_pak_free();
|
||||
triesLeft--;
|
||||
status = osEepromLongWrite(&gSIEventMesgQueue, offset, buffer, size);
|
||||
status = osEepromLongWrite(&gSIEventMesgQueue, file, offset, buffer, size);
|
||||
release_rumble_pak_control();
|
||||
} while (triesLeft > 0 && status != 0);
|
||||
}
|
||||
|
|
@ -194,32 +175,16 @@ static inline s32 write_eeprom_savefile(const u32 file, const u32 slot, const u3
|
|||
if (INVALID_FILE_INDEX(file)) { return 0; }
|
||||
if (INVALID_SRC_SLOT(slot)) { return 0; }
|
||||
// calculate the EEPROM address using the file number and slot
|
||||
const uintptr_t ofs = (u8*)&gSaveBuffer.files[file][slot] - (u8*)&gSaveBuffer;
|
||||
const uintptr_t ofs = (u8*)&gSaveBuffer.files[file][slot] - (u8*)&gSaveBuffer.files[file];
|
||||
|
||||
#if IS_BIG_ENDIAN
|
||||
return write_eeprom_data(&gSaveBuffer.files[file][slot], num * sizeof(struct SaveFile), ofs);
|
||||
return write_eeprom_data(file, &gSaveBuffer.files[file][slot], num * sizeof(struct SaveFile), ofs);
|
||||
#else
|
||||
// byteswap the data and then write it
|
||||
struct SaveFile sf[num];
|
||||
bcopy(&gSaveBuffer.files[file][slot], sf, num * sizeof(sf[0]));
|
||||
for (u32 i = 0; i < num; ++i) bswap_savefile(&sf[i]);
|
||||
return write_eeprom_data(&sf, sizeof(sf), ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline s32 write_eeprom_menudata(const u32 slot, const u32 num) {
|
||||
if (INVALID_SRC_SLOT(slot)) { return 0; }
|
||||
// calculate the EEPROM address using the slot
|
||||
const uintptr_t ofs = (u8*)&gSaveBuffer.menuData[slot] - (u8*)&gSaveBuffer;
|
||||
|
||||
#if IS_BIG_ENDIAN
|
||||
return write_eeprom_data(&gSaveBuffer.menuData[slot], num * sizeof(struct MainMenuSaveData), ofs);
|
||||
#else
|
||||
// byteswap the data and then write it
|
||||
struct MainMenuSaveData md[num];
|
||||
bcopy(&gSaveBuffer.menuData[slot], md, num * sizeof(md[0]));
|
||||
for (u32 i = 0; i < num; ++i) bswap_menudata(&md[i]);
|
||||
return write_eeprom_data(&md, sizeof(md), ofs);
|
||||
return write_eeprom_data(file, &sf, sizeof(sf), ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -261,98 +226,6 @@ static void add_save_block_signature(void *buffer, s32 size, u16 magic) {
|
|||
sig->chksum = calc_checksum(buffer, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy main menu data from one backup slot to the other slot.
|
||||
*/
|
||||
UNUSED static void restore_main_menu_data(s32 srcSlot) {
|
||||
if (INVALID_SRC_SLOT(srcSlot)) { return; }
|
||||
s32 destSlot = srcSlot ^ 1;
|
||||
if (INVALID_SRC_SLOT(destSlot)) { return; }
|
||||
|
||||
// Compute checksum on source data
|
||||
add_save_block_signature(&gSaveBuffer.menuData[srcSlot], sizeof(gSaveBuffer.menuData[srcSlot]), MENU_DATA_MAGIC);
|
||||
|
||||
// Copy source data to destination
|
||||
bcopy(&gSaveBuffer.menuData[srcSlot], &gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot]));
|
||||
|
||||
// Write destination data to EEPROM
|
||||
write_eeprom_menudata(destSlot, 1);
|
||||
}
|
||||
|
||||
static void save_main_menu_data(void) {
|
||||
if (gMainMenuDataModified) {
|
||||
// Compute checksum
|
||||
add_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC);
|
||||
|
||||
// Back up data
|
||||
bcopy(&gSaveBuffer.menuData[0], &gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]));
|
||||
|
||||
// Write to EEPROM
|
||||
write_eeprom_menudata(0, 2);
|
||||
|
||||
gMainMenuDataModified = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
UNUSED static void wipe_main_menu_data(void) {
|
||||
bzero(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]));
|
||||
|
||||
// Set score ages for all courses to 3, 2, 1, and 0, respectively.
|
||||
gSaveBuffer.menuData[0].coinScoreAges[0] = 0x3FFFFFFF;
|
||||
gSaveBuffer.menuData[0].coinScoreAges[1] = 0x2AAAAAAA;
|
||||
gSaveBuffer.menuData[0].coinScoreAges[2] = 0x15555555;
|
||||
|
||||
gMainMenuDataModified = TRUE;
|
||||
save_main_menu_data();
|
||||
}
|
||||
|
||||
static s32 get_coin_score_age(s32 fileIndex, s32 courseIndex) {
|
||||
if (INVALID_FILE_INDEX(fileIndex)) { return 0; }
|
||||
return (gSaveBuffer.menuData[0].coinScoreAges[fileIndex] >> (2 * courseIndex)) & 0x3;
|
||||
}
|
||||
|
||||
static void set_coin_score_age(s32 fileIndex, s32 courseIndex, s32 age) {
|
||||
if (INVALID_FILE_INDEX(fileIndex)) { return; }
|
||||
s32 mask = 0x3 << (2 * courseIndex);
|
||||
|
||||
gSaveBuffer.menuData[0].coinScoreAges[fileIndex] &= ~mask;
|
||||
gSaveBuffer.menuData[0].coinScoreAges[fileIndex] |= age << (2 * courseIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a coin score for a save file as the newest out of all save files.
|
||||
*/
|
||||
void touch_coin_score_age(s32 fileIndex, s32 courseIndex) {
|
||||
if (INVALID_FILE_INDEX(fileIndex)) { return; }
|
||||
s32 i;
|
||||
u32 age;
|
||||
u32 currentAge = get_coin_score_age(fileIndex, courseIndex);
|
||||
|
||||
if (currentAge != 0) {
|
||||
for (i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
age = get_coin_score_age(i, courseIndex);
|
||||
if (age < currentAge) {
|
||||
set_coin_score_age(i, courseIndex, age + 1);
|
||||
}
|
||||
}
|
||||
|
||||
set_coin_score_age(fileIndex, courseIndex, 0);
|
||||
gMainMenuDataModified = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all coin scores for a save file as new.
|
||||
*/
|
||||
static void touch_high_score_ages(s32 fileIndex) {
|
||||
if (INVALID_FILE_INDEX(fileIndex)) { return; }
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < 15; i++) {
|
||||
touch_coin_score_age(fileIndex, i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy save file data from one backup slot to the other slot.
|
||||
*/
|
||||
|
|
@ -380,8 +253,6 @@ UNUSED static void restore_save_file_data(s32 fileIndex, s32 srcSlot) {
|
|||
static u8 save_file_need_bswap(const struct SaveBuffer *buf) {
|
||||
// check all signatures just in case
|
||||
for (s32 i = 0; i < 2; ++i) {
|
||||
if (buf->menuData[i].signature.magic == BSWAP16(MENU_DATA_MAGIC))
|
||||
return TRUE;
|
||||
for (s32 j = 0; j < NUM_SAVE_FILES; ++j) {
|
||||
if (buf->files[j][i].signature.magic == BSWAP16(SAVE_FILE_MAGIC))
|
||||
return TRUE;
|
||||
|
|
@ -394,14 +265,166 @@ static u8 save_file_need_bswap(const struct SaveBuffer *buf) {
|
|||
* Byteswap all multibyte fields in a SaveBuffer.
|
||||
*/
|
||||
static void save_file_bswap(struct SaveBuffer *buf) {
|
||||
bswap_menudata(buf->menuData + 0);
|
||||
bswap_menudata(buf->menuData + 1);
|
||||
for (s32 i = 0; i < NUM_SAVE_FILES; ++i) {
|
||||
bswap_savefile(buf->files[i] + 0);
|
||||
bswap_savefile(buf->files[i] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts old 512 byte save files into 4 new 128 byte save files
|
||||
*/
|
||||
static void save_file_convert_old_to_new() {
|
||||
struct LegacySaveBuffer saveBuffer = { 0 };
|
||||
s32 status = osEepromLongRead(&gSIEventMesgQueue, 0, (void*)&saveBuffer, sizeof(saveBuffer), (char*)fs_get_write_path(SAVE_FILENAME), 512);
|
||||
if (status != 0) return;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
write_eeprom_data(i, saveBuffer.files[i], sizeof(saveBuffer.files[i]), 0);
|
||||
save_file_rename_file(i, configSaveNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all save file names
|
||||
*/
|
||||
void save_file_get_all_filenames(char filenames[NUM_SAVE_FILES][MAX_SAVE_NAME_STRING]) {
|
||||
char* directory = (char*)fs_get_write_path(SAVE_DIRECTORY);
|
||||
if (!directory) return;
|
||||
|
||||
if (!fs_sys_dir_exists(directory)) return;
|
||||
struct dirent* dir = NULL;
|
||||
|
||||
DIR* d = opendir(directory);
|
||||
if (!d) { return; }
|
||||
|
||||
// iterate
|
||||
char path[SYS_MAX_PATH] = { 0 };
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
// sanity check
|
||||
if (!directory_sanity_check(dir, directory, path)) continue;
|
||||
snprintf(path, SYS_MAX_PATH, "%s", dir->d_name);
|
||||
if (strlen(path) == 0 || strlen(path) >= 256) continue;
|
||||
|
||||
// verify filename follows format (index)_(name)(SAVE_EXTENSION)
|
||||
int index = 0;
|
||||
char name[MAX_SAVE_NAME_STRING];
|
||||
char extension[12];
|
||||
if (sscanf(path, "%d_%31[^.]%11s", &index, name, extension) == 3) {
|
||||
if (index < 0 || index >= NUM_SAVE_FILES) continue;
|
||||
if (strlen(name) == 0) continue;
|
||||
if (strcmp(extension, SAVE_EXTENSION) != 0) continue;
|
||||
snprintf(filenames[index], 256, "%s", name);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets save file name for index. If it does not exist, use SM64
|
||||
*/
|
||||
void save_file_get_filename_at_index(int fileIndex, char outFilename[MAX_SAVE_NAME_STRING]) {
|
||||
char filenames[NUM_SAVE_FILES][MAX_SAVE_NAME_STRING] = { 0 };
|
||||
save_file_get_all_filenames(filenames);
|
||||
if (filenames[fileIndex] && filenames[fileIndex][0] != '\0') {
|
||||
snprintf(outFilename, MAX_SAVE_NAME_STRING, "%s", filenames[fileIndex]);
|
||||
} else {
|
||||
snprintf(outFilename, MAX_SAVE_NAME_STRING, "%s", "SM64");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get directory for a save file at index
|
||||
*/
|
||||
void save_file_get_dir(int fileIndex, char* outPath, size_t size, char* overrideName) {
|
||||
char name[MAX_SAVE_NAME_STRING] = { 0 };
|
||||
if (overrideName == NULL) {
|
||||
save_file_get_filename_at_index(fileIndex, name);
|
||||
} else {
|
||||
snprintf(name, MAX_SAVE_NAME_STRING, "%s", overrideName);
|
||||
}
|
||||
snprintf(outPath, size, "%s%d_%s%s", SAVE_DIRECTORY, fileIndex, name, SAVE_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first available index that is not being used. Returns the number of save files on failure
|
||||
*/
|
||||
s32 save_file_get_first_available_index() {
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) return 0;
|
||||
for (int i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
char filePath[256];
|
||||
save_file_get_dir(i, filePath, 256, NULL);
|
||||
if (!fs_sys_file_exists(fs_get_write_path(filePath))) return i;
|
||||
}
|
||||
return NUM_SAVE_FILES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first available index that is active. Returns 0 on failure
|
||||
*/
|
||||
s32 save_file_get_first_active_index() {
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) return 0;
|
||||
for (int i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
char filePath[256];
|
||||
save_file_get_dir(i, filePath, 256, NULL);
|
||||
if (fs_sys_file_exists(fs_get_write_path(filePath))) return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of available indexes
|
||||
*/
|
||||
s32 save_file_get_amount_of_available_indexes() {
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) return 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
char filePath[256];
|
||||
save_file_get_dir(i, filePath, 256, NULL);
|
||||
if (!fs_sys_file_exists(fs_get_write_path(filePath))) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a specific save file. Returns false on failure
|
||||
*/
|
||||
bool save_file_rename_file(s32 fileIndex, char* name) {
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) return false;
|
||||
if (strstr(name, ".")) return false;
|
||||
|
||||
char filePath[SYS_MAX_PATH];
|
||||
save_file_get_dir(fileIndex, filePath, 256, NULL);
|
||||
char newFilePath[SYS_MAX_PATH];
|
||||
save_file_get_dir(fileIndex, newFilePath, 256, name);
|
||||
|
||||
if (strcmp(filePath, newFilePath) == 0) return false;
|
||||
if (!fs_sys_file_exists(fs_get_write_path(filePath))) return false;
|
||||
|
||||
// write the save data of the file to a variable
|
||||
u8 content[EEPROM_SIZE] = { 0 };
|
||||
fs_file_t* oldFile = fs_open(filePath);
|
||||
if (oldFile == NULL) return false;
|
||||
fs_read(oldFile, content, EEPROM_SIZE);
|
||||
fs_close(oldFile);
|
||||
|
||||
// create a new file with the data
|
||||
FILE* fp = fopen(fs_get_write_path(newFilePath), "wb");
|
||||
if (fp == NULL) return false;
|
||||
bool success = fwrite(content, 1, EEPROM_SIZE, fp) == EEPROM_SIZE;
|
||||
fclose(fp);
|
||||
if (success) {
|
||||
// nuke old file!
|
||||
remove(fs_get_write_path(filePath));
|
||||
} else {
|
||||
// uh oh! new file failed to be written to :( nuke new file!!
|
||||
remove(fs_get_write_path(newFilePath));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves file data to the disk
|
||||
*/
|
||||
void save_file_do_save(s32 fileIndex, s8 forceSave) {
|
||||
if (INVALID_FILE_INDEX(fileIndex)) { return; }
|
||||
if (gNetworkType != NT_SERVER) {
|
||||
|
|
@ -426,20 +449,27 @@ void save_file_do_save(s32 fileIndex, s8 forceSave) {
|
|||
|
||||
gSaveFileModified = FALSE;
|
||||
}
|
||||
save_main_menu_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases save file ingame and from the disk
|
||||
*/
|
||||
void save_file_erase(s32 fileIndex) {
|
||||
if (INVALID_FILE_INDEX(fileIndex)) { return; }
|
||||
|
||||
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);
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) return;
|
||||
char filepath[256];
|
||||
save_file_get_dir(fileIndex, filepath, 256, NULL);
|
||||
if (!fs_sys_file_exists(fs_get_write_path(filepath))) return;
|
||||
remove(fs_get_write_path(filepath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads save file from the disk and updates mario's stars
|
||||
*/
|
||||
void save_file_reload(u8 loadAll) {
|
||||
gSaveFileModified = TRUE;
|
||||
update_all_mario_stars();
|
||||
|
|
@ -451,6 +481,9 @@ void save_file_reload(u8 loadAll) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases the current backup save
|
||||
*/
|
||||
void save_file_erase_current_backup_save(void) {
|
||||
if (INVALID_FILE_INDEX(gCurrSaveFileNum-1)) { return; }
|
||||
if (gNetworkType != NT_SERVER) { return; }
|
||||
|
|
@ -465,7 +498,6 @@ BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex) {
|
|||
if (INVALID_FILE_INDEX(srcFileIndex)) { return; }
|
||||
if (INVALID_FILE_INDEX(destFileIndex)) { return; }
|
||||
|
||||
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],
|
||||
|
|
@ -475,15 +507,23 @@ BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex) {
|
|||
save_file_do_save(destFileIndex, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads save file data from disk
|
||||
*/
|
||||
void save_file_load_all(UNUSED u8 reload) {
|
||||
//s32 file;
|
||||
|
||||
gMainMenuDataModified = FALSE;
|
||||
gSaveFileModified = FALSE;
|
||||
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) {
|
||||
save_file_convert_old_to_new();
|
||||
}
|
||||
|
||||
bzero(&gSaveBuffer, sizeof(gSaveBuffer));
|
||||
|
||||
read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer));
|
||||
for (int file = 0; file < NUM_SAVE_FILES; file++) {
|
||||
read_eeprom_data(file, &gSaveBuffer.files[file], sizeof(gSaveBuffer.files[file]));
|
||||
}
|
||||
|
||||
if (save_file_need_bswap(&gSaveBuffer))
|
||||
save_file_bswap(&gSaveBuffer);
|
||||
|
|
@ -491,19 +531,6 @@ void save_file_load_all(UNUSED u8 reload) {
|
|||
// 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;
|
||||
switch (validSlots) {
|
||||
case 0: // Neither copy is correct
|
||||
wipe_main_menu_data();
|
||||
break;
|
||||
case 1: // Slot 0 is correct and slot 1 is incorrect
|
||||
restore_main_menu_data(0);
|
||||
break;
|
||||
case 2: // Slot 1 is correct and slot 0 is incorrect
|
||||
restore_main_menu_data(1);
|
||||
break;
|
||||
}
|
||||
|
||||
for (file = 0; file < NUM_SAVE_FILES; file++) {
|
||||
// Verify the save file and create a backup copy if only one of the slots is valid.
|
||||
|
|
@ -522,7 +549,6 @@ void save_file_load_all(UNUSED u8 reload) {
|
|||
}
|
||||
}
|
||||
*/
|
||||
stub_save_file_1();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -557,7 +583,6 @@ 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][gSaveFileUsingBackupSlot].courseCoinScores[courseIndex] = coinScore;
|
||||
touch_coin_score_age(fileIndex, courseIndex);
|
||||
|
||||
gGotFileCoinHiScore = TRUE;
|
||||
gSaveFileModified = TRUE;
|
||||
|
|
@ -595,24 +620,21 @@ s32 save_file_exists(s32 fileIndex) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the maximum coin score across all files for a course. The lower 16 bits
|
||||
* Get the maximum coin score across all save files for a course. The lower 16 bits
|
||||
* of the returned value are the score, and the upper 16 bits are the file number
|
||||
* of the save file with this score.
|
||||
*/
|
||||
u32 save_file_get_max_coin_score(s32 courseIndex) {
|
||||
s32 fileIndex;
|
||||
s32 maxCoinScore = -1;
|
||||
s32 maxScoreAge = -1;
|
||||
s32 maxScoreFileNum = 0;
|
||||
|
||||
for (fileIndex = 0; fileIndex < NUM_SAVE_FILES; fileIndex++) {
|
||||
if (save_file_get_star_flags(fileIndex, courseIndex) != 0) {
|
||||
s32 coinScore = save_file_get_course_coin_score(fileIndex, courseIndex);
|
||||
s32 scoreAge = get_coin_score_age(fileIndex, courseIndex);
|
||||
|
||||
if (coinScore > maxCoinScore || (coinScore == maxCoinScore && scoreAge > maxScoreAge)) {
|
||||
if (coinScore > maxCoinScore) {
|
||||
maxCoinScore = coinScore;
|
||||
maxScoreAge = scoreAge;
|
||||
maxScoreFileNum = fileIndex + 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -805,16 +827,16 @@ s32 save_file_get_cap_pos(VEC_OUT Vec3s capPos) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
void save_file_set_sound_mode(u16 mode) {
|
||||
set_sound_mode(mode);
|
||||
void save_file_set_sound_mode(UNUSED u16 mode) {
|
||||
/*set_sound_mode(mode);
|
||||
gSaveBuffer.menuData[0].soundMode = mode;
|
||||
|
||||
gMainMenuDataModified = TRUE;
|
||||
save_main_menu_data();
|
||||
save_main_menu_data();*/
|
||||
}
|
||||
|
||||
u16 save_file_get_sound_mode(void) {
|
||||
return gSaveBuffer.menuData[0].soundMode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void save_file_move_cap_to_default_location(void) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
#include "course_table.h"
|
||||
|
||||
#define EEPROM_SIZE 0x200
|
||||
#define NUM_SAVE_FILES 4
|
||||
#define NUM_SAVE_FILES 64
|
||||
#define MAX_SAVE_NAME_STRING 32
|
||||
// size of savebuffer
|
||||
#define EEPROM_SIZE 128
|
||||
|
||||
struct SaveBlockSignature
|
||||
{
|
||||
|
|
@ -45,12 +47,27 @@ enum SaveFileIndex {
|
|||
SAVE_FILE_D
|
||||
};
|
||||
|
||||
struct MainMenuSaveData
|
||||
struct SingleSaveFile
|
||||
{
|
||||
// Each save file has two copies. If one is bad, the other is used as a backup.
|
||||
struct SaveFile files[2];
|
||||
// Filler to make a single save file equal the eeprom size
|
||||
u8 filler[EEPROM_SIZE - (sizeof(struct SaveFile) * 2)];
|
||||
};
|
||||
|
||||
struct SaveBuffer
|
||||
{
|
||||
// For all save files, each save has two copies. If one is bad, the other is used as a backup.
|
||||
struct SaveFile files[NUM_SAVE_FILES][2];
|
||||
};
|
||||
|
||||
// Legacy save info for loading old save files
|
||||
struct LegacyMainMenuSaveData
|
||||
{
|
||||
// Each save file has a 2 bit "age" for each course. The higher this value,
|
||||
// the older the high score is. This is used for tie-breaking when displaying
|
||||
// on the high score screen.
|
||||
u32 coinScoreAges[NUM_SAVE_FILES];
|
||||
u32 coinScoreAges[4];
|
||||
u16 soundMode;
|
||||
|
||||
#ifdef VERSION_EU
|
||||
|
|
@ -61,17 +78,16 @@ struct MainMenuSaveData
|
|||
#endif
|
||||
|
||||
// Pad to match the EEPROM size of 0x200 (10 bytes on JP/US, 8 bytes on EU)
|
||||
u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))];
|
||||
u8 filler[512 / 2 - SUBTRAHEND - 4 * (4 + sizeof(struct SaveFile))];
|
||||
|
||||
struct SaveBlockSignature signature;
|
||||
};
|
||||
|
||||
struct SaveBuffer
|
||||
{
|
||||
struct LegacySaveBuffer {
|
||||
// Each of the four save files has two copies. If one is bad, the other is used as a backup.
|
||||
struct SaveFile files[NUM_SAVE_FILES][2];
|
||||
struct SaveFile files[4][2];
|
||||
// The main menu data has two copies. If one is bad, the other is used as a backup.
|
||||
struct MainMenuSaveData menuData[2];
|
||||
struct LegacyMainMenuSaveData menuData[2];
|
||||
};
|
||||
|
||||
extern u8 gLastCompletedCourseNum;
|
||||
|
|
@ -123,22 +139,35 @@ struct WarpCheckpoint {
|
|||
/*0x04*/ u8 warpNode;
|
||||
};
|
||||
|
||||
struct WarpNode;
|
||||
|
||||
extern struct WarpCheckpoint gWarpCheckpoint;
|
||||
|
||||
extern s8 gMainMenuDataModified;
|
||||
extern s8 gSaveFileModified;
|
||||
|
||||
s32 read_eeprom_data(u8 file, void *buffer, s32 size);
|
||||
s32 write_eeprom_data(u8 file, void *buffer, s32 size, const uintptr_t baseofs);
|
||||
|
||||
/* |description|Gets the course number's corresponding level number|descriptionEnd| */
|
||||
s8 get_level_num_from_course_num(s16 courseNum);
|
||||
|
||||
/* |description|Gets the level number's corresponding course number|descriptionEnd| */
|
||||
s8 get_level_course_num(s16 levelNum);
|
||||
|
||||
/* |description|
|
||||
Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update.
|
||||
Useful for leaderboard tracking or displaying recent progress
|
||||
|descriptionEnd| */
|
||||
void touch_coin_score_age(s32 fileIndex, s32 courseIndex);
|
||||
void save_file_get_all_filenames(char filenames[NUM_SAVE_FILES][MAX_SAVE_NAME_STRING]);
|
||||
|
||||
void save_file_get_filename_at_index(int fileIndex, char outFilename[MAX_SAVE_NAME_STRING]);
|
||||
|
||||
void save_file_get_dir(int fileIndex, char* outPath, size_t size, char* overrideName);
|
||||
|
||||
s32 save_file_get_first_available_index();
|
||||
|
||||
s32 save_file_get_amount_of_available_indexes();
|
||||
|
||||
s32 save_file_get_first_active_index();
|
||||
|
||||
bool save_file_rename_file(s32 fileIndex, char* name);
|
||||
|
||||
/* |description|
|
||||
Saves the current state of the game into a specified save file. Includes data verification and backup management.
|
||||
|
|
@ -158,7 +187,7 @@ Erases the backup data for the current save file without affecting the primary s
|
|||
void save_file_erase_current_backup_save(void);
|
||||
|
||||
BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex);
|
||||
void save_file_load_all(u8 reload);
|
||||
void save_file_load_all(UNUSED u8 reload);
|
||||
|
||||
/* |description|
|
||||
Reloads the save file data into memory, optionally resetting all save files. Marks the save file as modified.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
#include "network/moderator_list.h"
|
||||
#include "debuglog.h"
|
||||
#include "djui/djui_hud_utils.h"
|
||||
#include "game/save_file.h"
|
||||
#include "pc/network/network_player.h"
|
||||
#include "pc/pc_main.h"
|
||||
|
||||
|
|
@ -58,6 +57,8 @@ struct FunctionConfigOption {
|
|||
/*
|
||||
*Config options and default values
|
||||
*/
|
||||
|
||||
// still exists for compatibility reasons
|
||||
char configSaveNames[4][MAX_SAVE_NAME_STRING] = {
|
||||
"SM64",
|
||||
"SM64",
|
||||
|
|
@ -549,9 +550,10 @@ static void save_name_read(char** tokens, int numTokens) {
|
|||
}
|
||||
|
||||
static void save_name_write(FILE* file) {
|
||||
for (int i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
// don't write save name data anymore, this only exists for compat
|
||||
/*for (int i = 0; i < 4; i++) {
|
||||
fprintf(file, "%s %d %s\n", "save-name:", i, configSaveNames[i]);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
static const struct FunctionConfigOption functionOptions[] = {
|
||||
|
|
@ -791,6 +793,15 @@ NEXT_OPTION:
|
|||
if (configDjuiTheme >= DJUI_THEME_MAX) { configDjuiTheme = 0; }
|
||||
if (configDjuiScale >= 5) { configDjuiScale = 0; }
|
||||
|
||||
if (configHostSaveSlot >= NUM_SAVE_FILES) {
|
||||
configHostSaveSlot = save_file_get_first_active_index() + 1;
|
||||
} else {
|
||||
char filePath[256];
|
||||
save_file_get_dir(configHostSaveSlot - 1, filePath, 256, NULL);
|
||||
if (!fs_sys_file_exists(fs_get_write_path(filePath)))
|
||||
configHostSaveSlot = save_file_get_first_active_index() + 1;
|
||||
}
|
||||
|
||||
if (gCLIOpts.fullscreen == 1) {
|
||||
configWindow.fullscreen = true;
|
||||
} else if (gCLIOpts.fullscreen == 2) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
#include <stdbool.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "game/player_palette.h"
|
||||
#include "pc/lua/smlua_autogen.h"
|
||||
#include "game/area.h"
|
||||
#include "game/save_file.h"
|
||||
|
||||
#define CONFIGFILE_DEFAULT "sm64config.txt"
|
||||
#define CONFIGFILE_BACKUP "sm64config-backup.txt"
|
||||
|
|
@ -11,7 +14,6 @@
|
|||
#define MAX_BINDS 3
|
||||
#define MAX_VOLUME 127
|
||||
#define MAX_CONFIG_STRING 64
|
||||
#define MAX_SAVE_NAME_STRING 32
|
||||
|
||||
#define DEFAULT_PORT 7777
|
||||
#define DEFAULT_COOPNET_IP "net.coop64.us"
|
||||
|
|
|
|||
|
|
@ -173,7 +173,9 @@ void djui_panel_host_create(struct DjuiBase* caller) {
|
|||
djui_text_set_drop_shadow(text1, 64, 64, 64, 100);
|
||||
|
||||
char starString[64] = { 0 };
|
||||
snprintf(starString, 64, "%c x%d - %s", '~' + 1, save_file_get_total_star_count(configHostSaveSlot - 1, 0, 24), configSaveNames[configHostSaveSlot - 1]);
|
||||
char saveName[MAX_SAVE_NAME_STRING] = { 0 };
|
||||
save_file_get_filename_at_index(configHostSaveSlot - 1, saveName);
|
||||
snprintf(starString, 64, "%c x%d - %s", '~' + 1, save_file_get_total_star_count(configHostSaveSlot - 1, 0, 24), saveName);
|
||||
struct DjuiButton* button1 = djui_button_create(&rect2->base, starString, DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_save_create);
|
||||
djui_base_set_size(&button1->base, 0.45f, 32);
|
||||
djui_base_set_alignment(&button1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
|
||||
|
|
|
|||
|
|
@ -7,38 +7,78 @@
|
|||
#include "game/save_file.h"
|
||||
#include "pc/configfile.h"
|
||||
|
||||
static struct DjuiFlowLayout* sSaveLayout = NULL;
|
||||
static struct DjuiPaginated* sSavePaginated = NULL;
|
||||
static struct DjuiInputbox* sSaveNameInputBox = NULL;
|
||||
static struct DjuiBase* sSaveButtonCaller = NULL;
|
||||
static struct DjuiButton* sSaveButtons[NUM_SAVE_FILES] = { NULL };
|
||||
static char sSaveName[MAX_SAVE_NAME_STRING] = { 0 };
|
||||
static s32 sButtonTag = 0;
|
||||
|
||||
static char* sSaveLetters[NUM_SAVE_FILES] = { "A", "B", "C", "D" };
|
||||
static bool sEditing = true;
|
||||
|
||||
static void djui_panel_host_save_update_button(struct DjuiButton* button, int slot);
|
||||
static void djui_panel_host_save_add_saves(struct DjuiBase* base);
|
||||
|
||||
static void djui_panel_host_reload_saves() {
|
||||
djui_base_destroy_children(&sSaveLayout->base);
|
||||
djui_panel_host_save_add_saves(&sSaveLayout->base);
|
||||
djui_paginated_calculate_height(sSavePaginated);
|
||||
}
|
||||
|
||||
static void djui_panel_host_save_update_save_name() {
|
||||
save_file_rename_file(sButtonTag, sSaveName);
|
||||
djui_panel_host_reload_saves();
|
||||
}
|
||||
|
||||
static void djui_panel_host_save_save_name_change(UNUSED struct DjuiBase* caller) {
|
||||
snprintf(configSaveNames[sButtonTag], MAX_SAVE_NAME_STRING, "%s", sSaveNameInputBox->buffer);
|
||||
snprintf(sSaveName, MAX_SAVE_NAME_STRING, "%s", sSaveNameInputBox->buffer);
|
||||
if (strlen(sSaveNameInputBox->buffer) >= 64) {
|
||||
djui_inputbox_set_text(sSaveNameInputBox, configSaveNames[sButtonTag]);
|
||||
djui_inputbox_set_text(sSaveNameInputBox, sSaveName);
|
||||
}
|
||||
|
||||
if (strstr(sSaveName, ".")) {
|
||||
djui_inputbox_set_text_color(sSaveNameInputBox, 255, 0, 0, 255);
|
||||
} else {
|
||||
djui_inputbox_set_text_color(sSaveNameInputBox, 0, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
static bool djui_panel_edit_back(UNUSED struct DjuiBase* caller) {
|
||||
if (configSaveNames[sButtonTag][0] == '\0') {
|
||||
snprintf(configSaveNames[sButtonTag], MAX_SAVE_NAME_STRING, "SM64");
|
||||
static void djui_panel_create_create(struct DjuiBase* caller) {
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY)))
|
||||
fs_sys_mkdir(fs_get_write_path(SAVE_DIRECTORY));
|
||||
char filePath[256];
|
||||
save_file_get_dir(sButtonTag, filePath, 256, sSaveName);
|
||||
if (fs_sys_file_exists(fs_get_write_path(filePath))) return;
|
||||
u8 content[EEPROM_SIZE] = { 0 };
|
||||
FILE *fp = fopen(fs_get_write_path(filePath), "wb");
|
||||
if (fp == NULL) return;
|
||||
fwrite(content, 1, EEPROM_SIZE, fp);
|
||||
fclose(fp);
|
||||
djui_panel_host_reload_saves();
|
||||
djui_panel_menu_back(caller);
|
||||
}
|
||||
|
||||
static void djui_panel_edit_save(UNUSED struct DjuiBase* caller) {
|
||||
if (sSaveName[0] == '\0' || strstr(sSaveName, ".")) {
|
||||
snprintf(sSaveName, MAX_SAVE_NAME_STRING, "SM64");
|
||||
djui_panel_menu_back(caller);
|
||||
return;
|
||||
}
|
||||
djui_panel_host_save_update_save_name();
|
||||
djui_panel_host_save_update_button(sSaveButtons[sButtonTag], sButtonTag);
|
||||
return false;
|
||||
djui_panel_menu_back(caller);
|
||||
}
|
||||
|
||||
static void djui_panel_edit_create(struct DjuiBase* caller) {
|
||||
struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(HOST_SAVE, EDIT_TITLE), false);
|
||||
struct DjuiThreePanel* panel = djui_panel_menu_create(sEditing ? DLANG(HOST_SAVE, EDIT_TITLE) : DLANG(HOST_SAVE, CREATE_TITLE), false);
|
||||
struct DjuiBase* body = djui_three_panel_get_body(panel);
|
||||
{
|
||||
struct DjuiRect* rect1 = djui_rect_container_create(body, 32);
|
||||
{
|
||||
char buffer[64] = { 0 };
|
||||
djui_language_replace(DLANG(HOST_SAVE, EDIT_NAME), buffer, 64, '@', sSaveLetters[sButtonTag]);
|
||||
char slotBuffer[4];
|
||||
snprintf(slotBuffer, sizeof(slotBuffer), "%d", sButtonTag + 1);
|
||||
djui_language_replace(sEditing ? DLANG(HOST_SAVE, EDIT_NAME) : DLANG(HOST_SAVE, CREATE_NAME), buffer, 64, '@', slotBuffer);
|
||||
struct DjuiText* text = djui_text_create(&rect1->base, buffer);
|
||||
djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_color(&text->base, 220, 220, 220, 255);
|
||||
|
|
@ -50,25 +90,52 @@ static void djui_panel_edit_create(struct DjuiBase* caller) {
|
|||
djui_base_set_size_type(&sSaveNameInputBox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&sSaveNameInputBox->base, 0.45f, 32);
|
||||
djui_base_set_alignment(&sSaveNameInputBox->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
|
||||
char saveName[MAX_SAVE_NAME_STRING] = { 0 };
|
||||
snprintf(saveName, MAX_SAVE_NAME_STRING, "%s", configSaveNames[sButtonTag]);
|
||||
djui_inputbox_set_text(sSaveNameInputBox, saveName);
|
||||
save_file_get_filename_at_index(sButtonTag, sSaveName);
|
||||
djui_inputbox_set_text(sSaveNameInputBox, sSaveName);
|
||||
djui_interactable_hook_value_change(&sSaveNameInputBox->base, djui_panel_host_save_save_name_change);
|
||||
}
|
||||
|
||||
djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
|
||||
struct DjuiRect* rect = djui_rect_container_create(body, 64);
|
||||
{
|
||||
djui_button_left_create(&rect->base, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
|
||||
if (!sEditing) {
|
||||
djui_button_right_create(&rect->base, DLANG(HOST_SAVE, CREATE), DJUI_BUTTON_STYLE_NORMAL, djui_panel_create_create);
|
||||
} else {
|
||||
djui_button_right_create(&rect->base, DLANG(HOST_SAVE, APPLY), DJUI_BUTTON_STYLE_NORMAL, djui_panel_edit_save);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panel->on_back = djui_panel_edit_back;
|
||||
djui_panel_add(caller, panel, NULL);
|
||||
}
|
||||
|
||||
static void djui_panel_save_create(struct DjuiBase* caller) {
|
||||
sEditing = false;
|
||||
sButtonTag = save_file_get_first_available_index();
|
||||
if (sButtonTag >= NUM_SAVE_FILES) return;
|
||||
djui_panel_edit_create(caller);
|
||||
}
|
||||
|
||||
static void djui_panel_host_save_edit(struct DjuiBase* caller) {
|
||||
sEditing = true;
|
||||
sButtonTag = caller->tag;
|
||||
djui_panel_edit_create(caller);
|
||||
}
|
||||
|
||||
static void djui_panel_host_save_update_button(struct DjuiButton* button, int slot) {
|
||||
if (!button || !button->text) return;
|
||||
char starString[64] = { 0 };
|
||||
snprintf(starString, 64, "%c x%d - %s", '~' + 1, save_file_get_total_star_count(slot, 0, 24), configSaveNames[slot]);
|
||||
char name[MAX_SAVE_NAME_STRING] = { 0 };
|
||||
save_file_get_filename_at_index(slot, name);
|
||||
snprintf(starString, 64, "%c x%d - %s", '~' + 1, save_file_get_total_star_count(slot, 0, 24), name);
|
||||
djui_text_set_text(button->text, starString);
|
||||
}
|
||||
|
||||
static bool djui_panel_host_save_back(UNUSED struct DjuiBase* caller) {
|
||||
djui_panel_host_save_update_button((struct DjuiButton*)sSaveButtonCaller, configHostSaveSlot - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void djui_panel_host_save_button_click(struct DjuiBase* caller) {
|
||||
configHostSaveSlot = caller->tag + 1;
|
||||
djui_panel_host_save_update_button((struct DjuiButton*)sSaveButtonCaller, configHostSaveSlot - 1);
|
||||
|
|
@ -79,20 +146,42 @@ static void djui_panel_host_save_erase_yes(struct DjuiBase* caller) {
|
|||
save_file_erase(sButtonTag);
|
||||
play_character_sound(gMarioState, CHAR_SOUND_WAAAOOOW);
|
||||
djui_panel_host_save_update_button(sSaveButtons[sButtonTag], sButtonTag);
|
||||
djui_panel_host_reload_saves();
|
||||
djui_panel_menu_back(caller);
|
||||
}
|
||||
|
||||
static void djui_panel_host_save_erase(struct DjuiBase* caller) {
|
||||
sButtonTag = caller->tag;
|
||||
djui_panel_confirm_create(caller,
|
||||
DLANG(HOST_SAVE, ERASE_TITLE),
|
||||
DLANG(HOST_SAVE, CONFIRM),
|
||||
djui_panel_host_save_erase_yes);
|
||||
djui_panel_confirm_create(caller, DLANG(HOST_SAVE, ERASE_TITLE), DLANG(HOST_SAVE, CONFIRM), djui_panel_host_save_erase_yes);
|
||||
}
|
||||
|
||||
static void djui_panel_host_save_edit(struct DjuiBase* caller) {
|
||||
sButtonTag = caller->tag;
|
||||
djui_panel_edit_create(caller);
|
||||
void djui_panel_host_save_add_saves(struct DjuiBase* base) {
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) return;
|
||||
for (int i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
char filepath[256];
|
||||
save_file_get_dir(i, filepath, 256, NULL);
|
||||
if (!fs_sys_file_exists(fs_get_write_path(filepath))) continue;
|
||||
struct DjuiRect* rect1 = djui_rect_container_create(base, 32);
|
||||
{
|
||||
struct DjuiButton* button1 = djui_button_create(&rect1->base, "", DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_save_button_click);
|
||||
djui_panel_host_save_update_button(button1, i);
|
||||
djui_base_set_size(&button1->base, 0.6f, 32);
|
||||
button1->base.tag = i;
|
||||
sSaveButtons[i] = button1;
|
||||
|
||||
struct DjuiButton* button2 = djui_button_create(&rect1->base, DLANG(HOST_SAVE, ERASE), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_save_erase);
|
||||
button2->base.tag = i;
|
||||
djui_base_set_size(&button2->base, 0.19f, 32);
|
||||
djui_base_set_alignment(&button2->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_TOP);
|
||||
djui_base_set_location(&button2->base, configDjuiThemeCenter ? 127 : button1->rect->base.width.value + 98, 0);
|
||||
djui_base_set_enabled(&button2->base, gDjuiInMainMenu || gCurrSaveFileNum - 1 != i);
|
||||
|
||||
struct DjuiButton* button3 = djui_button_create(&rect1->base, DLANG(HOST_SAVE, EDIT), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_save_edit);
|
||||
button3->base.tag = i;
|
||||
djui_base_set_size(&button3->base, 0.19f, 32);
|
||||
djui_base_set_alignment(&button3->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void djui_panel_host_save_create(struct DjuiBase* caller) {
|
||||
|
|
@ -101,31 +190,17 @@ void djui_panel_host_save_create(struct DjuiBase* caller) {
|
|||
struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(HOST_SAVE, SAVE_TITLE), false);
|
||||
struct DjuiBase* body = djui_three_panel_get_body(panel);
|
||||
{
|
||||
for (int i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
struct DjuiRect* rect1 = djui_rect_container_create(body, 32);
|
||||
{
|
||||
struct DjuiButton* button1 = djui_button_create(&rect1->base, "", DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_save_button_click);
|
||||
djui_panel_host_save_update_button(button1, i);
|
||||
djui_base_set_size(&button1->base, 0.6f, 32);
|
||||
button1->base.tag = i;
|
||||
sSaveButtons[i] = button1;
|
||||
|
||||
struct DjuiButton* button2 = djui_button_create(&rect1->base, DLANG(HOST_SAVE, ERASE), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_save_erase);
|
||||
button2->base.tag = i;
|
||||
djui_base_set_size(&button2->base, 0.19f, 32);
|
||||
djui_base_set_alignment(&button2->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_TOP);
|
||||
djui_base_set_location(&button2->base, configDjuiThemeCenter ? 127 : button1->rect->base.width.value + 98, 0);
|
||||
djui_base_set_enabled(&button2->base, gDjuiInMainMenu || gCurrSaveFileNum - 1 != i);
|
||||
|
||||
struct DjuiButton* button3 = djui_button_create(&rect1->base, DLANG(HOST_SAVE, EDIT), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_save_edit);
|
||||
button3->base.tag = i;
|
||||
djui_base_set_size(&button3->base, 0.19f, 32);
|
||||
djui_base_set_alignment(&button3->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
|
||||
}
|
||||
}
|
||||
struct DjuiPaginated* paginated = djui_paginated_create(body, 8);
|
||||
paginated->showMaxCount = true;
|
||||
sSaveLayout = paginated->layout;
|
||||
djui_panel_host_save_add_saves(&paginated->layout->base);
|
||||
djui_paginated_calculate_height(paginated);
|
||||
sSavePaginated = paginated;
|
||||
|
||||
djui_button_create(body, DLANG(HOST_SAVE, CREATE_SAVE), DJUI_BUTTON_STYLE_NORMAL, djui_panel_save_create);
|
||||
djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_NORMAL, djui_panel_menu_back);
|
||||
}
|
||||
|
||||
panel->on_back = djui_panel_host_save_back;
|
||||
djui_panel_add(caller, panel, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include "../platform.h"
|
||||
|
||||
#define SAVE_FILENAME "sm64_save_file.bin"
|
||||
#define SAVE_DIRECTORY "saves/"
|
||||
#define SAVE_EXTENSION ".bin"
|
||||
|
||||
extern char fs_writepath[];
|
||||
|
||||
|
|
|
|||
|
|
@ -3052,7 +3052,7 @@ char gSmluaConstants[] = ""
|
|||
"EMBLEM=7\n"
|
||||
"PLAYER_PART_MAX=8\n"
|
||||
"METAL=CAP\n"
|
||||
"NUM_SAVE_FILES=4\n"
|
||||
"NUM_SAVE_FILES=64\n"
|
||||
"SAVE_FILE_A=0\n"
|
||||
"SAVE_FILE_B=1\n"
|
||||
"SAVE_FILE_C=2\n"
|
||||
|
|
|
|||
|
|
@ -29798,25 +29798,6 @@ int smlua_func_get_level_course_num(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int smlua_func_touch_coin_score_age(lua_State* L) {
|
||||
if (L == NULL) { return 0; }
|
||||
|
||||
int top = lua_gettop(L);
|
||||
if (top != 2) {
|
||||
LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "touch_coin_score_age", 2, top);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 fileIndex = smlua_to_integer(L, 1);
|
||||
if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "touch_coin_score_age"); return 0; }
|
||||
s32 courseIndex = smlua_to_integer(L, 2);
|
||||
if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "touch_coin_score_age"); return 0; }
|
||||
|
||||
touch_coin_score_age(fileIndex, courseIndex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int smlua_func_save_file_do_save(lua_State* L) {
|
||||
if (L == NULL) { return 0; }
|
||||
|
||||
|
|
@ -38445,7 +38426,6 @@ void smlua_bind_functions_autogen(void) {
|
|||
// save_file.h
|
||||
smlua_bind_function(L, "get_level_num_from_course_num", smlua_func_get_level_num_from_course_num);
|
||||
smlua_bind_function(L, "get_level_course_num", smlua_func_get_level_course_num);
|
||||
smlua_bind_function(L, "touch_coin_score_age", smlua_func_touch_coin_score_age);
|
||||
smlua_bind_function(L, "save_file_do_save", smlua_func_save_file_do_save);
|
||||
smlua_bind_function(L, "save_file_erase", smlua_func_save_file_erase);
|
||||
smlua_bind_function(L, "save_file_erase_current_backup_save", smlua_func_save_file_erase_current_backup_save);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include "pc/djui/djui_popup.h"
|
||||
#include "mods.h"
|
||||
#include "mods_utils.h"
|
||||
#include "game/save_file.h"
|
||||
#include "buffers/buffers.h"
|
||||
|
||||
static bool mod_import_lua(char* src) {
|
||||
char dst[SYS_MAX_PATH] = { 0 };
|
||||
|
|
@ -252,10 +254,48 @@ static bool mod_import_zip(char* path, bool* isLua, bool* isDynos) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool mod_import_save(char* src) {
|
||||
// check to see if it's the old format first
|
||||
struct LegacySaveBuffer legacySaveBuffer = { 0 };
|
||||
s32 status = osEepromLongRead(NULL, 0, (void*)&legacySaveBuffer, sizeof(legacySaveBuffer), src, 512);
|
||||
if (status == 0) {
|
||||
// old data is a go, write eeprom data
|
||||
if (save_file_get_amount_of_available_indexes() < 4) {
|
||||
LOG_ERROR("Ran out of save files slots");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int file = save_file_get_first_available_index();
|
||||
write_eeprom_data(file, legacySaveBuffer.files[i], sizeof(legacySaveBuffer.files[i]), 0);
|
||||
}
|
||||
LOG_INFO("Imported save: '%s' into 4 parts", src);
|
||||
save_file_load_all(TRUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// it's not legacy, try to load it using the new format
|
||||
int firstIndex = save_file_get_first_available_index();
|
||||
if (firstIndex == NUM_SAVE_FILES) {
|
||||
LOG_ERROR("Ran out of save files slots");
|
||||
return false;
|
||||
}
|
||||
status = osEepromLongRead(NULL, 0, (void*)&gSaveBuffer.files[firstIndex], sizeof(gSaveBuffer.files[firstIndex]), src, EEPROM_SIZE);
|
||||
if (status == 0) {
|
||||
// data is a go, write to eeprom data
|
||||
write_eeprom_data(firstIndex, gSaveBuffer.files[firstIndex], sizeof(gSaveBuffer.files[firstIndex]), 0);
|
||||
LOG_INFO("Imported save: '%s'", src);
|
||||
save_file_load_all(TRUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mod_import_file(char* path) {
|
||||
bool isLua = false;
|
||||
bool isDynos = false;
|
||||
bool isPalette = false;
|
||||
bool isSave = false;
|
||||
bool ret = false;
|
||||
|
||||
if (gNetworkType != NT_NONE && !path_ends_with(path, ".ini")) {
|
||||
|
|
@ -271,6 +311,9 @@ bool mod_import_file(char* path) {
|
|||
ret = mod_import_palette(path);
|
||||
} else if (path_ends_with(path, ".zip")) {
|
||||
ret = mod_import_zip(path, &isLua, &isDynos);
|
||||
} else if (path_ends_with(path, SAVE_EXTENSION)) {
|
||||
isSave = true;
|
||||
ret = mod_import_save(path);
|
||||
}
|
||||
|
||||
char msg[SYS_MAX_PATH] = { 0 };
|
||||
|
|
@ -288,6 +331,9 @@ bool mod_import_file(char* path) {
|
|||
} else if (isPalette) {
|
||||
djui_language_replace(DLANG(NOTIF, IMPORT_PALETTE_SUCCESS), msg, SYS_MAX_PATH, '@', basename);
|
||||
djui_popup_create(msg, 2);
|
||||
} else if (isSave) {
|
||||
djui_language_replace(DLANG(NOTIF, IMPORT_SAVE_SUCCESS), msg, SYS_MAX_PATH, '@', basename);
|
||||
djui_popup_create(msg, 2);
|
||||
}
|
||||
} else {
|
||||
djui_language_replace(DLANG(NOTIF, IMPORT_FAIL), msg, SYS_MAX_PATH, '@', basename);
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ static bool char_valid(const char* buffer, bool isKey) {
|
|||
}
|
||||
|
||||
static void mod_storage_get_filename(char* dest) {
|
||||
const char* path = fs_get_write_path(SAVE_DIRECTORY); // get user path
|
||||
const char* path = fs_get_write_path(MS_SAVE_DIRECTORY); // get user path
|
||||
snprintf(dest, SYS_MAX_PATH - 1, "%s/%s", path, gLuaActiveMod->relativePath); // append sav folder
|
||||
strdelete(dest, ".lua"); // delete ".lua" from sav name
|
||||
strcat(dest, SAVE_EXTENSION); // append SAVE_EXTENSION
|
||||
strcat(dest, MS_SAVE_EXTENSION); // append MS_SAVE_EXTENSION
|
||||
normalize_path(dest); // fix any out of place slashes
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ static bool mod_storage_check_inputs(const char *key, const char *value, char *f
|
|||
if (!char_valid(value, false)) { return false; }
|
||||
|
||||
// write: ensure savPath exists
|
||||
const char* savPath = fs_get_write_path(SAVE_DIRECTORY);
|
||||
const char* savPath = fs_get_write_path(MS_SAVE_DIRECTORY);
|
||||
if (!fs_sys_dir_exists(savPath)) { fs_sys_mkdir(savPath); }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ extern "C" {
|
|||
|
||||
#define MAX_KEYS 4096
|
||||
#define MAX_KEY_VALUE_LENGTH 1024
|
||||
#define SAVE_DIRECTORY "sav"
|
||||
#define SAVE_EXTENSION ".sav"
|
||||
#define MS_SAVE_DIRECTORY "sav"
|
||||
#define MS_SAVE_EXTENSION ".sav"
|
||||
|
||||
/* |description|Saves a `key` corresponding to a string `value` to mod storage|descriptionEnd| */
|
||||
bool mod_storage_save(const char* key, const char* value);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "game/first_person_cam.h"
|
||||
#include "game/envfx_snow.h"
|
||||
#include "game/mario.h"
|
||||
#include "game/save_file.h"
|
||||
#include "engine/math_util.h"
|
||||
#include "engine/lighting_engine.h"
|
||||
#include "src/audio/load.h"
|
||||
|
|
@ -169,8 +170,8 @@ bool network_init(enum NetworkType inNetworkType, bool reconnecting) {
|
|||
dynos_behavior_hook_all_custom_behaviors();
|
||||
|
||||
network_player_connected(NPT_LOCAL, 0, configPlayerModel, &configPlayerPalette, configPlayerName, get_local_discord_id());
|
||||
extern u8* gOverrideEeprom;
|
||||
gOverrideEeprom = NULL;
|
||||
extern u8* gOverrideEeprom[NUM_SAVE_FILES];
|
||||
memset(gOverrideEeprom, 0, sizeof(gOverrideEeprom));
|
||||
|
||||
if (gCurrLevelNum != (s16)gLevelValues.entryLevel) {
|
||||
extern s16 gChangeLevelTransition;
|
||||
|
|
@ -239,6 +240,8 @@ bool network_allow_unknown_local_index(enum PacketType packetType) {
|
|||
|| (packetType == PACKET_MOD_LIST_ENTRY)
|
||||
|| (packetType == PACKET_MOD_LIST_FILE)
|
||||
|| (packetType == PACKET_MOD_LIST_DONE)
|
||||
|| (packetType == PACKET_DOWNLOAD_SAVE_REQUEST)
|
||||
|| (packetType == PACKET_DOWNLOAD_SAVE_FILE)
|
||||
|| (packetType == PACKET_DOWNLOAD_REQUEST)
|
||||
|| (packetType == PACKET_DOWNLOAD)
|
||||
|| (packetType == PACKET_KEEP_ALIVE)
|
||||
|
|
@ -703,8 +706,8 @@ void network_shutdown(bool sendLeaving, bool exiting, bool popup, bool reconnect
|
|||
dynos_model_clear_pool(MODEL_POOL_SESSION);
|
||||
|
||||
// reset other stuff
|
||||
extern u8* gOverrideEeprom;
|
||||
gOverrideEeprom = NULL;
|
||||
extern u8* gOverrideEeprom[NUM_SAVE_FILES];
|
||||
memset(gOverrideEeprom, 0, sizeof(gOverrideEeprom));
|
||||
extern u8 gOverrideFreezeCamera;
|
||||
gOverrideFreezeCamera = false;
|
||||
gDjuiHudLockMouse = false;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ void packet_process(struct Packet* p) {
|
|||
case PACKET_MODERATOR: network_receive_moderator(p); break;
|
||||
case PACKET_KEEP_ALIVE: network_receive_keep_alive(p); break;
|
||||
case PACKET_LEAVING: network_receive_leaving(p); break;
|
||||
case PACKET_DOWNLOAD_SAVE_REQUEST: network_receive_download_saves_request(p); break;
|
||||
case PACKET_DOWNLOAD_SAVE_FILE: network_receive_download_save(p); break;
|
||||
case PACKET_SAVE_FILE: network_receive_save_file(p); break;
|
||||
case PACKET_SAVE_SET_FLAG: network_receive_save_set_flag(p); break;
|
||||
case PACKET_SAVE_REMOVE_FLAG: network_receive_save_remove_flag(p); break;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ enum PacketType {
|
|||
PACKET_KICK,
|
||||
PACKET_KEEP_ALIVE,
|
||||
PACKET_LEAVING,
|
||||
PACKET_DOWNLOAD_SAVE_REQUEST,
|
||||
PACKET_DOWNLOAD_SAVE_FILE,
|
||||
PACKET_FINISHED_DOWNLOAD_SAVE_FILE,
|
||||
PACKET_SAVE_FILE,
|
||||
PACKET_SAVE_SET_FLAG,
|
||||
PACKET_SAVE_REMOVE_FLAG,
|
||||
|
|
@ -74,7 +77,7 @@ enum PacketType {
|
|||
|
||||
PACKET_LUA_CUSTOM,
|
||||
PACKET_LUA_CUSTOM_BYTESTRING,
|
||||
|
||||
|
||||
PACKET_COMMAND,
|
||||
PACKET_MODERATOR,
|
||||
|
||||
|
|
@ -256,6 +259,12 @@ void network_receive_keep_alive(struct Packet* p);
|
|||
void network_send_leaving(u8 globalIndex);
|
||||
void network_receive_leaving(struct Packet* p);
|
||||
|
||||
// packet_download_save_files.c
|
||||
void network_send_download_save_files_request(void);
|
||||
void network_receive_download_saves_request(UNUSED struct Packet* p);
|
||||
void network_send_download_save(int chunk);
|
||||
void network_receive_download_save(struct Packet* p);
|
||||
|
||||
// packet_save_file.c
|
||||
void network_send_save_file(s32 fileIndex);
|
||||
void network_receive_save_file(struct Packet* p);
|
||||
|
|
@ -308,7 +317,6 @@ void network_send_area_request(struct NetworkPlayer* fromNp, struct NetworkPlaye
|
|||
void network_receive_area_request(struct Packet* p);
|
||||
|
||||
// packet_area.c
|
||||
|
||||
void area_remove_sync_ids_add(u32 syncId);
|
||||
void area_remove_sync_ids_clear(void);
|
||||
void network_send_area(struct NetworkPlayer* toNp);
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ static void network_update_offset_groups(void) {
|
|||
mod->enabled = true;
|
||||
}
|
||||
LOG_INFO("Download complete!");
|
||||
network_send_join_request();
|
||||
network_send_download_save_files_request();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
100
src/pc/network/packets/packet_download_save_files.c
Normal file
100
src/pc/network/packets/packet_download_save_files.c
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#include <stdio.h>
|
||||
#include "../network.h"
|
||||
#include "PR/os_eeprom.h"
|
||||
#include "game/save_file.h"
|
||||
#include "pc/debuglog.h"
|
||||
|
||||
extern u8* gOverrideEeprom[NUM_SAVE_FILES];
|
||||
static u8 eeprom[NUM_SAVE_FILES][EEPROM_SIZE] = { 0 };
|
||||
static int filledEepromData = 0;
|
||||
static int chunks = (NUM_SAVE_FILES * EEPROM_SIZE + (PACKET_LENGTH - 8) - 1) / (PACKET_LENGTH - 8);
|
||||
|
||||
void network_send_download_save_files_request(void) {
|
||||
SOFT_ASSERT(gNetworkType == NT_CLIENT);
|
||||
|
||||
filledEepromData = 0;
|
||||
|
||||
struct Packet p = { 0 };
|
||||
packet_init(&p, PACKET_DOWNLOAD_SAVE_REQUEST, true, PLMT_NONE);
|
||||
|
||||
network_send_to((gNetworkPlayerServer != NULL) ? gNetworkPlayerServer->localIndex : 0, &p);
|
||||
LOG_INFO("sending download save files request");
|
||||
}
|
||||
|
||||
void network_receive_download_saves_request(UNUSED struct Packet* p) {
|
||||
SOFT_ASSERT(gNetworkType == NT_SERVER);
|
||||
|
||||
for (int i = 0; i < chunks; i++) {
|
||||
network_send_download_save(i);
|
||||
}
|
||||
|
||||
LOG_INFO("sending save info");
|
||||
}
|
||||
|
||||
void network_send_download_save(int chunk) {
|
||||
SOFT_ASSERT(gNetworkType == NT_SERVER);
|
||||
|
||||
int startingSaveFile = chunk * (NUM_SAVE_FILES + chunks - 1) / chunks;
|
||||
int endSaveFile = (chunk + 1) * (NUM_SAVE_FILES + chunks - 1) / chunks;
|
||||
if (endSaveFile > NUM_SAVE_FILES) endSaveFile = NUM_SAVE_FILES;
|
||||
|
||||
struct Packet p = { 0 };
|
||||
packet_init(&p, PACKET_DOWNLOAD_SAVE_FILE, true, PLMT_NONE);
|
||||
packet_write(&p, &startingSaveFile, sizeof(startingSaveFile));
|
||||
packet_write(&p, &endSaveFile, sizeof(endSaveFile));
|
||||
|
||||
for (int i = startingSaveFile; i < endSaveFile; i++) {
|
||||
u8 content[EEPROM_SIZE] = { 0 };
|
||||
char filePath[256];
|
||||
save_file_get_dir(i, filePath, 256, NULL);
|
||||
fs_file_t* fp = fs_open(filePath);
|
||||
if (fp != NULL) {
|
||||
fs_read(fp, content, EEPROM_SIZE);
|
||||
fs_close(fp);
|
||||
}
|
||||
packet_write(&p, content, sizeof(content));
|
||||
}
|
||||
|
||||
network_send_to(0, &p);
|
||||
}
|
||||
|
||||
void network_receive_download_save(struct Packet* p) {
|
||||
SOFT_ASSERT(gNetworkType == NT_CLIENT);
|
||||
|
||||
if (p->localIndex != UNKNOWN_LOCAL_INDEX) {
|
||||
if (gNetworkPlayerServer == NULL || gNetworkPlayerServer->localIndex != p->localIndex) {
|
||||
LOG_ERROR("Received download from known local index '%d'", p->localIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (filledEepromData >= NUM_SAVE_FILES * EEPROM_SIZE) {
|
||||
LOG_ERROR("Received eeprom data after eeprom was filled");
|
||||
djui_popup_create(DLANG(NOTIF, DISCONNECT_CLOSED), 1);
|
||||
network_shutdown(false, false, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
int startingSaveFile = 0;
|
||||
int endSaveFile = 0;
|
||||
packet_read(p, &startingSaveFile, sizeof(startingSaveFile));
|
||||
packet_read(p, &endSaveFile, sizeof(endSaveFile));
|
||||
|
||||
for (int i = startingSaveFile; i < endSaveFile; i++) {
|
||||
packet_read(p, &eeprom[i], sizeof(eeprom[i]));
|
||||
filledEepromData += EEPROM_SIZE;
|
||||
}
|
||||
|
||||
if (filledEepromData == NUM_SAVE_FILES * EEPROM_SIZE) {
|
||||
for (int i = 0; i < NUM_SAVE_FILES; i++) {
|
||||
gOverrideEeprom[i] = eeprom[i];
|
||||
}
|
||||
filledEepromData = 0;
|
||||
network_send_join_request();
|
||||
} else if (filledEepromData > NUM_SAVE_FILES * EEPROM_SIZE) {
|
||||
LOG_ERROR("Filled eeprom data too much! Should be %d, but is %d", NUM_SAVE_FILES * EEPROM_SIZE, filledEepromData);
|
||||
djui_popup_create(DLANG(NOTIF, DISCONNECT_CLOSED), 1);
|
||||
network_shutdown(false, false, false, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,10 +27,7 @@
|
|||
#include "pc/configfile.h"
|
||||
#include "pc/lua/utils/smlua_misc_utils.h"
|
||||
|
||||
extern u8* gOverrideEeprom;
|
||||
static u8 eeprom[512] = { 0 };
|
||||
|
||||
static u8 sJoinRequestPlayerModel;
|
||||
static u8 sJoinRequestPlayerModel;
|
||||
static struct PlayerPalette sJoinRequestPlayerPalette;
|
||||
static char sJoinRequestPlayerName[MAX_CONFIG_STRING];
|
||||
static char sJoinRequestDiscordId[64];
|
||||
|
|
@ -40,7 +37,6 @@ void network_send_join_request(void) {
|
|||
SOFT_ASSERT(gNetworkType == NT_CLIENT);
|
||||
|
||||
gNetworkSentJoin = true;
|
||||
gOverrideEeprom = eeprom;
|
||||
|
||||
struct Packet p = { 0 };
|
||||
packet_init(&p, PACKET_JOIN_REQUEST, true, PLMT_NONE);
|
||||
|
|
@ -99,13 +95,6 @@ void network_send_join(struct Packet* joinRequestPacket) {
|
|||
|
||||
// do connection event
|
||||
network_player_connected(NPT_CLIENT, globalIndex, sJoinRequestPlayerModel, &sJoinRequestPlayerPalette, sJoinRequestPlayerName, sJoinRequestDiscordId);
|
||||
|
||||
fs_file_t* fp = fs_open(SAVE_FILENAME);
|
||||
if (fp != NULL) {
|
||||
fs_read(fp, eeprom, 512);
|
||||
fs_close(fp);
|
||||
}
|
||||
|
||||
char version[MAX_VERSION_LENGTH] = { 0 };
|
||||
snprintf(version, MAX_VERSION_LENGTH, "%s", get_version());
|
||||
LOG_INFO("sending version: %s", version);
|
||||
|
|
@ -126,7 +115,6 @@ void network_send_join(struct Packet* joinRequestPacket) {
|
|||
packet_write(&p, &gServerSettings.maxPlayers, sizeof(u8));
|
||||
packet_write(&p, &gServerSettings.pauseAnywhere, sizeof(u8));
|
||||
packet_write(&p, &gServerSettings.pvpType, sizeof(u8));
|
||||
packet_write(&p, eeprom, sizeof(u8) * 512);
|
||||
|
||||
network_send_to(globalIndex, &p);
|
||||
LOG_INFO("sending join packet");
|
||||
|
|
@ -140,8 +128,6 @@ void network_receive_join(struct Packet* p) {
|
|||
LOG_INFO("received join packet");
|
||||
gCurrentlyJoining = true;
|
||||
|
||||
gOverrideEeprom = eeprom;
|
||||
|
||||
char version[MAX_VERSION_LENGTH] = { 0 };
|
||||
snprintf(version, MAX_VERSION_LENGTH, "%s", get_version());
|
||||
LOG_INFO("client has version: %s", version);
|
||||
|
|
@ -179,7 +165,6 @@ void network_receive_join(struct Packet* p) {
|
|||
packet_read(p, &gServerSettings.maxPlayers, sizeof(u8));
|
||||
packet_read(p, &gServerSettings.pauseAnywhere, sizeof(u8));
|
||||
packet_read(p, &gServerSettings.pvpType, sizeof(u8));
|
||||
packet_read(p, eeprom, sizeof(u8) * 512);
|
||||
|
||||
network_player_connected(NPT_SERVER, 0, 0, &DEFAULT_MARIO_PALETTE, "Player", "0");
|
||||
network_player_connected(NPT_LOCAL, myGlobalIndex, configPlayerModel, &configPlayerPalette, configPlayerName, get_local_discord_id());
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ void network_remember_reliable(struct Packet* p) {
|
|||
|
||||
static float adjust_max_elapsed(enum PacketType packetType, float maxElapsed) {
|
||||
switch (packetType) {
|
||||
case PACKET_DOWNLOAD_SAVE_REQUEST:
|
||||
case PACKET_DOWNLOAD_SAVE_FILE:
|
||||
case PACKET_DOWNLOAD_REQUEST:
|
||||
case PACKET_DOWNLOAD:
|
||||
case PACKET_MOD_LIST_REQUEST:
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
#include "macros.h"
|
||||
#include "platform.h"
|
||||
#include "fs/fs.h"
|
||||
#include "configfile.h"
|
||||
#include "game/save_file.h"
|
||||
|
||||
u8* gOverrideEeprom = NULL;
|
||||
u8* gOverrideEeprom[NUM_SAVE_FILES] = { NULL };
|
||||
|
||||
extern OSMgrArgs piMgrArgs;
|
||||
|
||||
|
|
@ -124,20 +126,39 @@ s32 osEepromProbe(UNUSED OSMesgQueue *mq) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) {
|
||||
if (gOverrideEeprom != NULL) {
|
||||
memcpy(buffer, gOverrideEeprom + address * 8, nbytes);
|
||||
s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes, char *path, size_t size) {
|
||||
u8 content[size];
|
||||
s32 ret = -1;
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
return -1;
|
||||
}
|
||||
if (fread(content, 1, size, fp) == size) {
|
||||
memcpy(buffer, content + address * 8, nbytes);
|
||||
ret = 0;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 osEepromLongReadFile(UNUSED OSMesgQueue *mq, u8 fileIndex, u8 address, u8 *buffer, int nbytes) {
|
||||
if (gOverrideEeprom[fileIndex] != NULL) {
|
||||
memcpy(buffer, gOverrideEeprom[fileIndex] + address * 8, nbytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 content[512];
|
||||
u8 content[EEPROM_SIZE];
|
||||
s32 ret = -1;
|
||||
|
||||
fs_file_t *fp = fs_open(SAVE_FILENAME);
|
||||
char filePath[256];
|
||||
save_file_get_dir(fileIndex, filePath, 256, NULL);
|
||||
fs_file_t *fp = fs_open(filePath);
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (fs_read(fp, content, 512) == 512) {
|
||||
if (fs_read(fp, content, EEPROM_SIZE) == EEPROM_SIZE) {
|
||||
memcpy(buffer, content + address * 8, nbytes);
|
||||
ret = 0;
|
||||
}
|
||||
|
|
@ -146,23 +167,29 @@ s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes)
|
|||
return ret;
|
||||
}
|
||||
|
||||
s32 osEepromLongWrite(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) {
|
||||
if (gOverrideEeprom != NULL) {
|
||||
memcpy(gOverrideEeprom + address * 8, buffer, nbytes);
|
||||
s32 osEepromLongWrite(UNUSED OSMesgQueue *mq, u8 fileIndex, u8 address, u8 *buffer, int nbytes) {
|
||||
if (gOverrideEeprom[fileIndex] != NULL) {
|
||||
memcpy(gOverrideEeprom[fileIndex] + address * 8, buffer, nbytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 content[512] = { 0 };
|
||||
if (address != 0 || nbytes != 512) {
|
||||
osEepromLongRead(mq, 0, content, 512);
|
||||
u8 content[EEPROM_SIZE] = { 0 };
|
||||
if (address != 0 || nbytes != EEPROM_SIZE) {
|
||||
osEepromLongReadFile(mq, fileIndex, 0, content, EEPROM_SIZE);
|
||||
}
|
||||
memcpy(content + address * 8, buffer, nbytes);
|
||||
|
||||
FILE *fp = fopen(fs_get_write_path(SAVE_FILENAME), "wb");
|
||||
if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) {
|
||||
fs_sys_mkdir(fs_get_write_path(SAVE_DIRECTORY));
|
||||
}
|
||||
|
||||
char filePath[256];
|
||||
save_file_get_dir(fileIndex, filePath, 256, NULL);
|
||||
FILE *fp = fopen(fs_get_write_path(filePath), "wb");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
s32 ret = fwrite(content, 1, 512, fp) == 512 ? 0 : -1;
|
||||
s32 ret = fwrite(content, 1, EEPROM_SIZE, fp) == EEPROM_SIZE ? 0 : -1;
|
||||
fclose(fp);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue