From e8db57c398bcd90f58d764cecad71c6f1de94412 Mon Sep 17 00:00:00 2001 From: Derek Hensley Date: Sat, 5 Apr 2025 09:05:12 -0700 Subject: [PATCH] Bootstrap z_sram --- spec/spec | 5 + src/code/z_sram_JPN.c | 2042 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2047 insertions(+) create mode 100644 src/code/z_sram_JPN.c diff --git a/spec/spec b/spec/spec index 6402882459..1b90376fd0 100644 --- a/spec/spec +++ b/spec/spec @@ -677,6 +677,9 @@ beginseg include "$(BUILD_DIR)/src/code/z_snap.o" include "$(BUILD_DIR)/src/code/z_sub_s.o" include "$(BUILD_DIR)/src/code/z_circle_tex.o" +#if MM_VERSION < N64_US + include "$(BUILD_DIR)/src/code/z_sram_JPN.o" +#endif include "$(BUILD_DIR)/src/code/z_rumble.o" include "$(BUILD_DIR)/src/code/z_view.o" include "$(BUILD_DIR)/src/code/z_vimode.o" @@ -686,7 +689,9 @@ beginseg include "$(BUILD_DIR)/src/code/z_viszbuf.o" include "$(BUILD_DIR)/src/code/z_vr_box.o" include "$(BUILD_DIR)/src/code/z_vr_box_draw.o" +#if MM_VERSION >= N64_US include "$(BUILD_DIR)/src/code/z_sram_NES.o" +#endif include "$(BUILD_DIR)/src/code/z_message.o" include "$(BUILD_DIR)/src/code/z_message_nes.o" include "$(BUILD_DIR)/src/code/z_message_staff.o" diff --git a/src/code/z_sram_JPN.c b/src/code/z_sram_JPN.c new file mode 100644 index 0000000000..c6790a1a5d --- /dev/null +++ b/src/code/z_sram_JPN.c @@ -0,0 +1,2042 @@ +#include "z64save.h" +#include "global.h" + +#include "sys_flashrom.h" +#include "z64horse.h" +#include "overlays/gamestates/ovl_file_choose/z_file_select.h" + +void Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages); +void func_80147314(SramContext* sramCtx, s32 fileNum); +void func_80147414(SramContext* sramCtx, s32 fileNum, s32 arg2); + +#define CHECK_NEWF(newf) \ + ((newf)[0] != 'Z' || (newf)[1] != 'E' || (newf)[2] != 'L' || (newf)[3] != 'D' || (newf)[4] != 'A' || \ + (newf)[5] != '3') + +typedef struct PersistentCycleSceneFlags { + /* 0x0 */ u32 switch0; + /* 0x4 */ u32 switch1; + /* 0x8 */ u32 chest; + /* 0xC */ u32 collectible; +} PersistentCycleSceneFlags; // size = 0x10 + +#define PERSISTENT_CYCLE_FLAGS_SET(switch0, switch1, chest, collectible) { switch0, switch1, chest, collectible }, +#define PERSISTENT_CYCLE_FLAGS_NONE PERSISTENT_CYCLE_FLAGS_SET(0, 0, 0, 0) + +#define DEFINE_SCENE(_name, _enumValue, _textId, _drawConfig, _restrictionFlags, persistentCycleFlags) \ + persistentCycleFlags +#define DEFINE_SCENE_UNSET(_enumValue) PERSISTENT_CYCLE_FLAGS_NONE + +/** + * Array of bitwise flags which won't be turned off on a cycle reset (will persist between cycles) + */ +PersistentCycleSceneFlags sPersistentCycleSceneFlags[SCENE_MAX] = { +#include "tables/scene_table.h" +}; + +#undef DEFINE_SCENE +#undef DEFINE_SCENE_UNSET + +// Each flag has 2 bits to store persistence over the three-day reset cycle +// Only 1 of these bits need to be set to persist (Values 1, 2, 3). +// Therefore, the final game does not distinguish between these two macros in use +#define PERSISTENT_WEEKEVENTREG(flag) (3 << (2 * BIT_FLAG_TO_SHIFT(flag))) +#define PERSISTENT_WEEKEVENTREG_ALT(flag) (2 << (2 * BIT_FLAG_TO_SHIFT(flag))) + +// weekEventReg flags which will be not be cleared on a cycle reset +//! @note The index of the flag in this array must be the same to its index in the WeekeventReg array +//! Only the mask is read from the `PERSISTENT_` macros. +u16 sPersistentCycleWeekEventRegs[ARRAY_COUNT(gSaveContext.save.saveInfo.weekEventReg)] = { + /* 0 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_TERMINA_FIELD) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_IKANA_GRAVEYARD) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_ROMANI_RANCH) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_GORMAN_TRACK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_MOUNTAIN_VILLAGE_WINTER) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_GORON_SHRINE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_SNOWHEAD), + /* 1 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_SOUTHERN_SWAMP_POISONED) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_WOODFALL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_DEKU_PALACE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_GREAT_BAY_COAST) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_PIRATES_FORTRESS) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_ZORA_HALL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_WATERFALL_RAPIDS) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_IKANA_CANYON), + /* 2 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_IKANA_CASTLE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_STONE_TOWER) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_STONE_TOWER_INVERTED) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_EAST_CLOCK_TOWN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_WEST_CLOCK_TOWN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_NORTH_CLOCK_TOWN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_WOODFALL_TEMPLE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_SNOWHEAD_TEMPLE), + /* 3 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_PIRATES_FORTRESS_EXTERIOR) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_STONE_TOWER_TEMPLE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_STONE_TOWER_TEMPLE_INVERTED) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_THE_MOON) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_MOON_DEKU_TRIAL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_MOON_GORON_TRIAL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_MOON_ZORA_TRIAL) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_03_80), + /* 4 */ 0, + /* 5 */ 0, + /* 6 */ 0, + /* 7 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_ENTERED_WOODFALL_TEMPLE_PRISON), + /* 8 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_DOGGY_RACETRACK_HEART_PIECE), + /* 9 */ 0, + /* 10 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_BANK_WALLET_UPGRADE), + /* 11 */ 0, + /* 12 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_KOTAKE_BOTTLE), + /* 13 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_OCEANSIDE_WALLET_UPGRADE), + /* 14 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_DEKU_PLAYGROUND_HEART_PIECE), + /* 15 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_15_20), + /* 16 */ 0, + /* 17 */ 0, + /* 18 */ 0, + /* 19 */ 0, + /* 20 */ 0, + /* 21 */ 0, + /* 22 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_ALIENS_BOTTLE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_HONEY_AND_DARLING_HEART_PIECE), + /* 23 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_GREAT_SPIN_ATTACK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_BEAVER_RACE_BOTTLE), + /* 24 */ PERSISTENT_WEEKEVENTREG_ALT(WEEKEVENTREG_24_02) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_24_80), + /* 25 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_BEAVER_BROS_HEART_PIECE), + /* 26 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_26_40), + /* 27 */ 0, + /* 28 */ 0, + /* 29 */ 0, + /* 30 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_30_10) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_30_20) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_30_40) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_30_80), + /* 31 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_31_01) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_31_02) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_31_04) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_31_20), + /* 32 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_SEAHORSE_HEART_PIECE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_SWAMP_SHOOTING_GALLERY_HEART_PIECE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_TOWN_SHOOTING_GALLERY_HEART_PIECE), + /* 33 */ 0, + /* 34 */ 0, + /* 35 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_CLOCK_TOWN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_WOODFALL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_ROMANI_RANCH) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_GREAT_BAY) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_FROG_CHOIR_HEART_PIECE), + /* 36 */ 0, + /* 37 */ 0, + /* 38 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_38_20), + /* 39 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_EVAN_HEART_PIECE), + /* 40 */ 0, + /* 41 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_GORON_RACE_BOTTLE), + /* 42 */ 0, + /* 43 */ 0, + /* 44 */ 0, + /* 45 */ 0, + /* 46 */ 0, + /* 47 */ 0, + /* 48 */ 0, + /* 49 */ 0, + /* 50 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_50_02) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_50_04), + /* 51 */ PERSISTENT_WEEKEVENTREG_ALT(WEEKEVENTREG_51_04), + /* 52 */ 0, + /* 53 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_BUSINESS_SCRUB_HEART_PIECE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_53_40), + /* 54 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_SPIRIT_HOUSE_HEART_PIECE), + /* 55 */ 0, + /* 56 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_MARINE_RESEARCH_LAB_FISH_HEART_PIECE), + /* 57 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_57_08), + /* 58 */ 0, + /* 59 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_59_04) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_BANK_HEART_PIECE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_SWAMP_SHOOTING_GALLERY_QUIVER_UPGRADE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_TOWN_SHOOTING_GALLERY_QUIVER_UPGRADE), + /* 60 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_MAYOR_HEART_PIECE), + /* 61 */ 0, + /* 62 */ 0, + /* 63 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_SWORDSMANS_SCHOOL_HEART_PIECE), + /* 64 */ 0, + /* 65 */ 0, + /* 66 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_ANJU) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_KAFEI) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_CURIOSITY_SHOP_MAN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_BOMB_SHOP_LADY) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_ROMANI) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_CREMIA) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_MAYOR_DOTOUR) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_MADAME_AROMA), + /* 67 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_TOTO) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_GORMAN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_POSTMAN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_ROSA_SISTERS) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_TOILET_HAND) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_ANJUS_GRANDMOTHER) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_KAMARO) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_GROG), + /* 68 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_GORMAN_BROTHERS) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_SHIRO) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_GURU_GURU) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_MET_BOMBERS) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_ROOM_KEY) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_PROMISED_MIDNIGHT_MEETING) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_PROMISED_TO_MEET_KAFEI) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_LETTER_TO_KAFEI), + /* 69 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_DEPOSITED_LETTER_TO_KAFEI) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_PENDANT_OF_MEMORIES) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_DELIVERED_PENDANT_OF_MEMORIES) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_ESCAPED_SAKONS_HIDEOUT) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_PROMISED_TO_HELP_WITH_ALIENS) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_DEFENDED_AGAINST_ALIENS) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_ALIENS_BOTTLE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_ESCORTED_CREMIA), + /* 70 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_ROMANIS_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_KEATON_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_PRIORITY_MAIL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_DELIVERED_PRIORITY_MAIL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_LEARNED_SECRET_CODE) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_BOMBERS_NOTEBOOK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_MAYOR_HP) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_ROSA_SISTERS_HP), + /* 71 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_TOILET_HAND_HP) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_GRANDMA_SHORT_STORY_HP) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_GRANDMA_LONG_STORY_HP) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_POSTMAN_HP) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_KAFEIS_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_ALL_NIGHT_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_BUNNY_HOOD) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_GAROS_MASK), + /* 72 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_CIRCUS_LEADERS_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_POSTMANS_HAT) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_COUPLES_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_BLAST_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_KAMAROS_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_STONE_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_BREMEN_MASK) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_72_80), + /* 73 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_73_08), + /* 74 */ 0, + /* 75 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_ROSA_SISTERS_HEART_PIECE), + /* 76 */ 0, + /* 77 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_POSTMAN_COUNTING_GAME_HEART_PIECE), + /* 78 */ 0, + /* 79 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_KEATON_HEART_PIECE), + /* 80 */ 0, + /* 81 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_81_08), + /* 82 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_FISHERMANS_JUMPING_GAME_HEART_PIECE), + /* 83 */ 0, + /* 84 */ 0, + /* 85 */ 0, + /* 86 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_86_80), + /* 87 */ + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_87_04) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_87_08) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_87_10) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_87_20) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_87_40) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_87_80), + /* 88 */ 0, + /* 89 */ 0, + /* 90 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_GOSSIP_STONE_GROTTO_HEART_PIECE), + /* 91 */ 0, + /* 92 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_92_80), + /* 93 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_93_04) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_93_08), + /* 94 */ 0, + /* 95 */ 0, + /* 96 */ 0, + /* 97 */ 0, + /* 98 */ 0, + /* 99 */ 0, +}; + +// Unused remnant values from OoT. Not the correct sizes in MM. +#define OOT_SAVECONTEXT_SIZE 0x1428 +#define OOT_SLOT_SIZE (OOT_SAVECONTEXT_SIZE + 0x28) +#define OOT_SRAM_HEADER_SIZE 0x10 +#define OOT_SLOT_OFFSET(index) (OOT_SRAM_HEADER_SIZE + 0x10 + (index * OOT_SLOT_SIZE)) + +// used in other files +u32 gSramSlotOffsets[] = { + OOT_SLOT_OFFSET(0), + OOT_SLOT_OFFSET(1), + OOT_SLOT_OFFSET(2), + // the latter three saves are backup saves for the former saves (in OoT) + OOT_SLOT_OFFSET(3), + OOT_SLOT_OFFSET(4), + OOT_SLOT_OFFSET(5), +}; + +u8 gAmmoItems[ITEM_NUM_SLOTS] = { + ITEM_NONE, // SLOT_OCARINA + ITEM_BOW, // SLOT_BOW + ITEM_NONE, // SLOT_ARROW_FIRE + ITEM_NONE, // SLOT_ARROW_ICE + ITEM_NONE, // SLOT_ARROW_LIGHT + ITEM_NONE, // SLOT_TRADE_DEED + ITEM_BOMB, // SLOT_BOMB + ITEM_BOMBCHU, // SLOT_BOMBCHU + ITEM_DEKU_STICK, // SLOT_DEKU_STICK + ITEM_DEKU_NUT, // SLOT_DEKU_NUT + ITEM_MAGIC_BEANS, // SLOT_MAGIC_BEANS + ITEM_NONE, // SLOT_TRADE_KEY_MAMA + ITEM_POWDER_KEG, // SLOT_POWDER_KEG + ITEM_PICTOGRAPH_BOX, // SLOT_PICTOGRAPH_BOX + ITEM_NONE, // SLOT_LENS_OF_TRUTH + ITEM_NONE, // SLOT_HOOKSHOT + ITEM_NONE, // SLOT_SWORD_GREAT_FAIRY + ITEM_NONE, // SLOT_TRADE_COUPLE + ITEM_NONE, // SLOT_BOTTLE_1 + ITEM_NONE, // SLOT_BOTTLE_2 + ITEM_NONE, // SLOT_BOTTLE_3 + ITEM_NONE, // SLOT_BOTTLE_4 + ITEM_NONE, // SLOT_BOTTLE_5 + ITEM_NONE, // SLOT_BOTTLE_6 +}; + +// Stores flash start page number +s32 gFlashSaveStartPages[] = { + 0, // File 1 New Cycle Save + 0x40, // File 1 New Cycle Save Backup + 0x80, // File 2 New Cycle Save + 0xC0, // File 2 New Cycle Save Backup + 0x100, // File 1 Owl Save + 0x180, // File 1 Owl Save Backup + 0x200, // File 2 Owl Save + 0x280, // File 2 Owl Save Backup + 0x300, // Sram Header (SaveOptions) + 0x380, // Sram Header Backup (SaveOptions) +}; + +// Flash rom number of pages +s32 gFlashSaveNumPages[] = { + 0x40, // File 1 New Cycle Save + 0x40, // File 1 New Cycle Save Backup + 0x40, // File 2 New Cycle Save + 0x40, // File 2 New Cycle Save Backup + 0x80, // File 1 Owl Save + 0x80, // File 1 Owl Save Backup + 0x80, // File 2 Owl Save + 0x80, // File 2 Owl Save Backup + 1, // Sram Header (SaveOptions) + 1, // Sram Header Backup (SaveOptions) +}; + +// Flash rom number of pages on very first time Player enters South Clock Town from the Clock Tower +s32 gFlashSpecialSaveNumPages[] = { + 0x80, // File 1 New Cycle Save + 0x80, // File 1 New Cycle Save Backup + 0x80, // File 2 New Cycle Save + 0x80, // File 2 New Cycle Save Backup + 0x80, // File 1 Owl Save + 0x80, // File 1 Owl Save Backup + 0x80, // File 2 Owl Save + 0x80, // File 2 Owl Save Backup + 1, // Sram Header (SaveOptions) + 1, // Sram Header Backup (SaveOptions) +}; + +// Owl Save flash rom start page number +s32 gFlashOwlSaveStartPages[] = { + 0x100, // File 1 Owl Save + 0x180, // File 1 Owl Save Backup + 0x200, // File 2 Owl Save + 0x280, // File 2 Owl Save Backup +}; + +// Owl Save flash rom number of pages +s32 gFlashOwlSaveNumPages[] = { + 0x80, // File 1 Owl Save + 0x80, // File 1 Owl Save Backup + 0x80, // File 2 Owl Save + 0x80, // File 2 Owl Save Backup +}; + +// Save Options Sram Header flash rom start page number +s32 gFlashOptionsSaveStartPages[] = { + 0x300, // Sram Header (SaveOptions) + 0x380, // Sram Header Backup (SaveOptions) +}; + +// Save Options Sram Header flash rom number of pages +s32 gFlashOptionsSaveNumPages[] = { + 1, // Sram Header (SaveOptions) + 1, // Sram Header Backup (SaveOptions) +}; + +// Flash rom actual size needed +s32 gFlashSaveSizes[] = { + sizeof(Save), // size = 0x100C - File 1 New Cycle Save + sizeof(Save), // size = 0x100C - File 1 New Cycle Save Backup + sizeof(Save), // size = 0x100C - File 2 New Cycle Save + sizeof(Save), // size = 0x100C - File 2 New Cycle Save Backup + offsetof(SaveContext, fileNum), // size = 0x3CA0 - File 1 Owl Save + offsetof(SaveContext, fileNum), // size = 0x3CA0 - File 1 Owl Save Backup + offsetof(SaveContext, fileNum), // size = 0x3CA0 - File 2 Owl Save + offsetof(SaveContext, fileNum), // size = 0x3CA0 - File 2 Owl Save Backup +}; + +// Bit Flag array in which sBitFlags8[n] is (1 << n) +u8 sBitFlags8[] = { + (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), +}; + +u16 D_801F6AF0; +u8 D_801F6AF2; + +void Sram_ActivateOwl(u8 owlWarpId) { + SET_OWL_STATUE_ACTIVATED(owlWarpId); + + if (gSaveContext.save.saveInfo.playerData.owlWarpId == OWL_WARP_NONE) { + gSaveContext.save.saveInfo.playerData.owlWarpId = owlWarpId; + } +} + +void Sram_ClearHighscores(void) { + HS_SET_BOAT_ARCHERY_HIGH_SCORE(19); + HS_SET_HIGH_SCORE_3_LOWER(10); + HS_SET_HORSE_BACK_BALLOON_TIME(SECONDS_TO_TIMER(60)); + HS_SET_TOWN_SHOOTING_GALLERY_HIGH_SCORE(39); + HS_SET_SWAMP_SHOOTING_GALLERY_HIGH_SCORE(10); + + gSaveContext.save.saveInfo.dekuPlaygroundHighScores[0] = SECONDS_TO_TIMER(75); + gSaveContext.save.saveInfo.dekuPlaygroundHighScores[1] = SECONDS_TO_TIMER(75); + gSaveContext.save.saveInfo.dekuPlaygroundHighScores[2] = SECONDS_TO_TIMER(76); +} + +/** + * Clears specific weekEventReg flags. Used by the "Dawn of the First Day" message + */ +void Sram_ClearFlagsAtDawnOfTheFirstDay(void) { + CLEAR_WEEKEVENTREG(WEEKEVENTREG_55_02); + CLEAR_WEEKEVENTREG(WEEKEVENTREG_90_01); + CLEAR_WEEKEVENTREG(WEEKEVENTREG_89_40); + CLEAR_WEEKEVENTREG(WEEKEVENTREG_89_08); + CLEAR_WEEKEVENTREG(WEEKEVENTREG_85_80); +} + +/** + * Used by Song of Time (when clicking "Yes") and (indirectly) by the "Dawn of the New Day" cutscene + */ +void Sram_SaveEndOfCycle(PlayState* play) { + s16 sceneId; + s32 j; + s32 i; + u8 slot; + u8 item; + + gSaveContext.save.timeSpeedOffset = 0; + gSaveContext.save.eventDayCount = 0; + gSaveContext.save.day = 0; + gSaveContext.save.time = CLOCK_TIME(6, 0) - 1; + + gSaveContext.save.saveInfo.playerData.threeDayResetCount++; + if (gSaveContext.save.saveInfo.playerData.threeDayResetCount > 999) { + gSaveContext.save.saveInfo.playerData.threeDayResetCount = 999; + } + + sceneId = Play_GetOriginalSceneId(play->sceneId); + Play_SaveCycleSceneFlags(play); + + play->actorCtx.sceneFlags.chest &= sPersistentCycleSceneFlags[sceneId].chest; + play->actorCtx.sceneFlags.switches[0] &= sPersistentCycleSceneFlags[sceneId].switch0; + play->actorCtx.sceneFlags.switches[1] &= sPersistentCycleSceneFlags[sceneId].switch1; + play->actorCtx.sceneFlags.collectible[0] &= sPersistentCycleSceneFlags[sceneId].collectible; + play->actorCtx.sceneFlags.clearedRoom = 0; + + for (i = 0; i < SCENE_MAX; i++) { + gSaveContext.cycleSceneFlags[i].switch0 = + ((void)0, gSaveContext.cycleSceneFlags[i].switch0) & sPersistentCycleSceneFlags[i].switch0; + gSaveContext.cycleSceneFlags[i].switch1 = + ((void)0, gSaveContext.cycleSceneFlags[i].switch1) & sPersistentCycleSceneFlags[i].switch1; + gSaveContext.cycleSceneFlags[i].chest = + ((void)0, gSaveContext.cycleSceneFlags[i].chest) & sPersistentCycleSceneFlags[i].chest; + gSaveContext.cycleSceneFlags[i].collectible = + ((void)0, gSaveContext.cycleSceneFlags[i].collectible) & sPersistentCycleSceneFlags[i].collectible; + gSaveContext.cycleSceneFlags[i].clearedRoom = 0; + gSaveContext.save.saveInfo.permanentSceneFlags[i].unk_14 = 0; + gSaveContext.save.saveInfo.permanentSceneFlags[i].rooms = 0; + } + + for (; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { + gSaveContext.cycleSceneFlags[i].chest = 0; + gSaveContext.cycleSceneFlags[i].switch0 = 0; + gSaveContext.cycleSceneFlags[i].switch1 = 0; + gSaveContext.cycleSceneFlags[i].clearedRoom = 0; + gSaveContext.cycleSceneFlags[i].collectible = 0; + } + + for (i = 0; i < ARRAY_COUNT(gSaveContext.masksGivenOnMoon); i++) { + gSaveContext.masksGivenOnMoon[i] = 0; + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_84_20)) { + Inventory_DeleteItem(ITEM_MASK_FIERCE_DEITY, SLOT(ITEM_MASK_FIERCE_DEITY)); + } + + for (i = 0; i < ARRAY_COUNT(sPersistentCycleWeekEventRegs); i++) { + u16 isPersistentBits = sPersistentCycleWeekEventRegs[i]; + + for (j = 0; j < ARRAY_COUNT(sBitFlags8); j++) { + if (!(isPersistentBits & 3)) { + gSaveContext.save.saveInfo.weekEventReg[i] = + ((void)0, gSaveContext.save.saveInfo.weekEventReg[i]) & (0xFF ^ sBitFlags8[j]); + } + isPersistentBits >>= 2; + } + } + + for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) { + gSaveContext.eventInf[i] = 0; + } + + CLEAR_EVENTINF(EVENTINF_THREEDAYRESET_LOST_RUPEES); + CLEAR_EVENTINF(EVENTINF_THREEDAYRESET_LOST_BOMB_AMMO); + CLEAR_EVENTINF(EVENTINF_THREEDAYRESET_LOST_NUT_AMMO); + CLEAR_EVENTINF(EVENTINF_THREEDAYRESET_LOST_STICK_AMMO); + CLEAR_EVENTINF(EVENTINF_THREEDAYRESET_LOST_ARROW_AMMO); + + if (gSaveContext.save.saveInfo.playerData.rupees != 0) { + SET_EVENTINF(EVENTINF_THREEDAYRESET_LOST_RUPEES); + } + + if (INV_CONTENT(ITEM_BOMB) == ITEM_BOMB) { + item = INV_CONTENT(ITEM_BOMB); + if (AMMO(item) != 0) { + SET_EVENTINF(EVENTINF_THREEDAYRESET_LOST_BOMB_AMMO); + } + } + if (INV_CONTENT(ITEM_DEKU_NUT) == ITEM_DEKU_NUT) { + item = INV_CONTENT(ITEM_DEKU_NUT); + if (AMMO(item) != 0) { + SET_EVENTINF(EVENTINF_THREEDAYRESET_LOST_NUT_AMMO); + } + } + if (INV_CONTENT(ITEM_DEKU_STICK) == ITEM_DEKU_STICK) { + item = INV_CONTENT(ITEM_DEKU_STICK); + if (AMMO(item) != 0) { + SET_EVENTINF(EVENTINF_THREEDAYRESET_LOST_STICK_AMMO); + } + } + if (INV_CONTENT(ITEM_BOW) == ITEM_BOW) { + item = INV_CONTENT(ITEM_BOW); + if (AMMO(item) != 0) { + SET_EVENTINF(EVENTINF_THREEDAYRESET_LOST_ARROW_AMMO); + } + } + + for (i = 0; i < ITEM_NUM_SLOTS; i++) { + if (gAmmoItems[i] != ITEM_NONE) { + if ((gSaveContext.save.saveInfo.inventory.items[i] != ITEM_NONE) && (i != SLOT_PICTOGRAPH_BOX)) { + item = gSaveContext.save.saveInfo.inventory.items[i]; + AMMO(item) = 0; + } + } + } + + for (i = SLOT_BOTTLE_1; i <= SLOT_BOTTLE_6; i++) { + // Check for all bottled items + if (gSaveContext.save.saveInfo.inventory.items[i] >= ITEM_POTION_RED) { + if (gSaveContext.save.saveInfo.inventory.items[i] <= ITEM_OBABA_DRINK) { + for (j = EQUIP_SLOT_C_LEFT; j <= EQUIP_SLOT_C_RIGHT; j++) { + if (GET_CUR_FORM_BTN_ITEM(j) == gSaveContext.save.saveInfo.inventory.items[i]) { + SET_CUR_FORM_BTN_ITEM(j, ITEM_BOTTLE); + Interface_LoadItemIconImpl(play, j); + } + } + gSaveContext.save.saveInfo.inventory.items[i] = ITEM_BOTTLE; + } + } + } + + REMOVE_QUEST_ITEM(QUEST_PICTOGRAPH); + + if (gSaveContext.save.saveInfo.playerData.health < 0x30) { + gSaveContext.save.saveInfo.playerData.health = 0x30; + } + + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) <= EQUIP_VALUE_SWORD_RAZOR) { + SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI); + + if (CUR_FORM == 0) { + if ((STOLEN_ITEM_1 >= ITEM_SWORD_GILDED) || (STOLEN_ITEM_2 >= ITEM_SWORD_GILDED)) { + CUR_FORM_EQUIP(EQUIP_SLOT_B) = ITEM_SWORD_GILDED; + SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_GILDED); + } else { + CUR_FORM_EQUIP(EQUIP_SLOT_B) = ITEM_SWORD_KOKIRI; + } + } else { + if ((STOLEN_ITEM_1 >= ITEM_SWORD_GILDED) || (STOLEN_ITEM_2 >= ITEM_SWORD_GILDED)) { + BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_SWORD_GILDED; + SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_GILDED); + } else { + BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_SWORD_KOKIRI; + } + } + } + + if ((STOLEN_ITEM_1 == ITEM_SWORD_GREAT_FAIRY) || (STOLEN_ITEM_2 == ITEM_SWORD_GREAT_FAIRY)) { + INV_CONTENT(ITEM_SWORD_GREAT_FAIRY) = ITEM_SWORD_GREAT_FAIRY; + } + + if (STOLEN_ITEM_1 == ITEM_BOTTLE) { + slot = SLOT(ITEM_BOTTLE); + for (i = BOTTLE_FIRST; i < BOTTLE_MAX; i++) { + if (gSaveContext.save.saveInfo.inventory.items[slot + i] == ITEM_NONE) { + gSaveContext.save.saveInfo.inventory.items[slot + i] = ITEM_BOTTLE; + break; + } + } + } + + if (STOLEN_ITEM_2 == ITEM_BOTTLE) { + slot = SLOT(ITEM_BOTTLE); + for (i = BOTTLE_FIRST; i < BOTTLE_MAX; i++) { + if (gSaveContext.save.saveInfo.inventory.items[slot + i] == ITEM_NONE) { + gSaveContext.save.saveInfo.inventory.items[slot + i] = ITEM_BOTTLE; + break; + } + } + } + + SET_STOLEN_ITEM_1(STOLEN_ITEM_NONE); + SET_STOLEN_ITEM_2(STOLEN_ITEM_NONE); + + Inventory_DeleteItem(ITEM_OCARINA_FAIRY, SLOT_TRADE_DEED); + Inventory_DeleteItem(ITEM_SLINGSHOT, SLOT_TRADE_KEY_MAMA); + Inventory_DeleteItem(ITEM_LONGSHOT, SLOT_TRADE_COUPLE); + + for (j = EQUIP_SLOT_C_LEFT; j <= EQUIP_SLOT_C_RIGHT; j++) { + if ((GET_CUR_FORM_BTN_ITEM(j) >= ITEM_MOONS_TEAR) && (GET_CUR_FORM_BTN_ITEM(j) <= ITEM_PENDANT_OF_MEMORIES)) { + SET_CUR_FORM_BTN_ITEM(j, ITEM_NONE); + Interface_LoadItemIconImpl(play, j); + } + } + + gSaveContext.save.saveInfo.skullTokenCount &= ~0xFFFF0000; + gSaveContext.save.saveInfo.skullTokenCount &= ~0x0000FFFF; + gSaveContext.save.saveInfo.unk_EA0 = 0; + + gSaveContext.save.saveInfo.alienInfo[0] = 0; + gSaveContext.save.saveInfo.alienInfo[1] = 0; + gSaveContext.save.saveInfo.alienInfo[2] = 0; + gSaveContext.save.saveInfo.alienInfo[3] = 0; + gSaveContext.save.saveInfo.alienInfo[4] = 0; + gSaveContext.save.saveInfo.alienInfo[5] = 0; + gSaveContext.save.saveInfo.alienInfo[6] = 0; + + Sram_ClearHighscores(); + + for (i = 0; i < 8; i++) { + gSaveContext.save.saveInfo.inventory.dungeonItems[i] &= (u8)~1; // remove boss key + DUNGEON_KEY_COUNT(i) = 0; + gSaveContext.save.saveInfo.inventory.strayFairies[i] = 0; + } + + gSaveContext.save.saveInfo.playerData.rupees = 0; + gSaveContext.save.saveInfo.scarecrowSpawnSongSet = false; + gSaveContext.powderKegTimer = 0; + gSaveContext.unk_1014 = 0; + gSaveContext.jinxTimer = 0; + gSaveContext.rupeeAccumulator = 0; + + Horse_ResetHorseData(play); +} + +void Sram_IncrementDay(void) { + if (CURRENT_DAY <= 3) { + gSaveContext.save.day++; + gSaveContext.save.eventDayCount++; + } + + gSaveContext.save.saveInfo.bombersCaughtNum = 0; + gSaveContext.save.saveInfo.bombersCaughtOrder[0] = 0; + gSaveContext.save.saveInfo.bombersCaughtOrder[1] = 0; + gSaveContext.save.saveInfo.bombersCaughtOrder[2] = 0; + gSaveContext.save.saveInfo.bombersCaughtOrder[3] = 0; + gSaveContext.save.saveInfo.bombersCaughtOrder[4] = 0; + + CLEAR_WEEKEVENTREG(WEEKEVENTREG_73_10); + CLEAR_WEEKEVENTREG(WEEKEVENTREG_85_02); +} + +u16 Sram_CalcChecksum(void* data, size_t count) { + u8* dataPtr = data; + u16 chkSum = 0; + + while (count-- > 0) { + chkSum += *dataPtr; + dataPtr++; + } + return chkSum; +} + +// Resets `Save` substruct +void Sram_ResetSave(void) { + gSaveContext.save.entrance = ENTRANCE(CUTSCENE, 0); + gSaveContext.save.equippedMask = 0; + gSaveContext.save.isFirstCycle = false; + gSaveContext.save.unk_06 = 0; + gSaveContext.save.linkAge = 0; + gSaveContext.save.isNight = false; + gSaveContext.save.timeSpeedOffset = 0; + gSaveContext.save.snowheadCleared = 0; + gSaveContext.save.hasTatl = false; + gSaveContext.save.isOwlSave = false; + + bzero(&gSaveContext.save.saveInfo, sizeof(SaveInfo)); +} + +/** + * Initializes with random values the following fields: + * - lotteryCodes + * - spiderHouseMaskOrder + * - bomberCode + */ +void Sram_GenerateRandomSaveFields(void) { + s32 randBombers; + s16 sp2A; + s16 pad; + s16 i; + s16 j; + s32 k; + s16 randSpiderHouse; + + Sram_ClearHighscores(); + + gSaveContext.save.saveInfo.lotteryCodes[0][0] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[0][1] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[0][2] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[1][0] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[1][1] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[1][2] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[2][0] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[2][1] = Rand_S16Offset(0, 10); + gSaveContext.save.saveInfo.lotteryCodes[2][2] = Rand_S16Offset(0, 10); + + // Needed to match... + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) {} + } + + i = 0; + sp2A = Rand_S16Offset(0, 16) & 3; + k = 6; + while (i != k) { + randSpiderHouse = Rand_S16Offset(0, 16) & 3; + if (sp2A != randSpiderHouse) { + gSaveContext.save.saveInfo.spiderHouseMaskOrder[i] = randSpiderHouse; + i++; + sp2A = randSpiderHouse; + } + } + + do { + randBombers = Rand_S16Offset(0, 6); + } while ((randBombers <= 0) || (randBombers >= 6)); + + gSaveContext.save.saveInfo.bomberCode[0] = randBombers; + + i = 1; + while (i != 5) { + k = false; + + do { + randBombers = Rand_S16Offset(0, 6); + } while ((randBombers <= 0) || (randBombers >= 6)); + + sp2A = 0; + do { + if (randBombers == gSaveContext.save.saveInfo.bomberCode[sp2A]) { + k = true; + } + sp2A++; + } while (sp2A < i); + + if (k == false) { + gSaveContext.save.saveInfo.bomberCode[i] = randBombers; + i++; + } + } +} + +SavePlayerData sSaveDefaultPlayerData = { + { '\0', '\0', '\0', '\0', '\0', '\0' }, // newf + 0, // threeDayResetCount + { 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E }, // playerName " " + 0x30, // healthCapacity + 0x30, // health + 0, // magicLevel + MAGIC_NORMAL_METER, // magic + 0, // rupees + 0, // swordHealth + 0, // tatlTimer + false, // isMagicAcquired + false, // isDoubleMagicAcquired + 0, // doubleDefense + 0, // unk_1F + OWL_WARP_NONE, // owlWarpId + 0, // owlActivationFlags + 0xFF, // unk_24 + SCENE_SPOT00, // savedSceneId +}; + +ItemEquips sSaveDefaultItemEquips = { + { + { ITEM_SWORD_KOKIRI, ITEM_NONE, ITEM_NONE, ITEM_NONE }, + { ITEM_SWORD_KOKIRI, ITEM_NONE, ITEM_NONE, ITEM_NONE }, + { ITEM_SWORD_KOKIRI, ITEM_NONE, ITEM_NONE, ITEM_NONE }, + { ITEM_FD, ITEM_NONE, ITEM_NONE, ITEM_NONE }, + }, + { + { SLOT_OCARINA, SLOT_NONE, SLOT_NONE, SLOT_NONE }, + { SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }, + { SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }, + { SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }, + }, + 0x11, +}; + +Inventory sSaveDefaultInventory = { + // items + { + ITEM_NONE, // SLOT_OCARINA + ITEM_NONE, // SLOT_BOW + ITEM_NONE, // SLOT_ARROW_FIRE + ITEM_NONE, // SLOT_ARROW_ICE + ITEM_NONE, // SLOT_ARROW_LIGHT + ITEM_NONE, // SLOT_TRADE_DEED + ITEM_NONE, // SLOT_BOMB + ITEM_NONE, // SLOT_BOMBCHU + ITEM_NONE, // SLOT_DEKU_STICK + ITEM_NONE, // SLOT_DEKU_NUT + ITEM_NONE, // SLOT_MAGIC_BEANS + ITEM_NONE, // SLOT_TRADE_KEY_MAMA + ITEM_NONE, // SLOT_POWDER_KEG + ITEM_NONE, // SLOT_PICTOGRAPH_BOX + ITEM_NONE, // SLOT_LENS_OF_TRUTH + ITEM_NONE, // SLOT_HOOKSHOT + ITEM_NONE, // SLOT_SWORD_GREAT_FAIRY + ITEM_NONE, // SLOT_TRADE_COUPLE + ITEM_NONE, // SLOT_BOTTLE_1 + ITEM_NONE, // SLOT_BOTTLE_2 + ITEM_NONE, // SLOT_BOTTLE_3 + ITEM_NONE, // SLOT_BOTTLE_4 + ITEM_NONE, // SLOT_BOTTLE_5 + ITEM_NONE, // SLOT_BOTTLE_6 + ITEM_NONE, // SLOT_MASK_POSTMAN + ITEM_NONE, // SLOT_MASK_ALL_NIGHT + ITEM_NONE, // SLOT_MASK_BLAST + ITEM_NONE, // SLOT_MASK_STONE + ITEM_NONE, // SLOT_MASK_GREAT_FAIRY + ITEM_NONE, // SLOT_MASK_DEKU + ITEM_NONE, // SLOT_MASK_KEATON + ITEM_NONE, // SLOT_MASK_BREMEN + ITEM_NONE, // SLOT_MASK_BUNNY + ITEM_NONE, // SLOT_MASK_DON_GERO + ITEM_NONE, // SLOT_MASK_SCENTS + ITEM_NONE, // SLOT_MASK_GORON + ITEM_NONE, // SLOT_MASK_ROMANI + ITEM_NONE, // SLOT_MASK_CIRCUS_LEADER + ITEM_NONE, // SLOT_MASK_KAFEIS_MASK + ITEM_NONE, // SLOT_MASK_COUPLE + ITEM_NONE, // SLOT_MASK_TRUTH + ITEM_NONE, // SLOT_MASK_ZORA + ITEM_NONE, // SLOT_MASK_KAMARO + ITEM_NONE, // SLOT_MASK_GIBDO + ITEM_NONE, // SLOT_MASK_GARO + ITEM_NONE, // SLOT_MASK_CAPTAIN + ITEM_NONE, // SLOT_MASK_GIANT + ITEM_NONE, // SLOT_MASK_FIERCE_DEITY + }, + // ammo + { + 0, // SLOT_OCARINA + 0, // SLOT_BOW + 0, // SLOT_ARROW_FIRE + 0, // SLOT_ARROW_ICE + 0, // SLOT_ARROW_LIGHT + 0, // SLOT_TRADE_DEED + 0, // SLOT_BOMB + 0, // SLOT_BOMBCHU + 0, // SLOT_DEKU_STICK + 0, // SLOT_DEKU_NUT + 0, // SLOT_MAGIC_BEANS + 0, // SLOT_TRADE_KEY_MAMA + 0, // SLOT_POWDER_KEG + 0, // SLOT_PICTOGRAPH_BOX + 0, // SLOT_LENS_OF_TRUTH + 0, // SLOT_HOOKSHOT + 0, // SLOT_SWORD_GREAT_FAIRY + 0, // SLOT_TRADE_COUPLE + 0, // SLOT_BOTTLE_1 + 0, // SLOT_BOTTLE_2 + 0, // SLOT_BOTTLE_3 + 0, // SLOT_BOTTLE_4 + 0, // SLOT_BOTTLE_5 + 0, // SLOT_BOTTLE_6 + }, + // upgrades + (0 << 0) | // UPG_QUIVER + (0 << 3) | // UPG_BOMB_BAG + (0 << 6) | // UPG_STRENGTH + (0 << 9) | // UPG_SCALE + (0 << 12) | // UPG_WALLET + (0 << 14) | // UPG_BULLET_BAG + (1 << 17) | // UPG_DEKU_STICKS + (1 << 20), // UPG_DEKU_NUTS + // questItems + 0, + // dungeonItems + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + // dungeonKeys + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, + // defenseHearts + 0, + // strayFairies + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + // dekuPlaygroundPlayerName + { + // "LINK " + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, + // "LINK " + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, + // "LINK " + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, + }, +}; + +u16 sSaveDefaultChecksum = 0; + +/** + * Initialize new save. + * This save has an empty inventory with 3 hearts, sword and shield. + */ +void Sram_InitNewSave(void) { + gSaveContext.save.playerForm = PLAYER_FORM_HUMAN; + gSaveContext.save.eventDayCount = 0; + gSaveContext.save.day = 0; + gSaveContext.save.time = CLOCK_TIME(6, 0) - 1; + Sram_ResetSave(); + + Lib_MemCpy(&gSaveContext.save.saveInfo.playerData, &sSaveDefaultPlayerData, sizeof(SavePlayerData)); + Lib_MemCpy(&gSaveContext.save.saveInfo.equips, &sSaveDefaultItemEquips, sizeof(ItemEquips)); + Lib_MemCpy(&gSaveContext.save.saveInfo.inventory, &sSaveDefaultInventory, sizeof(Inventory)); + Lib_MemCpy(&gSaveContext.save.saveInfo.checksum, &sSaveDefaultChecksum, + sizeof(gSaveContext.save.saveInfo.checksum)); + + gSaveContext.save.saveInfo.horseData.sceneId = SCENE_F01; + gSaveContext.save.saveInfo.horseData.pos.x = -1420; + gSaveContext.save.saveInfo.horseData.pos.y = 257; + gSaveContext.save.saveInfo.horseData.pos.z = -1285; + gSaveContext.save.saveInfo.horseData.yaw = -0x7554; + + gSaveContext.nextCutsceneIndex = 0; + gSaveContext.save.saveInfo.playerData.magicLevel = 0; + Sram_GenerateRandomSaveFields(); +} + +SavePlayerData sSaveDebugPlayerData = { + { 'Z', 'E', 'L', 'D', 'A', '3' }, // newf + 0, // threeDayResetCount + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, // playerName "LINK " + 0x80, // healthCapacity + 0x80, // health + 0, // magicLevel + MAGIC_NORMAL_METER, // magic + 50, // rupees + 100, // swordHealth + 0, // tatlTimer + true, // isMagicAcquired + false, // isDoubleMagicAcquired + 0, // doubleDefense + 0, // unk_1F + OWL_WARP_NONE, // owlWarpId + 0, // owlActivationFlags + 0xFF, // unk_24 + SCENE_SPOT00, // savedSceneId +}; + +ItemEquips sSaveDebugItemEquips = { + { + { ITEM_SWORD_KOKIRI, ITEM_BOW, ITEM_POTION_RED, ITEM_OCARINA_OF_TIME }, + { ITEM_SWORD_KOKIRI, ITEM_BOW, ITEM_MASK_GORON, ITEM_OCARINA_OF_TIME }, + { ITEM_SWORD_KOKIRI, ITEM_BOW, ITEM_MASK_ZORA, ITEM_OCARINA_OF_TIME }, + { ITEM_DEKU_NUT, ITEM_DEKU_NUT, ITEM_MASK_DEKU, ITEM_OCARINA_OF_TIME }, + }, + { + { SLOT_OCARINA, SLOT_BOW, SLOT_BOTTLE_2, SLOT_OCARINA }, + { SLOT_OCARINA, SLOT_MAGIC_BEANS, SLOT_MASK_GORON, SLOT_BOMBCHU }, + { SLOT_OCARINA, SLOT_POWDER_KEG, SLOT_MASK_ZORA, SLOT_BOMBCHU }, + { SLOT_OCARINA, SLOT_BOW, SLOT_MASK_DEKU, SLOT_BOMBCHU }, + }, + 0x11, +}; + +Inventory sSaveDebugInventory = { + // items + { + ITEM_OCARINA_OF_TIME, // SLOT_OCARINA + ITEM_BOW, // SLOT_BOW + ITEM_ARROW_FIRE, // SLOT_ARROW_FIRE + ITEM_ARROW_ICE, // SLOT_ARROW_ICE + ITEM_ARROW_LIGHT, // SLOT_ARROW_LIGHT + ITEM_MOONS_TEAR, // SLOT_TRADE_DEED + ITEM_BOMB, // SLOT_BOMB + ITEM_BOMBCHU, // SLOT_BOMBCHU + ITEM_DEKU_STICK, // SLOT_DEKU_STICK + ITEM_DEKU_NUT, // SLOT_DEKU_NUT + ITEM_MAGIC_BEANS, // SLOT_MAGIC_BEANS + ITEM_ROOM_KEY, // SLOT_TRADE_KEY_MAMA + ITEM_POWDER_KEG, // SLOT_POWDER_KEG + ITEM_PICTOGRAPH_BOX, // SLOT_PICTOGRAPH_BOX + ITEM_LENS_OF_TRUTH, // SLOT_LENS_OF_TRUTH + ITEM_HOOKSHOT, // SLOT_HOOKSHOT + ITEM_SWORD_GREAT_FAIRY, // SLOT_SWORD_GREAT_FAIRY + ITEM_LETTER_TO_KAFEI, // SLOT_TRADE_COUPLE + ITEM_BOTTLE, // SLOT_BOTTLE_1 + ITEM_POTION_RED, // SLOT_BOTTLE_2 + ITEM_POTION_GREEN, // SLOT_BOTTLE_3 + ITEM_POTION_BLUE, // SLOT_BOTTLE_4 + ITEM_NONE, // SLOT_BOTTLE_5 + ITEM_NONE, // SLOT_BOTTLE_6 + ITEM_MASK_POSTMAN, // SLOT_MASK_POSTMAN + ITEM_MASK_ALL_NIGHT, // SLOT_MASK_ALL_NIGHT + ITEM_MASK_BLAST, // SLOT_MASK_BLAST + ITEM_MASK_STONE, // SLOT_MASK_STONE + ITEM_MASK_GREAT_FAIRY, // SLOT_MASK_GREAT_FAIRY + ITEM_MASK_DEKU, // SLOT_MASK_DEKU + ITEM_MASK_KEATON, // SLOT_MASK_KEATON + ITEM_MASK_BREMEN, // SLOT_MASK_BREMEN + ITEM_MASK_BUNNY, // SLOT_MASK_BUNNY + ITEM_MASK_DON_GERO, // SLOT_MASK_DON_GERO + ITEM_MASK_SCENTS, // SLOT_MASK_SCENTS + ITEM_MASK_GORON, // SLOT_MASK_GORON + ITEM_MASK_ROMANI, // SLOT_MASK_ROMANI + ITEM_MASK_CIRCUS_LEADER, // SLOT_MASK_CIRCUS_LEADER + ITEM_MASK_KAFEIS_MASK, // SLOT_MASK_KAFEIS_MASK + ITEM_MASK_COUPLE, // SLOT_MASK_COUPLE + ITEM_MASK_TRUTH, // SLOT_MASK_TRUTH + ITEM_MASK_ZORA, // SLOT_MASK_ZORA + ITEM_MASK_KAMARO, // SLOT_MASK_KAMARO + ITEM_MASK_GIBDO, // SLOT_MASK_GIBDO + ITEM_MASK_GARO, // SLOT_MASK_GARO + ITEM_MASK_CAPTAIN, // SLOT_MASK_CAPTAIN + ITEM_MASK_GIANT, // SLOT_MASK_GIANT + ITEM_MASK_FIERCE_DEITY, // SLOT_MASK_FIERCE_DEITY + }, + // ammo + { + 1, // SLOT_OCARINA + 30, // SLOT_BOW + 1, // SLOT_ARROW_FIRE + 1, // SLOT_ARROW_ICE + 1, // SLOT_ARROW_LIGHT + 1, // SLOT_TRADE_DEED + 30, // SLOT_BOMB + 30, // SLOT_BOMBCHU + 30, // SLOT_DEKU_STICK + 30, // SLOT_DEKU_NUT + 1, // SLOT_MAGIC_BEANS + 1, // SLOT_TRADE_KEY_MAMA + 1, // SLOT_POWDER_KEG + 1, // SLOT_PICTOGRAPH_BOX + 30, // SLOT_LENS_OF_TRUTH + 1, // SLOT_HOOKSHOT + 1, // SLOT_SWORD_GREAT_FAIRY + 1, // SLOT_TRADE_COUPLE + 1, // SLOT_BOTTLE_1 + 1, // SLOT_BOTTLE_2 + 1, // SLOT_BOTTLE_3 + 1, // SLOT_BOTTLE_4 + 0, // SLOT_BOTTLE_5 + 0, // SLOT_BOTTLE_6 + }, + // upgrades + (1 << 0) | // UPG_QUIVER + (1 << 3) | // UPG_BOMB_BAG + (0 << 6) | // UPG_STRENGTH + (0 << 9) | // UPG_SCALE + (0 << 12) | // UPG_WALLET + (0 << 14) | // UPG_BULLET_BAG + (1 << 17) | // UPG_DEKU_STICKS + (1 << 20), // UPG_DEKU_NUTS + // questItems + (1 << QUEST_SONG_SONATA) | (1 << QUEST_SONG_LULLABY) | (1 << QUEST_SONG_BOSSA_NOVA) | (1 << QUEST_SONG_ELEGY) | + (1 << QUEST_SONG_OATH) | (1 << QUEST_SONG_TIME) | (1 << QUEST_SONG_HEALING) | (1 << QUEST_SONG_EPONA) | + (1 << QUEST_SONG_SOARING) | (1 << QUEST_SONG_STORMS) | (1 << QUEST_BOMBERS_NOTEBOOK) | + (1 << QUEST_SONG_LULLABY_INTRO), + // dungeonItems + { + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + (1 << DUNGEON_BOSS_KEY) | (1 << DUNGEON_COMPASS) | (1 << DUNGEON_MAP), + }, + // dungeonKeys + { 8, 8, 8, 8, 8, 8, 8, 8, 8 }, + // defenseHearts + 0, + // strayFairies + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + // dekuPlaygroundPlayerName + { + // "LINK " + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, + // "LINK " + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, + // "LINK " + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, + }, +}; + +u16 sSaveDebugChecksum = 0; + +u8 D_801C6A48[PLAYER_FORM_MAX] = { + ITEM_MASK_FIERCE_DEITY, // PLAYER_FORM_FIERCE_DEITY + ITEM_MASK_GORON, // PLAYER_FORM_GORON + ITEM_MASK_ZORA, // PLAYER_FORM_ZORA + ITEM_MASK_DEKU, // PLAYER_FORM_DEKU + ITEM_MASK_FIERCE_DEITY, // PLAYER_FORM_HUMAN +}; + +u8 D_801C6A50[PLAYER_FORM_MAX] = { + SLOT_MASK_FIERCE_DEITY, // PLAYER_FORM_FIERCE_DEITY + SLOT_MASK_GORON, // PLAYER_FORM_GORON + SLOT_MASK_ZORA, // PLAYER_FORM_ZORA + SLOT_MASK_DEKU, // PLAYER_FORM_DEKU + SLOT_MASK_FIERCE_DEITY, // PLAYER_FORM_HUMAN +}; + +/** + * Initialize debug save. This is also used on the Title Screen + * This save has a mostly full inventory, every mask and 10 hearts. + * + * Some noteable flags that are set: + * TODO: Investigate the flags + */ +void Sram_InitDebugSave(void) { + Sram_ResetSave(); + + Lib_MemCpy(&gSaveContext.save.saveInfo.playerData, &sSaveDebugPlayerData, sizeof(SavePlayerData)); + Lib_MemCpy(&gSaveContext.save.saveInfo.equips, &sSaveDebugItemEquips, sizeof(ItemEquips)); + Lib_MemCpy(&gSaveContext.save.saveInfo.inventory, &sSaveDebugInventory, sizeof(Inventory)); + Lib_MemCpy(&gSaveContext.save.saveInfo.checksum, &sSaveDebugChecksum, sizeof(gSaveContext.save.saveInfo.checksum)); + + if (GET_PLAYER_FORM != PLAYER_FORM_HUMAN) { + BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_C_DOWN) = D_801C6A48[GET_PLAYER_FORM]; + C_SLOT_EQUIP(0, EQUIP_SLOT_C_DOWN) = D_801C6A50[GET_PLAYER_FORM]; + } + + gSaveContext.save.hasTatl = true; + + gSaveContext.save.saveInfo.horseData.sceneId = SCENE_F01; + gSaveContext.save.saveInfo.horseData.pos.x = -1420; + gSaveContext.save.saveInfo.horseData.pos.y = 257; + gSaveContext.save.saveInfo.horseData.pos.z = -1285; + gSaveContext.save.saveInfo.horseData.yaw = -0x7554; + + gSaveContext.save.entrance = ENTRANCE(CUTSCENE, 0); + gSaveContext.save.isFirstCycle = true; + + SET_WEEKEVENTREG(WEEKEVENTREG_15_20); + SET_WEEKEVENTREG(WEEKEVENTREG_59_04); + SET_WEEKEVENTREG(WEEKEVENTREG_31_04); + + gSaveContext.cycleSceneFlags[SCENE_INSIDETOWER].switch0 = 1; + gSaveContext.save.saveInfo.permanentSceneFlags[SCENE_INSIDETOWER].switch0 = 1; + gSaveContext.save.saveInfo.playerData.magicLevel = 0; + + Sram_GenerateRandomSaveFields(); +} + +void Sram_ResetSaveFromMoonCrash(SramContext* sramCtx) { + s32 i; + s32 cutsceneIndex = gSaveContext.save.cutsceneIndex; + + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2], + gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1], + gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]); + } + Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save)); + if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2 + 1], + gFlashSaveNumPages[gSaveContext.fileNum * 2 + 1]); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save)); + } + gSaveContext.save.cutsceneIndex = cutsceneIndex; + + for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) { + gSaveContext.eventInf[i] = 0; + } + + for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { + gSaveContext.cycleSceneFlags[i].chest = gSaveContext.save.saveInfo.permanentSceneFlags[i].chest; + gSaveContext.cycleSceneFlags[i].switch0 = gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0; + gSaveContext.cycleSceneFlags[i].switch1 = gSaveContext.save.saveInfo.permanentSceneFlags[i].switch1; + gSaveContext.cycleSceneFlags[i].clearedRoom = gSaveContext.save.saveInfo.permanentSceneFlags[i].clearedRoom; + gSaveContext.cycleSceneFlags[i].collectible = gSaveContext.save.saveInfo.permanentSceneFlags[i].collectible; + } + + for (i = 0; i < TIMER_ID_MAX; i++) { + gSaveContext.timerStates[i] = TIMER_STATE_OFF; + gSaveContext.timerCurTimes[i] = SECONDS_TO_TIMER(0); + gSaveContext.timerTimeLimits[i] = SECONDS_TO_TIMER(0); + gSaveContext.timerStartOsTimes[i] = 0; + gSaveContext.timerStopTimes[i] = SECONDS_TO_TIMER(0); + gSaveContext.timerPausedOsTimes[i] = 0; + } + + D_801BDAA0 = true; + gHorseIsMounted = false; + gSaveContext.powderKegTimer = 0; + gSaveContext.unk_1014 = 0; + gSaveContext.jinxTimer = 0; +} + +static u16 sOwlWarpEntrances[OWL_WARP_MAX - 1] = { + ENTRANCE(GREAT_BAY_COAST, 11), // OWL_WARP_GREAT_BAY_COAST + ENTRANCE(ZORA_CAPE, 6), // OWL_WARP_ZORA_CAPE + ENTRANCE(SNOWHEAD, 3), // OWL_WARP_SNOWHEAD + ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 8), // OWL_WARP_MOUNTAIN_VILLAGE + ENTRANCE(SOUTH_CLOCK_TOWN, 9), // OWL_WARP_CLOCK_TOWN + ENTRANCE(MILK_ROAD, 4), // OWL_WARP_MILK_ROAD + ENTRANCE(WOODFALL, 4), // OWL_WARP_WOODFALL + ENTRANCE(SOUTHERN_SWAMP_POISONED, 10), // OWL_WARP_SOUTHERN_SWAMP + ENTRANCE(IKANA_CANYON, 4), // OWL_WARP_IKANA_CANYON + ENTRANCE(STONE_TOWER, 3), // OWL_WARP_STONE_TOWER +}; + +void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) { + s32 i; + s32 pad; + s32 phi_t1; + s32 pad1; + s32 fileNum; + + if (gSaveContext.flashSaveAvailable) { + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + + if (gSaveContext.fileNum == 0xFF) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[0], gFlashSaveNumPages[0]); + } else if (fileSelect->isOwlSave[gSaveContext.fileNum + 2]) { + phi_t1 = gSaveContext.fileNum + 2; + phi_t1 *= 2; + + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1], gFlashSaveNumPages[phi_t1]) != 0) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1 + 1], gFlashSaveNumPages[phi_t1 + 1]); + } + } else { + phi_t1 = gSaveContext.fileNum; + phi_t1 *= 2; + + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1], gFlashSaveNumPages[phi_t1]) != 0) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1 + 1], gFlashSaveNumPages[phi_t1 + 1]); + } + } + + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[phi_t1]); + + if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf)) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1 + 1], gFlashSaveNumPages[phi_t1 + 1]); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[phi_t1]); + } + } + + gSaveContext.save.saveInfo.playerData.magicLevel = 0; + + if (!gSaveContext.save.isOwlSave) { + for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) { + gSaveContext.eventInf[i] = 0; + } + + for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { + gSaveContext.cycleSceneFlags[i].chest = gSaveContext.save.saveInfo.permanentSceneFlags[i].chest; + gSaveContext.cycleSceneFlags[i].switch0 = gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0; + gSaveContext.cycleSceneFlags[i].switch1 = gSaveContext.save.saveInfo.permanentSceneFlags[i].switch1; + gSaveContext.cycleSceneFlags[i].clearedRoom = gSaveContext.save.saveInfo.permanentSceneFlags[i].clearedRoom; + gSaveContext.cycleSceneFlags[i].collectible = gSaveContext.save.saveInfo.permanentSceneFlags[i].collectible; + } + + for (i = 0; i < TIMER_ID_MAX; i++) { + gSaveContext.timerStates[i] = TIMER_STATE_OFF; + gSaveContext.timerCurTimes[i] = SECONDS_TO_TIMER(0); + gSaveContext.timerTimeLimits[i] = SECONDS_TO_TIMER(0); + gSaveContext.timerStartOsTimes[i] = 0; + gSaveContext.timerStopTimes[i] = SECONDS_TO_TIMER(0); + gSaveContext.timerPausedOsTimes[i] = 0; + } + + if (gSaveContext.save.isFirstCycle) { + gSaveContext.save.entrance = ENTRANCE(SOUTH_CLOCK_TOWN, 0); + gSaveContext.save.day = 0; + gSaveContext.save.time = CLOCK_TIME(6, 0) - 1; + } else { + gSaveContext.save.entrance = ENTRANCE(CUTSCENE, 0); + gSaveContext.nextCutsceneIndex = 0; + gSaveContext.save.playerForm = PLAYER_FORM_HUMAN; + } + } else { + gSaveContext.save.entrance = sOwlWarpEntrances[(void)0, gSaveContext.save.owlWarpId]; + if ((gSaveContext.save.entrance == ENTRANCE(SOUTHERN_SWAMP_POISONED, 10)) && + CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE)) { + gSaveContext.save.entrance = ENTRANCE(SOUTHERN_SWAMP_CLEARED, 10); + } else if ((gSaveContext.save.entrance == ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 8)) && + CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE)) { + gSaveContext.save.entrance = ENTRANCE(MOUNTAIN_VILLAGE_SPRING, 8); + } + + for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { + gSaveContext.cycleSceneFlags[i].chest = gSaveContext.save.saveInfo.permanentSceneFlags[i].chest; + gSaveContext.cycleSceneFlags[i].switch0 = gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0; + gSaveContext.cycleSceneFlags[i].switch1 = gSaveContext.save.saveInfo.permanentSceneFlags[i].switch1; + gSaveContext.cycleSceneFlags[i].clearedRoom = gSaveContext.save.saveInfo.permanentSceneFlags[i].clearedRoom; + gSaveContext.cycleSceneFlags[i].collectible = gSaveContext.save.saveInfo.permanentSceneFlags[i].collectible; + } + + if (gSaveContext.save.saveInfo.scarecrowSpawnSongSet) { + Lib_MemCpy(gScarecrowSpawnSongPtr, gSaveContext.save.saveInfo.scarecrowSpawnSong, + sizeof(gSaveContext.save.saveInfo.scarecrowSpawnSong)); + + for (i = 0; i < ARRAY_COUNT(gSaveContext.save.saveInfo.scarecrowSpawnSong); i++) {} + } + + fileNum = gSaveContext.fileNum; + func_80147314(sramCtx, fileNum); + } +} + +// Similar to func_80145698, but accounts for owl saves? +void func_8014546C(SramContext* sramCtx) { + s32 i; + + if (gSaveContext.save.isOwlSave) { + for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { + gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest; + gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0; + gSaveContext.save.saveInfo.permanentSceneFlags[i].switch1 = gSaveContext.cycleSceneFlags[i].switch1; + gSaveContext.save.saveInfo.permanentSceneFlags[i].clearedRoom = gSaveContext.cycleSceneFlags[i].clearedRoom; + gSaveContext.save.saveInfo.permanentSceneFlags[i].collectible = gSaveContext.cycleSceneFlags[i].collectible; + } + + gSaveContext.save.saveInfo.checksum = 0; + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext, offsetof(SaveContext, fileNum)); + + Lib_MemCpy(sramCtx->saveBuf, &gSaveContext, offsetof(SaveContext, fileNum)); + } else { + for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { + gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest; + gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0; + gSaveContext.save.saveInfo.permanentSceneFlags[i].switch1 = gSaveContext.cycleSceneFlags[i].switch1; + gSaveContext.save.saveInfo.permanentSceneFlags[i].clearedRoom = gSaveContext.cycleSceneFlags[i].clearedRoom; + gSaveContext.save.saveInfo.permanentSceneFlags[i].collectible = gSaveContext.cycleSceneFlags[i].collectible; + } + + gSaveContext.save.saveInfo.checksum = 0; + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext.save, sizeof(Save)); + + if (gSaveContext.flashSaveAvailable) { + Lib_MemCpy(sramCtx->saveBuf, &gSaveContext, sizeof(Save)); + Lib_MemCpy(&sramCtx->saveBuf[0x2000], &gSaveContext.save, sizeof(Save)); + } + } +} + +/** + * Save permanent scene flags, calculate checksum, copy save context to the save buffer + */ +void func_80145698(SramContext* sramCtx) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(gSaveContext.cycleSceneFlags); i++) { + gSaveContext.save.saveInfo.permanentSceneFlags[i].chest = gSaveContext.cycleSceneFlags[i].chest; + gSaveContext.save.saveInfo.permanentSceneFlags[i].switch0 = gSaveContext.cycleSceneFlags[i].switch0; + gSaveContext.save.saveInfo.permanentSceneFlags[i].switch1 = gSaveContext.cycleSceneFlags[i].switch1; + gSaveContext.save.saveInfo.permanentSceneFlags[i].clearedRoom = gSaveContext.cycleSceneFlags[i].clearedRoom; + gSaveContext.save.saveInfo.permanentSceneFlags[i].collectible = gSaveContext.cycleSceneFlags[i].collectible; + } + + gSaveContext.save.saveInfo.checksum = 0; + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext.save, sizeof(Save)); + if (gSaveContext.flashSaveAvailable) { + Lib_MemCpy(sramCtx->saveBuf, &gSaveContext, sizeof(Save)); + Lib_MemCpy(&sramCtx->saveBuf[0x2000], &gSaveContext.save, sizeof(Save)); + } +} + +// Verifies save and use backup if corrupted? +void func_801457CC(GameState* gameState, SramContext* sramCtx) { + FileSelectState* fileSelect = (FileSelectState*)gameState; + u16 sp7A; + u16 oldCheckSum; + u16 sp76; + u16 sp64; + u16 phi_s2; + u16 pad; + u16 sp6E; + u16 newCheckSum; + u16 maskCount; + + if (gSaveContext.flashSaveAvailable) { + D_801F6AF0 = CURRENT_TIME; + D_801F6AF2 = gSaveContext.flashSaveAvailable; + sp64 = 0; + + for (sp76 = 0; sp76 < 5; sp76++, sp64 += 2) { + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + + phi_s2 = false; + sp6E = 0; + // read main save from flash + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64], gFlashSaveNumPages[sp64]) != 0) { + // main save didn't work + sp6E = 1; + // read backup save from flash + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64 + 1], gFlashSaveNumPages[sp64 + 1]) != + 0) { + // backup save didn't work + phi_s2 = true; + } + } + + if (sp76 < 2) { + // Non-owl save + // sp76 = 0: main save + // sp76 = 1: backup save + + fileSelect->isOwlSave[sp76] = 0; + if (phi_s2) { + // both main save and backup save failed + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + } else { + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + + // test checksum of main save + phi_s2 = gSaveContext.save.saveInfo.checksum; + gSaveContext.save.saveInfo.checksum = 0; + newCheckSum = Sram_CalcChecksum(&gSaveContext, gFlashSaveSizes[sp64]); + gSaveContext.save.saveInfo.checksum = phi_s2; + + if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf) || (newCheckSum != phi_s2)) { + // checksum didnt match, try backup save + sp6E = 1; + + if ((gSaveContext.save.saveInfo.playerData.newf[0] == 'Z') && + (gSaveContext.save.saveInfo.playerData.newf[1] == 'E') && + (gSaveContext.save.saveInfo.playerData.newf[2] == 'L') && + (gSaveContext.save.saveInfo.playerData.newf[3] == 'D') && + (gSaveContext.save.saveInfo.playerData.newf[4] == 'A')) {} + + // read backup save from flash + phi_s2 = false; + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64 + 1], + gFlashSaveNumPages[sp64 + 1]) != 0) { + // backup save didn't work + phi_s2 = true; + } + + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + + // test checksum of backup save + oldCheckSum = gSaveContext.save.saveInfo.checksum; + gSaveContext.save.saveInfo.checksum = 0; + + if (phi_s2 || CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf) || + (oldCheckSum != Sram_CalcChecksum(&gSaveContext, gFlashSaveSizes[sp64]))) { + // backup save didn't work + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save)); + sp6E = 999; + } + } + } + + gSaveContext.save.saveInfo.checksum = 0; + //! FAKE: [sp64 + 0]? + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext, gFlashSaveSizes[sp64 + 0]); + + for (sp7A = 0; sp7A < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.newf); sp7A++) { + fileSelect->newf[sp76][sp7A] = gSaveContext.save.saveInfo.playerData.newf[sp7A]; + } + + if (!CHECK_NEWF(fileSelect->newf[sp76])) { + fileSelect->threeDayResetCount[sp76] = gSaveContext.save.saveInfo.playerData.threeDayResetCount; + + for (sp7A = 0; sp7A < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); sp7A++) { + fileSelect->fileNames[sp76][sp7A] = gSaveContext.save.saveInfo.playerData.playerName[sp7A]; + } + + fileSelect->healthCapacity[sp76] = gSaveContext.save.saveInfo.playerData.healthCapacity; + fileSelect->health[sp76] = gSaveContext.save.saveInfo.playerData.health; + fileSelect->defenseHearts[sp76] = gSaveContext.save.saveInfo.inventory.defenseHearts; + fileSelect->questItems[sp76] = gSaveContext.save.saveInfo.inventory.questItems; + fileSelect->time[sp76] = CURRENT_TIME; + fileSelect->day[sp76] = gSaveContext.save.day; + fileSelect->isOwlSave[sp76] = gSaveContext.save.isOwlSave; + fileSelect->rupees[sp76] = gSaveContext.save.saveInfo.playerData.rupees; + fileSelect->walletUpgrades[sp76] = CUR_UPG_VALUE(UPG_WALLET); + + for (sp7A = 0, maskCount = 0; sp7A < MASK_NUM_SLOTS; sp7A++) { + if (gSaveContext.save.saveInfo.inventory.items[sp7A + ITEM_NUM_SLOTS] == ITEM_NONE) { + continue; + } + maskCount++; + } + fileSelect->maskCount[sp76] = maskCount; + fileSelect->heartPieceCount[sp76] = GET_QUEST_HEART_PIECE_COUNT; + } + + if (sp6E == 1) { + // backup save + Lib_MemCpy(&sramCtx->saveBuf[0x2000], &gSaveContext.save, sizeof(Save)); + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64], gFlashSpecialSaveNumPages[sp64]); + } else if (!sp6E) { + // main save + oldCheckSum = gSaveContext.save.saveInfo.checksum; + + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64 + 1], + gFlashSaveNumPages[sp64 + 1]) != 0) { + phi_s2 = 1; + } else { + Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save)); + phi_s2 = gSaveContext.save.saveInfo.checksum; + gSaveContext.save.saveInfo.checksum = 0; + sp7A = Sram_CalcChecksum(&gSaveContext.save, sizeof(Save)); + } + + if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf) || (sp7A != phi_s2) || + (oldCheckSum != phi_s2)) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64], gFlashSaveNumPages[sp64]); + Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save)); + Lib_MemCpy(&sramCtx->saveBuf[0x2000], &gSaveContext.save, sizeof(Save)); + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64], gFlashSpecialSaveNumPages[sp64]); + } + } + } else if (sp76 < 4) { + // Owl Save: + // sp76 = 3: main owl save + // sp76 = 4: backup owl save + fileSelect->isOwlSave[sp76] = 0; + + if (!CHECK_NEWF(fileSelect->newf[sp76 - 2])) { + if (phi_s2) { + // both main save and backup save failed + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + } else { + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + phi_s2 = gSaveContext.save.saveInfo.checksum; + + // test checksum of main save + gSaveContext.save.saveInfo.checksum = 0; + newCheckSum = Sram_CalcChecksum(&gSaveContext, gFlashSaveSizes[sp64]); + gSaveContext.save.saveInfo.checksum = phi_s2; + + if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf) || (newCheckSum != phi_s2)) { + // checksum didnt match, try backup save + sp6E = 1; + if ((gSaveContext.save.saveInfo.playerData.newf[0] == 'Z') && + (gSaveContext.save.saveInfo.playerData.newf[1] == 'E') && + (gSaveContext.save.saveInfo.playerData.newf[2] == 'L') && + (gSaveContext.save.saveInfo.playerData.newf[3] == 'D') && + (gSaveContext.save.saveInfo.playerData.newf[4] == 'A')) {} + + phi_s2 = false; + // read backup save from flash + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64 + 1], + gFlashSaveNumPages[sp64 + 1]) != 0) { + // backup save didn't work + phi_s2 = true; + } + + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + + // test checksum of backup save + oldCheckSum = gSaveContext.save.saveInfo.checksum; + gSaveContext.save.saveInfo.checksum = 0; + + if (phi_s2 || CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf) || + (oldCheckSum != Sram_CalcChecksum(&gSaveContext, gFlashSaveSizes[sp64]))) { + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + sp6E = 999; + } + } + } + + gSaveContext.save.saveInfo.checksum = 0; + //! FAKE: [sp64 + 0]? + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext, gFlashSaveSizes[sp64 + 0]); + + for (sp7A = 0; sp7A < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.newf); sp7A++) { + fileSelect->newf[sp76][sp7A] = gSaveContext.save.saveInfo.playerData.newf[sp7A]; + } + + if (!CHECK_NEWF(fileSelect->newf[sp76])) { + fileSelect->threeDayResetCount[sp76] = gSaveContext.save.saveInfo.playerData.threeDayResetCount; + + for (sp7A = 0; sp7A < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); sp7A++) { + fileSelect->fileNames[sp76][sp7A] = + (u32)gSaveContext.save.saveInfo.playerData.playerName[sp7A]; + } + + fileSelect->healthCapacity[sp76] = gSaveContext.save.saveInfo.playerData.healthCapacity; + fileSelect->health[sp76] = gSaveContext.save.saveInfo.playerData.health; + fileSelect->defenseHearts[sp76] = gSaveContext.save.saveInfo.inventory.defenseHearts; + fileSelect->questItems[sp76] = gSaveContext.save.saveInfo.inventory.questItems; + fileSelect->time[sp76] = CURRENT_TIME; + fileSelect->day[sp76] = gSaveContext.save.day; + fileSelect->isOwlSave[sp76] = gSaveContext.save.isOwlSave; + fileSelect->rupees[sp76] = gSaveContext.save.saveInfo.playerData.rupees; + fileSelect->walletUpgrades[sp76] = CUR_UPG_VALUE(UPG_WALLET); + + for (sp7A = 0, maskCount = 0; sp7A < MASK_NUM_SLOTS; sp7A++) { + if (gSaveContext.save.saveInfo.inventory.items[sp7A + ITEM_NUM_SLOTS] == ITEM_NONE) { + continue; + } + maskCount++; + } + fileSelect->maskCount[sp76] = maskCount; + fileSelect->heartPieceCount[sp76] = GET_QUEST_HEART_PIECE_COUNT; + } + + if (sp6E == 1) { + // backup save + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64], gFlashSaveNumPages[sp64]); + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64 + 1], gFlashSaveNumPages[sp64 + 1]); + } else if (!sp6E) { + // main save + oldCheckSum = gSaveContext.save.saveInfo.checksum; + + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64 + 1], + gFlashSaveNumPages[sp64 + 1]) != 0) { + phi_s2 = 1; + } else { + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + phi_s2 = gSaveContext.save.saveInfo.checksum; + gSaveContext.save.saveInfo.checksum = 0; + sp7A = Sram_CalcChecksum(&gSaveContext, gFlashSaveSizes[sp64]); + } + + if (CHECK_NEWF(gSaveContext.save.saveInfo.playerData.newf) || (sp7A != phi_s2) || + (oldCheckSum != phi_s2)) { + SysFlashrom_Read(sramCtx->saveBuf, gFlashSaveStartPages[sp64], gFlashSaveNumPages[sp64]); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64], gFlashSaveNumPages[sp64]); + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64 + 1], + gFlashSaveNumPages[sp64 + 1]); + } + } + } else { + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, gFlashSaveSizes[sp64]); + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64], gFlashSaveNumPages[sp64]); + Sram_SyncWriteToFlash(sramCtx, gFlashSaveStartPages[sp64 + 1], gFlashSaveNumPages[sp64 + 1]); + } + } else { + if (phi_s2) { + gSaveContext.options.optionId = 0xA51D; + gSaveContext.options.language = LANGUAGE_ENG; + gSaveContext.options.audioSetting = SAVE_AUDIO_STEREO; + gSaveContext.options.languageSetting = 0; + gSaveContext.options.zTargetSetting = 0; + } else { + Lib_MemCpy(&gSaveContext.options, sramCtx->saveBuf, sizeof(SaveOptions)); + if (gSaveContext.options.optionId != 0xA51D) { + gSaveContext.options.optionId = 0xA51D; + gSaveContext.options.language = LANGUAGE_ENG; + gSaveContext.options.audioSetting = SAVE_AUDIO_STEREO; + gSaveContext.options.languageSetting = 0; + gSaveContext.options.zTargetSetting = 0; + } + } + Audio_SetFileSelectSettings(gSaveContext.options.audioSetting); + } + } + + gSaveContext.save.time = D_801F6AF0; + gSaveContext.flashSaveAvailable = D_801F6AF2; + } + + gSaveContext.options.language = LANGUAGE_ENG; +} + +void Sram_EraseSave(FileSelectState* fileSelect2, SramContext* sramCtx, s32 fileNum) { + FileSelectState* fileSelect = fileSelect2; + s32 pad; + + if (gSaveContext.flashSaveAvailable) { + if (fileSelect->isOwlSave[fileNum + 2]) { + func_80147314(sramCtx, fileNum); + fileSelect->isOwlSave[fileNum + 2] = false; + } + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, sizeof(Save)); + } + + gSaveContext.save.time = D_801F6AF0; + gSaveContext.flashSaveAvailable = D_801F6AF2; +} + +void Sram_CopySave(FileSelectState* fileSelect2, SramContext* sramCtx) { + FileSelectState* fileSelect = fileSelect2; + u16 i; + s16 maskCount; + + if (gSaveContext.flashSaveAvailable) { + if (fileSelect->isOwlSave[fileSelect->selectedFileIndex + 2]) { + func_80147414(sramCtx, fileSelect->selectedFileIndex, fileSelect->copyDestFileIndex); + fileSelect->threeDayResetCount[fileSelect->copyDestFileIndex + 2] = + gSaveContext.save.saveInfo.playerData.threeDayResetCount; + + for (i = 0; i < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); i++) { + fileSelect->fileNames[fileSelect->copyDestFileIndex + 2][i] = + gSaveContext.save.saveInfo.playerData.playerName[i]; + } + + fileSelect->healthCapacity[fileSelect->copyDestFileIndex + 2] = + gSaveContext.save.saveInfo.playerData.healthCapacity; + fileSelect->health[fileSelect->copyDestFileIndex + 2] = gSaveContext.save.saveInfo.playerData.health; + fileSelect->defenseHearts[fileSelect->copyDestFileIndex + 2] = + gSaveContext.save.saveInfo.inventory.defenseHearts; + fileSelect->questItems[fileSelect->copyDestFileIndex + 2] = gSaveContext.save.saveInfo.inventory.questItems; + fileSelect->time[fileSelect->copyDestFileIndex + 2] = CURRENT_TIME; + fileSelect->day[fileSelect->copyDestFileIndex + 2] = gSaveContext.save.day; + fileSelect->isOwlSave[fileSelect->copyDestFileIndex + 2] = gSaveContext.save.isOwlSave; + fileSelect->rupees[fileSelect->copyDestFileIndex + 2] = gSaveContext.save.saveInfo.playerData.rupees; + fileSelect->walletUpgrades[fileSelect->copyDestFileIndex + 2] = CUR_UPG_VALUE(UPG_WALLET); + + for (i = 0, maskCount = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] != ITEM_NONE) { + maskCount++; + } + } + + fileSelect->maskCount[fileSelect->copyDestFileIndex + 2] = maskCount; + fileSelect->heartPieceCount[fileSelect->copyDestFileIndex + 2] = GET_QUEST_HEART_PIECE_COUNT; + } + + // clear buffer + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + // read to buffer + if (SysFlashrom_Read(&sramCtx->saveBuf[0], gFlashSaveStartPages[fileSelect->selectedFileIndex * 2], + gFlashSaveNumPages[fileSelect->selectedFileIndex * 2])) {} + + if (SysFlashrom_Read(&sramCtx->saveBuf[0x2000], gFlashSaveStartPages[fileSelect->selectedFileIndex * 2 + 1], + gFlashSaveNumPages[fileSelect->selectedFileIndex * 2 + 1])) {} + + // copy buffer to save context + Lib_MemCpy(&gSaveContext.save, sramCtx->saveBuf, sizeof(Save)); + + fileSelect->threeDayResetCount[fileSelect->copyDestFileIndex] = + gSaveContext.save.saveInfo.playerData.threeDayResetCount; + + for (i = 0; i < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); i++) { + fileSelect->fileNames[fileSelect->copyDestFileIndex][i] = + gSaveContext.save.saveInfo.playerData.playerName[i]; + } + + fileSelect->healthCapacity[fileSelect->copyDestFileIndex] = + gSaveContext.save.saveInfo.playerData.healthCapacity; + fileSelect->health[fileSelect->copyDestFileIndex] = gSaveContext.save.saveInfo.playerData.health; + fileSelect->defenseHearts[fileSelect->copyDestFileIndex] = gSaveContext.save.saveInfo.inventory.defenseHearts; + fileSelect->questItems[fileSelect->copyDestFileIndex] = gSaveContext.save.saveInfo.inventory.questItems; + fileSelect->time[fileSelect->copyDestFileIndex] = CURRENT_TIME; + fileSelect->day[fileSelect->copyDestFileIndex] = gSaveContext.save.day; + fileSelect->isOwlSave[fileSelect->copyDestFileIndex] = gSaveContext.save.isOwlSave; + fileSelect->rupees[fileSelect->copyDestFileIndex] = gSaveContext.save.saveInfo.playerData.rupees; + fileSelect->walletUpgrades[fileSelect->copyDestFileIndex] = CUR_UPG_VALUE(UPG_WALLET); + + for (i = 0, maskCount = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] != ITEM_NONE) { + maskCount++; + } + } + + fileSelect->maskCount[fileSelect->copyDestFileIndex] = maskCount; + fileSelect->heartPieceCount[fileSelect->copyDestFileIndex] = GET_QUEST_HEART_PIECE_COUNT; + } + + gSaveContext.save.time = D_801F6AF0; + gSaveContext.flashSaveAvailable = D_801F6AF2; +} + +void Sram_InitSave(FileSelectState* fileSelect2, SramContext* sramCtx) { + s32 phi_v0; + u16 i; + FileSelectState* fileSelect = fileSelect2; + s16 maskCount; + + if (gSaveContext.flashSaveAvailable) { + Sram_InitNewSave(); + if (fileSelect->buttonIndex == 0) { + gSaveContext.save.cutsceneIndex = 0xFFF0; + } + + for (phi_v0 = 0; phi_v0 < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); phi_v0++) { + gSaveContext.save.saveInfo.playerData.playerName[phi_v0] = + fileSelect->fileNames[fileSelect->buttonIndex][phi_v0]; + } + + gSaveContext.save.saveInfo.playerData.newf[0] = 'Z'; + gSaveContext.save.saveInfo.playerData.newf[1] = 'E'; + gSaveContext.save.saveInfo.playerData.newf[2] = 'L'; + gSaveContext.save.saveInfo.playerData.newf[3] = 'D'; + gSaveContext.save.saveInfo.playerData.newf[4] = 'A'; + gSaveContext.save.saveInfo.playerData.newf[5] = '3'; + + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext.save, sizeof(Save)); + + Lib_MemCpy(sramCtx->saveBuf, &gSaveContext.save, sizeof(Save)); + Lib_MemCpy(&sramCtx->saveBuf[0x2000], &gSaveContext.save, sizeof(Save)); + + for (i = 0; i < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.newf); i++) { + fileSelect->newf[fileSelect->buttonIndex][i] = gSaveContext.save.saveInfo.playerData.newf[i]; + } + + fileSelect->threeDayResetCount[fileSelect->buttonIndex] = + gSaveContext.save.saveInfo.playerData.threeDayResetCount; + + for (i = 0; i < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); i++) { + fileSelect->fileNames[fileSelect->buttonIndex][i] = gSaveContext.save.saveInfo.playerData.playerName[i]; + } + + fileSelect->healthCapacity[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.playerData.healthCapacity; + fileSelect->health[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.playerData.health; + fileSelect->defenseHearts[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.inventory.defenseHearts; + fileSelect->questItems[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.inventory.questItems; + fileSelect->time[fileSelect->buttonIndex] = CURRENT_TIME; + fileSelect->day[fileSelect->buttonIndex] = gSaveContext.save.day; + fileSelect->isOwlSave[fileSelect->buttonIndex] = gSaveContext.save.isOwlSave; + fileSelect->rupees[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.playerData.rupees; + fileSelect->walletUpgrades[fileSelect->buttonIndex] = CUR_UPG_VALUE(UPG_WALLET); + + for (i = 0, maskCount = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] != ITEM_NONE) { + maskCount++; + } + } + + fileSelect->maskCount[fileSelect->buttonIndex] = maskCount; + fileSelect->heartPieceCount[fileSelect->buttonIndex] = GET_QUEST_HEART_PIECE_COUNT; + } + + gSaveContext.save.time = D_801F6AF0; + gSaveContext.flashSaveAvailable = D_801F6AF2; +} + +/** + * Write the SaveOptions of SaveContext to the save buffer + */ +void Sram_WriteSaveOptionsToBuffer(SramContext* sramCtx) { + if (gSaveContext.flashSaveAvailable) { + gSaveContext.options.language = LANGUAGE_ENG; + Lib_MemCpy(sramCtx->saveBuf, &gSaveContext.options, sizeof(SaveOptions)); + } +} + +void Sram_InitSram(GameState* gameState, SramContext* sramCtx) { + if (gSaveContext.save.entrance) {} // Required to match + + Audio_SetFileSelectSettings(gSaveContext.options.audioSetting); +} + +void Sram_Alloc(GameState* gameState, SramContext* sramCtx) { + if (gSaveContext.flashSaveAvailable) { + sramCtx->saveBuf = THA_AllocTailAlign16(&gameState->tha, SAVE_BUFFER_SIZE); + sramCtx->status = 0; + } +} + +/** + * Synchronous flash write + */ +void Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages) { + sramCtx->curPage = curPage; + sramCtx->numPages = numPages; + SysFlashrom_WriteSync(sramCtx->saveBuf, curPage, numPages); +} + +/** + * Saves the game on the very first time Player enters South Clock Town from the Clock Tower + */ +void Sram_SaveSpecialEnterClockTown(PlayState* play) { + s32 pad[2]; + SramContext* sramCtx = &play->sramCtx; + + gSaveContext.save.isFirstCycle = true; + gSaveContext.save.isOwlSave = false; + func_80145698(sramCtx); + SysFlashrom_WriteSync(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2], + gFlashSpecialSaveNumPages[gSaveContext.fileNum * 2]); +} + +/** + * Saves when beating the game, after showing the "Dawn of the New Day" message + */ +void Sram_SaveSpecialNewDay(PlayState* play) { + s32 cutsceneIndex = gSaveContext.save.cutsceneIndex; + s32 day; + u16 time = CURRENT_TIME; + + day = gSaveContext.save.day; + + CLEAR_WEEKEVENTREG(WEEKEVENTREG_84_20); + + Sram_SaveEndOfCycle(play); + func_8014546C(&play->sramCtx); + + gSaveContext.save.day = day; + gSaveContext.save.time = time; + gSaveContext.save.cutsceneIndex = cutsceneIndex; + SysFlashrom_WriteSync(play->sramCtx.saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2], + gFlashSaveNumPages[gSaveContext.fileNum * 2]); +} + +void Sram_SetFlashPagesDefault(SramContext* sramCtx, u32 curPage, u32 numPages) { + sramCtx->curPage = curPage; + sramCtx->numPages = numPages; + sramCtx->status = 1; +} + +void Sram_StartWriteToFlashDefault(SramContext* sramCtx) { + // async flash write + SysFlashrom_WriteAsync(sramCtx->saveBuf, sramCtx->curPage, sramCtx->numPages); + + sramCtx->startWriteOsTime = osGetTime(); + sramCtx->status = 2; +} + +void Sram_UpdateWriteToFlashDefault(SramContext* sramCtx) { + if (sramCtx->status == 2) { + if (SysFlashrom_IsBusy() != 0) { // if task running + if (SysFlashrom_AwaitResult() == 0) { // wait for task done + // task success + sramCtx->status = 4; + } else { + // task failure + sramCtx->status = 4; + } + } + } else if (OSTIME_TO_TIMER(osGetTime() - sramCtx->startWriteOsTime) >= SECONDS_TO_TIMER(2)) { + // Finished status is hardcoded to 2 seconds instead of when the task finishes + sramCtx->status = 0; + } +} + +void Sram_SetFlashPagesOwlSave(SramContext* sramCtx, s32 curPage, s32 numPages) { + sramCtx->curPage = curPage; + sramCtx->numPages = numPages; + sramCtx->status = 6; +} + +void Sram_StartWriteToFlashOwlSave(SramContext* sramCtx) { + SysFlashrom_WriteAsync(sramCtx->saveBuf, sramCtx->curPage, sramCtx->numPages); + + sramCtx->startWriteOsTime = osGetTime(); + sramCtx->status = 7; +} + +void Sram_UpdateWriteToFlashOwlSave(SramContext* sramCtx) { + if (sramCtx->status == 7) { + if (SysFlashrom_IsBusy() != 0) { // Is task running + if (SysFlashrom_AwaitResult() == 0) { // Wait for task done + SysFlashrom_WriteAsync(sramCtx->saveBuf, sramCtx->curPage + 0x80, sramCtx->numPages); + sramCtx->status = 8; + } else { + SysFlashrom_WriteAsync(sramCtx->saveBuf, sramCtx->curPage + 0x80, sramCtx->numPages); + sramCtx->status = 8; + } + } + } else if (sramCtx->status == 8) { + if (SysFlashrom_IsBusy() != 0) { // Is task running + if (SysFlashrom_AwaitResult() == 0) { // Wait for task done + sramCtx->status = 4; + } else { + sramCtx->status = 4; + } + } + } else if (OSTIME_TO_TIMER(osGetTime() - sramCtx->startWriteOsTime) >= SECONDS_TO_TIMER(2)) { + // Finished status is hardcoded to 2 seconds instead of when the task finishes + sramCtx->status = 0; + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + gSaveContext.save.isOwlSave = false; + gSaveContext.save.saveInfo.checksum = 0; + // flash read to buffer then copy to save context + SysFlashrom_Read(sramCtx->saveBuf, sramCtx->curPage, sramCtx->numPages); + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, offsetof(SaveContext, fileNum)); + } +} + +void func_80147314(SramContext* sramCtx, s32 fileNum) { + s32 pad; + + gSaveContext.save.isOwlSave = false; + + gSaveContext.save.saveInfo.playerData.newf[0] = '\0'; + gSaveContext.save.saveInfo.playerData.newf[1] = '\0'; + gSaveContext.save.saveInfo.playerData.newf[2] = '\0'; + gSaveContext.save.saveInfo.playerData.newf[3] = '\0'; + gSaveContext.save.saveInfo.playerData.newf[4] = '\0'; + gSaveContext.save.saveInfo.playerData.newf[5] = '\0'; + + gSaveContext.save.saveInfo.checksum = 0; + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext, offsetof(SaveContext, fileNum)); + + Lib_MemCpy(sramCtx->saveBuf, &gSaveContext, offsetof(SaveContext, fileNum)); + Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[fileNum * 2], gFlashOwlSaveNumPages[fileNum * 2]); + //! Note: should be `gFlashOwlSaveNumPages[fileNum * 2 + 1]`? + Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[fileNum * 2 + 1], gFlashOwlSaveNumPages[fileNum * 2]); + + gSaveContext.save.isOwlSave = true; + + gSaveContext.save.saveInfo.playerData.newf[0] = 'Z'; + gSaveContext.save.saveInfo.playerData.newf[1] = 'E'; + gSaveContext.save.saveInfo.playerData.newf[2] = 'L'; + gSaveContext.save.saveInfo.playerData.newf[3] = 'D'; + gSaveContext.save.saveInfo.playerData.newf[4] = 'A'; + gSaveContext.save.saveInfo.playerData.newf[5] = '3'; +} + +// Used by `Sram_CopySave` with `isOwlSave` set +void func_80147414(SramContext* sramCtx, s32 fileNum, s32 arg2) { + s32 pad; + + // Clear save buffer + bzero(sramCtx->saveBuf, SAVE_BUFFER_SIZE); + + // Read save file + if (SysFlashrom_Read(sramCtx->saveBuf, gFlashOwlSaveStartPages[fileNum * 2], gFlashOwlSaveNumPages[fileNum * 2]) != + 0) { + // If failed, read backup save file + SysFlashrom_Read(sramCtx->saveBuf, gFlashOwlSaveStartPages[fileNum * 2 + 1], + gFlashOwlSaveNumPages[fileNum * 2 + 1]); + } + + // Copy buffer to save context + Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, offsetof(SaveContext, fileNum)); + + Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[arg2 * 2], gFlashOwlSaveNumPages[arg2 * 2]); + Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[arg2 * 2 + 1], gFlashOwlSaveNumPages[arg2 * 2]); +} + +void Sram_nop8014750C(UNK_TYPE4 arg0) { +}