diff --git a/desmume/src/GPU.cpp b/desmume/src/GPU.cpp index 810995681..cfc12284a 100644 --- a/desmume/src/GPU.cpp +++ b/desmume/src/GPU.cpp @@ -1151,72 +1151,12 @@ FORCEINLINE void rot_BMP_map(const s32 auxX, const s32 auxY, const int lg, const void gpu_savestate(EMUFILE &os) { - const NDSDisplayInfo &dispInfo = GPU->GetDisplayInfo(); - const GPUEngineA *mainEngine = GPU->GetEngineMain(); - const GPUEngineB *subEngine = GPU->GetEngineSub(); - - //version - os.write_32LE(1); - - os.fwrite((u8 *)dispInfo.masterCustomBuffer, GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * sizeof(u16) * 2); - - os.write_32LE(mainEngine->savedBG2X.value); - os.write_32LE(mainEngine->savedBG2Y.value); - os.write_32LE(mainEngine->savedBG3X.value); - os.write_32LE(mainEngine->savedBG3Y.value); - os.write_32LE(subEngine->savedBG2X.value); - os.write_32LE(subEngine->savedBG2Y.value); - os.write_32LE(subEngine->savedBG3X.value); - os.write_32LE(subEngine->savedBG3Y.value); + GPU->SaveState(os); } bool gpu_loadstate(EMUFILE &is, int size) { - const NDSDisplayInfo &dispInfo = GPU->GetDisplayInfo(); - GPUEngineA *mainEngine = GPU->GetEngineMain(); - GPUEngineB *subEngine = GPU->GetEngineSub(); - - //read version - u32 version; - - //sigh.. shouldve used a new version number - if (size == GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * sizeof(u16) * 2) - { - version = 0; - } - else if (size == 0x30024) - { - is.read_32LE(version); - version = 1; - } - else - { - if (is.read_32LE(version) != 1) return false; - } - - if (version > 1) return false; - - is.fread((u8 *)dispInfo.masterCustomBuffer, GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * sizeof(u16) * 2); - - if (version == 1) - { - is.read_32LE(mainEngine->savedBG2X.value); - is.read_32LE(mainEngine->savedBG2Y.value); - is.read_32LE(mainEngine->savedBG3X.value); - is.read_32LE(mainEngine->savedBG3Y.value); - is.read_32LE(subEngine->savedBG2X.value); - is.read_32LE(subEngine->savedBG2Y.value); - is.read_32LE(subEngine->savedBG3X.value); - is.read_32LE(subEngine->savedBG3Y.value); - //removed per nitsuja feedback. anyway, this same thing will happen almost immediately in gpu line=0 - //mainEngine->refreshAffineStartRegs(-1,-1); - //subEngine->refreshAffineStartRegs(-1,-1); - } - - mainEngine->ParseAllRegisters(); - subEngine->ParseAllRegisters(); - - return !is.fail(); + return GPU->LoadState(is, size); } /*****************************************************************************/ @@ -9081,6 +9021,96 @@ void GPUSubsystem::ClearWithColor(const u16 colorBGRA5551) } } +void GPUSubsystem::SaveState(EMUFILE &os) +{ + // Savestate chunk version + os.write_32LE(2); + + // Version 0 + os.fwrite((u8 *)this->_displayInfo.masterCustomBuffer, GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * sizeof(u16) * 2); + + // Version 1 + os.write_32LE(this->_engineMain->savedBG2X.value); + os.write_32LE(this->_engineMain->savedBG2Y.value); + os.write_32LE(this->_engineMain->savedBG3X.value); + os.write_32LE(this->_engineMain->savedBG3Y.value); + os.write_32LE(this->_engineSub->savedBG2X.value); + os.write_32LE(this->_engineSub->savedBG2Y.value); + os.write_32LE(this->_engineSub->savedBG3X.value); + os.write_32LE(this->_engineSub->savedBG3Y.value); + + // Version 2 + os.write_floatLE(_backlightIntensityTotal[0]); + os.write_floatLE(_backlightIntensityTotal[1]); +} + +bool GPUSubsystem::LoadState(EMUFILE &is, int size) +{ + u32 version; + + //sigh.. shouldve used a new version number + if (size == GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * sizeof(u16) * 2) + { + version = 0; + } + else if (size == 0x30024) + { + is.read_32LE(version); + version = 1; + } + else + { + if (is.read_32LE(version) < 1) return false; + } + + if (version > 2) return false; + + // Version 0 + is.fread((u8 *)this->_displayInfo.masterCustomBuffer, GPU_FRAMEBUFFER_NATIVE_WIDTH * GPU_FRAMEBUFFER_NATIVE_HEIGHT * sizeof(u16) * 2); + + // Version 1 + if (version >= 1) + { + is.read_32LE(this->_engineMain->savedBG2X.value); + is.read_32LE(this->_engineMain->savedBG2Y.value); + is.read_32LE(this->_engineMain->savedBG3X.value); + is.read_32LE(this->_engineMain->savedBG3Y.value); + is.read_32LE(this->_engineSub->savedBG2X.value); + is.read_32LE(this->_engineSub->savedBG2Y.value); + is.read_32LE(this->_engineSub->savedBG3X.value); + is.read_32LE(this->_engineSub->savedBG3Y.value); + //removed per nitsuja feedback. anyway, this same thing will happen almost immediately in gpu line=0 + //this->_engineMain->refreshAffineStartRegs(-1,-1); + //this->_engineSub->refreshAffineStartRegs(-1,-1); + } + + // Version 2 + if (version >= 2) + { + is.read_floatLE(_backlightIntensityTotal[0]); + is.read_floatLE(_backlightIntensityTotal[1]); + } + else + { + // UpdateAverageBacklightIntensityTotal() adds to _backlightIntensityTotal, and is called 263 times per frame. + // Of these, 71 calls are after _displayInfo.backlightIntensity is set. + // This emulates those calls as a way of guessing what the backlight values were in a savestate which doesn't contain that information. + this->_backlightIntensityTotal[0] = 0.0f; + this->_backlightIntensityTotal[1] = 0.0f; + this->UpdateAverageBacklightIntensityTotal(); + this->_displayInfo.backlightIntensity[0] = this->_backlightIntensityTotal[0]; + this->_displayInfo.backlightIntensity[1] = this->_backlightIntensityTotal[1]; + this->_backlightIntensityTotal[0] *= 71; + this->_backlightIntensityTotal[1] *= 71; + } + + // Parse all GPU engine related registers based on a previously read MMU savestate chunk. + this->_engineMain->ParseAllRegisters(); + this->_engineSub->ParseAllRegisters(); + + return !is.fail(); +} + void GPUEventHandlerDefault::DidFrameBegin(const size_t line, const bool isFrameSkipRequested, const size_t pageCount, u8 &selectedBufferIndexInOut) { if ( (pageCount > 1) && (line == 0) && !isFrameSkipRequested ) diff --git a/desmume/src/GPU.h b/desmume/src/GPU.h index b88393cd2..500dceb22 100644 --- a/desmume/src/GPU.h +++ b/desmume/src/GPU.h @@ -1864,6 +1864,9 @@ public: template void RenderLine(const size_t l); void UpdateAverageBacklightIntensityTotal(); void ClearWithColor(const u16 colorBGRA5551); + + void SaveState(EMUFILE &os); + bool LoadState(EMUFILE &is, int size); }; class GPUClientFetchObject diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index 861c2c3a7..c8457f45b 100755 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -1231,7 +1231,7 @@ struct Sequencer if (!divider.load(is)) return false; if (!sqrtunit.load(is)) return false; if (!gxfifo.load(is)) return false; - if (!readslot1.load(is)) return false; + if (version >= 4) if (!readslot1.load(is)) return false; if (version >= 1) if(!wifi.load(is)) return false; #define LOAD(I,X,Y) if(!I##_##X##_##Y .load(is)) return false; LOAD(timer,0,0); LOAD(timer,0,1); LOAD(timer,0,2); LOAD(timer,0,3); @@ -1762,7 +1762,7 @@ static bool loadUserInput(EMUFILE &is, int version); void nds_savestate(EMUFILE &os) { //version - os.write_32LE(3); + os.write_32LE(4); sequencer.save(os); @@ -1782,7 +1782,13 @@ bool nds_loadstate(EMUFILE &is, int size) u32 version; if (is.read_32LE(version) != 1) return false; - if (version > 3) return false; + if (version > 4) return false; + // hacky fix; commit 281268e added to the saved info but didn't update version + if (version == 3) + { + if (size == 497) + version = 4; + } bool temp = true; temp &= sequencer.load(is, version); diff --git a/desmume/src/saves.cpp b/desmume/src/saves.cpp index 703eed3c4..7e57b4963 100644 --- a/desmume/src/saves.cpp +++ b/desmume/src/saves.cpp @@ -1095,6 +1095,7 @@ static bool ReadStateChunks(EMUFILE &is, s32 totalsize) { bool ret = true; bool haveInfo = false; + bool chunkError = false; s64 save_time = 0; u32 romsize = 0; @@ -1122,6 +1123,7 @@ static bool ReadStateChunks(EMUFILE &is, s32 totalsize) if (!is.read_32LE(t)) { ret=false; break; } if (t == 0xFFFFFFFF) break; if (!is.read_32LE(size)) { ret=false; break; } + u32 endPos = is.ftell() + size; switch(t) { @@ -1169,9 +1171,20 @@ static bool ReadStateChunks(EMUFILE &is, s32 totalsize) default: return false; } + + if (is.ftell() != endPos) + { + // Should we just go ahead and return false? + chunkError = true; + is.fseek(endPos, SEEK_SET); + } + if(!ret) return false; } + + if (chunkError) + msgbox->warn("There was an error loading the savestate. Your game session is probably corrupt now."); if (haveInfo) {