#include "global.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_10_08), /* 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_22_02) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_22_80), /* 23 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_OBTAINED_GREAT_SPIN_ATTACK) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_23_80), /* 24 */ PERSISTENT_WEEKEVENTREG_ALT(WEEKEVENTREG_24_02) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_24_80), /* 25 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_25_01), /* 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_32_01) | 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_35_80), /* 36 */ 0, /* 37 */ 0, /* 38 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_38_20), /* 39 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_39_20), /* 40 */ 0, /* 41 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_41_08), /* 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_53_02) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_53_40), /* 54 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_54_40), /* 55 */ 0, /* 56 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_56_02), /* 57 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_57_08), /* 58 */ 0, /* 59 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_59_04) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_59_08) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_SWAMP_SHOOTING_GALLERY_QUIVER_UPGRADE) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_TOWN_SHOOTING_GALLERY_QUIVER_UPGRADE), /* 60 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_60_10), /* 61 */ 0, /* 62 */ 0, /* 63 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_63_20), /* 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_THEM) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_DEFENDED_AGAINST_THEM) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_MILK_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_75_80), /* 76 */ 0, /* 77 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_77_01), /* 78 */ 0, /* 79 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_79_80), /* 80 */ 0, /* 81 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_81_08), /* 82 */ PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_82_10), /* 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_90_10), /* 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->state); 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.unk_E64[0] = 0; gSaveContext.save.saveInfo.unk_E64[1] = 0; gSaveContext.save.saveInfo.unk_E64[2] = 0; gSaveContext.save.saveInfo.unk_E64[3] = 0; gSaveContext.save.saveInfo.unk_E64[4] = 0; gSaveContext.save.saveInfo.unk_E64[5] = 0; gSaveContext.save.saveInfo.unk_E64[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_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[gSaveContext.fileNum * 2], gFlashSaveNumPages[gSaveContext.fileNum * 2]) != 0) { SysFlashrom_ReadData(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_ReadData(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_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[0], gFlashSaveNumPages[0]); } else if (fileSelect->isOwlSave[gSaveContext.fileNum + 2]) { phi_t1 = gSaveContext.fileNum + 2; phi_t1 *= 2; if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1], gFlashSaveNumPages[phi_t1]) != 0) { SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1 + 1], gFlashSaveNumPages[phi_t1 + 1]); } } else { phi_t1 = gSaveContext.fileNum; phi_t1 *= 2; if (SysFlashrom_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[phi_t1], gFlashSaveNumPages[phi_t1]) != 0) { SysFlashrom_ReadData(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_ReadData(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_ReadData(sramCtx->saveBuf, gFlashSaveStartPages[sp64], gFlashSaveNumPages[sp64]) != 0) { // main save didn't work sp6E = 1; // read backup save from flash if (SysFlashrom_ReadData(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_ReadData(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_ReadData(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_ReadData(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_ReadData(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_ReadData(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_ReadData(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_ReadData(&sramCtx->saveBuf[0], gFlashSaveStartPages[fileSelect->selectedFileIndex * 2], gFlashSaveNumPages[fileSelect->selectedFileIndex * 2])) {} if (SysFlashrom_ReadData(&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_WriteDataSync(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_WriteDataSync(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_WriteDataSync(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_WriteDataAsync(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_WriteDataAsync(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_WriteDataAsync(sramCtx->saveBuf, sramCtx->curPage + 0x80, sramCtx->numPages); sramCtx->status = 8; } else { SysFlashrom_WriteDataAsync(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_ReadData(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_ReadData(sramCtx->saveBuf, gFlashOwlSaveStartPages[fileNum * 2], gFlashOwlSaveNumPages[fileNum * 2]) != 0) { // If failed, read backup save file SysFlashrom_ReadData(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) { }