From 4f25de2a73d5f4429c1c8dd2cf792095144a07f1 Mon Sep 17 00:00:00 2001 From: "y.demaisonregne" Date: Thu, 2 Mar 2023 02:13:08 +0100 Subject: [PATCH] Start decompiling save file code --- asm/disasm_0x08000c48.s | 42 +- asm/disasm_0x080051d4.s | 38 +- asm/disasm_0x08067080.s | 18 +- database.txt | 2 +- include/data/save_file_data.h | 30 + include/data/shortcut_pointers.h | 3 +- include/funcs.h | 2 +- include/globals.h | 3 - include/macros.h | 3 +- include/save_file.h | 63 + include/sram/sram.h | 8 +- include/structs/game_state.h | 9 +- include/structs/in_game_timer.h | 1 + include/structs/samus.h | 1 + include/structs/save_file.h | 234 ++++ linker.ld | 8 +- src/data/cutscenes/statue_opening_data.c | 2 +- src/data/save_file_data.c | 72 + src/data/shortcut_pointers.c | 2 +- src/erase_sram_func.c | 9 +- src/init_game.c | 2 +- src/menus/erase_sram.c | 4 +- src/menus/game_over.c | 4 +- src/menus/title_screen.c | 2 +- src/save_file.c | 1557 ++++++++++++++++++++++ src/softreset.c | 2 +- src/sprites_AI/item_banner.c | 2 +- src/sram/sram.c | 20 +- 28 files changed, 2055 insertions(+), 88 deletions(-) create mode 100644 include/data/save_file_data.h create mode 100644 include/save_file.h create mode 100644 include/structs/save_file.h create mode 100644 src/data/save_file_data.c create mode 100644 src/save_file.c diff --git a/asm/disasm_0x08000c48.s b/asm/disasm_0x08000c48.s index f930ee55..4e1a496f 100644 --- a/asm/disasm_0x08000c48.s +++ b/asm/disasm_0x08000c48.s @@ -100,7 +100,7 @@ lbl_08000d10: ldr r5, lbl_08000d5c @ =0x08754bcc ldr r1, [r5] movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked adds r4, r0, #0 cmp r4, #0 beq lbl_08000d36 @@ -113,7 +113,7 @@ lbl_08000d36: ldr r2, lbl_08000d54 @ =0x00006d40 adds r1, r1, r2 movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000d4c: .4byte 0x040000d4 @@ -129,7 +129,7 @@ lbl_08000d60: ldr r1, [r1] adds r1, #0x40 movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000d74: .4byte 0x08754bc8 @@ -143,7 +143,7 @@ lbl_08000d7c: ldr r1, [r1] adds r1, r1, r2 movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000d94: .4byte 0x08754bc8 @@ -168,7 +168,7 @@ lbl_08000da0: adds r1, r1, r2 movs r2, #0x91 lsls r2, r2, #5 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000dcc: .4byte 0x08754bc8 @@ -194,7 +194,7 @@ lbl_08000dd8: adds r1, r1, r2 movs r2, #0x91 lsls r2, r2, #5 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000e04: .4byte 0x08754bc8 @@ -211,7 +211,7 @@ lbl_08000e14: ldr r1, [r1] adds r1, r1, r2 movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000e2c: .4byte 0x08754bc8 @@ -225,7 +225,7 @@ lbl_08000e34: ldr r1, [r1] adds r1, r1, r2 movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000e4c: .4byte 0x08754bc8 @@ -241,7 +241,7 @@ lbl_08000e58: ldr r1, [r1] adds r1, r1, r2 movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000e70: .4byte 0x08754bc8 @@ -255,7 +255,7 @@ lbl_08000e78: ldr r1, [r1] adds r1, r1, r2 movs r2, #0x40 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000e90: .4byte 0x08754bc8 @@ -272,7 +272,7 @@ lbl_08000e9c: adds r1, r1, r2 movs r2, #0x80 lsls r2, r2, #1 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000eb8: .4byte 0x08754bc8 @@ -288,7 +288,7 @@ lbl_08000ec0: adds r1, r1, r2 movs r2, #0x80 lsls r2, r2, #1 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000edc: .4byte 0x08754bc8 @@ -305,7 +305,7 @@ lbl_08000ee4: movs r7, #0x80 lsls r7, r7, #2 adds r2, r7, #0 - bl SRAMWriteChecked + bl SramWriteChecked adds r4, r0, #0 cmp r4, #0 bne lbl_08000faa @@ -316,7 +316,7 @@ lbl_08000ee4: ldr r1, [r5] adds r1, r1, r2 adds r2, r7, #0 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000f18: .4byte 0x08754bc8 @@ -325,7 +325,7 @@ lbl_08000f20: ldr r0, lbl_08000f2c @ =0x0203ff70 ldr r1, lbl_08000f30 @ =0x0e007f70 movs r2, #0x10 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000f2c: .4byte 0x0203ff70 @@ -335,7 +335,7 @@ lbl_08000f34: ldr r1, lbl_08000f48 @ =0x0e007800 movs r2, #0xa0 lsls r2, r2, #2 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_08000fa8 .align 2, 0 lbl_08000f44: .4byte 0x0203f800 @@ -346,7 +346,7 @@ lbl_08000f4c: ldr r1, lbl_08000f5c @ =0x02038000 movs r2, #0x80 lsls r2, r2, #8 - bl SRAMWriteUnchecked + bl SramWriteUnchecked b lbl_08000faa .align 2, 0 lbl_08000f5c: .4byte 0x02038000 @@ -369,7 +369,7 @@ lbl_08000f60: adds r1, r1, r2 movs r2, #0x91 lsls r2, r2, #5 - bl SRAMWriteUnchecked + bl SramWriteUnchecked b lbl_08000faa .align 2, 0 lbl_08000f8c: .4byte 0x08754bcc @@ -382,7 +382,7 @@ lbl_08000f98: ldr r1, [r1] movs r2, #0x80 lsls r2, r2, #8 - bl SRAMCheck + bl SramCheck lbl_08000fa8: adds r4, r0, #0 lbl_08000faa: @@ -470,7 +470,7 @@ lbl_0800103c: adds r1, r1, r2 adds r1, r1, r4 adds r2, r5, #0 - bl SRAMWriteChecked + bl SramWriteChecked b lbl_0800108e .align 2, 0 lbl_08001054: .4byte 0x08754bc8 @@ -497,7 +497,7 @@ lbl_08001064: adds r1, r1, r2 adds r1, r1, r4 adds r2, r5, #0 - bl SRAMWriteUnchecked + bl SramWriteUnchecked lbl_0800108e: movs r2, #0 adds r1, r4, r5 diff --git a/asm/disasm_0x080051d4.s b/asm/disasm_0x080051d4.s index df589407..e5c3d325 100644 --- a/asm/disasm_0x080051d4.s +++ b/asm/disasm_0x080051d4.s @@ -2,8 +2,8 @@ .syntax unified - thumb_func_start SRAMWriteUncheckedInternal -SRAMWriteUncheckedInternal: @ 0x080051d4 + thumb_func_start SramWriteUncheckedInternal +SramWriteUncheckedInternal: @ 0x080051d4 push {r4, lr} adds r4, r0, #0 subs r3, r2, #1 @@ -25,8 +25,8 @@ lbl_080051f0: bx r0 .align 2, 0 - thumb_func_start SRAMWriteUnchecked -SRAMWriteUnchecked: @ 0x080051f8 + thumb_func_start SramWriteUnchecked +SramWriteUnchecked: @ 0x080051f8 push {r4, r5, r6, lr} sub sp, #0x80 adds r4, r0, #0 @@ -39,20 +39,20 @@ SRAMWriteUnchecked: @ 0x080051f8 movs r1, #3 orrs r0, r1 strh r0, [r2] - ldr r3, lbl_0800522c @ =SRAMWriteUncheckedInternal + ldr r3, lbl_0800522c @ =SramWriteUncheckedInternal movs r0, #1 bics r3, r0 mov r2, sp - ldr r0, lbl_08005230 @ =SRAMWriteUnchecked - ldr r1, lbl_0800522c @ =SRAMWriteUncheckedInternal + ldr r0, lbl_08005230 @ =SramWriteUnchecked + ldr r1, lbl_0800522c @ =SramWriteUncheckedInternal subs r0, r0, r1 lsls r0, r0, #0xf b lbl_08005240 .align 2, 0 lbl_08005224: .4byte 0x04000204 lbl_08005228: .4byte 0x0000fffc -lbl_0800522c: .4byte SRAMWriteUncheckedInternal -lbl_08005230: .4byte SRAMWriteUnchecked +lbl_0800522c: .4byte SramWriteUncheckedInternal +lbl_08005230: .4byte SramWriteUnchecked lbl_08005234: ldrh r0, [r3] strh r0, [r2] @@ -75,8 +75,8 @@ lbl_08005240: pop {r0} bx r0 - thumb_func_start SRAMWrite -SRAMWrite: @ 0x0800525c + thumb_func_start SramWrite +SramWrite: @ 0x0800525c push {r4, r5, lr} adds r5, r0, #0 adds r4, r1, #0 @@ -141,8 +141,8 @@ lbl_080052c4: bx r1 .align 2, 0 - thumb_func_start SRAMCheck -SRAMCheck: @ 0x080052cc + thumb_func_start SramCheck +SramCheck: @ 0x080052cc push {r4, r5, r6, lr} sub sp, #0xc0 adds r4, r0, #0 @@ -159,7 +159,7 @@ SRAMCheck: @ 0x080052cc movs r0, #1 bics r3, r0 mov r2, sp - ldr r0, lbl_08005304 @ =SRAMCheck + ldr r0, lbl_08005304 @ =SramCheck ldr r1, lbl_08005300 @ =SRAMCheckInternal subs r0, r0, r1 lsls r0, r0, #0xf @@ -168,7 +168,7 @@ SRAMCheck: @ 0x080052cc lbl_080052f8: .4byte 0x04000204 lbl_080052fc: .4byte 0x0000fffc lbl_08005300: .4byte SRAMCheckInternal -lbl_08005304: .4byte SRAMCheck +lbl_08005304: .4byte SramCheck lbl_08005308: ldrh r0, [r3] strh r0, [r2] @@ -191,8 +191,8 @@ lbl_08005314: pop {r1} bx r1 - thumb_func_start SRAMWriteChecked -SRAMWriteChecked: @ 0x08005330 + thumb_func_start SramWriteChecked +SramWriteChecked: @ 0x08005330 push {r4, r5, r6, r7, lr} adds r6, r0, #0 adds r5, r1, #0 @@ -209,11 +209,11 @@ lbl_08005342: adds r0, r6, #0 adds r1, r5, #0 adds r2, r4, #0 - bl SRAMWrite + bl SramWrite adds r0, r6, #0 adds r1, r5, #0 adds r2, r4, #0 - bl SRAMCheck + bl SramCheck adds r3, r0, #0 cmp r3, #0 bne lbl_0800533c diff --git a/asm/disasm_0x08067080.s b/asm/disasm_0x08067080.s index 81e5bbd7..2dbcabd9 100644 --- a/asm/disasm_0x08067080.s +++ b/asm/disasm_0x08067080.s @@ -25010,8 +25010,8 @@ lbl_08073292: .align 2, 0 lbl_08073298: .4byte 0x08754bc4 - thumb_func_start read_sram -read_sram: @ 0x0807329c + thumb_func_start SramRead_All +SramRead_All: @ 0x0807329c push {r4, r5, lr} movs r4, #3 ldr r5, lbl_080732b8 @ =0x03000c1c @@ -25265,7 +25265,7 @@ lbl_080734a4: lbl_080734b0: .4byte 0x08754bc8 lbl_080734b4: .4byte 0x00006d40 lbl_080734b8: - bl EraseSRAM + bl EraseSram ldr r1, lbl_080734e0 @ =0x0841147c ldr r2, lbl_080734e4 @ =0x03000014 movs r0, #0x10 @@ -26127,8 +26127,8 @@ lbl_08073b9a: pop {r1} bx r1 - thumb_func_start save_file -save_file: @ 0x08073ba4 + thumb_func_start SramSaveFile +SramSaveFile: @ 0x08073ba4 push {lr} sub sp, #4 ldr r1, lbl_08073bd4 @ =0x03000043 @@ -27003,14 +27003,14 @@ sub_08074304: @ 0x08074304 ldr r4, lbl_0807436c @ =0x0e007f80 adds r1, r4, #0 movs r2, #0x10 - bl SRAMWriteChecked + bl SramWriteChecked rsbs r1, r0, #0 orrs r1, r0 lsrs r5, r1, #0x1f adds r0, r4, #0 mov r1, sp movs r2, #0x10 - bl SRAMWriteUnchecked + bl SramWriteUnchecked movs r2, #0 lbl_0807432c: mov r0, sp @@ -27025,7 +27025,7 @@ lbl_0807432c: mov r0, sp adds r1, r4, #0 movs r2, #0x10 - bl SRAMWriteChecked + bl SramWriteChecked cmp r0, #0 beq lbl_08074350 movs r0, #2 @@ -27034,7 +27034,7 @@ lbl_08074350: adds r0, r4, #0 mov r1, sp movs r2, #0x10 - bl SRAMWriteUnchecked + bl SramWriteUnchecked movs r2, #0 mov r0, sp ldrb r1, [r0] diff --git a/database.txt b/database.txt index 7346f115..d92c1956 100644 --- a/database.txt +++ b/database.txt @@ -824,7 +824,7 @@ Blob_34099c_345868.bin;20172;0x34099c;1 Blob_345d00_360130.bin;107568;0x345d00;1 Blob_3602e8_36bdac.bin;47812;0x3602e8;1 Blob_375cc4_386f60.bin;70300;0x375cc4;1 -Blob_3f0390_411528.bin;135576;0x3f0390;1 +Blob_3f0390_411400.bin;135280;0x3f0390;1 Blob_415460_446d68.bin;203016;0x415460;1 Blob_44f11c_45f45c.bin;66368;0x44f11c;1 Blob_479400_754bb4.bin;2996148;0x479400;1 diff --git a/include/data/save_file_data.h b/include/data/save_file_data.h new file mode 100644 index 00000000..f918e14b --- /dev/null +++ b/include/data/save_file_data.h @@ -0,0 +1,30 @@ +#ifndef SAVE_FILE_DATA_H +#define SAVE_FILE_DATA_H + +#include "types.h" +#include "structs/save_file.h" + +extern const u8 sMetZeroSramCheck_Text[SRAM_TEXT_SIZE]; +extern const u8 sZERO_MISSION_010_Text[SRAM_TEXT_SIZE]; +extern const u8 sPlanetZebes_Text[SRAM_TEXT_SIZE]; +extern const u8 sSamusAran_Text[SRAM_TEXT_SIZE]; + +extern const u8 sJpnVer_Text[20]; +extern const u8 sEurVer_Text[20]; +extern const u8 sUsaVer_Text[20]; + +extern const struct FileScreenOptionsUnlocked sFileScreenOptionsUnlocked_Empty; + +extern const u8 sMostRecentFileSave_Text[2][SRAM_TEXT_SIZE]; +extern const u8 sSoundModeSave_Text[2][SRAM_TEXT_SIZE]; +extern const u8 sLanguageSave_Text[2][SRAM_TEXT_SIZE]; +extern const u8 sTimeAttackSave_Text[SRAM_TEXT_SIZE * 2 + 8]; + +extern const struct ButtonAssignments sDefaultButtonAssignments; +extern const struct InGameTimer sInGameTimer_Empty; +extern const struct InGameTimer sBestCompletionTime_Empty; +extern const u8 sUnk_411520[4]; + +extern const struct StartingInfo sStartingInfo; + +#endif /* SAVE_FILE_DATA_H */ diff --git a/include/data/shortcut_pointers.h b/include/data/shortcut_pointers.h index 169fb3e0..e0f5d6d8 100644 --- a/include/data/shortcut_pointers.h +++ b/include/data/shortcut_pointers.h @@ -3,13 +3,14 @@ #include "types.h" #include "temp_globals.h" +#include "structs/save_file.h" extern void* sEwramPointer; extern void* sBgPalramPointer; extern void* sObjPalramPointer; extern u32* sVisitedMinimapTilesPointer; extern union NonGameplayRAM* sNonGameplayRamPointer; -extern void* sSramEwramPointer; +extern struct Sram* sSramEwramPointer; extern void* sSramFlashPointer; #endif /* SHORTCUT_POINTERS_H */ diff --git a/include/funcs.h b/include/funcs.h index fa930f83..939afffa 100644 --- a/include/funcs.h +++ b/include/funcs.h @@ -5,7 +5,7 @@ void DMATransfer(u8 channel, void *src, void *dst, u32 len, u8 bitSize); void init_sound(void); -void read_sram(void); +void SramRead_All(void); void start_new_demo(void); int softreset_main(void); int fileselect_main(void); diff --git a/include/globals.h b/include/globals.h index 11e851b3..115246b3 100644 --- a/include/globals.h +++ b/include/globals.h @@ -28,11 +28,8 @@ union TileData { /// -extern u8 unk_02038000[]; - extern struct BG2Movement gBG2Movement; extern u16 gInterruptCode[0x100]; -extern u8 gSRAMCorruptFlag; extern void *sp_sys; extern void *sp_irq; diff --git a/include/macros.h b/include/macros.h index bb61628e..0188d9b9 100644 --- a/include/macros.h +++ b/include/macros.h @@ -8,7 +8,8 @@ #define bomb_chain_type_to_flag(type) (1 << type) #define check_samus_turning() ((pData->direction ^ (KEY_RIGHT | KEY_LEFT)) & gButtonInput) -#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#define ARRAY_SIZE(a) ((int)(sizeof((a)) / sizeof((a)[0]))) +#define OFFSET_OF(type, element) ((int)&(((type *)0)->element)) #define RED(c) ((c) & COLOR_MASK) #define GREEN(c) (((c) & (COLOR_MASK << 5)) >> 5) diff --git a/include/save_file.h b/include/save_file.h new file mode 100644 index 00000000..f14ebbfb --- /dev/null +++ b/include/save_file.h @@ -0,0 +1,63 @@ +#ifndef SAVE_FILE_H +#define SAVE_FILE_H + +#include "types.h" + +void SramRead_All(void); +void SramWrite_FileScreenOptionsUnlocked(void); +void SramRead_FileScreenOptionsUnlocked(void); +u32 SramCheck_FileScreenOptionsUnlocked(u8 fileNumber); +void SramCopy_FileScreenOptionsUnlocked(void); + +u32 SramProcessIntroSave(void); +void SramWrite_HeaderAndGameInfo(void); + +u32 SramProcessEndingSave(void); +void SramCheckSetNewBestCompletionTime(void); +void SramCopy_GameCompletion(void); +u32 SramProcessEndingSave_Unused(void); + +u32 SramSaveFile(void); +void SramWrite_ToEwram(void); +void SramRead_FromEwram(void); + +void StringCopy(u8* dst, const u8* const src, u8 length); +void SramTestFlash(void); +void unk_743a4(void); +void unk_74574(void); +void unk_74624(u8 param_1); + +void SramLoadFile(void); +void SramLoadFile_Unused(void); + +void SramWrite_Arrays(void); +void SramRead_Arrays(void); + +void SramWrite_MostRecentSaveFile(void); +void SramRead_MostRecentSaveFile(void); + +void SramWrite_SoundMode(void); +void SramRead_SoundMode(void); + +void SramWrite_Language(void); +u32 SramRead_Language(void); + +void SramWrite_TimeAttack(void); +void SramRead_TimeAttack(void); + +void SramWrite_ToEwram_Unused(void); +void SramLoad_DemoRamValues(void); + +u32 SramDeleteFile(u8 file); +u32 SramCopyFile(u8 src, u8 dst); + +void SramWrite_FileInfo(void); +void unk_757c8(u8 file); +void unk_7584c(u8 param_1); +void Sram_CheckLoadSaveFile(void); +void Sram_InitSaveFile(void); + +void Sram_VblankEmpty(void); +u32 unk_75c04(u8 param_1); + +#endif /* SAVE_FILE_H */ diff --git a/include/sram/sram.h b/include/sram/sram.h index 908f36c9..e9f03ac2 100644 --- a/include/sram/sram.h +++ b/include/sram/sram.h @@ -3,9 +3,9 @@ #include "types.h" -void SRAMWriteUnchecked(u8 *src, u8 *dest, u32 size); -void SRAMWrite(u8 *src, u8 *dest, u32 size); -u8 *SRAMCheck(u8 *src, u8 *dest, u32 size); -u8 *SRAMWriteChecked(u8 *src, u8 *dest, u32 size); +void SramWriteUnchecked(u8* src, u8* dest, u32 size); +void SramWrite(u8* src, u8* dest, u32 size); +u8* SramCheck(u8* src, u8* dest, u32 size); +u8* SramWriteChecked(u8* src, u8* dest, u32 size); #endif /* SRAM_SRAM_H */ diff --git a/include/structs/game_state.h b/include/structs/game_state.h index 6f1fe46a..47371989 100644 --- a/include/structs/game_state.h +++ b/include/structs/game_state.h @@ -16,6 +16,9 @@ struct FileScreenOptionsUnlocked { u16 galleryImages; u16 fusionGalleryImages; u8 soundTestAndOgMetroid; + u8 unk_5; + u8 unk_6; + u8 unk_7; u32 timeAttack; }; @@ -29,6 +32,7 @@ extern u8 gSramErrorFlag; extern u8 gDisablePause; extern u8 gDisableScrolling; extern u8 gShipLandingFlag; +extern u8 gTimeAttackFlag; extern u16 gButtonInput; extern u16 gPreviousButtonInput; extern u16 gChangedInput; @@ -49,4 +53,7 @@ extern i8 gGameModeSub2; extern i8 gGameModeSub3; extern u8 gSubGameModeStage; -#endif \ No newline at end of file +extern u8 gHasSaved; +extern i8 gMostRecentSaveFile; + +#endif diff --git a/include/structs/in_game_timer.h b/include/structs/in_game_timer.h index c94caf78..d387bc7d 100644 --- a/include/structs/in_game_timer.h +++ b/include/structs/in_game_timer.h @@ -15,5 +15,6 @@ struct InGameTimer { extern u8 gMaxInGameTimerFlag; extern struct InGameTimer gInGameTimer; extern struct InGameTimer gInGameTimerAtBosses[MAX_AMOUNT_OF_IGT_AT_BOSSES]; +extern struct InGameTimer gBestCompletionTimes[12]; #endif \ No newline at end of file diff --git a/include/structs/samus.h b/include/structs/samus.h index 6c7ee991..89b75c31 100644 --- a/include/structs/samus.h +++ b/include/structs/samus.h @@ -138,6 +138,7 @@ struct SamusEcho { u8 timer; u8 position; u8 distance; + u32 padding_4; u16 previous64XPositions[64]; u16 previous64YPositions[64]; u16 previousPositionCounter; diff --git a/include/structs/save_file.h b/include/structs/save_file.h new file mode 100644 index 00000000..aa16a81d --- /dev/null +++ b/include/structs/save_file.h @@ -0,0 +1,234 @@ +#ifndef SAVE_FILE_STRUCT_H +#define SAVE_FILE_STRUCT_H + +#include "types.h" +#include "structs/game_state.h" +#include "structs/samus.h" +#include "structs/scroll.h" +#include "structs/connection.h" +#include "structs/in_game_timer.h" +#include "structs/time_attack.h" +#include "structs/demo.h" + +#define SRAM_TEXT_SIZE 16 + +#define SRAM_GET_CHECKSUM_SIZE(type, iteration, checksumType) (sizeof(type) / sizeof(checksumType) / iteration - 1) + +struct StartingInfo { + u8 startingArea; + u8 unk_1; + u8 unk_2; + u8 unk_3; +}; + +struct SaveFileInfo { + u8 exists; + u8 unk_1; + u8 currentArea; + u16 currentEnergy; + u16 maxEnergy; + u16 currentMissiles; + u16 maxMissiles; + u8 suitType; + u8 igtHours; + u8 igtMinutes; + u8 igtSconds; + u8 hasSaved; + u8 completedGame; + i8 introPlayed; + i8 language; + u8 difficulty; + u8 timeAttack; +}; + +struct SaveMusicInfo { + u16 musicTrack; + u16 unk_2; + u8 unk_4; + u8 priority; +}; + +struct SaveFile { + u8 ZERO_MISSION_010_Text[SRAM_TEXT_SIZE]; + i32 checksum; + i32 notChecksum; + + struct GameCompletion gameCompletion; + u8 hasSaved; + u8 currentArea; + u8 currentRoom; + u8 LastDoorUsed; + u8 mapX; + u8 mapY; + + struct Camera camera; + u16 bg0XPosition; + u16 bg0YPosition; + u16 bg1XPosition; + u16 bg1YPosition; + u16 bg2XPosition; + u16 bg2YPosition; + u16 bg3XPosition; + u16 bg3YPosition; + + u8 difficulty; + u8 useMotherShipDoor; + u8 timeAttack; + + u8 numberOfNeverReformBlocksBroken[MAX_AMOUNT_OF_AREAS]; + u8 numberOfItemsCollected[MAX_AMOUNT_OF_AREAS]; + + u8 PlanetZebes_Text[SRAM_TEXT_SIZE]; + + struct SamusData samusData; + struct WeaponInfo samusWeaponInfo; + struct SamusEcho samusEcho; + struct ScrewSpeedAnimation screwSpeedAnimation; + struct Equipment equipment; + struct HazardDamage hazardDamage; + struct EnvironmentalEffect environmentalEffects[5]; + u16 preventMovementTimer; + u8 disableDrawingSamusAndScrolling; + + u16 alarmTimer; + struct InGameTimer inGameTimer; + struct InGameTimer bestCompletionTimes[12]; + struct InGameTimer inGameTimerAtBosses[MAX_AMOUNT_OF_IGT_AT_BOSSES]; + + struct SaveMusicInfo musicInfo; + u32 unk_248; + + u32 unk_24C[1]; + + u8 SamusAran_Text[SRAM_TEXT_SIZE]; + + u8 freespace[160]; + + u32 visitedMinimapTiles[MAX_AMOUNT_OF_AREAS][32]; + u8 neverReformBLocksBroken[2048]; + u8 itemsCollected[512]; + u32 hatchesOpened[MAX_AMOUNT_OF_AREAS][8]; + u32 eventsTriggered[8]; +}; + +struct SaveFile_Unused { + u8 currentArea; + u8 lastDoorUsed; + + struct SamusData samusData; + struct WeaponInfo samusWeaponInfo; + struct SamusEcho samusEcho; + struct ScrewSpeedAnimation screwSpeedAnimation; + struct Equipment equipment; + struct HazardDamage hazardDamage; + struct EnvironmentalEffect environmentalEffects[5]; + + u32 visitedMinimapTiles[32]; + u32 hatchesOpened[8]; + u8 unk_238; + u8 unk_239; + u8 unk_23A; + u8 unk_23B; + u8 unk_23C; + u8 unk_23D; + u8 unk_23E; + u8 unk_23F; + u8 useMotherShipDoors; + + u8 padding_241[60]; +}; + +struct SaveFileScreenOptions { + u32 checksum; + u32 notChecksum; + + u8 unk_8; + u8 unk_9; + u16 counter; + + u16 galleryImages; + u8 soundTestAndOriginalMetroid; + + u8 unk_F; + u8 unk_10; + u8 unk_11; + u8 unk_12; + u8 unk_13; + u16 fusionGalleryImages; + + u32 timeAttack; + u8 ZeroMissionUSA_Text[20]; + + u8 padding_2A[16]; +}; + +struct SaveValue { + u8 magicNumber; + u8 counter; + u16 checksum; + u16 notChecksum; + + u8 startText[SRAM_TEXT_SIZE]; + u8 value; + u8 endText[SRAM_TEXT_SIZE]; + + u8 padding_27[24]; +}; + +struct SaveTimeAttack { + u8 magicNumber; + + u8 padding_1[16]; + + u16 checksum; + u16 notChecksum; + u8 counter; + + u8 startText[SRAM_TEXT_SIZE]; + struct TimeAttackRecord value; + u8 endText[SRAM_TEXT_SIZE]; + + u8 padding_68[152]; +}; + +struct Sram { + struct SaveFileScreenOptions fileScreenOptions_fileA; + struct SaveFileScreenOptions fileScreenOptions_fileB; + + struct SaveFile files[3]; + struct SaveFile filesCopy[3]; + + struct SaveFileScreenOptions fileScreenOptions_fileC; + + struct SaveValue mostRecentFileSave; + struct SaveValue languagesSave[2]; + struct SaveValue soundModeSave; + struct SaveTimeAttack timeAttackSaves[2]; + + u16 demoInputData[DEMO_MAX_DURATION]; + u16 demoInputDuration[DEMO_MAX_DURATION]; + + u8 padding_7480[896]; + + struct SaveFile_Unused file_unused; + + u8 padding_7a80[1264]; + + u8 unk_7f70[16]; + u8 MetZeroSramCheck_Text[SRAM_TEXT_SIZE]; + + u8 padding_7f90[112]; +}; + +// Temp +extern u32 gUnk_3000050[1]; + +extern u8 gSramOperationStage; +extern u8 gSramCorruptFlag; +extern struct Sram gSram; + +extern u8 gUnk_3000c20; +extern struct SaveFileInfo gSaveFilesInfo[3]; +extern struct StartingInfo gStartingInfo; + +#endif /* SAVE_FILE_STRUCT_H */ diff --git a/linker.ld b/linker.ld index ec14d7e3..99f109ee 100644 --- a/linker.ld +++ b/linker.ld @@ -24,7 +24,7 @@ SECTIONS { . = 0x00037c00; gHatchesOpened = .; . = 0x00037e00; gEventsTriggered = .; . = 0x00037e20; gMinimapTilesGFX = .; - . = 0x00038000; unk_02038000 = .; + . = 0x00038000; gSram = .; } >ewram iwram (NOLOAD) : ALIGN(4) { @@ -45,6 +45,7 @@ SECTIONS { . = 0x0000002B; gSamusOnTopOfBackgrounds = .; . = 0x0000002C; gDifficulty = .; . = 0x0000002D; gUseMotherShipDoors = .; + . = 0x0000002E; gTimeAttackFlag = .; . = 0x0000002F; gCutsceneToSkip = .; . = 0x00000030; gMusicTrackInfo = .; . = 0x00000038; gCurrentDemo = .; @@ -153,7 +154,7 @@ SECTIONS { . = 0x00000c06; gCurrentOamScaling = .; . = 0x00000c08; gLastAreaNameVisited = .; . = 0x00000c0c; gCurrentMessage = .; - . = 0x00000c1c; gSRAMCorruptFlag = .; + . = 0x00000c1c; gSramCorruptFlag = .; . = 0x00000c1d; gIsLoadingFile = .; . = 0x00000c70; gMainGameMode = .; . = 0x00000c72; gGameModeSub1 = .; @@ -526,7 +527,8 @@ SECTIONS { src/data/cutscenes/getting_fully_powered_suit_data.o(.rodata); src/data/cutscenes/before_charlie_data.o(.rodata); src/data/cutscenes/statue_opening_data.o(.rodata); - /* 3f0390-411528 */ + /* 3f0390-411400 */ + src/data/save_file_data.o(.rodata); src/data/menus/erase_sram_data.o(.rodata); /* 415460-446d68 */ src/data/menus/title_screen_data.o(.rodata); diff --git a/src/data/cutscenes/statue_opening_data.c b/src/data/cutscenes/statue_opening_data.c index eff314f6..aa526479 100644 --- a/src/data/cutscenes/statue_opening_data.c +++ b/src/data/cutscenes/statue_opening_data.c @@ -77,4 +77,4 @@ const u32 sStatueOpeningRoomGFX[2357] = INCBIN_U32("data/cutscenes/StatueOpening const u32 sStatueOpeningRoomTileTable[318] = INCBIN_U32("data/cutscenes/StatueOpening/Room.tt"); const u32 sStatueOpening_3effc8[242] = INCBIN_U32("data/cutscenes/StatueOpening/3effc8.tt"); -const u8 sTempArray_3f0390[0x21198] = INCBIN_U8("data/Blob_3f0390_411528.bin"); +const u8 sTempArray_3f0390[0x21070] = INCBIN_U8("data/Blob_3f0390_411400.bin"); diff --git a/src/data/save_file_data.c b/src/data/save_file_data.c new file mode 100644 index 00000000..e654d857 --- /dev/null +++ b/src/data/save_file_data.c @@ -0,0 +1,72 @@ +#include "data/save_file_data.h" +#include "gba.h" + +#include "constants/connection.h" + +const u8 sMetZeroSramCheck_Text[SRAM_TEXT_SIZE] = "MetZeroSramCheck"; +const u8 sZERO_MISSION_010_Text[SRAM_TEXT_SIZE] = "ZERO_MISSION_010"; +const u8 sPlanetZebes_Text[SRAM_TEXT_SIZE] = "Planet Zebes... "; +const u8 sSamusAran_Text[SRAM_TEXT_SIZE] = " - Samus Aran - "; + +const u8 sJpnVer_Text[20] = "ZeroMissionJPNver005"; +const u8 sEurVer_Text[20] = "ZeroMissionEURver005"; +const u8 sUsaVer_Text[20] = "ZeroMissionUSAver005"; + +const struct FileScreenOptionsUnlocked sFileScreenOptionsUnlocked_Empty = { + .galleryImages = 0, + .fusionGalleryImages = 0, + .soundTestAndOgMetroid = 0, + .unk_5 = 0, + .unk_6 = 0, + .unk_7 = 0, + .timeAttack = 0 +}; + +const u8 sMostRecentFileSave_Text[2][SRAM_TEXT_SIZE] = { + "SELECTSAVENUM_00", + "ENDDATA_SEL_SAVE" +}; + +const u8 sSoundModeSave_Text[2][SRAM_TEXT_SIZE] = { + "_SOUNDMODE_SAVE_", + "SoundModeSAVEEND" +}; + +const u8 sLanguageSave_Text[2][SRAM_TEXT_SIZE] = { + "_SELECT_LANGUAGE", + "ENDKEY_LANGUAGE_" +}; + +const u8 sTimeAttackSave_Text[SRAM_TEXT_SIZE * 2 + 8] = "SUKPIFZAILU25976_StartKey_198129ATRUNEND"; + +const struct ButtonAssignments sDefaultButtonAssignments = { + .armMissiles = KEY_R, + .diagonalAim = KEY_L, + .pause = KEY_START, + .swapMissiles = KEY_SELECT +}; + +const struct InGameTimer sInGameTimer_Empty = { + .hours = 0, + .minutes = 0, + .seconds = 0, + .frames = 0 +}; + +const struct InGameTimer sBestCompletionTime_Empty = { + .hours = UCHAR_MAX, + .minutes = UCHAR_MAX, + .seconds = UCHAR_MAX, + .frames = UCHAR_MAX +}; + +const u8 sUnk_411520[4] = { + 8, 8, 0, 0 +}; + +const struct StartingInfo sStartingInfo = { + .startingArea = AREA_CRATERIA, + .unk_1 = 5, + .unk_2 = 0, + .unk_3 = 0 +}; diff --git a/src/data/shortcut_pointers.c b/src/data/shortcut_pointers.c index 12a886d3..dc7234c8 100644 --- a/src/data/shortcut_pointers.c +++ b/src/data/shortcut_pointers.c @@ -15,7 +15,7 @@ u32* sVisitedMinimapTilesPointer = gVisitedMinimapTiles; FORCE_RODATA union NonGameplayRAM* sNonGameplayRamPointer = &gNonGameplayRAM; FORCE_RODATA -void* sSramEwramPointer = EWRAM_BASE + 0x38000; +struct Sram* sSramEwramPointer = EWRAM_BASE + 0x38000; FORCE_RODATA void* sSramFlashPointer = SRAM_BASE; diff --git a/src/erase_sram_func.c b/src/erase_sram_func.c index e16dd6cb..60c973ac 100644 --- a/src/erase_sram_func.c +++ b/src/erase_sram_func.c @@ -4,11 +4,12 @@ #include "memory.h" #include "sram/sram.h" #include "types.h" +#include "structs/save_file.h" -void EraseSRAM(void) +void EraseSram(void) { - BitFill(3, 0xffff, &unk_02038000, SRAM_SIZE, 16); - if (gSRAMCorruptFlag == 0) { - SRAMWriteChecked(unk_02038000, SRAM_BASE, SRAM_SIZE); + BitFill(3, 0xffff, &gSram, SRAM_SIZE, 16); + if (!gSramCorruptFlag) { + SramWriteChecked((u8*)&gSram, SRAM_BASE, SRAM_SIZE); } } diff --git a/src/init_game.c b/src/init_game.c index 99e96b4c..38a6b708 100644 --- a/src/init_game.c +++ b/src/init_game.c @@ -23,7 +23,7 @@ void InitializeGame(void) ClearGfxRam(); LoadInterruptCode(); CallbackSetVBlank(SoftresetVBlankCallback); - read_sram(); + SramRead_All(); init_sound(); write16(REG_IE, IF_VBLANK | IF_DMA2 | IF_GAMEPAK); diff --git a/src/menus/erase_sram.c b/src/menus/erase_sram.c index 1eef5f22..0af3b93e 100644 --- a/src/menus/erase_sram.c +++ b/src/menus/erase_sram.c @@ -110,7 +110,7 @@ u32 EraseSramSubroutine(void) case 7: unk_33dc(); - EraseSRAM(); + EraseSram(); leaving = TRUE; } @@ -383,7 +383,7 @@ void EraseSramResetOAM(void) ERASE_SRAM_DATA.currentOption = ERASE_SRAM_OPTION_QUESTION_NO; ERASE_SRAM_DATA.nextOption = ERASE_SRAM_OPTION_CHANGED_FLAG | ERASE_SRAM_OPTION_QUESTION_NO; - for (i = 0; i < (int)ARRAY_SIZE(ERASE_SRAM_DATA.oam); i++) + for (i = 0; i < ARRAY_SIZE(ERASE_SRAM_DATA.oam); i++) { ERASE_SRAM_DATA.oam[i] = *(struct MenuOamData*)0x840d048; // FIXME *pOam = sEraseSramMenuOamData_Empty; diff --git a/src/menus/game_over.c b/src/menus/game_over.c index c31a8d45..fa712824 100644 --- a/src/menus/game_over.c +++ b/src/menus/game_over.c @@ -446,9 +446,9 @@ void GameOverUpdateLettersPalette(void) row = GAME_OVER_DATA.dynamicPalette.currentPaletteRow; i = 0; - while (i < (int)ARRAY_SIZE(GAME_OVER_DATA.dynamicPalette.palette)) + while (i < ARRAY_SIZE(GAME_OVER_DATA.dynamicPalette.palette)) { - if (row >= (int)ARRAY_SIZE(GAME_OVER_DATA.dynamicPalette.palette)) + if (row >= ARRAY_SIZE(GAME_OVER_DATA.dynamicPalette.palette)) row = 0; GAME_OVER_DATA.dynamicPalette.palette[i] = src[row]; diff --git a/src/menus/title_screen.c b/src/menus/title_screen.c index 4cfb6d65..6bb8b20f 100644 --- a/src/menus/title_screen.c +++ b/src/menus/title_screen.c @@ -93,7 +93,7 @@ void TitleScreenResetOAM(void) pOam = TITLE_SCREEN_DATA.oam; i = 0; - while (i < (int)ARRAY_SIZE(TITLE_SCREEN_DATA.oam)) + while (i < ARRAY_SIZE(TITLE_SCREEN_DATA.oam)) { *pOam = *(struct MenuOamData*)0x840d028; // FIXME *pOam = sMenuOamData_Empty; diff --git a/src/save_file.c b/src/save_file.c new file mode 100644 index 00000000..0f8ece59 --- /dev/null +++ b/src/save_file.c @@ -0,0 +1,1557 @@ +#include "save_file.h" +#include "callbacks.h" +#include "macros.h" +#include "event.h" + +#include "data/shortcut_pointers.h" +#include "data/save_file_data.h" + +#include "constants/game_state.h" +#include "constants/connection.h" +#include "constants/samus.h" + +#include "structs/audio.h" +#include "structs/bg_clip.h" +#include "structs/color_effects.h" +#include "structs/game_state.h" +#include "structs/minimap.h" +#include "structs/sprite.h" +#include "structs/save_file.h" + +#define SRAM_OPERATION_WRITE_FILE_SCREEN_OPTIONS 0 +#define SRAM_OPERATION_SAVE_MOST_RECENT_FILE 5 +#define SRAM_OPERATION_SAVE_LANGUAGE 6 +#define SRAM_OPERATION_SAVE_LANGUAGE2 7 +#define SRAM_OPERATION_SAVE_SOUND_MODE 8 +#define SRAM_OPERATION_SAVE_TIME_ATTACK 9 +#define SRAM_OPERATION_SAVE_TIME_ATTACK2 10 +#define SRAM_OPERATION_SAVE_RECORDED_DEMO 11 +#define SRAM_OPERATION_SAVE_UNUSED_SRAM 13 +#define SRAM_OPERATION_READ_ALL_FLASH 14 +#define SRAM_OPERATION_READ_RECENT_FILE_UNCHECKED 15 +#define SRAM_OPERATION_CHECK_ALL 16 + +u8* do_sram_operation(u8); + +/** + * @brief 7329c | 64 | Fully reads the flash save into Ewram + * + */ +void SramRead_All(void) +{ + i32 i; + u32 corrupt; + + i = 3; + while (i != 0) + { + SramTestFlash(); + corrupt = gSramCorruptFlag; + i--; + if (!corrupt) + break; + } + + for (i = 3; i != 0; i--) + { + do_sram_operation(SRAM_OPERATION_READ_ALL_FLASH); + if (do_sram_operation(SRAM_OPERATION_CHECK_ALL) == NULL) + break; + } + + SramRead_FileScreenOptionsUnlocked(); + unk_743a4(); + SramRead_MostRecentSaveFile(); + SramRead_TimeAttack(); + + if (SramRead_Language()) + { + gLanguage = LANGUAGE_ENGLISH; + SramWrite_Language(); + } +} + +/** + * @brief 73300 | 94 | Writes the file screen options unlocked to flash sram + * + */ +void SramWrite_FileScreenOptionsUnlocked(void) +{ + i32 i; + u32* ptr; + u32 checksum; + struct SaveFileScreenOptions* pOptions; + + pOptions = &sSramEwramPointer->fileScreenOptions_fileA; + + // Write data and magic values + pOptions->unk_8 = 0x30; + pOptions->unk_9 = 0x31; + pOptions->counter++; + pOptions->galleryImages = gFileScreenOptionsUnlocked.galleryImages; + pOptions->soundTestAndOriginalMetroid = gFileScreenOptionsUnlocked.soundTestAndOgMetroid; + pOptions->unk_F = 0x2; + pOptions->unk_10 = 0x34; + pOptions->unk_11 = gFileScreenOptionsUnlocked.unk_5; + pOptions->unk_12 = gFileScreenOptionsUnlocked.unk_6; + pOptions->unk_13 = gFileScreenOptionsUnlocked.unk_7; + pOptions->fusionGalleryImages = gFileScreenOptionsUnlocked.fusionGalleryImages; + pOptions->timeAttack = gFileScreenOptionsUnlocked.timeAttack; + + for (i = 0; i < ARRAY_SIZE(pOptions->ZeroMissionUSA_Text); i++) + pOptions->ZeroMissionUSA_Text[i] = sUsaVer_Text[i]; + + // Reset checksum + pOptions->checksum = 0; + pOptions->notChecksum = ~0; + + ptr = (u32*)&sSramEwramPointer->fileScreenOptions_fileA; + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveFileScreenOptions, 4, u32); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + pOptions->checksum = checksum; + pOptions->notChecksum = ~checksum; + + do_sram_operation(SRAM_OPERATION_WRITE_FILE_SCREEN_OPTIONS); +} + +/** + * @brief 73394 | 154 | Reads the file screen options unlocked from flash sram + * + */ +void SramRead_FileScreenOptionsUnlocked(void) +{ + u16 buffer; + u16* ptr; + u32 fileASanityCheck; + u32 fileBSanityCheck; + u32 fileCSanityCheck; + + fileASanityCheck = SramCheck_FileScreenOptionsUnlocked(0); + fileBSanityCheck = SramCheck_FileScreenOptionsUnlocked(1); + fileCSanityCheck = SramCheck_FileScreenOptionsUnlocked(2); + + // Convoluted (and somewhat useless) state machine to sync the file screen options of all files, + // even though only the one for file A actually matters + if (fileASanityCheck) + { + ptr = &buffer; + buffer = USHORT_MAX; + dma_set(3, ptr, &sSramEwramPointer->fileScreenOptions_fileA, + (DMA_ENABLE | DMA_SRC_FIXED) << 16 | sizeof(sSramEwramPointer->fileScreenOptions_fileA) / 2); + + if (fileCSanityCheck) + { + buffer = USHORT_MAX; + dma_set(3, ptr, &sSramEwramPointer->fileScreenOptions_fileC, + (DMA_ENABLE | DMA_SRC_FIXED) << 16 | sizeof(sSramEwramPointer->fileScreenOptions_fileC) / 2); + + if (fileBSanityCheck) + { + buffer = USHORT_MAX; + dma_set(3, ptr, &sSramEwramPointer->fileScreenOptions_fileB, + (DMA_ENABLE | DMA_SRC_FIXED) << 16 | sizeof(sSramEwramPointer->fileScreenOptions_fileB) / 2); + } + else + { + DMATransfer(3, &sSramEwramPointer->fileScreenOptions_fileB, &sSramEwramPointer->fileScreenOptions_fileA, + sizeof(sSramEwramPointer->fileScreenOptions_fileA), 0x10); + fileASanityCheck = 0; + do_sram_operation(SRAM_OPERATION_WRITE_FILE_SCREEN_OPTIONS); + } + } + else + { + DMATransfer(3, &sSramEwramPointer->fileScreenOptions_fileC, &sSramEwramPointer->fileScreenOptions_fileA, + sizeof(sSramEwramPointer->fileScreenOptions_fileA), 0x10); + fileASanityCheck = 0; + do_sram_operation(SRAM_OPERATION_WRITE_FILE_SCREEN_OPTIONS); + + if (fileBSanityCheck) + { + DMATransfer(3, &sSramEwramPointer->fileScreenOptions_fileA, &sSramEwramPointer->fileScreenOptions_fileB, + sizeof(sSramEwramPointer->fileScreenOptions_fileB), 0x10); + do_sram_operation(1); + } + } + } + else + { + DMATransfer(3, &sSramEwramPointer->fileScreenOptions_fileA, &sSramEwramPointer->fileScreenOptions_fileB, + sizeof(sSramEwramPointer->fileScreenOptions_fileB), 0x10); + do_sram_operation(1); + if (fileCSanityCheck != 0) + { + DMATransfer(3, &sSramEwramPointer->fileScreenOptions_fileA, &sSramEwramPointer->fileScreenOptions_fileC, + sizeof(sSramEwramPointer->fileScreenOptions_fileC), 0x10); + do_sram_operation(2); + } + } + + if (fileASanityCheck == 0) + { + SramCopy_FileScreenOptionsUnlocked(); + return; + } + + // Sram is considered corrupted, fully clear it + EraseSram(); + DMATransfer(3, &sFileScreenOptionsUnlocked_Empty, &gFileScreenOptionsUnlocked, sizeof(gFileScreenOptionsUnlocked), 0x10); + SramWrite_FileScreenOptionsUnlocked(); +} + +u32 SramCheck_FileScreenOptionsUnlocked(u8 fileNumber) +{ + // https://decomp.me/scratch/AgVh2 + + i32 i; + struct SaveFileScreenOptions* pOptions; + u32 flags; + u32* ptr; + u32 checksum; + + flags = 0; + if (fileNumber == 2) + pOptions = &sSramEwramPointer->fileScreenOptions_fileC; + else if (fileNumber == 1) + pOptions = &sSramEwramPointer->fileScreenOptions_fileB; + else + pOptions = &sSramEwramPointer->fileScreenOptions_fileA; + + checksum = 0; + + ptr = (u32*)pOptions; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveFileScreenOptions, 4, u32); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + if (pOptions->checksum != checksum) + flags |= 1; + + for (i = 0; i < ARRAY_SIZE(pOptions->ZeroMissionUSA_Text); i++) + { + if (pOptions->ZeroMissionUSA_Text[i] != sUsaVer_Text[i]) + flags |= 2; + } + + if (pOptions->checksum != ~pOptions->notChecksum) + flags |= 4; + + return flags; +} + +/** + * @brief 73584 | 2c | Copies the file screen options unlocked from Sram + * + */ +void SramCopy_FileScreenOptionsUnlocked(void) +{ + struct SaveFileScreenOptions* pOptions; + + pOptions = &sSramEwramPointer->fileScreenOptions_fileA; + + gFileScreenOptionsUnlocked.galleryImages = pOptions->galleryImages; + gFileScreenOptionsUnlocked.soundTestAndOgMetroid = pOptions->soundTestAndOriginalMetroid; + gFileScreenOptionsUnlocked.unk_5 = pOptions->unk_11; + gFileScreenOptionsUnlocked.unk_6 = pOptions->unk_12; + gFileScreenOptionsUnlocked.unk_7 = pOptions->unk_13; + gFileScreenOptionsUnlocked.fusionGalleryImages = pOptions->fusionGalleryImages; + gFileScreenOptionsUnlocked.timeAttack = pOptions->timeAttack; +} + +/** + * @brief 735b0 | 128 | Processes saving the current file during the intro + * + * @return u32 bool, ended + */ +u32 SramProcessIntroSave(void) +{ + if (!gDisableSoftreset) + { + // Start, disable soft reset + gSramOperationStage = 0; + gDisableSoftreset = TRUE; + } + + switch (gSramOperationStage) + { + case 0: + // Set intro played flag, and carry the completed game flags + gGameCompletion.introPlayed = TRUE; + gSaveFilesInfo[gMostRecentSaveFile].introPlayed = TRUE; + gGameCompletion.completedGame = gSaveFilesInfo[gMostRecentSaveFile].completedGame; + + // Reset non array part of the struct + BitFill(3, 0, &sSramEwramPointer->files[gMostRecentSaveFile], OFFSET_OF(struct SaveFile, visitedMinimapTiles), 0x10); + gSramOperationStage++; + break; + + case 1: + // Write header and game info + SramWrite_HeaderAndGameInfo(); + gUnk_3000c20 = 0; + gSramOperationStage++; + break; + + case 2: + // Send to flash sram + if (unk_fbc(0)) + gSramOperationStage++; + break; + + case 3: + // Make a backup of the file + DMATransfer(3, &sSramEwramPointer->files[gMostRecentSaveFile], &sSramEwramPointer->filesCopy[gMostRecentSaveFile], + sizeof(struct SaveFile), 0x10); + gSramOperationStage++; + break; + + case 4: + default: + // End, re-enable soft reset + gDisableSoftreset = FALSE; + gSramOperationStage = 0; + } + + // If soft reset is enabled, then the save is complete + return gDisableSoftreset ^ TRUE; +} + +/** + * @brief 736d8 | f4 | Writes the header and game info to the current file + * + */ +void SramWrite_HeaderAndGameInfo(void) +{ + i32 i; + u32* ptr; + u32 checksum; + struct SaveFile* pFile; + + pFile = &sSramEwramPointer->files[gMostRecentSaveFile]; + + // Write header strings + StringCopy(pFile->ZERO_MISSION_010_Text, sZERO_MISSION_010_Text, SRAM_TEXT_SIZE); + StringCopy(pFile->PlanetZebes_Text, sPlanetZebes_Text, SRAM_TEXT_SIZE); + + // Reset checksum + pFile->checksum = 0; + pFile->notChecksum = ~0; + + // Write game info + pFile->gameCompletion.introPlayed = gGameCompletion.introPlayed; + pFile->gameCompletion.completedGame = gGameCompletion.completedGame; + pFile->difficulty = gDifficulty; + pFile->timeAttack = gTimeAttackFlag; + pFile->gameCompletion.language = gLanguage; + + // Write best completion times + for (i = 0; i < ARRAY_SIZE(pFile->bestCompletionTimes); i++) + pFile->bestCompletionTimes[i] = gBestCompletionTimes[i]; + + // Write SAMUS ARAN text + StringCopy(pFile->SamusAran_Text, sSamusAran_Text, SRAM_TEXT_SIZE); + + // Calculate checksum + ptr = (u32*)&sSramEwramPointer->files[gMostRecentSaveFile]; + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveFile, 8, u32); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + // Write checksum + pFile->checksum = checksum; + pFile->notChecksum = ~checksum; +} + +/** + * @brief 737cc | 124 | Processes saving the current file during the ending + * + * @return u32 bool, ended + */ +u32 SramProcessEndingSave(void) +{ + u32 ended; + u32 bit; + + ended = FALSE; + + switch (gSramOperationStage) + { + case 0: + gUnk_3000c20 = 0; + gSramOperationStage++; + break; + + case 1: + if (unk_fbc(2)) + gSramOperationStage++; + break; + + case 2: + // Set completed game flag based on difficulty and language + // 0 | 0 | Hard hiragana | Normal hiragana | Easy hiragana | Hard english | Normal english | Easy english + bit = 1; + if (gLanguage == LANGUAGE_HIRAGANA) + bit = 8; + + gGameCompletion.completedGame |= bit << gDifficulty; + + SramCheckSetNewBestCompletionTime(); + SramCopy_GameCompletion(); + gUnk_3000c20 = 0; + gSramOperationStage++; + break; + + case 3: + if (unk_fbc(0)) + { + // Save completion flags + gSaveFilesInfo[gMostRecentSaveFile].completedGame = gGameCompletion.completedGame; + gSramOperationStage++; + } + break; + + case 4: + // Make a backup of the file + DMATransfer(3, &sSramEwramPointer->files[gMostRecentSaveFile], &sSramEwramPointer->filesCopy[gMostRecentSaveFile], + sizeof(struct SaveFile), 0x10); + gSramOperationStage++; + break; + + case 5: + default: + gSramOperationStage = 0; + ended = TRUE; + break; + } + + return ended; +} + +void SramCheckSetNewBestCompletionTime(void) +{ + +} + +/** + * @brief 739e8 | 9c | Writes the game completion to the current file + * + */ +void SramCopy_GameCompletion(void) +{ + i32 i; + u32* ptr; + u32 checksum; + struct SaveFile* pFile; + + pFile = &sSramEwramPointer->files[gMostRecentSaveFile]; + + // Reset checksum + pFile->checksum = 0; + pFile->notChecksum = ~0; + + // Write completed game flags + pFile->gameCompletion.completedGame = gGameCompletion.completedGame; + + // Write best completion times + for (i = 0; i < ARRAY_SIZE(pFile->bestCompletionTimes); i++) + pFile->bestCompletionTimes[i] = gBestCompletionTimes[i]; + + // Calculate checksum + ptr = (u32*)&sSramEwramPointer->files[gMostRecentSaveFile]; + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveFile, 8, u32); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + // Write checksum + pFile->checksum = checksum; + pFile->notChecksum = ~checksum; +} + +/** + * @brief 73a84 | 120 | Processes saving the current file during the ending (unused, doesn't account for best completion times) + * + * @return u32 bool, ended + */ +u32 SramProcessEndingSave_Unused(void) +{ + u32 ended; + u32 bit; + + ended = FALSE; + + switch (gSramOperationStage) + { + case 0: + gUnk_3000c20 = 0; + gSramOperationStage++; + break; + + case 1: + if (unk_fbc(2)) + gSramOperationStage++; + break; + + case 2: + // Set completed game flag based on difficulty and language + // 0 | 0 | Hard hiragana | Normal hiragana | Easy hiragana | Hard english | Normal english | Easy english + bit = 1; + if (gLanguage == LANGUAGE_HIRAGANA) + bit = 8; + + gGameCompletion.completedGame |= bit << gDifficulty; + + SramCopy_GameCompletion(); + gUnk_3000c20 = 0; + gSramOperationStage++; + break; + + case 3: + if (unk_fbc(0)) + { + // Save completion flags + gSaveFilesInfo[gMostRecentSaveFile].completedGame = gGameCompletion.completedGame; + gSramOperationStage++; + } + break; + + case 4: + // Make a backup of the file + DMATransfer(3, &sSramEwramPointer->files[gMostRecentSaveFile], &sSramEwramPointer->filesCopy[gMostRecentSaveFile], + sizeof(struct SaveFile), 0x10); + gSramOperationStage++; + break; + + case 5: + default: + gSramOperationStage = 0; + ended = TRUE; + break; + } + + return ended; +} + +/** + * @brief 73ba4 | 140 | Saves the current file to flash sram + * + * @return u32 bool, ended + */ +u32 SramSaveFile(void) +{ + if (!gDisableSoftreset) + { + // Start, disable soft reset + gDisableSoftreset = TRUE; + gHasSaved = TRUE; + gSramOperationStage = 0; + gUnk_3000c20 = 0; + } + + switch (gSramOperationStage) + { + case 0: + if (unk_fbc(4)) + gSramOperationStage++; + break; + + case 1: + // Write arrays + SramWrite_Arrays(); + gUnk_3000c20 = 0; + gSramOperationStage++; + break; + + case 2: + // Write data + SramWrite_ToEwram(); + gUnk_3000c20 = 0; + gSramOperationStage++; + break; + + case 3: + // Send to flash sram + if (unk_fbc(0)) + { + gSaveFilesInfo[gMostRecentSaveFile].exists = TRUE; + gSramOperationStage++; + } + break; + + case 4: + // Make a backup of the file + DMATransfer(3, &sSramEwramPointer->files[gMostRecentSaveFile], &sSramEwramPointer->filesCopy[gMostRecentSaveFile], + sizeof(struct SaveFile), 0x10); + gSramOperationStage++; + break; + + case 5: + default: + // End, re-enable soft reset + gDisableSoftreset = FALSE; + } + + // If soft reset is enabled, then the save is complete + return gDisableSoftreset ^ TRUE; +} + +/** + * @brief 73ce4 | 350 | Writes all the current RAM values to the save file values (in Ewram) + * + */ +void SramWrite_ToEwram(void) +{ + i32 i; + u32* ptr; + u32 checksum; + struct SaveFile* pFile; + struct SaveMusicInfo musicInfo; + + musicInfo.musicTrack = gMusicInfo.musicTrack; + musicInfo.unk_2 = gMusicInfo.unknown_1E; + musicInfo.unk_4 = gMusicInfo.unknown_20; + musicInfo.priority = gMusicInfo.priority; + + pFile = &sSramEwramPointer->files[gMostRecentSaveFile]; + + StringCopy(pFile->ZERO_MISSION_010_Text, sZERO_MISSION_010_Text, SRAM_TEXT_SIZE); + StringCopy(pFile->PlanetZebes_Text, sPlanetZebes_Text, SRAM_TEXT_SIZE); + + pFile->checksum = 0; + pFile->notChecksum = ~0; + + pFile->gameCompletion = gGameCompletion; + pFile->hasSaved = gHasSaved; + pFile->currentArea = gCurrentArea; + pFile->currentRoom = gCurrentRoom; + pFile->LastDoorUsed = gLastDoorUsed; + pFile->mapX = gMinimapX; + pFile->mapY = gMinimapY; + + pFile->camera = gCamera; + pFile->bg0XPosition = gBG0XPosition; + pFile->bg0YPosition = gBG0YPosition; + pFile->bg1XPosition = gBG1XPosition; + pFile->bg1YPosition = gBG1YPosition; + pFile->bg2XPosition = gBG2XPosition; + pFile->bg2YPosition = gBG2YPosition; + pFile->bg3XPosition = gBG3XPosition; + pFile->bg3YPosition = gBG3YPosition; + + pFile->difficulty = gDifficulty; + pFile->useMotherShipDoor = gUseMotherShipDoors; + pFile->timeAttack = gTimeAttackFlag; + + for (i = 0; i < MAX_AMOUNT_OF_AREAS; i++) + pFile->numberOfNeverReformBlocksBroken[i] = gNumberOfNeverReformBlocks[i]; + + for (i = 0; i < MAX_AMOUNT_OF_AREAS; i++) + pFile->numberOfItemsCollected[i] = gNumberOfItemsCollected[i]; + + pFile->samusData = gSamusData; + pFile->samusWeaponInfo = gSamusWeaponInfo; + pFile->samusEcho = gSamusEcho; + pFile->screwSpeedAnimation = gScrewSpeedAnimation; + pFile->equipment = gEquipment; + pFile->hazardDamage = gSamusHazardDamage; + pFile->environmentalEffects[0] = gSamusEnvironmentalEffects[0]; + pFile->environmentalEffects[1] = gSamusEnvironmentalEffects[1]; + pFile->environmentalEffects[2] = gSamusEnvironmentalEffects[2]; + pFile->environmentalEffects[3] = gSamusEnvironmentalEffects[3]; + pFile->environmentalEffects[4] = gSamusEnvironmentalEffects[4]; + pFile->preventMovementTimer = gPreventMovementTimer; + pFile->disableDrawingSamusAndScrolling = gDisableDrawingSamusAndScrolling; + + pFile->alarmTimer = gAlarmTimer; + pFile->inGameTimer = gInGameTimer; + + for (i = 0; i < 12; i++) + pFile->bestCompletionTimes[i] = gBestCompletionTimes[i]; + + for (i = 0; i < MAX_AMOUNT_OF_IGT_AT_BOSSES; i++) + pFile->inGameTimerAtBosses[i] = gInGameTimerAtBosses[i]; + + pFile->musicInfo = musicInfo; + for (i = 0; i < 1; i++) + pFile->unk_24C[i] = gUnk_3000050[i]; + + StringCopy(pFile->SamusAran_Text, sSamusAran_Text, SRAM_TEXT_SIZE); + + // Calculate checksum + ptr = (u32*)&sSramEwramPointer->files[gMostRecentSaveFile]; + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveFile, 8, u32); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + // Write checksum + pFile->checksum = checksum; + pFile->notChecksum = ~checksum; +} + +/** + * @brief 74034 | 2ac | Loads all the current save file values (in Ewram) to RAM + * + */ +void SramRead_FromEwram(void) +{ + i32 i; + struct SaveFile* pFile; + struct SaveMusicInfo musicInfo; + + pFile = &sSramEwramPointer->files[gMostRecentSaveFile]; + + gGameCompletion = pFile->gameCompletion; + gHasSaved = pFile->hasSaved; + gCurrentArea = pFile->currentArea; + gCurrentRoom = pFile->currentRoom; + gLastDoorUsed = pFile->LastDoorUsed; + gMinimapX = pFile->mapX; + gMinimapY = pFile->mapY; + + gCamera = pFile->camera; + gBG0XPosition = pFile->bg0XPosition; + gBG0YPosition = pFile->bg0YPosition; + gBG1XPosition = pFile->bg1XPosition; + gBG1YPosition = pFile->bg1YPosition; + gBG2XPosition = pFile->bg2XPosition; + gBG2YPosition = pFile->bg2YPosition; + gBG3XPosition = pFile->bg3XPosition; + gBG3YPosition = pFile->bg3YPosition; + + gDifficulty = pFile->difficulty; + gUseMotherShipDoors = pFile->useMotherShipDoor; + gTimeAttackFlag = pFile->timeAttack; + + for (i = 0; i < MAX_AMOUNT_OF_AREAS; i++) + gNumberOfNeverReformBlocks[i] = pFile->numberOfNeverReformBlocksBroken[i]; + + for (i = 0; i < MAX_AMOUNT_OF_AREAS; i++) + gNumberOfItemsCollected[i] = pFile->numberOfItemsCollected[i]; + + gSamusData = pFile->samusData; + gSamusWeaponInfo = pFile->samusWeaponInfo; + gSamusEcho = pFile->samusEcho; + gScrewSpeedAnimation = pFile->screwSpeedAnimation; + gEquipment = pFile->equipment; + gSamusHazardDamage = pFile->hazardDamage; + gSamusEnvironmentalEffects[0] = pFile->environmentalEffects[0]; + gSamusEnvironmentalEffects[1] = pFile->environmentalEffects[1]; + gSamusEnvironmentalEffects[2] = pFile->environmentalEffects[2]; + gSamusEnvironmentalEffects[3] = pFile->environmentalEffects[3]; + gSamusEnvironmentalEffects[4] = pFile->environmentalEffects[4]; + gPreventMovementTimer = pFile->preventMovementTimer; + gDisableDrawingSamusAndScrolling = pFile->disableDrawingSamusAndScrolling; + + gAlarmTimer = pFile->alarmTimer; + gInGameTimer = pFile->inGameTimer; + + for (i = 0; i < 12; i++) + gBestCompletionTimes[i] = pFile->bestCompletionTimes[i]; + + for (i = 0; i < MAX_AMOUNT_OF_IGT_AT_BOSSES; i++) + gInGameTimerAtBosses[i] = pFile->inGameTimerAtBosses[i]; + + musicInfo = pFile->musicInfo; + for (i = 0; i < 1; i++) + gUnk_3000050[i] = pFile->unk_24C[i]; + + gMusicInfo.musicTrack = musicInfo.musicTrack; + gMusicInfo.unknown_1E = musicInfo.unk_2; + gMusicInfo.unknown_20 = musicInfo.unk_4; + gMusicInfo.priority = musicInfo.priority; +} + +/** + * @brief 742e0 | 24 | Copies a string from src to dst, of length size + * + * @param dst Destination pointer + * @param src Source pointer + * @param length String length + */ +void StringCopy(u8* dst, const u8* const src, u8 length) +{ + i32 i; + + for (i = 0; i < length; i++) + dst[i] = src[i]; +} + +/** + * @brief 74304 | a0 | Performs a series of tests on flash sram to verify it's working correctly + * + */ +void SramTestFlash(void) +{ + u32 flags; + i32 i; + u8 text[SRAM_TEXT_SIZE]; + + flags = 0; + gSramCorruptFlag = FALSE; + + // Perform a write Text -> Flash + if (SramWriteChecked(sMetZeroSramCheck_Text, SRAM_BASE + OFFSET_OF(struct Sram, MetZeroSramCheck_Text), SRAM_TEXT_SIZE)) + flags = 1; // Internal check failed + + // Read the text previously written into a local buffer + SramWriteUnchecked(SRAM_BASE + OFFSET_OF(struct Sram, MetZeroSramCheck_Text), text, SRAM_TEXT_SIZE); + + // Add 1 to every element + for (i = 0; i < SRAM_TEXT_SIZE; i++) + text[i]++; + + // Write the modified text to flash + if (SramWriteChecked(text, SRAM_BASE + OFFSET_OF(struct Sram, MetZeroSramCheck_Text), SRAM_TEXT_SIZE)) + flags |= 2; // Internal check failed + + // Read the text previously written into a local buffer + SramWriteUnchecked(SRAM_BASE + OFFSET_OF(struct Sram, MetZeroSramCheck_Text), text, SRAM_TEXT_SIZE); + + // Verify the contents of the buffer, it should contain the test text with 1 added to every letter + for (i = 0; i < SRAM_TEXT_SIZE; i++) + { + if (text[i] != sMetZeroSramCheck_Text[i] + 1) + { + // Incorrect data + flags |= 4; + break; + } + } + + if (flags) + gSramCorruptFlag = flags; +} + +void unk_743a4(void) +{ + +} + +void unk_74574(void) +{ + +} + +void unk_74624(u8 param_1) +{ + +} + +/** + * @brief 7478c | 20 | Reads a save file from Ewram + * + */ +void SramLoadFile(void) +{ + if (gIsLoadingFile == TRUE) + { + SramRead_FromEwram(); + SramRead_Arrays(); + } +} + +/** + * @brief 747ac | 10 | Reads a save file from Ewram, unused + * + */ +void SramLoadFile_Unused(void) +{ + SramRead_FromEwram(); + SramRead_Arrays(); +} + + +void SramWrite_Arrays(void) +{ + // https://decomp.me/scratch/2UPuJ + + struct SaveFile* pFile; + i32 i; + u32 offset; + u32 size; + + pFile = &sSramEwramPointer->files[gMostRecentSaveFile]; + DMATransfer(3, gVisitedMinimapTiles, pFile->visitedMinimapTiles, sizeof(pFile->visitedMinimapTiles), 0x10); + DMATransfer(3, gHatchesOpened, pFile->hatchesOpened, sizeof(pFile->hatchesOpened), 0x10); + DMATransfer(3, gEventsTriggered, pFile->eventsTriggered, sizeof(pFile->eventsTriggered), 0x10); + + offset = 0; + for (i = 0; i < 8; i++) + { + if (gNumberOfNeverReformBlocks[i] == 0) + continue; + + size = gNumberOfNeverReformBlocks[i] * 2; + DMATransfer(3, gNeverReformBlocks[i], pFile->neverReformBLocksBroken[offset], size, 0x10); + offset += size; + } + + offset = 0; + for (i = 0; i < 8; i++) + { + if (gNumberOfItemsCollected[i] == 0) + continue; + + size = gNumberOfItemsCollected[i] * sizeof(struct ItemInfo); + DMATransfer(3, gItemsCollected[i], pFile->itemsCollected[offset], size, 0x10); + offset += size; + } +} + +void SramRead_Arrays(void) +{ + +} + +/** + * @brief 749e4 | 98 | Writes the most recent save file id to flash sram + * + */ +void SramWrite_MostRecentSaveFile(void) +{ + struct SaveValue* pSave; + i32 i; + u16 checksum; + u16* ptr; + + pSave = &sSramEwramPointer->mostRecentFileSave; + ptr = (u16*)pSave; + + pSave->magicNumber = 0x1E; + pSave->counter++; + pSave->checksum = 0; + pSave->notChecksum = ~0; + pSave->value = gMostRecentSaveFile; + + // Write start/end strings + for (i = 0; i < SRAM_TEXT_SIZE; i++) + { + pSave->startText[i] = sMostRecentFileSave_Text[0][i]; + pSave->endText[i] = sMostRecentFileSave_Text[1][i]; + } + + // Calculate checksum + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveValue, 4, u16); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + pSave->checksum = checksum; + pSave->notChecksum = ~checksum; + + do_sram_operation(SRAM_OPERATION_SAVE_MOST_RECENT_FILE); +} + +/** + * @brief 74a7c | f0 | Read the most recent save file id from flash sram + * + */ +void SramRead_MostRecentSaveFile(void) +{ + u16 buffer; + u32 error; + i32 i; + u16 checksum; + u16 actualChecksum; + u16* ptr; + struct SaveValue* pSave; + + pSave = &sSramEwramPointer->mostRecentFileSave; + if (pSave->magicNumber == 0x1E) + error = 0; + else + error = 1; + + if (error == 0) + { + // Calculate checksum + ptr = (u16*)&sSramEwramPointer->mostRecentFileSave; + checksum = 0; + actualChecksum = pSave->checksum; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveValue, 4, u16); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + if (actualChecksum != checksum) + error = 2; + + if (error == 0) + { + for (i = 0; i < SRAM_TEXT_SIZE; i++) + { + if (pSave->startText[i] != sMostRecentFileSave_Text[0][i]) + { + error = 3; + break; + } + + if (sMostRecentFileSave_Text[1][i]) + { + error = 3; + break; + } + } + } + } + + if (error == 0) + { + gMostRecentSaveFile = pSave->value; + return; + } + + ptr = &buffer; + buffer = USHORT_MAX; + dma_set(3, &buffer, &sSramEwramPointer->mostRecentFileSave, + (DMA_ENABLE | DMA_SRC_FIXED) << 16 | sizeof(sSramEwramPointer->mostRecentFileSave) / 2); + + gMostRecentSaveFile = 0; +} + +/** + * @brief 74b6c | 9c | Writes the sound mode (stereo) to flash sram + * + */ +void SramWrite_SoundMode(void) +{ + struct SaveValue* pSave; + i32 i; + u16 checksum; + u16* ptr; + + pSave = &sSramEwramPointer->soundModeSave; + ptr = (u16*)pSave; + + pSave->magicNumber = 0x1F; + pSave->counter++; + pSave->checksum = 0; + pSave->notChecksum = ~0; + pSave->value = gStereoFlag; + + // Write start/end strings + for (i = 0; i < SRAM_TEXT_SIZE; i++) + { + pSave->startText[i] = sSoundModeSave_Text[0][i]; + pSave->endText[i] = sSoundModeSave_Text[1][i]; + } + + // Calculate checksum + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveValue, 4, u16); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + pSave->checksum = checksum; + pSave->notChecksum = ~checksum; + + do_sram_operation(SRAM_OPERATION_SAVE_SOUND_MODE); +} + +/** + * @brief 74c08 | f0 | Reads the sound mode (stereo) from flash sram + * + */ +void SramRead_SoundMode(void) +{ + u16 buffer; + u32 error; + i32 i; + u16 checksum; + u16 actualChecksum; + u16* ptr; + struct SaveValue* pSave; + + pSave = &sSramEwramPointer->soundModeSave; + if (pSave->magicNumber == 0x1F) + error = 0; + else + error = 1; + + if (error == 0) + { + // Calculate checksum + ptr = (u16*)&sSramEwramPointer->soundModeSave; + checksum = 0; + actualChecksum = pSave->checksum; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveValue, 4, u16); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + if (actualChecksum != checksum) + error = 2; + + if (error == 0) + { + for (i = 0; i < SRAM_TEXT_SIZE; i++) + { + if (pSave->startText[i] != sSoundModeSave_Text[0][i]) + { + error = 3; + break; + } + if (pSave->endText[i] != sSoundModeSave_Text[1][i]) + { + error = 3; + break; + } + } + } + } + + if (error == 0) + { + gStereoFlag = pSave->value; + return; + } + + ptr = &buffer; + buffer = USHORT_MAX; + dma_set(3, &buffer, &sSramEwramPointer->soundModeSave, + (DMA_ENABLE | DMA_SRC_FIXED) << 16 | sizeof(sSramEwramPointer->soundModeSave) / 2); + + gStereoFlag = FALSE; +} + +/** + * @brief 74cf8 | ac | Writes the language to flash sram + * + */ +void SramWrite_Language(void) +{ + struct SaveValue* pSave; + i32 i; + u16 checksum; + u16* ptr; + u32 value; + + pSave = &sSramEwramPointer->languagesSave[0]; + ptr = (u16*)pSave; + + pSave->magicNumber = 0x1F; + pSave->counter++; + pSave->checksum = 0; + pSave->notChecksum = ~0; + + i = gLanguage; + if ((u32)i > LANGUAGE_SPANISH) + i = LANGUAGE_ENGLISH; + + pSave->value = i; + + // Write start/end strings + for (i = 0; i < SRAM_TEXT_SIZE; i++) + { + pSave->startText[i] = sLanguageSave_Text[0][i]; + pSave->endText[i] = sLanguageSave_Text[1][i]; + } + + // Calculate checksum + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveValue, 4, u16); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + pSave->checksum = checksum; + pSave->notChecksum = ~checksum; + + do_sram_operation(SRAM_OPERATION_SAVE_LANGUAGE); +} + +u32 SramRead_Language(void) +{ + +} + +/** + * @brief 74f58 | b8 | Writes the time attack to flash sram + * + */ +void SramWrite_TimeAttack(void) +{ + struct SaveTimeAttack* pSave; + u16 checksum; + u16* ptr; + i32 i; + + pSave = &sSramEwramPointer->timeAttackSaves[0]; + ptr = (u16*)pSave; + + pSave->magicNumber = 0x40; + pSave->counter++; + pSave->checksum = 0; + pSave->notChecksum = ~0; + + pSave->value = gTimeAttackRecord; + + // Write start/end strings + for (i = 0; i < SRAM_TEXT_SIZE; i++) + { + pSave->startText[i] = sTimeAttackSave_Text[i]; + pSave->endText[i] = sTimeAttackSave_Text[SRAM_TEXT_SIZE + i]; + } + + // Calculate checksum + checksum = 0; + for (i = SRAM_GET_CHECKSUM_SIZE(struct SaveTimeAttack, 4, u16); i >= 0; i--) + { + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + checksum += *ptr++; + } + + pSave->checksum = checksum; + pSave->notChecksum = ~checksum; + + do_sram_operation(SRAM_OPERATION_SAVE_TIME_ATTACK); +} + +void SramRead_TimeAttack(void) +{ + +} + + +void SramWrite_ToEwram_Unused(void) +{ + // https://decomp.me/scratch/wgziF + + struct SaveFile_Unused* pFile; + u32 value; + + value = USHORT_MAX; + pFile = &gSram.file_unused; + BitFill(3, value, pFile, sizeof(struct SaveFile_Unused), 0x10); + + pFile->currentArea = gCurrentArea; + pFile->lastDoorUsed = gLastDoorUsed; + pFile->samusData = gSamusData; + pFile->samusWeaponInfo = gSamusWeaponInfo; + pFile->samusEcho = gSamusEcho; + pFile->screwSpeedAnimation = gScrewSpeedAnimation; + pFile->equipment = gEquipment; + pFile->hazardDamage = gSamusHazardDamage; + + pFile->environmentalEffects[0] = gSamusEnvironmentalEffects[0]; + pFile->environmentalEffects[1] = gSamusEnvironmentalEffects[1]; + pFile->environmentalEffects[2] = gSamusEnvironmentalEffects[2]; + pFile->environmentalEffects[3] = gSamusEnvironmentalEffects[3]; + pFile->environmentalEffects[4] = gSamusEnvironmentalEffects[4]; + + DMATransfer(3, &gVisitedMinimapTiles[gCurrentArea * MINIMAP_SIZE], pFile->visitedMinimapTiles, sizeof(pFile->visitedMinimapTiles), 0x10); + DMATransfer(3, gHatchesOpened[gCurrentArea], pFile->hatchesOpened, sizeof(pFile->hatchesOpened), 0x10); + + pFile->unk_238 = 0x41; + pFile->unk_239 = 0x54; + pFile->unk_23A = 0x52; + pFile->unk_23B = 0x55; + pFile->unk_23C = 0x4E; + pFile->unk_23D = 0x45; + pFile->unk_23E = 0x4E; + pFile->unk_23F = 0x44; + + pFile->useMotherShipDoors = gUseMotherShipDoors; + do_sram_operation(SRAM_OPERATION_SAVE_UNUSED_SRAM); +} + +void SramLoad_DemoRamValues(void) +{ + +} + + +u32 SramDeleteFile(u8 file) +{ + +} + +/** + * @brief 755a4 | 11c | Copies a save file + * + * @param src Source file + * @param dst Destination file + * @return u32 bool, ended + */ +u32 SramCopyFile(u8 src, u8 dst) +{ + struct Sram* pSram; + + pSram = &gSram; + + if (!gDisableSoftreset) + { + gDisableSoftreset = TRUE; + gSramOperationStage = 0; + } + + switch (gSramOperationStage) + { + case 0: + // Copy save file info + gSaveFilesInfo[dst] = gSaveFilesInfo[src]; + + // Copy file + DMATransfer(3, &pSram->files[src], &pSram->files[dst], sizeof(struct SaveFile), 0x10); + gSramOperationStage++; + break; + + case 1: + gMostRecentSaveFile = dst; + if (unk_fbc(0)) + gSramOperationStage++; + gMostRecentSaveFile = src; + break; + + case 2: + // Copy copy of the file + DMATransfer(3, &pSram->filesCopy[src], &pSram->filesCopy[dst], sizeof(struct SaveFile), 0x10); + gSramOperationStage++; + break; + + case 3: + gMostRecentSaveFile = dst; + if (unk_fbc(1)) + gSramOperationStage++; + gMostRecentSaveFile = src; + break; + + case 4: + default: + // End, re-enable soft reset + gDisableSoftreset = FALSE; + gSramOperationStage = 0; + break; + } + + // If soft reset is enabled, then the process is complete + return gDisableSoftreset ^ TRUE; +} + + +void SramWrite_FileInfo_(void) +{ + // https://decomp.me/scratch/c9e6c + + i32 i; + + for (i = 0; i < 3; i++) + { + gSaveFilesInfo[i].currentArea = gSram.files[i].currentArea; + if (gSaveFilesInfo[i].exists == TRUE) + { + gSaveFilesInfo[i].currentEnergy = gSram.files[i].equipment.currentEnergy; + gSaveFilesInfo[i].maxEnergy = gSram.files[i].equipment.maxEnergy; + gSaveFilesInfo[i].currentMissiles = gSram.files[i].equipment.currentMissiles; + gSaveFilesInfo[i].maxMissiles = gSram.files[i].equipment.maxMissiles; + gSaveFilesInfo[i].suitType = gSram.files[i].equipment.suitType; + gSaveFilesInfo[i].igtHours = gSram.files[i].inGameTimer.hours; + gSaveFilesInfo[i].igtMinutes = gSram.files[i].inGameTimer.minutes; + gSaveFilesInfo[i].igtSconds = gSram.files[i].inGameTimer.seconds; + gSaveFilesInfo[i].hasSaved = gSram.files[i].hasSaved; + gSaveFilesInfo[i].completedGame = gSram.files[i].gameCompletion.completedGame; + gSaveFilesInfo[i].introPlayed = gSram.files[i].gameCompletion.introPlayed; + gSaveFilesInfo[i].language = gSram.files[i].gameCompletion.language; + gSaveFilesInfo[i].timeAttack = gSram.files[i].timeAttack; + + if (gSram.files[i].difficulty < MAX_AMOUNT_OF_DIFFICULTIES) + gSaveFilesInfo[i].difficulty = gSram.files[i].difficulty; + else + gSaveFilesInfo[i].difficulty = DIFF_NORMAL; + } + + if (!gSaveFilesInfo[i].exists || !gSaveFilesInfo[i].hasSaved) + { + gSaveFilesInfo[i].exists = FALSE; + gSaveFilesInfo[i].currentArea = AREA_BRINSTAR; + gSaveFilesInfo[i].currentEnergy = 0; + gSaveFilesInfo[i].maxEnergy = 0; + gSaveFilesInfo[i].currentMissiles = 0; + gSaveFilesInfo[i].maxMissiles = 0; + gSaveFilesInfo[i].suitType = SUIT_NORMAL; + gSaveFilesInfo[i].igtHours = 0; + gSaveFilesInfo[i].igtMinutes = 0; + gSaveFilesInfo[i].igtSconds = 0; + gSaveFilesInfo[i].language = gLanguage; + + if (gSaveFilesInfo[i].introPlayed) + { + gSaveFilesInfo[i].currentArea = AREA_CRATERIA; + gSaveFilesInfo[i].currentEnergy = 99; + gSaveFilesInfo[i].maxEnergy = 99; + gSaveFilesInfo[i].difficulty = DIFF_EASY; + + if (gSram.files[i].difficulty > DIFF_HARD) + gSaveFilesInfo[i].difficulty = DIFF_NORMAL; + + gSaveFilesInfo[i].difficulty = gSram.files[i].difficulty; + gSaveFilesInfo[i].language = gSram.files[i].gameCompletion.language; + gSaveFilesInfo[i].timeAttack = gSram.files[i].timeAttack; + } + } + } +} + +/** + * @brief 757c8 | 84 | To document + * + * @param file File number + */ +void unk_757c8(u8 file) +{ + i32 previousFile; + + previousFile = gMostRecentSaveFile; + gMostRecentSaveFile = file; + + switch (gSaveFilesInfo[gMostRecentSaveFile].unk_1) + { + case 1: + do_sram_operation(3); + break; + + case 2: + do_sram_operation(3); + do_sram_operation(4); + break; + + case 0: + case 3: + case 4: + break; + } + + gSaveFilesInfo[gMostRecentSaveFile].unk_1 = 0; + gMostRecentSaveFile = previousFile; +} + +void unk_7584c(u8 param_1) +{ + +} + +/** + * @brief 7596c | 128 | Checks if a save file should load + * + */ +void Sram_CheckLoadSaveFile(void) +{ + // Checks current save file exists + gIsLoadingFile = gSaveFilesInfo[gMostRecentSaveFile].exists; + if (!gIsLoadingFile) + { + // No save file, create one and setup data + Sram_InitSaveFile(); + + gColorFading.type = 0x15; // TODO enum + + gEquipment.downloadedMapStatus = 0; + gCurrentArea = gStartingInfo.startingArea; + gAreaBeforeTransition = gStartingInfo.startingArea; + + gCurrentRoom = 0; + gLastDoorUsed = 0; + + gGameCompletion.completedGame = gSaveFilesInfo[gMostRecentSaveFile].completedGame; + gGameCompletion.introPlayed = gSaveFilesInfo[gMostRecentSaveFile].introPlayed; + gGameCompletion.language = gSaveFilesInfo[gMostRecentSaveFile].language; + + gDifficulty = gSaveFilesInfo[gMostRecentSaveFile].difficulty; + gTimeAttackFlag = gSaveFilesInfo[gMostRecentSaveFile].timeAttack; + gUseMotherShipDoors = FALSE; + gShipLandingFlag = TRUE; + } + else + { + // Save file exists, load it + SramLoadFile(); + } + + gGameCompletion.language = gSaveFilesInfo[gMostRecentSaveFile].language; + gLanguage = gGameCompletion.language; + gSkipDoorTransition = FALSE; + gDebugFlag = FALSE; +} + +void Sram_InitSaveFile(void) +{ + +} + +/** + * @brief 75bf8 | c | Empty V-blank code for SRAM + * + */ +void Sram_VblankEmpty(void) +{ + vu8 c = 0; +} + +/** + * @brief 75c04 | 2c | To document + * + * @param param_1 To document + * @return u32 bool, is loading file + */ +u32 unk_75c04(u8 param_1) +{ + CallbackSetVBlank(Sram_VblankEmpty); + unk_7584c(param_1); + return gIsLoadingFile; +} diff --git a/src/softreset.c b/src/softreset.c index f52404af..5a6f4b50 100644 --- a/src/softreset.c +++ b/src/softreset.c @@ -44,7 +44,7 @@ void Softreset(void) ClearGfxRam(); LoadInterruptCode(); CallbackSetVBlank(SoftresetVBlankCallback); - read_sram(); + SramRead_All(); init_sound(); write16(REG_IE, IF_VBLANK | IF_DMA2 | IF_GAMEPAK); diff --git a/src/sprites_AI/item_banner.c b/src/sprites_AI/item_banner.c index 0202cadc..fb9d760e 100644 --- a/src/sprites_AI/item_banner.c +++ b/src/sprites_AI/item_banner.c @@ -410,7 +410,7 @@ void SaveYesNoCursor(void) break; case SAVE_YES_NO_CURSOR_POSE_SAVING: - if (save_file()) // Undefined + if (SramSaveFile()) gCurrentSprite.status = 0x0; break; } diff --git a/src/sram/sram.c b/src/sram/sram.c index b8f61ff9..937bed3c 100644 --- a/src/sram/sram.c +++ b/src/sram/sram.c @@ -3,14 +3,14 @@ #include "gba.h" #include "io.h" -static void SRAMWriteUncheckedInternal(u8 *src, u8 *dest, u32 size) +static void SramWriteUncheckedInternal(u8 *src, u8 *dest, u32 size) { while (size-- != 0) { *dest++ = *src++; } } -void SRAMWriteUnchecked(u8 *src, u8 *dest, u32 size) +void SramWriteUnchecked(u8 *src, u8 *dest, u32 size) { u16 code[0x40]; u16 *code_ptr; @@ -22,12 +22,12 @@ void SRAMWriteUnchecked(u8 *src, u8 *dest, u32 size) REG_WAITCNT, read16(REG_WAITCNT) & ~WAIT_SRAM_CYCLES_MASK | WAIT_SRAM_8CYCLES); - func_ptr = (u16 *)SRAMWriteUncheckedInternal; + func_ptr = (u16 *)SramWriteUncheckedInternal; func_ptr = (u16 *)((u32)func_ptr & ~1); code_ptr = code; for (csize = - ((u32)SRAMWriteUnchecked - (u32)SRAMWriteUncheckedInternal) + ((u32)SramWriteUnchecked - (u32)SramWriteUncheckedInternal) / 2; csize > 0; --csize) { @@ -38,7 +38,7 @@ void SRAMWriteUnchecked(u8 *src, u8 *dest, u32 size) func(src, dest, size); } -void SRAMWrite(u8 *src, u8 *dest, u32 size) +void SramWrite(u8 *src, u8 *dest, u32 size) { u16 w = read16(REG_WAITCNT) & ~WAIT_SRAM_CYCLES_MASK | WAIT_SRAM_8CYCLES; write16(REG_WAITCNT, w); @@ -59,7 +59,7 @@ static u8* SRAMCheckInternal(u8 *src, u8 *dest, u32 size) return NULL; } -u8* SRAMCheck(u8 *src, u8 *dest, u32 size) +u8* SramCheck(u8 *src, u8 *dest, u32 size) { u16 code[0x60]; u16 *code_ptr; @@ -75,7 +75,7 @@ u8* SRAMCheck(u8 *src, u8 *dest, u32 size) func_ptr = (u16 *)((u32)func_ptr & ~1); code_ptr = code; - for (csize = ((u32)SRAMCheck - (u32)SRAMCheckInternal) / 2; csize > 0; + for (csize = ((u32)SramCheck - (u32)SRAMCheckInternal) / 2; csize > 0; --csize) { *code_ptr++ = *func_ptr++; } @@ -84,14 +84,14 @@ u8* SRAMCheck(u8 *src, u8 *dest, u32 size) return func(src, dest, size); } -u8* SRAMWriteChecked(u8 *src, u8 *dest, u32 size) +u8* SramWriteChecked(u8 *src, u8 *dest, u32 size) { u8 *diff; u8 i; for (i = 0; i < 3; ++i) { - SRAMWrite(src, dest, size); - diff = SRAMCheck(src, dest, size); + SramWrite(src, dest, size); + diff = SramCheck(src, dest, size); if (!diff) { break; }