aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ARM.cpp23
-rw-r--r--src/ARM.h4
-rw-r--r--src/ARMInterpreter.cpp2
-rw-r--r--src/ARMInterpreter.h2
-rw-r--r--src/ARMInterpreter_ALU.cpp2
-rw-r--r--src/ARMInterpreter_ALU.h2
-rw-r--r--src/ARMInterpreter_Branch.cpp2
-rw-r--r--src/ARMInterpreter_Branch.h2
-rw-r--r--src/ARMInterpreter_LoadStore.cpp2
-rw-r--r--src/ARMInterpreter_LoadStore.h2
-rw-r--r--src/ARM_InstrTable.h2
-rw-r--r--src/CP15.cpp21
-rw-r--r--src/CP15.h4
-rw-r--r--src/Config.cpp24
-rw-r--r--src/Config.h4
-rw-r--r--src/DMA.cpp25
-rw-r--r--src/DMA.h4
-rw-r--r--src/FIFO.h12
-rw-r--r--src/GPU.cpp54
-rw-r--r--src/GPU.h8
-rw-r--r--src/GPU2D.cpp64
-rw-r--r--src/GPU2D.h4
-rw-r--r--src/GPU3D.cpp187
-rw-r--r--src/GPU3D.h5
-rw-r--r--src/GPU3D_Soft.cpp52
-rw-r--r--src/NDS.cpp198
-rw-r--r--src/NDS.h8
-rw-r--r--src/NDSCart.cpp132
-rw-r--r--src/NDSCart.h11
-rw-r--r--src/Platform.h2
-rw-r--r--src/RTC.cpp26
-rw-r--r--src/RTC.h4
-rw-r--r--src/SPI.cpp54
-rw-r--r--src/SPI.h7
-rw-r--r--src/SPU.cpp66
-rw-r--r--src/SPU.h8
-rw-r--r--src/Savestate.cpp275
-rw-r--r--src/Savestate.h55
-rw-r--r--src/Wifi.cpp36
-rw-r--r--src/Wifi.h5
-rw-r--r--src/WifiAP.cpp2
-rw-r--r--src/WifiAP.h2
-rw-r--r--src/libui_sdl/DlgEmuSettings.cpp2
-rw-r--r--src/libui_sdl/DlgEmuSettings.h2
-rw-r--r--src/libui_sdl/DlgInputConfig.cpp4
-rw-r--r--src/libui_sdl/DlgInputConfig.h2
-rw-r--r--src/libui_sdl/Platform.cpp2
-rw-r--r--src/libui_sdl/libui/unix/area.c4
-rw-r--r--src/libui_sdl/libui/unix/menu.c17
-rw-r--r--src/libui_sdl/main.cpp401
-rw-r--r--src/melon_fopen.h2
-rw-r--r--src/types.h2
-rw-r--r--src/version.h4
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)
diff --git a/src/ARM.h b/src/ARM.h
index 564c7bb..92d47bb 100644
--- a/src/ARM.h
+++ b/src/ARM.h
@@ -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()
{
diff --git a/src/CP15.h b/src/CP15.h
index eedea10..1b92191 100644
--- a/src/CP15.h
+++ b/src/CP15.h
@@ -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;
diff --git a/src/DMA.h b/src/DMA.h
index 11ee1ad..6157431 100644
--- a/src/DMA.h
+++ b/src/DMA.h
@@ -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();
diff --git a/src/FIFO.h b/src/FIFO.h
index 4130b85..7ce67e9 100644
--- a/src/FIFO.h
+++ b/src/FIFO.h
@@ -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
//
diff --git a/src/GPU.h b/src/GPU.h
index d45f383..2ef33d9 100644
--- a/src/GPU.h
+++ b/src/GPU.h
@@ -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()
{
diff --git a/src/NDS.h b/src/NDS.h
index 137c774..1bf8508 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -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)
{
diff --git a/src/RTC.h b/src/RTC.h
index 6ada5c1..ebfcda8 100644
--- a/src/RTC.h
+++ b/src/RTC.h
@@ -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)
{
diff --git a/src/SPI.h b/src/SPI.h
index d122da8..f2f8e0a 100644
--- a/src/SPI.h
+++ b/src/SPI.h
@@ -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++)
diff --git a/src/SPU.h b/src/SPU.h
index 85c27ee..d340a72 100644
--- a/src/SPU.h
+++ b/src/SPU.h
@@ -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)
{
diff --git a/src/Wifi.h b/src/Wifi.h
index 72334e0..72517a1 100644
--- a/src/Wifi.h
+++ b/src/Wifi.h
@@ -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/"