From 11012cdfc6d083c8ccb5eebe3101db1aada9eef9 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Wed, 1 May 2024 04:06:53 -0400 Subject: [PATCH] WIP autosave functionality --- patches/autosave.rgba32.bin | Bin 0 -> 1536 bytes patches/autosave.rgba32.png | Bin 0 -> 794 bytes patches/autosaving.c | 392 ++++++++++++++++++++++++++++++++++++ patches/input.c | 2 - patches/patches.h | 2 + patches/play_patches.c | 1 + patches/play_patches.h | 1 + patches/syms.ld | 2 + patches/ui_patches.c | 3 +- src/recomp/pi.cpp | 1 - 10 files changed, 400 insertions(+), 4 deletions(-) create mode 100644 patches/autosave.rgba32.bin create mode 100644 patches/autosave.rgba32.png create mode 100644 patches/autosaving.c diff --git a/patches/autosave.rgba32.bin b/patches/autosave.rgba32.bin new file mode 100644 index 0000000000000000000000000000000000000000..890baed2aaa3e6b894d853b68e389d8977b863c2 GIT binary patch literal 1536 zcmchWUr3W-6vnr;YMEdIQMZc;g9O7vU(eF{WyQd%rq8LJyZ9r?Ix$lG+8=P zsmEZK8*}e(BOI{e!Qj?pXkc4nlrcQ86HkL4Ob6Rgb8r{s_(5Kgx>V2g_S8KjFdMb# zZMEawL=U2qeHij};^W&P%#2^gu=fxOOnP`P>VHw%yPntTJhd(oa}?`QQkaK|QUi`1 zQGO%SvVP)IRAq;y+3L`bMjL)iZTLf(DNr70wKVZr0+QXOE=*EXgJ2 zrj*+9@^>}gta_x26f6FB!)>UoD^zB4xQ>N|c}zW0yo<(0r5$;^F z;X-3B{8!|uDfhEd^+*?EF-<1*J!FK)n~M|VPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Z%Jb`f!#RDq$odNTqcV zK@-dy6*5{d2`?mUS!AvcY;Ia$Q`g(`o}Fx(Bd_W=etXW&^StkK_MGkDpTZbxC4I8> z^69^_>UF%(oF_~b=0^W2=1%fOWSU$iL7y|oCelm#?p!VQT{~^&^&@hLghGxIl)Ojb zU`2@vpS^lmGB&_!)k92hq3p_RFX!Gq9+RS+>BU{ix6YGaIR)fcVzEA-cFyyeK>vkJHLJvbaW3^XY z4=Hhmnv92PLI`qPP9EknBRl7#xsF*=1; zB)^!iq=)3+`VGa^)87vzjVz0Fkl~Mz4w64E;@xhjsaveBuf, &gSaveContext, offsetof(SaveContext, fileNum)); + // Synchronously save into the owl save slot and the backup owl save slot. + Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[fileNum * 2], gFlashOwlSaveNumPages[fileNum * 2]); + Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[fileNum * 2 + 1], gFlashOwlSaveNumPages[fileNum * 2 + 1]); +} + +// @recomp Do not clear the save if the save was an autosave. +void func_80147314(SramContext* sramCtx, s32 fileNum) { + s32 save_type = gSaveContext.save.isOwlSave; + gSaveContext.save.isOwlSave = false; + + // @recomp Check if this owl save was actually an autosave and don't clear it if so. + if (save_type != SAVE_TYPE_AUTOSAVE) { + 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'; + } +} + +extern u16 D_801F6AF0; +extern u8 D_801F6AF2; + +// @recomp Patched to tag autosaves as owl saves so that the modified version of func_80147314 still clears them. +void Sram_EraseSave(FileSelectState* fileSelect2, SramContext* sramCtx, s32 fileNum) { + FileSelectState* fileSelect = fileSelect2; + s32 pad; + + if (gSaveContext.flashSaveAvailable) { + if (fileSelect->isOwlSave[fileNum + 2]) { + // @recomp Override the save type to a regular owl save. + fileSelect->isOwlSave[fileNum + 2] = true; + 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; +} + +SaveContext prev_save_ctx; +int bcmp_recomp(void* __s1, void* __s2, int __n); + +#define SAVE_COMPARE_MEMBER(ctx1, ctx2, member) \ + if ((ctx1)->member != ((ctx2)->member)) { \ + return true; \ + } + +#define SAVE_COMPARE_MEMBER_ARRAY(ctx1, ctx2, member) \ + if (bcmp_recomp(&(ctx1)->member, &(ctx2)->member, sizeof((ctx1)->member))) { \ + recomp_printf(#member " differed\n"); \ + return true; \ + } + +bool autosave_compare_saves(SaveContext* a, SaveContext* b) { + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.playerData.healthCapacity); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.playerData.isMagicAcquired); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.playerData.isDoubleMagicAcquired); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.playerData.doubleDefense); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.playerData.owlActivationFlags); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.inventory.items); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.inventory.upgrades); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.inventory.questItems); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.inventory.dungeonItems); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.inventory.dungeonKeys); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.inventory.strayFairies); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.permanentSceneFlags); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.dekuPlaygroundHighScores); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.scenesVisible); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.stolenItems); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.highScores); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.weekEventReg); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.regionsVisited); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.worldMapCloudVisibility); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.scarecrowSpawnSongSet); + SAVE_COMPARE_MEMBER_ARRAY(a, b, save.saveInfo.scarecrowSpawnSong); + // SAVE_COMPARE_MEMBER_ARRAY(a, b, eventInf); + + return false; +} + +Gfx* Gfx_DrawRect_DropShadow(Gfx* gfx, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy, + s16 r, s16 g, s16 b, s16 a); + +#define MIN_FRAMES_SINCE_CHANGED 10 + +OSTime last_autosave_time = 0; +int frames_since_save_changed = 0; +int autosave_icon_counter = 0; + +#define AUTOSAVE_ICON_FADE_OUT_FRAMES 5 +#define AUTOSAVE_ICON_FADE_IN_FRAMES 5 +#define AUTOSAVE_ICON_SHOW_FRAMES 30 +#define AUTOSAVE_ICON_TOTAL_FRAMES (AUTOSAVE_ICON_FADE_IN_FRAMES + AUTOSAVE_ICON_SHOW_FRAMES + AUTOSAVE_ICON_FADE_OUT_FRAMES) + +#define AUTOSAVE_ICON_WIDTH 24 +#define AUTOSAVE_ICON_HEIGHT 16 +#define AUTOSAVE_ICON_DRAW_WIDTH (AUTOSAVE_ICON_WIDTH * 3 / 4) +#define AUTOSAVE_ICON_DRAW_HEIGHT (AUTOSAVE_ICON_HEIGHT * 3 / 4) +#define AUTOSAVE_ICON_X (SCREEN_WIDTH - AUTOSAVE_ICON_DRAW_WIDTH - 4) +#define AUTOSAVE_ICON_Y (SCREEN_HEIGHT - AUTOSAVE_ICON_DRAW_HEIGHT - 4) + +INCBIN(autosave_icon, "autosave.rgba32.bin"); + +Gfx* GfxEx_DrawRect_DropShadow(Gfx* gfx, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy, + s16 r, s16 g, s16 b, s16 a, u16 origin) { + s16 dropShadowAlpha = a; + + if (a > 100) { + dropShadowAlpha = 100; + } + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, dropShadowAlpha); + gEXTextureRectangle(gfx++, origin, origin, (rectLeft + 2) * 4, (rectTop + 2) * 4, (rectLeft + rectWidth + 2) * 4, + (rectTop + rectHeight + 2) * 4, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, r, g, b, a); + + gEXTextureRectangle(gfx++, origin, origin, rectLeft * 4, rectTop * 4, (rectLeft + rectWidth) * 4, (rectTop + rectHeight) * 4, + G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return gfx; +} + +void draw_autosave_icon(PlayState* play) { + s32 alpha = 0; + if (autosave_icon_counter > (AUTOSAVE_ICON_SHOW_FRAMES + AUTOSAVE_ICON_FADE_OUT_FRAMES)) { + alpha = (255 * (AUTOSAVE_ICON_FADE_IN_FRAMES - (autosave_icon_counter - AUTOSAVE_ICON_SHOW_FRAMES - AUTOSAVE_ICON_FADE_OUT_FRAMES))) / AUTOSAVE_ICON_FADE_IN_FRAMES; + } + else if (autosave_icon_counter > AUTOSAVE_ICON_FADE_OUT_FRAMES) { + alpha = 255; + } + else if (autosave_icon_counter > 0) { + alpha = (255 * autosave_icon_counter) / AUTOSAVE_ICON_FADE_OUT_FRAMES; + } + + if (autosave_icon_counter > 0) { + autosave_icon_counter--; + } + + if (alpha != 0) { + OPEN_DISPS(play->state.gfxCtx); + + gEXForceUpscale2D(OVERLAY_DISP++, 1); + gDPLoadTextureBlock(OVERLAY_DISP++, autosave_icon, G_IM_FMT_RGBA, G_IM_SIZ_32b, AUTOSAVE_ICON_WIDTH, AUTOSAVE_ICON_HEIGHT, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + OVERLAY_DISP = GfxEx_DrawRect_DropShadow(OVERLAY_DISP, AUTOSAVE_ICON_X - SCREEN_WIDTH, AUTOSAVE_ICON_Y, AUTOSAVE_ICON_DRAW_WIDTH, AUTOSAVE_ICON_DRAW_HEIGHT, + (s32)(1024.0f * AUTOSAVE_ICON_WIDTH / AUTOSAVE_ICON_DRAW_WIDTH), (s32)(1024.0f * AUTOSAVE_ICON_HEIGHT / AUTOSAVE_ICON_DRAW_HEIGHT), + 255, 255, 255, alpha, G_EX_ORIGIN_RIGHT); + gEXForceUpscale2D(OVERLAY_DISP++, 0); + + CLOSE_DISPS(play->state.gfxCtx); + } +} + +void show_autosave_icon() { + autosave_icon_counter = AUTOSAVE_ICON_TOTAL_FRAMES; +} + +s32 recomp_autosave_enabled() { + return 1; +} + +u32 recomp_autosave_interval() { + return 2 * 60 * 1000; +} + +void autosave_post_play_update(PlayState* play) { + if (recomp_autosave_enabled()) { + if (autosave_compare_saves(&gSaveContext, &prev_save_ctx)) { + frames_since_save_changed = 0; + Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum)); + } + else { + frames_since_save_changed++; + } + + OSTime time_now = osGetTime(); + + // Check if enough time has passed since the previous autosave to create a new one. + if (OS_CYCLES_TO_USEC(time_now - last_autosave_time) > (1000 * recomp_autosave_interval())) { + // Check the following conditions: + // * The UI is in a normal state. + // * Time is passing. + // * No message is on screen. + // * The game is not paused. + // * No cutscene is running. + // * The game is not in cutscene mode. + if (gSaveContext.hudVisibility == HUD_VISIBILITY_ALL && + R_TIME_SPEED != 0 && + !Environment_IsTimeStopped() && + play->msgCtx.msgMode == MSGMODE_NONE && + play->pauseCtx.state == PAUSE_STATE_OFF && + gSaveContext.save.cutsceneIndex < 0xFFF0 && + !Play_InCsMode(play) + ) { + last_autosave_time = time_now; + do_autosave(&play->sramCtx); + show_autosave_icon(); + } + } + } + else { + // Update the last autosave time to the current time to prevent autosaving immediately if autosaves are turned back on. + last_autosave_time = osGetTime(); + } +} + +void autosave_init() { + last_autosave_time = osGetTime(); + Lib_MemCpy(&prev_save_ctx, &gSaveContext, offsetof(SaveContext, fileNum)); +} + +extern s32 gFlashSaveSizes[]; +extern u16 D_801C6A58[]; + +#define CHECK_NEWF(newf) \ + ((newf)[0] != 'Z' || (newf)[1] != 'E' || (newf)[2] != 'L' || (newf)[3] != 'D' || (newf)[4] != 'A' || \ + (newf)[5] != '3') + +// @recomp Patched to change the entrance for autosaves and initialize autosaves. +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; + } + } + // @recomp Handle autosaves. + else if (gSaveContext.save.isOwlSave == SAVE_TYPE_AUTOSAVE) { + gSaveContext.save.entrance = ENTRANCE(SOUTH_CLOCK_TOWN, 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; + } + + 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); + } + else { + gSaveContext.save.entrance = D_801C6A58[(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); + } + + // @recomp Initialize the autosave state tracking. + autosave_init(); +} diff --git a/patches/input.c b/patches/input.c index 148337d..5000b46 100644 --- a/patches/input.c +++ b/patches/input.c @@ -2085,8 +2085,6 @@ Gfx* Gfx_DrawRect_DropShadow(Gfx* gfx, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 r, s16 g, s16 b, s16 a); void draw_dpad(PlayState* play) { - InterfaceContext* interfaceCtx = &play->interfaceCtx; - OPEN_DISPS(play->state.gfxCtx); gEXForceUpscale2D(OVERLAY_DISP++, 1); diff --git a/patches/patches.h b/patches/patches.h index ca010ae..e52cd20 100644 --- a/patches/patches.h +++ b/patches/patches.h @@ -9,6 +9,7 @@ #define osFlashWriteArray osFlashWriteArray_recomp #define osFlashWriteBuffer osFlashWriteBuffer_recomp #define osWritebackDCache osWritebackDCache_recomp +#define osGetTime osGetTime_recomp #define osContStartReadData osContStartReadData_recomp #define osContGetReadData osContGetReadData_recomp @@ -91,6 +92,7 @@ void clear_camera_skipped(); void edit_billboard_groups(PlayState* play); bool camera_was_skipped(); void room_load_hook(PlayState* play, Room* room); +void draw_autosave_icon(PlayState* play); void recomp_crash(const char* err); diff --git a/patches/play_patches.c b/patches/play_patches.c index 3a59d96..3cc5a83 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -33,6 +33,7 @@ void Play_Main(GameState* thisx) { camera_pre_play_update(this); Play_Update(this); camera_post_play_update(this); + autosave_post_play_update(this); this->state.gfxCtx = gfxCtx; } diff --git a/patches/play_patches.h b/patches/play_patches.h index d6b7a58..62c39fb 100644 --- a/patches/play_patches.h +++ b/patches/play_patches.h @@ -7,5 +7,6 @@ void debug_play_update(PlayState* play); void camera_pre_play_update(PlayState* play); void camera_post_play_update(PlayState* play); void matrix_play_update(PlayState* play); +void autosave_post_play_update(PlayState* play); #endif diff --git a/patches/syms.ld b/patches/syms.ld index 4332214..a047b55 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -47,3 +47,5 @@ osContGetReadData_recomp = 0x8F000074; osContStartQuery_recomp = 0x8F000078; osContGetQuery_recomp = 0x8F00007C; recomp_get_mouse_deltas = 0x8F000080; +bcmp_recomp = 0x8F000084; +osGetTime_recomp = 0x8F000088; diff --git a/patches/ui_patches.c b/patches/ui_patches.c index 5b4fb30..2116fbe 100644 --- a/patches/ui_patches.c +++ b/patches/ui_patches.c @@ -486,10 +486,11 @@ void Interface_Draw(PlayState* play) { Magic_DrawMeter(play); - // @recomp Draw the D-Pad and its item icons + // @recomp Draw the D-Pad and its item icons as well as the autosave icon if the game is unpaused. if (pauseCtx->state != PAUSE_STATE_MAIN) { draw_dpad(play); draw_dpad_icons(play); + draw_autosave_icon(play); } // @recomp Right align and shift right/down for minimap diff --git a/src/recomp/pi.cpp b/src/recomp/pi.cpp index 809937a..e6a877d 100644 --- a/src/recomp/pi.cpp +++ b/src/recomp/pi.cpp @@ -107,7 +107,6 @@ void saving_thread_func(RDRAM_ARG1) { // If an action came through that affected the save file, save the updated contents. if (save_buffer_updated) { - printf("Writing updated save buffer to disk\n"); update_save_file(); } }