diff options
Diffstat (limited to 'src')
53 files changed, 1737 insertions, 109 deletions
diff --git a/src/ARM.cpp b/src/ARM.cpp index bbfb7f7..2368a1b 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -161,6 +161,27 @@ void ARM::Reset() JumpTo(ExceptionBase); } +void ARM::DoSavestate(Savestate* file) +{ + file->Section((char*)(Num ? "ARM7" : "ARM9")); + + file->Var32((u32*)&Cycles); + file->Var32((u32*)&CyclesToRun); + file->Var32(&Halted); + + file->VarArray(R, 16*sizeof(u32)); + file->Var32(&CPSR); + file->VarArray(R_FIQ, 8*sizeof(u32)); + file->VarArray(R_SVC, 3*sizeof(u32)); + file->VarArray(R_ABT, 3*sizeof(u32)); + file->VarArray(R_IRQ, 3*sizeof(u32)); + file->VarArray(R_UND, 3*sizeof(u32)); + file->Var32(&CurInstr); + file->VarArray(NextInstr, 2*sizeof(u32)); + + file->Var32(&ExceptionBase); +} + void ARM::JumpTo(u32 addr, bool restorecpsr) { if (restorecpsr) @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -38,6 +38,8 @@ public: void Reset(); + void DoSavestate(Savestate* file); + void JumpTo(u32 addr, bool restorecpsr = false); void RestoreCPSR(); diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 16eb111..32b5658 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARMInterpreter.h b/src/ARMInterpreter.h index 2d4c1a8..2982011 100644 --- a/src/ARMInterpreter.h +++ b/src/ARMInterpreter.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 4f76b8b..58bf94d 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARMInterpreter_ALU.h b/src/ARMInterpreter_ALU.h index 4cc3760..813135d 100644 --- a/src/ARMInterpreter_ALU.h +++ b/src/ARMInterpreter_ALU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp index 88f316d..740375d 100644 --- a/src/ARMInterpreter_Branch.cpp +++ b/src/ARMInterpreter_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARMInterpreter_Branch.h b/src/ARMInterpreter_Branch.h index 202f490..aa5b995 100644 --- a/src/ARMInterpreter_Branch.h +++ b/src/ARMInterpreter_Branch.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index 7edf88c..adb44a9 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARMInterpreter_LoadStore.h b/src/ARMInterpreter_LoadStore.h index 4ea0e54..58da587 100644 --- a/src/ARMInterpreter_LoadStore.h +++ b/src/ARMInterpreter_LoadStore.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/ARM_InstrTable.h b/src/ARM_InstrTable.h index 47b3e1c..afcc3b2 100644 --- a/src/ARM_InstrTable.h +++ b/src/ARM_InstrTable.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/CP15.cpp b/src/CP15.cpp index cc6a6af..e3f0bae 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -57,6 +57,25 @@ void Reset() DTCMSize = 0; } +void DoSavestate(Savestate* file) +{ + file->Section("CP15"); + + file->Var32(&Control); + + file->Var32(&DTCMSetting); + file->Var32(&ITCMSetting); + + if (!file->Saving) + { + UpdateDTCMSetting(); + UpdateITCMSetting(); + } + + file->VarArray(ITCM, 0x8000); + file->VarArray(DTCM, 0x4000); +} + void UpdateDTCMSetting() { @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -24,6 +24,8 @@ namespace CP15 void Reset(); +void DoSavestate(Savestate* file); + void UpdateDTCMSetting(); void UpdateITCMSetting(); diff --git a/src/Config.cpp b/src/Config.cpp index d9e2b50..be9e53f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -53,6 +53,8 @@ int Threaded3D; int SocketBindAnyAddr; +int SavestateRelocSRAM; + typedef struct { char Name[16]; @@ -75,7 +77,7 @@ ConfigEntry ConfigFile[] = {"Key_Up", 0, &KeyMapping[6], 328, NULL, 0}, {"Key_Down", 0, &KeyMapping[7], 336, NULL, 0}, {"Key_R", 0, &KeyMapping[8], 54, NULL, 0}, - {"Key_L", 0, &KeyMapping[9], 42, NULL, 0}, + {"Key_L", 0, &KeyMapping[9], 86, NULL, 0}, {"Key_X", 0, &KeyMapping[10], 17, NULL, 0}, {"Key_Y", 0, &KeyMapping[11], 30, NULL, 0}, @@ -107,6 +109,8 @@ ConfigEntry ConfigFile[] = {"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0}, + {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 1, NULL, 0}, + {"", -1, NULL, 0, NULL, 0} }; @@ -125,7 +129,20 @@ FILE* GetConfigFile(const char* fileName, const char* permissions) SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath); if (!appDataPath) return NULL; - CoTaskMemRealloc(appDataPath, (wcslen(appDataPath)+9+strlen(fileName)+1)*sizeof(WCHAR)); + + const WCHAR* appdir = L"\\melonDS\\"; + + int fnlen = MultiByteToWideChar(CP_UTF8, 0, fileName, -1, NULL, 0); + if (fnlen < 1) return NULL; + WCHAR* wfileName = new WCHAR[fnlen]; + int res = MultiByteToWideChar(CP_UTF8, 0, fileName, -1, wfileName, fnlen); + if (res != fnlen) { delete[] wfileName; return NULL; } // checkme? + + int pos = wcslen(appDataPath); + CoTaskMemRealloc(appDataPath, (pos+wcslen(appdir)+fnlen+1)*sizeof(WCHAR)); + + wcscpy(&appDataPath[pos], appdir); pos += wcslen(appdir); + wcscpy(&appDataPath[pos], wfileName); // this will be more than enough WCHAR fatperm[4]; @@ -136,6 +153,7 @@ FILE* GetConfigFile(const char* fileName, const char* permissions) f = _wfopen(appDataPath, fatperm); CoTaskMemFree(appDataPath); + delete[] wfileName; if (f) return f; #else // Now check XDG_CONFIG_HOME diff --git a/src/Config.h b/src/Config.h index 0f5ca9e..d7b0858 100644 --- a/src/Config.h +++ b/src/Config.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -46,6 +46,8 @@ extern int Threaded3D; extern int SocketBindAnyAddr; +extern int SavestateRelocSRAM; + } #endif // CONFIG_H diff --git a/src/DMA.cpp b/src/DMA.cpp index 39dcb85..f0c22b5 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -118,6 +118,29 @@ void DMA::Reset() InProgress = false; } +void DMA::DoSavestate(Savestate* file) +{ + char magic[5] = "DMAx"; + magic[3] = '0' + Num + (CPU*4); + file->Section(magic); + + file->Var32(&SrcAddr); + file->Var32(&DstAddr); + file->Var32(&Cnt); + + file->Var32(&StartMode); + file->Var32(&CurSrcAddr); + file->Var32(&CurDstAddr); + file->Var32(&RemCount); + file->Var32(&IterCount); + file->Var32(&SrcAddrInc); + file->Var32(&DstAddrInc); + + file->Var32((u32*)&Running); + file->Var32((u32*)&InProgress); + file->Var32((u32*)&IsGXFIFODMA); +} + void DMA::WriteCnt(u32 val) { u32 oldcnt = Cnt; @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -29,6 +29,8 @@ public: void Reset(); + void DoSavestate(Savestate* file); + void WriteCnt(u32 val); void Start(); @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -47,6 +47,16 @@ public: } + void DoSavestate(Savestate* file) + { + file->Var32(&NumOccupied); + file->Var32(&ReadPos); + file->Var32(&WritePos); + + file->VarArray(Entries, sizeof(T)*NumEntries); + } + + void Write(T val) { if (IsFull()) return; diff --git a/src/GPU.cpp b/src/GPU.cpp index 8ac3e74..aba97a5 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -156,6 +156,58 @@ void Stop() memset(Framebuffer, 0, 256*192*2*4); } +void DoSavestate(Savestate* file) +{ + file->Section("GPUG"); + + file->Var16(&VCount); + file->Var32(&NextVCount); + file->Var16(&TotalScanlines); + + file->Var16(&DispStat[0]); + file->Var16(&DispStat[1]); + file->Var16(&VMatch[0]); + file->Var16(&VMatch[1]); + + file->VarArray(Palette, 2*1024); + file->VarArray(OAM, 2*1024); + + file->VarArray(VRAM_A, 128*1024); + file->VarArray(VRAM_B, 128*1024); + file->VarArray(VRAM_C, 128*1024); + file->VarArray(VRAM_D, 128*1024); + file->VarArray(VRAM_E, 64*1024); + file->VarArray(VRAM_F, 16*1024); + file->VarArray(VRAM_G, 16*1024); + file->VarArray(VRAM_H, 32*1024); + file->VarArray(VRAM_I, 16*1024); + + file->VarArray(VRAMCNT, 9); + file->Var8(&VRAMSTAT); + + file->Var32(&VRAMMap_LCDC); + + file->VarArray(VRAMMap_ABG, sizeof(VRAMMap_ABG)); + file->VarArray(VRAMMap_AOBJ, sizeof(VRAMMap_AOBJ)); + file->VarArray(VRAMMap_BBG, sizeof(VRAMMap_BBG)); + file->VarArray(VRAMMap_BOBJ, sizeof(VRAMMap_BOBJ)); + + file->VarArray(VRAMMap_ABGExtPal, sizeof(VRAMMap_ABGExtPal)); + file->Var32(&VRAMMap_AOBJExtPal); + file->VarArray(VRAMMap_BBGExtPal, sizeof(VRAMMap_BBGExtPal)); + file->Var32(&VRAMMap_BOBJExtPal); + + file->VarArray(VRAMMap_Texture, sizeof(VRAMMap_Texture)); + file->VarArray(VRAMMap_TexPal, sizeof(VRAMMap_TexPal)); + + file->Var32(&VRAMMap_ARM7[0]); + file->Var32(&VRAMMap_ARM7[1]); + + GPU2D_A->DoSavestate(file); + GPU2D_B->DoSavestate(file); + GPU3D::DoSavestate(file); +} + // VRAM mapping notes // @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -72,6 +72,9 @@ void DeInit(); void Reset(); void Stop(); +void DoSavestate(Savestate* file); + + void MapVRAM_AB(u32 bank, u8 cnt); void MapVRAM_CD(u32 bank, u8 cnt); void MapVRAM_E(u32 bank, u8 cnt); @@ -389,6 +392,9 @@ void DisplaySwap(u32 val); void StartFrame(); void FinishFrame(u32 lines); void StartScanline(u32 line); +void StartHBlank(u32 line); + +void DisplayFIFO(u32 x); void SetDispStat(u32 cpu, u16 val); diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index 7550562..078374b 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -138,6 +138,64 @@ void GPU2D::Reset() OBJExtPalStatus = 0; } +void GPU2D::DoSavestate(Savestate* file) +{ + file->Section((char*)(Num ? "GP2B" : "GP2A")); + + file->Var32(&DispCnt); + file->VarArray(BGCnt, 4*2); + file->VarArray(BGXPos, 4*2); + file->VarArray(BGYPos, 4*2); + file->VarArray(BGXRef, 2*4); + file->VarArray(BGYRef, 2*4); + file->VarArray(BGXRefInternal, 2*4); + file->VarArray(BGYRefInternal, 2*4); + file->VarArray(BGRotA, 2*2); + file->VarArray(BGRotB, 2*2); + file->VarArray(BGRotC, 2*2); + file->VarArray(BGRotD, 2*2); + + file->VarArray(Win0Coords, 4); + file->VarArray(Win1Coords, 4); + file->VarArray(WinCnt, 4); + + file->VarArray(BGMosaicSize, 2); + file->VarArray(OBJMosaicSize, 2); + file->Var8(&BGMosaicY); + file->Var8(&BGMosaicYMax); + file->Var8(&OBJMosaicY); + file->Var8(&OBJMosaicYMax); + + file->Var16(&BlendCnt); + file->Var16(&BlendAlpha); + file->Var8(&EVA); + file->Var8(&EVB); + file->Var8(&EVY); + + file->Var16(&MasterBrightness); + + if (!Num) + { + file->VarArray(DispFIFO, 16*2); + file->Var32(&DispFIFOReadPtr); + file->Var32(&DispFIFOWritePtr); + + file->VarArray(DispFIFOBuffer, 256*2); + + file->Var32(&CaptureCnt); + } + + if (!file->Saving) + { + // refresh those + BGExtPalStatus[0] = 0; + BGExtPalStatus[1] = 0; + BGExtPalStatus[2] = 0; + BGExtPalStatus[3] = 0; + OBJExtPalStatus = 0; + } +} + void GPU2D::SetFramebuffer(u32* buf) { Framebuffer = buf; @@ -614,12 +672,12 @@ void GPU2D::DrawScanline(u32 line) for (int i = 0; i < 256; i++) { u32 c = dst[i]; - + u32 r = c << 18; u32 g = (c << 2) & 0xFC00; u32 b = (c >> 14) & 0xFC; c = r | g | b; - + dst[i] = c | ((c & 0x00C0C0C0) >> 6) | 0xFF000000; } } diff --git a/src/GPU2D.h b/src/GPU2D.h index d059fa0..307ff33 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -27,6 +27,8 @@ public: void Reset(); + void DoSavestate(Savestate* file); + void SetFramebuffer(u32* buf); u8 Read8(u32 addr); diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index b890f7f..6e4d26a 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -45,7 +45,7 @@ // the added bias affects interpolation. // // depth buffer: -// Z-buffering mode: val = ((Z * 0x800 * 0x1000) / W) + 0x7FFEFF +// Z-buffering mode: val = ((Z * 0x800 * 0x1000) / W) + 0x7FFEFF (nope, wrong. TODO update) // W-buffering mode: val = W // // formula for clear depth: (GBAtek is wrong there) @@ -150,10 +150,14 @@ const s32 CmdNumCycles[256] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -typedef struct +typedef union { - u8 Command; - u32 Param; + u64 _contents; + struct + { + u32 Param; + u8 Command; + }; } CmdFIFOEntry; @@ -351,6 +355,179 @@ void Reset() SoftRenderer::Reset(); } +void DoSavestate(Savestate* file) +{ + file->Section("GP3D"); + + CmdFIFO->DoSavestate(file); + CmdPIPE->DoSavestate(file); + + file->Var32(&NumCommands); + file->Var32(&CurCommand); + file->Var32(&ParamCount); + file->Var32(&TotalParams); + + file->Var32(&NumPushPopCommands); + file->Var32(&NumTestCommands); + + file->Var32(&DispCnt); + file->Var8(&AlphaRef); + + file->Var32(&GXStat); + + file->VarArray(ExecParams, 32*4); + file->Var32(&ExecParamCount); + file->Var32((u32*)&CycleCount); + + file->Var32(&MatrixMode); + + file->VarArray(ProjMatrix, 16*4); + file->VarArray(PosMatrix, 16*4); + file->VarArray(VecMatrix, 16*4); + file->VarArray(TexMatrix, 16*4); + + file->VarArray(ProjMatrixStack, 16*4); + file->VarArray(PosMatrixStack, 31*16*4); + file->VarArray(VecMatrixStack, 31*16*4); + file->VarArray(TexMatrixStack, 16*4); + + file->Var32((u32*)&ProjMatrixStackPointer); + file->Var32((u32*)&PosMatrixStackPointer); + file->Var32((u32*)&TexMatrixStackPointer); + + file->VarArray(Viewport, sizeof(Viewport)); + + file->VarArray(PosTestResult, 4*4); + file->VarArray(VecTestResult, 2*3); + + file->Var32(&VertexNum); + file->Var32(&VertexNumInPoly); + file->Var32(&NumConsecutivePolygons); + + for (int i = 0; i < 4; i++) + { + Vertex* vtx = &TempVertexBuffer[i]; + + file->VarArray(vtx->Position, sizeof(s32)*4); + file->VarArray(vtx->Color, sizeof(s32)*3); + file->VarArray(vtx->TexCoords, sizeof(s16)*2); + + file->Var32((u32*)&vtx->Clipped); + + file->VarArray(vtx->FinalPosition, sizeof(s32)*2); + file->VarArray(vtx->FinalColor, sizeof(s32)*3); + } + + if (file->Saving) + { + u32 id; + if (LastStripPolygon) id = (u32)((LastStripPolygon - (&PolygonRAM[0])) / sizeof(Polygon)); + else id = -1; + file->Var32(&id); + } + else + { + u32 id; + file->Var32(&id); + if (id == -1) LastStripPolygon = NULL; + else LastStripPolygon = &PolygonRAM[id]; + } + + file->Var32(&CurRAMBank); + file->Var32(&NumVertices); + file->Var32(&NumPolygons); + file->Var32(&NumOpaquePolygons); + + file->Var32(&ClearAttr1); + file->Var32(&ClearAttr2); + + file->Var32(&FlushRequest); + file->Var32(&FlushAttributes); + + for (int i = 0; i < 6144*2; i++) + { + Vertex* vtx = &VertexRAM[i]; + + file->VarArray(vtx->Position, sizeof(s32)*4); + file->VarArray(vtx->Color, sizeof(s32)*3); + file->VarArray(vtx->TexCoords, sizeof(s16)*2); + + file->Var32((u32*)&vtx->Clipped); + + file->VarArray(vtx->FinalPosition, sizeof(s32)*2); + file->VarArray(vtx->FinalColor, sizeof(s32)*3); + } + + for(int i = 0; i < 2048*2; i++) + { + Polygon* poly = &PolygonRAM[i]; + + // this is a bit ugly, but eh + // we can't save the pointers as-is, that's a bad idea + if (file->Saving) + { + for (int j = 0; j < 10; j++) + { + Vertex* ptr = poly->Vertices[j]; + u32 id; + if (ptr) id = (u32)((ptr - (&VertexRAM[0])) / sizeof(Vertex)); + else id = -1; + file->Var32(&id); + } + } + else + { + for (int j = 0; j < 10; j++) + { + u32 id = -1; + file->Var32(&id); + if (id == -1) poly->Vertices[j] = NULL; + else poly->Vertices[j] = &VertexRAM[id]; + } + } + + file->Var32(&poly->NumVertices); + + file->VarArray(poly->FinalZ, sizeof(s32)*10); + file->VarArray(poly->FinalW, sizeof(s32)*10); + file->Var32((u32*)&poly->WBuffer); + + file->Var32(&poly->Attr); + file->Var32(&poly->TexParam); + file->Var32(&poly->TexPalette); + + file->Var32((u32*)&poly->FacingView); + file->Var32((u32*)&poly->Translucent); + + file->Var32((u32*)&poly->IsShadowMask); + file->Var32((u32*)&poly->IsShadow); + + file->Var32(&poly->VTop); + file->Var32(&poly->VBottom); + file->Var32((u32*)&poly->YTop); + file->Var32((u32*)&poly->YBottom); + file->Var32((u32*)&poly->XTop); + file->Var32((u32*)&poly->XBottom); + + file->Var32(&poly->SortKey); + } + + // probably not worth storing the vblank-latched Renderxxxxxx variables + + if (!file->Saving) + { + ClipMatrixDirty = true; + UpdateClipMatrix(); + + CurVertexRAM = &VertexRAM[CurRAMBank ? 6144 : 0]; + CurPolygonRAM = &PolygonRAM[CurRAMBank ? 2048 : 0]; + + // better safe than sorry, I guess + // might cause a blank frame but atleast it won't shit itself + RenderNumPolygons = 0; + } +} + void MatrixLoadIdentity(s32* m) diff --git a/src/GPU3D.h b/src/GPU3D.h index 3ee1a7f..c997a8f 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -20,6 +20,7 @@ #define GPU3D_H #include <array> +#include "Savestate.h" namespace GPU3D { @@ -85,6 +86,8 @@ bool Init(); void DeInit(); void Reset(); +void DoSavestate(Savestate* file); + void ExecuteCommand(); void Run(s32 cycles); diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 8f7d041..bb48c97 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -1229,7 +1229,14 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y) continue; if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr)) - StencilBuffer[256*(y&0x1) + x] = 1; + StencilBuffer[256*(y&0x1) + x] |= 0x1; + + if (dstattr & 0x3) + { + pixeladdr += BufferSize; + if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr])) + StencilBuffer[256*(y&0x1) + x] |= 0x2; + } } // part 2: polygon inside @@ -1247,6 +1254,13 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y) if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr)) StencilBuffer[256*(y&0x1) + x] = 1; + + if (dstattr & 0x3) + { + pixeladdr += BufferSize; + if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr])) + StencilBuffer[256*(y&0x1) + x] |= 0x2; + } } // part 3: right edge @@ -1268,6 +1282,13 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y) if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr)) StencilBuffer[256*(y&0x1) + x] = 1; + + if (dstattr & 0x3) + { + pixeladdr += BufferSize; + if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr])) + StencilBuffer[256*(y&0x1) + x] |= 0x2; + } } rp->XL = rp->SlopeL.Step(); @@ -1425,18 +1446,23 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y) for (; x < xlimit; x++) { u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x; + u32 dstattr = AttrBuffer[pixeladdr]; // check stencil buffer for shadows if (polygon->IsShadow) { - if (StencilBuffer[256*(y&0x1) + x] == 0) + u8 stencil = StencilBuffer[256*(y&0x1) + x]; + if (!stencil) continue; + if (!(stencil & 0x1)) + pixeladdr += BufferSize; + if (!(stencil & 0x2)) + dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges } interpX.SetX(x); s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer); - u32 dstattr = AttrBuffer[pixeladdr]; // if depth test against the topmost pixel fails, test // against the pixel underneath @@ -1514,18 +1540,23 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y) else for (; x < xlimit; x++) { u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x; + u32 dstattr = AttrBuffer[pixeladdr]; // check stencil buffer for shadows if (polygon->IsShadow) { - if (StencilBuffer[256*(y&0x1) + x] == 0) + u8 stencil = StencilBuffer[256*(y&0x1) + x]; + if (!stencil) continue; + if (!(stencil & 0x1)) + pixeladdr += BufferSize; + if (!(stencil & 0x2)) + dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges } interpX.SetX(x); s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer); - u32 dstattr = AttrBuffer[pixeladdr]; // if depth test against the topmost pixel fails, test // against the pixel underneath @@ -1582,18 +1613,23 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y) for (; x < xlimit; x++) { u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x; + u32 dstattr = AttrBuffer[pixeladdr]; // check stencil buffer for shadows if (polygon->IsShadow) { - if (StencilBuffer[256*(y&0x1) + x] == 0) + u8 stencil = StencilBuffer[256*(y&0x1) + x]; + if (!stencil) continue; + if (!(stencil & 0x1)) + pixeladdr += BufferSize; + if (!(stencil & 0x2)) + dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges } interpX.SetX(x); s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer); - u32 dstattr = AttrBuffer[pixeladdr]; // if depth test against the topmost pixel fails, test // against the pixel underneath diff --git a/src/NDS.cpp b/src/NDS.cpp index df9ed33..40f0c2d 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -112,6 +112,10 @@ u16 RCnt; bool Running; +void DivDone(u32 param); +void SqrtDone(u32 param); + + bool Init() { ARM9 = new ARM(0); @@ -356,9 +360,191 @@ void Stop() SPU::Stop(); } -bool LoadROM(const char* path, bool direct) +bool DoSavestate_Scheduler(Savestate* file) { - if (NDSCart::LoadROM(path, direct)) + // this is a bit of a hack + // but uh, your local coder realized that the scheduler list contains function pointers + // and that storing those as-is is not a very good idea + // unless you want it to crash and burn + + // this is the solution your local coder came up with. + // it's gross but I think it's the best solution for this problem. + // just remember to add here if you add more event callbacks, kay? + // atleast until we come up with something more elegant. + + void (*eventfuncs[])(u32) = + { + GPU::StartScanline, GPU::StartHBlank, GPU::FinishFrame, + SPU::Mix, + Wifi::USTimer, + + GPU::DisplayFIFO, + NDSCart::ROMPrepareData, NDSCart::ROMEndTransfer, + NDSCart::SPITransferDone, + SPI::TransferDone, + DivDone, + SqrtDone, + + NULL + }; + + int len = Event_MAX; + if (file->Saving) + { + for (int i = 0; i < len; i++) + { + SchedEvent* evt = &SchedList[i]; + + u32 funcid = -1; + if (evt->Func) + { + for (int j = 0; eventfuncs[j]; j++) + { + if (evt->Func == eventfuncs[j]) + { + funcid = j; + break; + } + } + if (funcid == -1) + { + printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK STAPLEBUTTER.\n", i); + return false; + } + } + + file->Var32(&funcid); + file->Var32((u32*)&evt->WaitCycles); + file->Var32(&evt->Param); + } + } + else + { + for (int i = 0; i < len; i++) + { + SchedEvent* evt = &SchedList[i]; + + u32 funcid; + file->Var32(&funcid); + + if (funcid != -1) + { + for (int j = 0; ; j++) + { + if (!eventfuncs[j]) + { + printf("savestate: VERY BAD!!!!!! EVENT FUNCTION POINTER ID %d IS OUT OF RANGE. HAX?????\n", j); + return false; + } + if (j == funcid) break; + } + + evt->Func = eventfuncs[funcid]; + } + else + evt->Func = NULL; + + file->Var32((u32*)&evt->WaitCycles); + file->Var32(&evt->Param); + } + } + + return true; +} + +bool DoSavestate(Savestate* file) +{ + file->Section("NDSG"); + + file->VarArray(MainRAM, 0x400000); + file->VarArray(SharedWRAM, 0x8000); + file->VarArray(ARM7WRAM, 0x10000); + + file->VarArray(ExMemCnt, 2*sizeof(u16)); + file->VarArray(ROMSeed0, 2*8); + file->VarArray(ROMSeed1, 2*8); + + file->VarArray(IME, 2*sizeof(u32)); + file->VarArray(IE, 2*sizeof(u32)); + file->VarArray(IF, 2*sizeof(u32)); + + file->Var8(&PostFlag9); + file->Var8(&PostFlag7); + file->Var16(&PowerControl9); + file->Var16(&PowerControl7); + + file->Var16(&ARM7BIOSProt); + + file->Var16(&IPCSync9); + file->Var16(&IPCSync7); + file->Var16(&IPCFIFOCnt9); + file->Var16(&IPCFIFOCnt7); + IPCFIFO9->DoSavestate(file); + IPCFIFO7->DoSavestate(file); + + file->Var16(&DivCnt); + file->Var16(&SqrtCnt); + + file->Var32(&CPUStop); + + for (int i = 0; i < 8; i++) + { + Timer* timer = &Timers[i]; + + file->Var16(&timer->Reload); + file->Var16(&timer->Cnt); + file->Var32(&timer->Counter); + file->Var32(&timer->CycleShift); + } + file->VarArray(TimerCheckMask, 2*sizeof(u8)); + + file->VarArray(DMA9Fill, 4*sizeof(u32)); + + //file->VarArray(SchedList, sizeof(SchedList)); + if (!DoSavestate_Scheduler(file)) return false; + file->Var32(&SchedListMask); + file->Var32((u32*)&CurIterationCycles); + file->Var32((u32*)&ARM7Offset); + + // TODO: save KeyInput???? + file->Var16(&KeyCnt); + file->Var16(&RCnt); + + + for (int i = 0; i < 8; i++) + DMAs[i]->DoSavestate(file); + + file->Var8(&WRAMCnt); + + if (!file->Saving) + { + // 'dept of redundancy dept' + // but we do need to update the mappings + MapSharedWRAM(WRAMCnt); + } + + ARM9->DoSavestate(file); + ARM7->DoSavestate(file); + CP15::DoSavestate(file); + + NDSCart::DoSavestate(file); + GPU::DoSavestate(file); + SPU::DoSavestate(file); + SPI::DoSavestate(file); + RTC::DoSavestate(file); + Wifi::DoSavestate(file); + + if (!file->Saving) + { + GPU::DisplaySwap(PowerControl9>>15); + } + + return true; +} + +bool LoadROM(const char* path, const char* sram, bool direct) +{ + if (NDSCart::LoadROM(path, sram, direct)) { Running = true; return true; @@ -376,6 +562,12 @@ void LoadBIOS() Running = true; } +void RelocateSave(const char* path, bool write) +{ + printf("SRAM: relocating to %s (write=%s)\n", path, write?"true":"false"); + NDSCart::RelocateSave(path, write); +} + void CalcIterationCycles() { @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -19,6 +19,7 @@ #ifndef NDS_H #define NDS_H +#include "Savestate.h" #include "types.h" namespace NDS @@ -110,9 +111,12 @@ void DeInit(); void Reset(); void Stop(); -bool LoadROM(const char* path, bool direct); +bool DoSavestate(Savestate* file); + +bool LoadROM(const char* path, const char* sram, bool direct); void LoadBIOS(); void SetupDirectBoot(); +void RelocateSave(const char* path, bool write); u32 RunFrame(); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 411d773..03dfe4c 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -28,10 +28,15 @@ namespace NDSCart_SRAM { +// BIG SRAM TODO!!!! +// USE A DATABASE TO IDENTIFY SAVE MEMORY TYPE +// (and maybe keep autodetect as a last resort??) +// AUTODETECT IS BLARGY AND IS A MESS + u8* SRAM; u32 SRAMLength; -char SRAMPath[256]; +char SRAMPath[1024]; void (*WriteFunc)(u8 val, bool islast); @@ -81,7 +86,59 @@ void Reset() Discover_Buffer = NULL; } -void LoadSave(char* path) +void DoSavestate(Savestate* file) +{ + file->Section("NDCS"); + + // CHECKME/TODO/whatever + // should the autodetect status shit go in the savestate??? + // I don't think so. + // worst case is that the savestate was taken before autodetect took place completely + // and that causes it to yield a different result, fucking things up + // but that is unlikely + // and we should just use a goddamn database anyway, this is a trainwreck + + // we reload the SRAM contents, tho. + // it should be the same file (as it should be the same ROM, duh) + // but the contents may change + // TODO maybe: possibility to save to a separate file when using savestates???? + + // also the SRAM size shouldn't change. unless something something autodetect something but fuck that code. + + //if (!file->Saving && SRAMLength) + // delete[] SRAM; + + u32 oldlen = SRAMLength; + + file->Var32(&SRAMLength); + if (SRAMLength != oldlen) + { + printf("savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); + printf("oh well. loading it anyway. adsfgdsf\n"); + + if (oldlen) delete[] SRAM; + if (SRAMLength) SRAM = new u8[SRAMLength]; + } + if (SRAMLength) + { + //if (!file->Saving) + // SRAM = new u8[SRAMLength]; + + file->VarArray(SRAM, SRAMLength); + } + + // SPI status shito + + file->Var32(&Hold); + file->Var8(&CurCmd); + file->Var32(&DataPos); + file->Var8(&Data); + + file->Var8(&StatusReg); + file->Var32(&Addr); +} + +void LoadSave(const char* path) { if (SRAM) delete[] SRAM; if (Discover_Buffer) delete[] Discover_Buffer; @@ -91,8 +148,8 @@ void LoadSave(char* path) Discover_AddrLength = 0x7FFFFFFF; Discover_LikelySize = 0; - strncpy(SRAMPath, path, 255); - SRAMPath[255] = '\0'; + strncpy(SRAMPath, path, 1023); + SRAMPath[1023] = '\0'; FILE* f = melon_fopen(path, "rb"); if (f) @@ -139,6 +196,28 @@ void LoadSave(char* path) StatusReg = 0x00; } +void RelocateSave(const char* path, bool write) +{ + if (!write) + { + LoadSave(path); // lazy + return; + } + + strncpy(SRAMPath, path, 1023); + SRAMPath[1023] = '\0'; + + FILE* f = melon_fopen(path, "wb"); + if (!f) + { + printf("NDSCart_SRAM::RelocateSave: failed to create new file. fuck\n"); + return; + } + + fwrite(SRAM, SRAMLength, 1, f); + fclose(f); +} + u8 Read() { return Data; @@ -813,6 +892,33 @@ void Reset() NDSCart_SRAM::Reset(); } +void DoSavestate(Savestate* file) +{ + file->Section("NDSC"); + + file->Var16(&SPICnt); + file->Var32(&ROMCnt); + + file->VarArray(ROMCommand, 8); + file->Var32(&ROMDataOut); + + file->VarArray(DataOut, 0x4000); + file->Var32(&DataOutPos); + file->Var32(&DataOutLen); + + // cart inserted/len/ROM/etc should be already populated + // savestate should be loaded after the right game is loaded + // (TODO: system to verify that indeed the right ROM is loaded) + // (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing) + + file->Var32(&CmdEncMode); + file->Var32(&DataEncMode); + + // TODO: check KEY1 shit?? + + NDSCart_SRAM::DoSavestate(file); +} + void ApplyDLDIPatch() { @@ -956,7 +1062,7 @@ void ApplyDLDIPatch() } -bool LoadROM(const char* path, bool direct) +bool LoadROM(const char* path, const char* sram, bool direct) { // TODO: streaming mode? for really big ROMs or systems with limited RAM // for now we're lazy @@ -995,7 +1101,7 @@ bool LoadROM(const char* path, bool direct) if (*(u32*)&CartROM[0x20] < 0x4000) { - ApplyDLDIPatch(); + //ApplyDLDIPatch(); } if (direct) @@ -1037,16 +1143,18 @@ bool LoadROM(const char* path, bool direct) // save - char savepath[256]; - strncpy(savepath, path, 255); - savepath[255] = '\0'; - strncpy(savepath + strlen(path) - 3, "sav", 3); - printf("Save file: %s\n", savepath); - NDSCart_SRAM::LoadSave(savepath); + printf("Save file: %s\n", sram); + NDSCart_SRAM::LoadSave(sram); return true; } +void RelocateSave(const char* path, bool write) +{ + // herp derp + NDSCart_SRAM::RelocateSave(path, write); +} + void ReadROM(u32 addr, u32 len, u32 offset) { if (!CartInserted) return; diff --git a/src/NDSCart.h b/src/NDSCart.h index 7267ad3..ac8f8fe 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -42,7 +42,10 @@ bool Init(); void DeInit(); void Reset(); -bool LoadROM(const char* path, bool direct); +void DoSavestate(Savestate* file); + +bool LoadROM(const char* path, const char* sram, bool direct); +void RelocateSave(const char* path, bool write); void WriteROMCnt(u32 val); u32 ReadROMData(); @@ -51,6 +54,10 @@ void WriteSPICnt(u16 val); u8 ReadSPIData(); void WriteSPIData(u8 val); +void ROMPrepareData(u32 param); +void ROMEndTransfer(u32 param); +void SPITransferDone(u32 param); + } #endif diff --git a/src/Platform.h b/src/Platform.h index 7fb6793..091e607 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/RTC.cpp b/src/RTC.cpp index 3d45bef..cd35643 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -73,6 +73,30 @@ void Reset() FreeReg = 0; } +void DoSavestate(Savestate* file) +{ + file->Section("RTC."); + + file->Var16(&IO); + + file->Var8(&Input); + file->Var32(&InputBit); + file->Var32(&InputPos); + + file->VarArray(Output, sizeof(Output)); + file->Var32(&OutputBit); + file->Var32(&OutputPos); + + file->Var8(&CurCmd); + + file->Var8(&StatusReg1); + file->Var8(&StatusReg2); + file->VarArray(Alarm1, sizeof(Alarm1)); + file->VarArray(Alarm2, sizeof(Alarm2)); + file->Var8(&ClockAdjust); + file->Var8(&FreeReg); +} + u8 BCD(u8 val) { @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -20,6 +20,7 @@ #define RTC_H #include "types.h" +#include "Savestate.h" namespace RTC { @@ -27,6 +28,7 @@ namespace RTC bool Init(); void DeInit(); void Reset(); +void DoSavestate(Savestate* file); u16 Read(); void Write(u16 val, bool byte); diff --git a/src/SPI.cpp b/src/SPI.cpp index 12e9291..3ffb20f 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -193,6 +193,22 @@ void Reset() StatusReg = 0x00; } +void DoSavestate(Savestate* file) +{ + file->Section("SPFW"); + + // CHECKME/TODO: trust the firmware to stay the same????? + // embedding the whole firmware in the savestate would be derpo tho?? + + file->Var32(&Hold); + file->Var8(&CurCmd); + file->Var32(&DataPos); + file->Var8(&Data); + + file->Var8(&StatusReg); + file->Var32(&Addr); +} + void SetupDirectBoot() { NDS::ARM9Write32(0x027FF864, 0); @@ -360,6 +376,19 @@ void Reset() RegMasks[4] = 0x0F; } +void DoSavestate(Savestate* file) +{ + file->Section("SPPW"); + + file->Var32(&Hold); + file->Var32(&DataPos); + file->Var8(&Index); + file->Var8(&Data); + + file->VarArray(Registers, 8); + file->VarArray(RegMasks, 8); // is that needed?? +} + u8 Read() { return Data; @@ -441,6 +470,17 @@ void Reset() ConvResult = 0; } +void DoSavestate(Savestate* file) +{ + file->Section("SPTS"); + + file->Var32(&DataPos); + file->Var8(&ControlByte); + file->Var8(&Data); + + file->Var16(&ConvResult); +} + void SetTouchCoords(u16 x, u16 y) { // scr.x = (adc.x-adc.x1) * (scr.x2-scr.x1) / (adc.x2-adc.x1) + (scr.x1-1) @@ -526,6 +566,18 @@ void Reset() SPI_TSC::Reset(); } +void DoSavestate(Savestate* file) +{ + file->Section("SPIG"); + + file->Var16(&Cnt); + file->Var32(&CurDevice); + + SPI_Firmware::DoSavestate(file); + SPI_Powerman::DoSavestate(file); + SPI_TSC::DoSavestate(file); +} + void WriteCnt(u16 val) { @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -19,6 +19,8 @@ #ifndef SPI_H #define SPI_H +#include "Savestate.h" + namespace SPI_Firmware { @@ -45,6 +47,7 @@ extern u16 Cnt; bool Init(); void DeInit(); void Reset(); +void DoSavestate(Savestate* file); u16 ReadCnt(); void WriteCnt(u16 val); @@ -52,6 +55,8 @@ void WriteCnt(u16 val); u8 ReadData(); void WriteData(u8 val); +void TransferDone(u32 param); + } #endif diff --git a/src/SPU.cpp b/src/SPU.cpp index 7f70e65..034e1aa 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -24,8 +24,6 @@ // SPU TODO // * loop mode 3, what does it do? -// * the FIFO, whatever it is. GBAtek mentions it but gives no details. -// * consider mixing every sample instead of every 16? namespace SPU @@ -121,6 +119,21 @@ void Stop() memset(OutputBuffer, 0, 2*OutputBufferSize*2); } +void DoSavestate(Savestate* file) +{ + file->Section("SPU."); + + file->Var16(&Cnt); + file->Var8(&MasterVolume); + file->Var16(&Bias); + + for (int i = 0; i < 16; i++) + Channels[i]->DoSavestate(file); + + Capture[0]->DoSavestate(file); + Capture[1]->DoSavestate(file); +} + void SetBias(u16 bias) { @@ -154,6 +167,36 @@ void Channel::Reset() FIFOLevel = 0; } +void Channel::DoSavestate(Savestate* file) +{ + file->Var32(&Cnt); + file->Var32(&SrcAddr); + file->Var16(&TimerReload); + file->Var32(&LoopPos); + file->Var32(&Length); + + file->Var8(&Volume); + file->Var8(&VolumeShift); + file->Var8(&Pan); + + file->Var32(&Timer); + file->Var32((u32*)&Pos); + file->Var16((u16*)&CurSample); + file->Var16(&NoiseVal); + + file->Var32((u32*)&ADPCMVal); + file->Var32((u32*)&ADPCMIndex); + file->Var32((u32*)&ADPCMValLoop); + file->Var32((u32*)&ADPCMIndexLoop); + file->Var8(&ADPCMCurByte); + + file->Var32(&FIFOReadPos); + file->Var32(&FIFOWritePos); + file->Var32(&FIFOReadOffset); + file->Var32(&FIFOLevel); + file->VarArray(FIFO, 8*4); +} + void Channel::FIFO_BufferData() { u32 totallen = LoopPos + Length; @@ -439,6 +482,23 @@ void CaptureUnit::Reset() FIFOLevel = 0; } +void CaptureUnit::DoSavestate(Savestate* file) +{ + file->Var8(&Cnt); + file->Var32(&DstAddr); + file->Var16(&TimerReload); + file->Var32(&Length); + + file->Var32(&Timer); + file->Var32((u32*)&Pos); + + file->Var32(&FIFOReadPos); + file->Var32(&FIFOWritePos); + file->Var32(&FIFOWriteOffset); + file->Var32(&FIFOLevel); + file->VarArray(FIFO, 4*4); +} + void CaptureUnit::FIFO_FlushData() { for (u32 i = 0; i < 4; i++) @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -19,6 +19,8 @@ #ifndef SPU_H #define SPU_H +#include "Savestate.h" + namespace SPU { @@ -27,6 +29,8 @@ void DeInit(); void Reset(); void Stop(); +void DoSavestate(Savestate* file); + void SetBias(u16 bias); void Mix(u32 samples); @@ -46,6 +50,7 @@ public: Channel(u32 num); ~Channel(); void Reset(); + void DoSavestate(Savestate* file); u32 Num; @@ -137,6 +142,7 @@ public: CaptureUnit(u32 num); ~CaptureUnit(); void Reset(); + void DoSavestate(Savestate* file); u32 Num; diff --git a/src/Savestate.cpp b/src/Savestate.cpp new file mode 100644 index 0000000..e295f4a --- /dev/null +++ b/src/Savestate.cpp @@ -0,0 +1,275 @@ +/* + Copyright 2016-2019 StapleButter + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include <stdio.h> +#include "Savestate.h" +#include "melon_fopen.h" + +/* + Savestate format + + header: + 00 - magic MELN + 04 - version major + 06 - version minor + 08 - length + 0C - game serial + 10 - ARM9 binary checksum + 14 - ARM7 binary checksum + 18 - reserved + 1C - reserved + + section header: + 00 - section magic + 04 - section length + 08 - reserved + 0C - reserved + + Implementation details + + version difference: + * different major means savestate file is incompatible + * different minor means adjustments may have to be made +*/ + +Savestate::Savestate(char* filename, bool save) +{ + char* magic = "MELN"; + + Error = false; + + if (save) + { + Saving = true; + file = melon_fopen(filename, "wb"); + if (!file) + { + printf("savestate: file %s doesn't exist\n", filename); + Error = true; + return; + } + + VersionMajor = SAVESTATE_MAJOR; + VersionMinor = SAVESTATE_MINOR; + + fwrite(magic, 4, 1, file); + fwrite(&VersionMajor, 2, 1, file); + fwrite(&VersionMinor, 2, 1, file); + fseek(file, 8, SEEK_CUR); // length to be fixed later + } + else + { + Saving = false; + file = melon_fopen(filename, "rb"); + if (!file) + { + printf("savestate: file %s doesn't exist\n", filename); + Error = true; + return; + } + + u32 len; + fseek(file, 0, SEEK_END); + len = (u32)ftell(file); + fseek(file, 0, SEEK_SET); + + u32 buf = 0; + + fread(&buf, 4, 1, file); + if (buf != ((u32*)magic)[0]) + { + printf("savestate: invalid magic %08X\n", buf); + Error = true; + return; + } + + VersionMajor = 0; + VersionMinor = 0; + + fread(&VersionMajor, 2, 1, file); + if (VersionMajor != SAVESTATE_MAJOR) + { + printf("savestate: bad version major %d, expecting %d\n", VersionMajor, SAVESTATE_MAJOR); + Error = true; + return; + } + + fread(&VersionMinor, 2, 1, file); + // TODO: handle it??? + + buf = 0; + fread(&buf, 4, 1, file); + if (buf != len) + { + printf("savestate: bad length %d\n", buf); + Error = true; + return; + } + + fseek(file, 4, SEEK_CUR); + } + + CurSection = -1; +} + +Savestate::~Savestate() +{ + if (Error) return; + + if (Saving) + { + if (CurSection != -1) + { + u32 pos = (u32)ftell(file); + fseek(file, CurSection+4, SEEK_SET); + + u32 len = pos - CurSection; + fwrite(&len, 4, 1, file); + + fseek(file, pos, SEEK_SET); + } + + fseek(file, 0, SEEK_END); + u32 len = (u32)ftell(file); + fseek(file, 8, SEEK_SET); + fwrite(&len, 4, 1, file); + } + + if (file) fclose(file); +} + +void Savestate::Section(char* magic) +{ + if (Error) return; + + if (Saving) + { + if (CurSection != -1) + { + u32 pos = (u32)ftell(file); + fseek(file, CurSection+4, SEEK_SET); + + u32 len = pos - CurSection; + fwrite(&len, 4, 1, file); + + fseek(file, pos, SEEK_SET); + } + + CurSection = (u32)ftell(file); + + fwrite(magic, 4, 1, file); + fseek(file, 12, SEEK_CUR); + } + else + { + fseek(file, 0x10, SEEK_SET); + + for (;;) + { + u32 buf = 0; + + fread(&buf, 4, 1, file); + if (buf != ((u32*)magic)[0]) + { + if (buf == 0) + { + printf("savestate: section %s not found. blarg\n", magic); + return; + } + + buf = 0; + fread(&buf, 4, 1, file); + fseek(file, buf-8, SEEK_CUR); + continue; + } + + fseek(file, 12, SEEK_CUR); + break; + } + } +} + +void Savestate::Var8(u8* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 1, 1, file); + } + else + { + fread(var, 1, 1, file); + } +} + +void Savestate::Var16(u16* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 2, 1, file); + } + else + { + fread(var, 2, 1, file); + } +} + +void Savestate::Var32(u32* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 4, 1, file); + } + else + { + fread(var, 4, 1, file); + } +} + +void Savestate::Var64(u64* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 8, 1, file); + } + else + { + fread(var, 8, 1, file); + } +} + +void Savestate::VarArray(void* data, u32 len) +{ + if (Error) return; + + if (Saving) + { + fwrite(data, len, 1, file); + } + else + { + fread(data, len, 1, file); + } +} diff --git a/src/Savestate.h b/src/Savestate.h new file mode 100644 index 0000000..fa01f6b --- /dev/null +++ b/src/Savestate.h @@ -0,0 +1,55 @@ +/* + Copyright 2016-2019 StapleButter + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef SAVESTATE_H +#define SAVESTATE_H + +#include <stdio.h> +#include "types.h" + +#define SAVESTATE_MAJOR 1 +#define SAVESTATE_MINOR 0 + +class Savestate +{ +public: + Savestate(char* filename, bool save); + ~Savestate(); + + bool Error; + + bool Saving; + u32 VersionMajor; + u32 VersionMinor; + + u32 CurSection; + + void Section(char* magic); + + void Var8(u8* var); + void Var16(u16* var); + void Var32(u32* var); + void Var64(u64* var); + + void VarArray(void* data, u32 len); + +private: + FILE* file; +}; + +#endif // SAVESTATE_H diff --git a/src/Wifi.cpp b/src/Wifi.cpp index ed92ad8..0e73422 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -213,6 +213,40 @@ void Reset() WifiAP::Reset(); } +void DoSavestate(Savestate* file) +{ + file->Section("WIFI"); + + // berp. + // not sure we're saving enough shit at all there. + // also: savestate and wifi can't fucking work together!! + // or it can but you would be disconnected + + file->VarArray(RAM, 0x2000); + file->VarArray(IO, 0x1000); + + file->Var16(&Random); + + file->VarArray(BBRegs, 0x100); + file->VarArray(BBRegsRO, 0x100); + + file->Var8(&RFVersion); + file->VarArray(RFRegs, 4*0x40); + + file->Var64(&USCounter); + file->Var64(&USCompare); + file->Var32((u32*)&BlockBeaconIRQ14); + + file->Var32(&ComStatus); + file->Var32(&TXCurSlot); + file->Var32(&RXCounter); + + file->Var32((u32*)&MPReplyTimer); + file->Var32((u32*)&MPNumReplies); + + file->Var32(&CmdCounter); +} + void SetIRQ(u32 irq) { @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -19,6 +19,8 @@ #ifndef WIFI_H #define WIFI_H +#include "Savestate.h" + namespace Wifi { @@ -146,6 +148,7 @@ extern bool MPInited; bool Init(); void DeInit(); void Reset(); +void DoSavestate(Savestate* file); void StartTX_Beacon(); diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index 963b4d2..3e8bdb0 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/WifiAP.h b/src/WifiAP.h index fccfa1a..6f963c4 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/libui_sdl/DlgEmuSettings.cpp b/src/libui_sdl/DlgEmuSettings.cpp index 8cb2ccc..77bb18e 100644 --- a/src/libui_sdl/DlgEmuSettings.cpp +++ b/src/libui_sdl/DlgEmuSettings.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/libui_sdl/DlgEmuSettings.h b/src/libui_sdl/DlgEmuSettings.h index f26ae14..f8da148 100644 --- a/src/libui_sdl/DlgEmuSettings.h +++ b/src/libui_sdl/DlgEmuSettings.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/libui_sdl/DlgInputConfig.cpp b/src/libui_sdl/DlgInputConfig.cpp index 5a98a91..6496307 100644 --- a/src/libui_sdl/DlgInputConfig.cpp +++ b/src/libui_sdl/DlgInputConfig.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -309,7 +309,7 @@ void Open() uiBoxAppend(in_ctrl, uiControl(g_key), 1); uiGrid* b_key = uiNewGrid(); uiGroupSetChild(g_key, uiControl(b_key)); - + const int width = 120; for (int i = 0; i < 12; i++) diff --git a/src/libui_sdl/DlgInputConfig.h b/src/libui_sdl/DlgInputConfig.h index 33529c2..c044016 100644 --- a/src/libui_sdl/DlgInputConfig.h +++ b/src/libui_sdl/DlgInputConfig.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/libui_sdl/Platform.cpp b/src/libui_sdl/Platform.cpp index 80aa0e9..0c29f6b 100644 --- a/src/libui_sdl/Platform.cpp +++ b/src/libui_sdl/Platform.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/libui_sdl/libui/unix/area.c b/src/libui_sdl/libui/unix/area.c index ea31676..40f8624 100644 --- a/src/libui_sdl/libui/unix/area.c +++ b/src/libui_sdl/libui/unix/area.c @@ -77,6 +77,10 @@ static void areaWidget_init(areaWidget *aw) static void areaWidget_dispose(GObject *obj) { + // remove any draw order that might still be pending + areaWidget *aw = areaWidget(obj); + while (g_idle_remove_by_data(aw->a)); + G_OBJECT_CLASS(areaWidget_parent_class)->dispose(obj); } diff --git a/src/libui_sdl/libui/unix/menu.c b/src/libui_sdl/libui/unix/menu.c index d6aa398..d641426 100644 --- a/src/libui_sdl/libui/unix/menu.c +++ b/src/libui_sdl/libui/unix/menu.c @@ -305,13 +305,13 @@ static void appendMenuItem(GtkMenuShell *submenu, uiMenuItem *item, uiWindow *w) { int j; uiMenu* m; - GtkWidget *submenu; + GtkWidget *c_submenu; m = item->popupchild; - submenu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + c_submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), c_submenu); for (j = 0; j < m->items->len; j++) - appendMenuItem(GTK_MENU_SHELL(submenu), g_array_index(m->items, uiMenuItem *, j), w); + appendMenuItem(GTK_MENU_SHELL(c_submenu), g_array_index(m->items, uiMenuItem *, j), w); } } @@ -347,6 +347,7 @@ GtkWidget *makeMenubar(uiWindow *w) struct freeMenuItemData { GArray *items; guint i; + guint* parent_i; }; static void freeMenu(GtkWidget *widget, gpointer data); @@ -359,7 +360,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data) item = g_array_index(fmi->items, uiMenuItem *, fmi->i); if (item->popupchild != NULL) - freeMenu(widget, &item->popupchild->id); + freeMenu(widget, fmi->parent_i);//&item->popupchild->id); w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget); if (g_hash_table_remove(item->windows, widget) == FALSE) implbug("GtkMenuItem %p not in menu item's item/window map", widget); @@ -374,14 +375,16 @@ static void freeMenu(GtkWidget *widget, gpointer data) GtkMenuItem *item; GtkWidget *submenu; struct freeMenuItemData fmi; - + m = g_array_index(menus, uiMenu *, *i); item = GTK_MENU_ITEM(widget); submenu = gtk_menu_item_get_submenu(item); fmi.items = m->items; fmi.i = 0; - gtk_container_foreach(GTK_CONTAINER(submenu), freeMenuItem, &fmi); (*i)++; + fmi.parent_i = i; + gtk_container_foreach(GTK_CONTAINER(submenu), freeMenuItem, &fmi); + //(*i)++; } void freeMenubar(GtkWidget *mb) diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index 6736fbc..4c19314 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -24,6 +24,7 @@ #include "libui/ui.h" #include "../types.h" +#include "../melon_fopen.h" #include "../version.h" #include "../Config.h" @@ -37,6 +38,13 @@ #include "../Platform.h" #include "../Config.h" +#include "../Savestate.h" + + +// savestate slot mapping +// 1-8: regular slots (quick access) +// '9': load/save arbitrary file +const int kSavestateNum[9] = {1, 2, 3, 4, 5, 6, 7, 8, 0}; const int kScreenRot[4] = {0, 1, 2, 3}; const int kScreenGap[6] = {0, 1, 8, 64, 90, 128}; @@ -47,10 +55,19 @@ const int kScreenSizing[4] = {0, 1, 2, 3}; uiWindow* MainWindow; uiArea* MainDrawArea; +uiMenuItem* MenuItem_SaveState; +uiMenuItem* MenuItem_LoadState; +uiMenuItem* MenuItem_UndoStateLoad; + +uiMenuItem* MenuItem_SaveStateSlot[9]; +uiMenuItem* MenuItem_LoadStateSlot[9]; + uiMenuItem* MenuItem_Pause; uiMenuItem* MenuItem_Reset; uiMenuItem* MenuItem_Stop; +uiMenuItem* MenuItem_SavestateSRAMReloc; + uiMenuItem* MenuItem_ScreenRot[4]; uiMenuItem* MenuItem_ScreenGap[6]; uiMenuItem* MenuItem_ScreenLayout[3]; @@ -62,6 +79,10 @@ volatile int EmuStatus; bool RunningSomething; char ROMPath[1024]; +char SRAMPath[1024]; +char PrevSRAMPath[1024]; // for savestate 'undo load' + +bool SavestateLoaded; bool ScreenDrawInited = false; uiDrawBitmap* ScreenBitmap = NULL; @@ -88,6 +109,20 @@ SDL_Joystick* Joystick; void SetupScreenRects(int width, int height); +void SaveState(int slot); +void LoadState(int slot); +void UndoStateLoad(); +void GetSavestateName(int slot, char* filename, int len); + + + +bool FileExists(char* name) +{ + FILE* f = melon_fopen(name, "rb"); + if (!f) return false; + fclose(f); + return true; +} void UpdateWindowTitle(void* data) @@ -301,8 +336,6 @@ int EmuThreadFunc(void* burp) } else { - EmuStatus = 2; - // paused nframes = 0; lasttick = SDL_GetTicks(); @@ -310,7 +343,13 @@ int EmuThreadFunc(void* burp) lastmeasuretick = lasttick; fpslimitcount = 0; - uiAreaQueueRedrawAll(MainDrawArea); + if (EmuRunning == 2) + { + uiAreaQueueRedrawAll(MainDrawArea); + } + + EmuStatus = EmuRunning; + SDL_Delay(100); } } @@ -438,6 +477,10 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) if (evt->Modifiers == 0x2) // ALT+key return 0; + // d0rp + if (!RunningSomething) + return 1; + if (evt->Up) { for (int i = 0; i < 12; i++) @@ -446,6 +489,22 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) } else if (!evt->Repeat) { + // F keys: 3B-44, 57-58 | SHIFT: mod. 0x4 + if (evt->Scancode >= 0x3B && evt->Scancode <= 0x42) // F1-F8, quick savestate + { + if (evt->Modifiers == 0x4) SaveState(1 + (evt->Scancode - 0x3B)); + else if (evt->Modifiers == 0x0) LoadState(1 + (evt->Scancode - 0x3B)); + } + else if (evt->Scancode == 0x43) // F9, savestate from/to file + { + if (evt->Modifiers == 0x4) SaveState(0); + else if (evt->Modifiers == 0x0) LoadState(0); + } + else if (evt->Scancode == 0x58) // F12, undo savestate + { + if (evt->Modifiers == 0x0) UndoStateLoad(); + } + for (int i = 0; i < 12; i++) if (evt->Scancode == Config::KeyMapping[i]) KeyInputMask &= ~(1<<i); @@ -710,6 +769,25 @@ void Run() EmuRunning = 1; RunningSomething = true; + uiMenuItemEnable(MenuItem_SaveState); + uiMenuItemEnable(MenuItem_LoadState); + + if (SavestateLoaded) + uiMenuItemEnable(MenuItem_UndoStateLoad); + else + uiMenuItemDisable(MenuItem_UndoStateLoad); + + for (int i = 0; i < 8; i++) + { + char ssfile[1024]; + GetSavestateName(i+1, ssfile, 1024); + if (FileExists(ssfile)) uiMenuItemEnable(MenuItem_LoadStateSlot[i]); + else uiMenuItemDisable(MenuItem_LoadStateSlot[i]); + } + + for (int i = 0; i < 9; i++) uiMenuItemEnable(MenuItem_SaveStateSlot[i]); + uiMenuItemEnable(MenuItem_LoadStateSlot[8]); + uiMenuItemEnable(MenuItem_Pause); uiMenuItemEnable(MenuItem_Reset); uiMenuItemEnable(MenuItem_Stop); @@ -723,6 +801,12 @@ void Stop(bool internal) while (EmuStatus != 2); RunningSomething = false; + uiWindowSetTitle(MainWindow, "melonDS " MELONDS_VERSION); + + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]); + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_LoadStateSlot[i]); + uiMenuItemDisable(MenuItem_UndoStateLoad); + uiMenuItemDisable(MenuItem_Pause); uiMenuItemDisable(MenuItem_Reset); uiMenuItemDisable(MenuItem_Stop); @@ -732,16 +816,33 @@ void Stop(bool internal) uiAreaQueueRedrawAll(MainDrawArea); } +void SetupSRAMPath() +{ + strncpy(SRAMPath, ROMPath, 1023); + SRAMPath[1023] = '\0'; + strncpy(SRAMPath + strlen(ROMPath) - 3, "sav", 3); +} + void TryLoadROM(char* file, int prevstatus) { char oldpath[1024]; + char oldsram[1024]; strncpy(oldpath, ROMPath, 1024); + strncpy(oldsram, SRAMPath, 1024); strncpy(ROMPath, file, 1023); ROMPath[1023] = '\0'; - if (NDS::LoadROM(ROMPath, Config::DirectBoot)) + SetupSRAMPath(); + + if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot)) + { + SavestateLoaded = false; + uiMenuItemDisable(MenuItem_UndoStateLoad); + + strncpy(PrevSRAMPath, SRAMPath, 1024); // safety Run(); + } else { uiMsgBoxError(MainWindow, @@ -749,18 +850,194 @@ void TryLoadROM(char* file, int prevstatus) "Make sure the file can be accessed and isn't opened in another application."); strncpy(ROMPath, oldpath, 1024); + strncpy(SRAMPath, oldsram, 1024); EmuRunning = prevstatus; } } -int OnCloseWindow(uiWindow* window, void* blarg) +// SAVESTATE TODO +// * configurable paths. not everyone wants their ROM directory to be polluted, I guess. + +void GetSavestateName(int slot, char* filename, int len) { - if (RunningSomething) + int pos; + + if (ROMPath[0] == '\0') // running firmware, no ROM { - EmuRunning = 2; - while (EmuStatus != 2); + strcpy(filename, "firmware"); + pos = 8; + } + else + { + int l = strlen(ROMPath); + pos = l; + while (ROMPath[pos] != '.' && pos > 0) pos--; + if (pos == 0) pos = l; + + // avoid buffer overflow. shoddy + if (pos > len-5) pos = len-5; + + strncpy(&filename[0], ROMPath, pos); + } + strcpy(&filename[pos], ".ml"); + filename[pos+3] = '0'+slot; + filename[pos+4] = '\0'; +} + +void LoadState(int slot) +{ + int prevstatus = EmuRunning; + EmuRunning = 2; + while (EmuStatus != 2); + + char filename[1024]; + + if (slot > 0) + { + GetSavestateName(slot, filename, 1024); + } + else + { + char* file = uiOpenFile(MainWindow, "melonDS savestate (any)|*.ml1;*.ml2;*.ml3;*.ml4;*.ml5;*.ml6;*.ml7;*.ml8;*.mln", NULL); + if (!file) + { + EmuRunning = prevstatus; + return; + } + + strncpy(filename, file, 1023); + filename[1023] = '\0'; + uiFreeText(file); + } + + if (!FileExists(filename)) + { + EmuRunning = prevstatus; + return; + } + + // backup + Savestate* backup = new Savestate("timewarp.mln", true); + NDS::DoSavestate(backup); + delete backup; + + Savestate* state = new Savestate(filename, false); + if (state->Error) + { + delete state; + + uiMsgBoxError(MainWindow, "Error", "Could not load savestate file."); + + // current state might be crapoed, so restore from sane backup + state = new Savestate("timewarp.mln", false); + } + + NDS::DoSavestate(state); + delete state; + + if (Config::SavestateRelocSRAM && ROMPath[0]!='\0') + { + strncpy(PrevSRAMPath, SRAMPath, 1024); + + strncpy(SRAMPath, filename, 1019); + int len = strlen(SRAMPath); + strcpy(&SRAMPath[len], ".sav"); + SRAMPath[len+4] = '\0'; + + NDS::RelocateSave(SRAMPath, false); + } + + SavestateLoaded = true; + uiMenuItemEnable(MenuItem_UndoStateLoad); + + EmuRunning = prevstatus; +} + +void SaveState(int slot) +{ + int prevstatus = EmuRunning; + EmuRunning = 2; + while (EmuStatus != 2); + + char filename[1024]; + + if (slot > 0) + { + GetSavestateName(slot, filename, 1024); } + else + { + char* file = uiSaveFile(MainWindow, "melonDS savestate (*.mln)|*.mln", NULL); + if (!file) + { + EmuRunning = prevstatus; + return; + } + + strncpy(filename, file, 1023); + filename[1023] = '\0'; + uiFreeText(file); + } + + Savestate* state = new Savestate(filename, true); + if (state->Error) + { + delete state; + + uiMsgBoxError(MainWindow, "Error", "Could not save state."); + } + else + { + NDS::DoSavestate(state); + delete state; + + if (slot > 0) + uiMenuItemEnable(MenuItem_LoadStateSlot[slot-1]); + + if (Config::SavestateRelocSRAM && ROMPath[0]!='\0') + { + strncpy(SRAMPath, filename, 1019); + int len = strlen(SRAMPath); + strcpy(&SRAMPath[len], ".sav"); + SRAMPath[len+4] = '\0'; + + NDS::RelocateSave(SRAMPath, true); + } + } + + EmuRunning = prevstatus; +} + +void UndoStateLoad() +{ + if (!SavestateLoaded) return; + + int prevstatus = EmuRunning; + EmuRunning = 2; + while (EmuStatus != 2); + + // pray that this works + // what do we do if it doesn't??? + // but it should work. + Savestate* backup = new Savestate("timewarp.mln", false); + NDS::DoSavestate(backup); + delete backup; + + if (ROMPath[0]!='\0') + { + strncpy(SRAMPath, PrevSRAMPath, 1024); + NDS::RelocateSave(SRAMPath, false); + } + + EmuRunning = prevstatus; +} + + +int OnCloseWindow(uiWindow* window, void* blarg) +{ + EmuRunning = 3; + while (EmuStatus != 3); uiQuit(); return 1; @@ -795,6 +1072,9 @@ void OnLoseFocus(uiWindow* window, void* blarg) void OnCloseByMenu(uiMenuItem* item, uiWindow* window, void* blarg) { + EmuRunning = 3; + while (EmuStatus != 3); + uiControlDestroy(uiControl(window)); uiQuit(); } @@ -816,6 +1096,23 @@ void OnOpenFile(uiMenuItem* item, uiWindow* window, void* blarg) uiFreeText(file); } +void OnSaveState(uiMenuItem* item, uiWindow* window, void* param) +{ + int slot = *(int*)param; + SaveState(slot); +} + +void OnLoadState(uiMenuItem* item, uiWindow* window, void* param) +{ + int slot = *(int*)param; + LoadState(slot); +} + +void OnUndoStateLoad(uiMenuItem* item, uiWindow* window, void* param) +{ + UndoStateLoad(); +} + void OnRun(uiMenuItem* item, uiWindow* window, void* blarg) { if (!RunningSomething) @@ -852,10 +1149,16 @@ void OnReset(uiMenuItem* item, uiWindow* window, void* blarg) EmuRunning = 2; while (EmuStatus != 2); + SavestateLoaded = false; + uiMenuItemDisable(MenuItem_UndoStateLoad); + if (ROMPath[0] == '\0') NDS::LoadBIOS(); else - NDS::LoadROM(ROMPath, Config::DirectBoot); + { + SetupSRAMPath(); + NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot); + } Run(); } @@ -878,6 +1181,12 @@ void OnOpenInputConfig(uiMenuItem* item, uiWindow* window, void* blarg) } +void OnSetSavestateSRAMReloc(uiMenuItem* item, uiWindow* window, void* param) +{ + Config::SavestateRelocSRAM = uiMenuItemChecked(item) ? 1:0; +} + + void EnsureProperMinSize() { bool isHori = (ScreenRotation == 1 || ScreenRotation == 3); @@ -1005,15 +1314,6 @@ void ApplyNewSettings() } -bool _fileexists(char* name) -{ - FILE* f = fopen(name, "rb"); - if (!f) return false; - fclose(f); - return true; -} - - int main(int argc, char** argv) { srand(time(NULL)); @@ -1072,6 +1372,48 @@ int main(int argc, char** argv) menuitem = uiMenuAppendItem(menu, "Open ROM..."); uiMenuItemOnClicked(menuitem, OnOpenFile, NULL); uiMenuAppendSeparator(menu); + { + uiMenu* submenu = uiNewMenu("Save state"); + + for (int i = 0; i < 9; i++) + { + char name[32]; + if (i < 8) + sprintf(name, "%d\tShift+F%d", kSavestateNum[i], kSavestateNum[i]); + else + strcpy(name, "File...\tShift+F9"); + + uiMenuItem* ssitem = uiMenuAppendItem(submenu, name); + uiMenuItemOnClicked(ssitem, OnSaveState, (void*)&kSavestateNum[i]); + + MenuItem_SaveStateSlot[i] = ssitem; + } + + MenuItem_SaveState = uiMenuAppendSubmenu(menu, submenu); + } + { + uiMenu* submenu = uiNewMenu("Load state"); + + for (int i = 0; i < 9; i++) + { + char name[32]; + if (i < 8) + sprintf(name, "%d\tF%d", kSavestateNum[i], kSavestateNum[i]); + else + strcpy(name, "File...\tF9"); + + uiMenuItem* ssitem = uiMenuAppendItem(submenu, name); + uiMenuItemOnClicked(ssitem, OnLoadState, (void*)&kSavestateNum[i]); + + MenuItem_LoadStateSlot[i] = ssitem; + } + + MenuItem_LoadState = uiMenuAppendSubmenu(menu, submenu); + } + menuitem = uiMenuAppendItem(menu, "Undo state load\tF12"); + uiMenuItemOnClicked(menuitem, OnUndoStateLoad, NULL); + MenuItem_UndoStateLoad = menuitem; + uiMenuAppendSeparator(menu); menuitem = uiMenuAppendItem(menu, "Quit"); uiMenuItemOnClicked(menuitem, OnCloseByMenu, NULL); @@ -1096,6 +1438,15 @@ int main(int argc, char** argv) uiMenuItemOnClicked(menuitem, OnOpenInputConfig, NULL); uiMenuAppendSeparator(menu); { + uiMenu* submenu = uiNewMenu("Savestate settings"); + + MenuItem_SavestateSRAMReloc = uiMenuAppendCheckItem(submenu, "Separate savefiles"); + uiMenuItemOnClicked(MenuItem_SavestateSRAMReloc, OnSetSavestateSRAMReloc, NULL); + + uiMenuAppendSubmenu(menu, submenu); + } + uiMenuAppendSeparator(menu); + { uiMenu* submenu = uiNewMenu("Screen rotation"); for (int i = 0; i < 4; i++) @@ -1167,6 +1518,12 @@ int main(int argc, char** argv) uiWindowOnGetFocus(MainWindow, OnGetFocus, NULL); uiWindowOnLoseFocus(MainWindow, OnLoseFocus, NULL); + //uiMenuItemDisable(MenuItem_SaveState); + //uiMenuItemDisable(MenuItem_LoadState); + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]); + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_LoadStateSlot[i]); + uiMenuItemDisable(MenuItem_UndoStateLoad); + uiMenuItemDisable(MenuItem_Pause); uiMenuItemDisable(MenuItem_Reset); uiMenuItemDisable(MenuItem_Stop); @@ -1195,6 +1552,8 @@ int main(int argc, char** argv) SANITIZE(ScreenSizing, 0, 3); #undef SANITIZE + uiMenuItemSetChecked(MenuItem_SavestateSRAMReloc, Config::SavestateRelocSRAM?1:0); + uiMenuItemSetChecked(MenuItem_ScreenRot[ScreenRotation], 1); uiMenuItemSetChecked(MenuItem_ScreenLayout[ScreenLayout], 1); uiMenuItemSetChecked(MenuItem_ScreenSizing[ScreenSizing], 1); @@ -1221,7 +1580,9 @@ int main(int argc, char** argv) strncpy(ROMPath, file, 1023); ROMPath[1023] = '\0'; - if (NDS::LoadROM(ROMPath, Config::DirectBoot)) + SetupSRAMPath(); + + if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot)) Run(); } } diff --git a/src/melon_fopen.h b/src/melon_fopen.h index 8e79e8b..a45ad97 100644 --- a/src/melon_fopen.h +++ b/src/melon_fopen.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/types.h b/src/types.h index 8a6c7e3..1c4f140 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/version.h b/src/version.h index 9e2d867..25156b0 100644 --- a/src/version.h +++ b/src/version.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -19,7 +19,7 @@ #ifndef VERSION_H #define VERSION_H -#define MELONDS_VERSION "0.6b" +#define MELONDS_VERSION "0.7" #define MELONDS_URL "http://melonds.kuribo64.net/" |