aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ARCodeFile.cpp15
-rw-r--r--src/ARCodeFile.h11
-rw-r--r--src/AREngine.cpp41
-rw-r--r--src/AREngine.h3
-rw-r--r--src/ARM.cpp2
-rw-r--r--src/ARM.h2
-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/ARMJIT.cpp31
-rw-r--r--src/ARMJIT.h3
-rw-r--r--src/ARMJIT_A64/ARMJIT_ALU.cpp2
-rw-r--r--src/ARMJIT_A64/ARMJIT_Branch.cpp2
-rw-r--r--src/ARMJIT_A64/ARMJIT_Compiler.cpp2
-rw-r--r--src/ARMJIT_A64/ARMJIT_Compiler.h2
-rw-r--r--src/ARMJIT_A64/ARMJIT_Linkage.S2
-rw-r--r--src/ARMJIT_A64/ARMJIT_LoadStore.cpp2
-rw-r--r--src/ARMJIT_Compiler.h2
-rw-r--r--src/ARMJIT_Internal.h2
-rw-r--r--src/ARMJIT_Memory.cpp44
-rw-r--r--src/ARMJIT_Memory.h4
-rw-r--r--src/ARMJIT_RegisterCache.h5
-rw-r--r--src/ARMJIT_x64/ARMJIT_ALU.cpp6
-rw-r--r--src/ARMJIT_x64/ARMJIT_Branch.cpp4
-rw-r--r--src/ARMJIT_x64/ARMJIT_Compiler.cpp2
-rw-r--r--src/ARMJIT_x64/ARMJIT_Compiler.h6
-rw-r--r--src/ARMJIT_x64/ARMJIT_GenOffsets.cpp2
-rw-r--r--src/ARMJIT_x64/ARMJIT_Linkage.S2
-rw-r--r--src/ARMJIT_x64/ARMJIT_LoadStore.cpp2
-rw-r--r--src/ARMJIT_x64/ARMJIT_Offsets.h2
-rw-r--r--src/ARM_InstrInfo.cpp2
-rw-r--r--src/ARM_InstrInfo.h6
-rw-r--r--src/ARM_InstrTable.h2
-rw-r--r--src/CMakeLists.txt247
-rw-r--r--src/CP15.cpp5
-rw-r--r--src/CRC32.cpp6
-rw-r--r--src/CRC32.h4
-rw-r--r--src/DMA.cpp2
-rw-r--r--src/DMA.h5
-rw-r--r--src/DMA_Timings.h14
-rw-r--r--src/DSi.cpp408
-rw-r--r--src/DSi.h11
-rw-r--r--src/DSiCrypto.cpp17
-rw-r--r--src/DSiCrypto.h24
-rw-r--r--src/DSi_AES.cpp39
-rw-r--r--src/DSi_AES.h5
-rw-r--r--src/DSi_Camera.cpp745
-rw-r--r--src/DSi_Camera.h75
-rw-r--r--src/DSi_DSP.cpp22
-rw-r--r--src/DSi_DSP.h12
-rw-r--r--src/DSi_I2C.cpp52
-rw-r--r--src/DSi_I2C.h22
-rw-r--r--src/DSi_NAND.cpp112
-rw-r--r--src/DSi_NAND.h6
-rw-r--r--src/DSi_NDMA.cpp49
-rw-r--r--src/DSi_NDMA.h3
-rw-r--r--src/DSi_NWifi.cpp41
-rw-r--r--src/DSi_NWifi.h5
-rw-r--r--src/DSi_SD.cpp97
-rw-r--r--src/DSi_SD.h13
-rw-r--r--src/DSi_SPI_TSC.cpp11
-rw-r--r--src/DSi_SPI_TSC.h5
-rw-r--r--src/FATStorage.cpp2
-rw-r--r--src/FATStorage.h2
-rw-r--r--src/FIFO.h3
-rw-r--r--src/FreeBIOS.h582
-rw-r--r--src/GBACart.cpp366
-rw-r--r--src/GBACart.h64
-rw-r--r--src/GPU.cpp21
-rw-r--r--src/GPU.h2
-rw-r--r--src/GPU2D.cpp55
-rw-r--r--src/GPU2D.h2
-rw-r--r--src/GPU2D_Soft.cpp59
-rw-r--r--src/GPU2D_Soft.h8
-rw-r--r--src/GPU3D.cpp2
-rw-r--r--src/GPU3D.h2
-rw-r--r--src/GPU3D_OpenGL.cpp2
-rw-r--r--src/GPU3D_OpenGL.h2
-rw-r--r--src/GPU3D_OpenGL_shaders.h2
-rw-r--r--src/GPU3D_Soft.cpp2
-rw-r--r--src/GPU3D_Soft.h2
-rw-r--r--src/GPU_OpenGL.cpp6
-rw-r--r--src/GPU_OpenGL.h2
-rw-r--r--src/GPU_OpenGL_shaders.h15
-rw-r--r--src/NDS.cpp317
-rw-r--r--src/NDS.h35
-rw-r--r--src/NDSCart.cpp582
-rw-r--r--src/NDSCart.h53
-rw-r--r--src/NDSCart_SRAMManager.cpp184
-rw-r--r--src/NDS_Header.h2
-rw-r--r--src/NonStupidBitfield.h2
-rw-r--r--src/OpenGLSupport.cpp2
-rw-r--r--src/OpenGLSupport.h2
-rw-r--r--src/Platform.h38
-rw-r--r--src/ROMList.h4
-rw-r--r--src/RTC.cpp2
-rw-r--r--src/RTC.h2
-rw-r--r--src/SPI.cpp136
-rw-r--r--src/SPI.h11
-rw-r--r--src/SPU.cpp8
-rw-r--r--src/SPU.h4
-rw-r--r--src/Savestate.cpp11
-rw-r--r--src/Savestate.h5
-rw-r--r--src/Wifi.cpp1372
-rw-r--r--src/Wifi.h37
-rw-r--r--src/WifiAP.cpp16
-rw-r--r--src/WifiAP.h5
-rw-r--r--src/frontend/FrontendUtil.h92
-rw-r--r--src/frontend/SharedConfig.h42
-rw-r--r--src/frontend/Util_Audio.cpp2
-rw-r--r--src/frontend/Util_ROM.cpp845
-rw-r--r--src/frontend/Util_Video.cpp2
-rw-r--r--src/frontend/mic_blow.h2
-rw-r--r--src/frontend/qt_sdl/ArchiveUtil.cpp96
-rw-r--r--src/frontend/qt_sdl/ArchiveUtil.h11
-rw-r--r--src/frontend/qt_sdl/AudioSettingsDialog.cpp20
-rw-r--r--src/frontend/qt_sdl/AudioSettingsDialog.h2
-rw-r--r--src/frontend/qt_sdl/AudioSettingsDialog.ui11
-rw-r--r--src/frontend/qt_sdl/CMakeLists.txt125
-rw-r--r--src/frontend/qt_sdl/CameraManager.cpp612
-rw-r--r--src/frontend/qt_sdl/CameraManager.h134
-rw-r--r--src/frontend/qt_sdl/CameraSettingsDialog.cpp304
-rw-r--r--src/frontend/qt_sdl/CameraSettingsDialog.h108
-rw-r--r--src/frontend/qt_sdl/CameraSettingsDialog.ui170
-rw-r--r--src/frontend/qt_sdl/CheatsDialog.cpp31
-rw-r--r--src/frontend/qt_sdl/CheatsDialog.h2
-rw-r--r--src/frontend/qt_sdl/Config.cpp536
-rw-r--r--src/frontend/qt_sdl/Config.h142
-rw-r--r--src/frontend/qt_sdl/EmuSettingsDialog.cpp119
-rw-r--r--src/frontend/qt_sdl/EmuSettingsDialog.h2
-rw-r--r--src/frontend/qt_sdl/FirmwareSettingsDialog.cpp43
-rw-r--r--src/frontend/qt_sdl/FirmwareSettingsDialog.h6
-rw-r--r--src/frontend/qt_sdl/FirmwareSettingsDialog.ui13
-rw-r--r--src/frontend/qt_sdl/Input.cpp2
-rw-r--r--src/frontend/qt_sdl/Input.h2
-rw-r--r--src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp9
-rw-r--r--src/frontend/qt_sdl/InputConfig/InputConfigDialog.h2
-rw-r--r--src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui1115
-rw-r--r--src/frontend/qt_sdl/InputConfig/MapButton.h29
-rw-r--r--src/frontend/qt_sdl/InterfaceSettingsDialog.cpp2
-rw-r--r--src/frontend/qt_sdl/InterfaceSettingsDialog.h2
-rw-r--r--src/frontend/qt_sdl/LAN_PCap.cpp16
-rw-r--r--src/frontend/qt_sdl/LAN_PCap.h2
-rw-r--r--src/frontend/qt_sdl/LAN_Socket.cpp2
-rw-r--r--src/frontend/qt_sdl/LAN_Socket.h2
-rw-r--r--src/frontend/qt_sdl/LocalMP.cpp634
-rw-r--r--src/frontend/qt_sdl/LocalMP.h (renamed from src/NDSCart_SRAMManager.h)32
-rw-r--r--src/frontend/qt_sdl/MPSettingsDialog.cpp73
-rw-r--r--src/frontend/qt_sdl/MPSettingsDialog.h65
-rw-r--r--src/frontend/qt_sdl/MPSettingsDialog.ui142
-rw-r--r--src/frontend/qt_sdl/OSD.cpp6
-rw-r--r--src/frontend/qt_sdl/OSD.h2
-rw-r--r--src/frontend/qt_sdl/OSD_shaders.h2
-rw-r--r--src/frontend/qt_sdl/PathSettingsDialog.cpp126
-rw-r--r--src/frontend/qt_sdl/PathSettingsDialog.h67
-rw-r--r--src/frontend/qt_sdl/PathSettingsDialog.ui156
-rw-r--r--src/frontend/qt_sdl/Platform.cpp290
-rw-r--r--src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp154
-rw-r--r--src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h77
-rw-r--r--src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui274
-rw-r--r--src/frontend/qt_sdl/PowerManagement/resources/battery.qrc9
-rw-r--r--src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg171
-rw-r--r--src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg171
-rw-r--r--src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg171
-rw-r--r--src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg171
-rw-r--r--src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg171
-rw-r--r--src/frontend/qt_sdl/QPathInput.h2
-rw-r--r--src/frontend/qt_sdl/RAMInfoDialog.cpp302
-rw-r--r--src/frontend/qt_sdl/RAMInfoDialog.h161
-rw-r--r--src/frontend/qt_sdl/RAMInfoDialog.ui237
-rw-r--r--src/frontend/qt_sdl/ROMInfoDialog.cpp20
-rw-r--r--src/frontend/qt_sdl/ROMInfoDialog.h6
-rw-r--r--src/frontend/qt_sdl/ROMInfoDialog.ui65
-rw-r--r--src/frontend/qt_sdl/ROMManager.cpp875
-rw-r--r--src/frontend/qt_sdl/ROMManager.h66
-rw-r--r--src/frontend/qt_sdl/SaveManager.cpp194
-rw-r--r--src/frontend/qt_sdl/SaveManager.h70
-rw-r--r--src/frontend/qt_sdl/TitleManagerDialog.cpp25
-rw-r--r--src/frontend/qt_sdl/TitleManagerDialog.h4
-rw-r--r--src/frontend/qt_sdl/VideoSettingsDialog.cpp2
-rw-r--r--src/frontend/qt_sdl/VideoSettingsDialog.h2
-rw-r--r--src/frontend/qt_sdl/WifiSettingsDialog.cpp21
-rw-r--r--src/frontend/qt_sdl/WifiSettingsDialog.h2
-rw-r--r--src/frontend/qt_sdl/WifiSettingsDialog.ui133
-rw-r--r--src/frontend/qt_sdl/font.h2
-rw-r--r--src/frontend/qt_sdl/main.cpp1311
-rw-r--r--src/frontend/qt_sdl/main.h74
-rw-r--r--src/frontend/qt_sdl/main_shaders.h2
-rw-r--r--src/frontend/qt_sdl/sem_timedwait.cpp488
-rw-r--r--src/frontend/qt_sdl/sem_timedwait.h8
-rw-r--r--src/melonDLDI.h6
-rw-r--r--src/teakra/CMakeLists.txt1
-rw-r--r--src/teakra/externals/catch/catch.hpp6939
-rw-r--r--src/teakra/src/interpreter.h1
-rw-r--r--src/teakra/src/teakra.cpp2
-rw-r--r--src/types.h2
-rw-r--r--src/version.h4
202 files changed, 18286 insertions, 6681 deletions
diff --git a/src/ARCodeFile.cpp b/src/ARCodeFile.cpp
index d79cd39..a95ebbd 100644
--- a/src/ARCodeFile.cpp
+++ b/src/ARCodeFile.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -26,10 +26,9 @@
// TODO: more user-friendly error reporting
-ARCodeFile::ARCodeFile(const char* filename)
+ARCodeFile::ARCodeFile(std::string filename)
{
- memset(Filename, 0, sizeof(Filename));
- strncpy(Filename, filename, 1023);
+ Filename = filename;
Error = false;
@@ -91,7 +90,7 @@ bool ARCodeFile::Load()
if (isincat) Categories.push_back(curcat);
isincat = true;
- memcpy(curcat.Name, catname, 128);
+ curcat.Name = catname;
curcat.Codes.clear();
}
else if (!strncasecmp(start, "CODE", 4))
@@ -118,7 +117,7 @@ bool ARCodeFile::Load()
if (isincode) curcat.Codes.push_back(curcode);
isincode = true;
- memcpy(curcode.Name, codename, 128);
+ curcode.Name = codename;
curcode.Enabled = enable!=0;
curcode.CodeLen = 0;
}
@@ -172,12 +171,12 @@ bool ARCodeFile::Save()
ARCodeCat& cat = *it;
if (it != Categories.begin()) fprintf(f, "\r\n");
- fprintf(f, "CAT %s\r\n\r\n", cat.Name);
+ fprintf(f, "CAT %s\r\n\r\n", cat.Name.c_str());
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
{
ARCode& code = *jt;
- fprintf(f, "CODE %d %s\r\n", code.Enabled, code.Name);
+ fprintf(f, "CODE %d %s\r\n", code.Enabled, code.Name.c_str());
for (u32 i = 0; i < code.CodeLen; i+=2)
{
diff --git a/src/ARCodeFile.h b/src/ARCodeFile.h
index a3c36e4..f9a1c13 100644
--- a/src/ARCodeFile.h
+++ b/src/ARCodeFile.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,13 +19,14 @@
#ifndef ARCODEFILE_H
#define ARCODEFILE_H
+#include <string>
#include <list>
#include "types.h"
struct ARCode
{
- char Name[128];
+ std::string Name;
bool Enabled;
u32 CodeLen;
u32 Code[2*64];
@@ -35,7 +36,7 @@ typedef std::list<ARCode> ARCodeList;
struct ARCodeCat
{
- char Name[128];
+ std::string Name;
ARCodeList Codes;
};
@@ -45,7 +46,7 @@ typedef std::list<ARCodeCat> ARCodeCatList;
class ARCodeFile
{
public:
- ARCodeFile(const char* filename);
+ ARCodeFile(std::string filename);
~ARCodeFile();
bool Error;
@@ -56,7 +57,7 @@ public:
ARCodeCatList Categories;
private:
- char Filename[1024];
+ std::string Filename;
};
#endif // ARCODEFILE_H
diff --git a/src/AREngine.cpp b/src/AREngine.cpp
index ec3b70e..0605d28 100644
--- a/src/AREngine.cpp
+++ b/src/AREngine.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -50,8 +50,6 @@ void DeInit()
void Reset()
{
- CodeFile = nullptr;
-
if (NDS::ConsoleType == 1)
{
BusRead8 = DSi::ARM7Read8;
@@ -73,6 +71,11 @@ void Reset()
}
+ARCodeFile* GetCodeFile()
+{
+ return CodeFile;
+}
+
void SetCodeFile(ARCodeFile* file)
{
CodeFile = file;
@@ -145,7 +148,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u32 chk = BusRead32(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u32 chk = BusRead32(addr);
cond = (b > chk) ? 1:0;
}
@@ -156,7 +161,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u32 chk = BusRead32(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u32 chk = BusRead32(addr);
cond = (b < chk) ? 1:0;
}
@@ -167,7 +174,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u32 chk = BusRead32(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u32 chk = BusRead32(addr);
cond = (b == chk) ? 1:0;
}
@@ -178,7 +187,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u32 chk = BusRead32(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u32 chk = BusRead32(addr);
cond = (b != chk) ? 1:0;
}
@@ -189,7 +200,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u16 val = BusRead16(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@@ -202,7 +215,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u16 val = BusRead16(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@@ -215,7 +230,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u16 val = BusRead16(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@@ -228,7 +245,9 @@ void RunCheat(ARCode& arcode)
condstack <<= 1;
condstack |= cond;
- u16 val = BusRead16(a & 0x0FFFFFFF);
+ u32 addr = a & 0x0FFFFFFF;
+ if (!addr) addr = offset;
+ u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
diff --git a/src/AREngine.h b/src/AREngine.h
index c142711..3e6e9dd 100644
--- a/src/AREngine.h
+++ b/src/AREngine.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -28,6 +28,7 @@ bool Init();
void DeInit();
void Reset();
+ARCodeFile* GetCodeFile();
void SetCodeFile(ARCodeFile* file);
void RunCheats();
diff --git a/src/ARM.cpp b/src/ARM.cpp
index 55f60bf..e19cb00 100644
--- a/src/ARM.cpp
+++ b/src/ARM.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARM.h b/src/ARM.h
index e00bf34..6cfb677 100644
--- a/src/ARM.h
+++ b/src/ARM.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp
index dfcb801..aaa1e7b 100644
--- a/src/ARMInterpreter.cpp
+++ b/src/ARMInterpreter.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter.h b/src/ARMInterpreter.h
index 7b82593..9a0fc28 100644
--- a/src/ARMInterpreter.h
+++ b/src/ARMInterpreter.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp
index 92d1571..3949bb1 100644
--- a/src/ARMInterpreter_ALU.cpp
+++ b/src/ARMInterpreter_ALU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter_ALU.h b/src/ARMInterpreter_ALU.h
index a904462..8dcc311 100644
--- a/src/ARMInterpreter_ALU.h
+++ b/src/ARMInterpreter_ALU.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp
index f810ced..848c03b 100644
--- a/src/ARMInterpreter_Branch.cpp
+++ b/src/ARMInterpreter_Branch.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter_Branch.h b/src/ARMInterpreter_Branch.h
index 70b0775..775e895 100644
--- a/src/ARMInterpreter_Branch.h
+++ b/src/ARMInterpreter_Branch.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp
index 1c81900..e7b83eb 100644
--- a/src/ARMInterpreter_LoadStore.cpp
+++ b/src/ARMInterpreter_LoadStore.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMInterpreter_LoadStore.h b/src/ARMInterpreter_LoadStore.h
index 80f33b2..efd4f18 100644
--- a/src/ARMInterpreter_LoadStore.h
+++ b/src/ARMInterpreter_LoadStore.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp
index 2f9cad9..32f20d5 100644
--- a/src/ARMJIT.cpp
+++ b/src/ARMJIT.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -1086,11 +1086,34 @@ void InvalidateByAddr(u32 localAddr)
void CheckAndInvalidateITCM()
{
- for (u32 i = 0; i < ITCMPhysicalSize; i+=16)
+ for (u32 i = 0; i < ITCMPhysicalSize; i+=512)
{
- if (CodeIndexITCM[i / 512].Code & (1 << ((i & 0x1FF) / 16)))
+ if (CodeIndexITCM[i / 512].Code)
{
- InvalidateByAddr(i | (ARMJIT_Memory::memregion_ITCM << 27));
+ // maybe using bitscan would be better here?
+ // The thing is that in densely populated sets
+ // The old fashioned way can actually be faster
+ for (u32 j = 0; j < 512; j += 16)
+ {
+ if (CodeIndexITCM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
+ InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_ITCM << 27));
+ }
+ }
+ }
+}
+
+void CheckAndInvalidateWVRAM(int bank)
+{
+ u32 start = bank == 1 ? 0x20000 : 0;
+ for (u32 i = start; i < start+0x20000; i+=512)
+ {
+ if (CodeIndexARM7WVRAM[i / 512].Code)
+ {
+ for (u32 j = 0; j < 512; j += 16)
+ {
+ if (CodeIndexARM7WVRAM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
+ InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_VWRAM << 27));
+ }
}
}
}
diff --git a/src/ARMJIT.h b/src/ARMJIT.h
index 09ba5d2..97c79cd 100644
--- a/src/ARMJIT.h
+++ b/src/ARMJIT.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -44,6 +44,7 @@ void DeInit();
void Reset();
void CheckAndInvalidateITCM();
+void CheckAndInvalidateWVRAM(int bank);
void InvalidateByAddr(u32 pseudoPhysical);
diff --git a/src/ARMJIT_A64/ARMJIT_ALU.cpp b/src/ARMJIT_A64/ARMJIT_ALU.cpp
index 1bd427f..3d52f28 100644
--- a/src/ARMJIT_A64/ARMJIT_ALU.cpp
+++ b/src/ARMJIT_A64/ARMJIT_ALU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_A64/ARMJIT_Branch.cpp b/src/ARMJIT_A64/ARMJIT_Branch.cpp
index 2f640c8..1cdefea 100644
--- a/src/ARMJIT_A64/ARMJIT_Branch.cpp
+++ b/src/ARMJIT_A64/ARMJIT_Branch.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp
index 1ad24b1..cc5275c 100644
--- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp
+++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h
index 235155f..5045cb5 100644
--- a/src/ARMJIT_A64/ARMJIT_Compiler.h
+++ b/src/ARMJIT_A64/ARMJIT_Compiler.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_A64/ARMJIT_Linkage.S b/src/ARMJIT_A64/ARMJIT_Linkage.S
index 37a83e9..c1ecd7c 100644
--- a/src/ARMJIT_A64/ARMJIT_Linkage.S
+++ b/src/ARMJIT_A64/ARMJIT_Linkage.S
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp
index 7ce6d24..f159d88 100644
--- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp
+++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_Compiler.h b/src/ARMJIT_Compiler.h
index 200972d..c5348f4 100644
--- a/src/ARMJIT_Compiler.h
+++ b/src/ARMJIT_Compiler.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h
index 6988db0..fb80307 100644
--- a/src/ARMJIT_Internal.h
+++ b/src/ARMJIT_Internal.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp
index 0f1dd25..d8a8c18 100644
--- a/src/ARMJIT_Memory.cpp
+++ b/src/ARMJIT_Memory.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -55,7 +55,7 @@
and map the memory regions as they're structured on the DS
in it.
- On most systems you have a single piece of main ram,
+ On most systems you have a single piece of main ram,
maybe some video ram and faster cache RAM and that's about it.
Here we have not only a lot more different memory regions,
but also two address spaces. Not only that but they all have
@@ -119,7 +119,7 @@ void HandleFault(u64 pc, u64 lr, u64 fp, u64 faultAddr, u32 desc);
extern "C"
{
-
+
void ARM_RestoreContext(u64* registers) __attribute__((noreturn));
extern char __start__;
@@ -146,7 +146,7 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx)
{
integerRegisters[32] = (u64)desc.FaultPC;
- ARM_RestoreContext(integerRegisters);
+ ARM_RestoreContext(integerRegisters);
}
HandleFault(ctx->pc.x, ctx->lr.x, ctx->fp.x, ctx->far.x, ctx->error_desc);
@@ -311,7 +311,7 @@ bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size)
{
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
#ifdef __SWITCH__
- Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(),
+ Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(),
(u64)(MemoryBaseCodeMem + offset), size));
return R_SUCCEEDED(r);
#elif defined(_WIN32)
@@ -452,7 +452,7 @@ void SetCodeProtection(int region, u32 offset, bool protect)
u32 effectiveAddr = mapping.Addr + (offset - mapping.LocalOffset);
if (mapping.Num == 0
- && region != memregion_DTCM
+ && region != memregion_DTCM
&& (effectiveAddr & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase)
continue;
@@ -621,7 +621,7 @@ bool MapAtAddress(u32 addr)
// this overcomplicated piece of code basically just finds whole pieces of code memory
// which can be mapped/protected
- u32 offset = 0;
+ u32 offset = 0;
bool skipDTCM = num == 0 && region != memregion_DTCM;
while (offset < mirrorSize)
{
@@ -700,10 +700,10 @@ void Init()
virtmemLock();
MemoryBaseCodeMem = (u8*)virtmemFindCodeMemory(MemoryTotalSize, 0x1000);
- bool succeded = R_SUCCEEDED(svcMapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem,
+ bool succeded = R_SUCCEEDED(svcMapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem,
(u64)MemoryBase, MemoryTotalSize));
assert(succeded);
- succeded = R_SUCCEEDED(svcSetProcessMemoryPermission(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem,
+ succeded = R_SUCCEEDED(svcSetProcessMemoryPermission(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem,
MemoryTotalSize, Perm_Rw));
assert(succeded);
@@ -740,7 +740,7 @@ void Init()
u8* basePtr = MemoryBase;
#else
// this used to be allocated with three different mmaps
- // The idea was to give the OS more freedom where to position the buffers,
+ // The idea was to give the OS more freedom where to position the buffers,
// but something was bad about this so instead we take this vmem eating monster
// which seems to work better.
MemoryBase = (u8*)mmap(NULL, AddrSpaceSize*4, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
@@ -855,7 +855,7 @@ bool IsFastmemCompatible(int region)
TODO: with some hacks, the smaller shared WRAM regions
could be mapped in some occaisons as well
*/
- if (region == memregion_DTCM
+ if (region == memregion_DTCM
|| region == memregion_SharedWRAM
|| region == memregion_NewSharedWRAM_B
|| region == memregion_NewSharedWRAM_C)
@@ -1071,7 +1071,7 @@ int ClassifyAddress9(u32 addr)
{
return memregion_DTCM;
}
- else
+ else
{
if (NDS::ConsoleType == 1 && addr >= 0xFFFF0000 && !(DSi::SCFG_BIOS & (1<<1)))
{
@@ -1107,6 +1107,8 @@ int ClassifyAddress9(u32 addr)
return memregion_IO9;
case 0x06000000:
return memregion_VRAM;
+ case 0x0C000000:
+ return (NDS::ConsoleType==1) ? memregion_MainRAM : memregion_Other;
default:
return memregion_Other;
}
@@ -1156,7 +1158,9 @@ int ClassifyAddress7(u32 addr)
case 0x06000000:
case 0x06800000:
return memregion_VWRAM;
-
+ case 0x0C000000:
+ case 0x0C800000:
+ return (NDS::ConsoleType==1) ? memregion_MainRAM : memregion_Other;
default:
return memregion_Other;
}
@@ -1219,8 +1223,8 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size)
{
switch (size | store)
{
- case 8: return (void*)GPU3D::Read8;
- case 9: return (void*)GPU3D::Write8;
+ case 8: return (void*)GPU3D::Read8;
+ case 9: return (void*)GPU3D::Write8;
case 16: return (void*)GPU3D::Read16;
case 17: return (void*)GPU3D::Write16;
case 32: return (void*)GPU3D::Read32;
@@ -1256,7 +1260,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size)
case 0x06000000:
switch (size | store)
{
- case 8: return (void*)VRAMRead<u8>;
+ case 8: return (void*)VRAMRead<u8>;
case 9: return NULL;
case 16: return (void*)VRAMRead<u16>;
case 17: return (void*)VRAMWrite<u16>;
@@ -1275,8 +1279,8 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size)
{
switch (size | store)
{
- case 8: return (void*)SPU::Read8;
- case 9: return (void*)SPU::Write8;
+ case 8: return (void*)SPU::Read8;
+ case 9: return (void*)SPU::Write8;
case 16: return (void*)SPU::Read16;
case 17: return (void*)SPU::Write16;
case 32: return (void*)SPU::Read32;
@@ -1289,7 +1293,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size)
switch (size | store)
{
case 8: return (void*)NDS::ARM7IORead8;
- case 9: return (void*)NDS::ARM7IOWrite8;
+ case 9: return (void*)NDS::ARM7IOWrite8;
case 16: return (void*)NDS::ARM7IORead16;
case 17: return (void*)NDS::ARM7IOWrite16;
case 32: return (void*)NDS::ARM7IORead32;
@@ -1301,7 +1305,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size)
switch (size | store)
{
case 8: return (void*)DSi::ARM7IORead8;
- case 9: return (void*)DSi::ARM7IOWrite8;
+ case 9: return (void*)DSi::ARM7IOWrite8;
case 16: return (void*)DSi::ARM7IORead16;
case 17: return (void*)DSi::ARM7IOWrite16;
case 32: return (void*)DSi::ARM7IORead32;
diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h
index a1e089e..726b710 100644
--- a/src/ARMJIT_Memory.h
+++ b/src/ARMJIT_Memory.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -78,4 +78,4 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size);
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h
index 1eb4c4b..e0c4a15 100644
--- a/src/ARMJIT_RegisterCache.h
+++ b/src/ARMJIT_RegisterCache.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@@ -20,6 +20,7 @@
#define ARMJIT_REGCACHE_H
#include "ARMJIT.h"
+#include "ARMJIT_Internal.h"
// TODO: replace this in the future
#include "dolphin/BitSet.h"
@@ -41,7 +42,7 @@ public:
{
for (int i = 0; i < 16; i++)
Mapping[i] = (Reg)-1;
-
+
PCAllocatableAsSrc = ~(pcAllocatableAsSrc
? 0
: (1 << 15));
diff --git a/src/ARMJIT_x64/ARMJIT_ALU.cpp b/src/ARMJIT_x64/ARMJIT_ALU.cpp
index 16fb22a..0a364ea 100644
--- a/src/ARMJIT_x64/ARMJIT_ALU.cpp
+++ b/src/ARMJIT_x64/ARMJIT_ALU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -364,7 +364,7 @@ void Compiler::A_Comp_Mul_Long()
{
BSR(32, RSCRATCH, R(RSCRATCH3));
}
-
+
SHR(32, R(RSCRATCH), Imm8(3));
SetJumpTarget(zeroBSR); // fortunately that's even right
Comp_AddCycles_CI(RSCRATCH, 2);
@@ -617,7 +617,7 @@ void Compiler::T_Comp_AddSub_()
int op = (CurInstr.Instr >> 9) & 0x3;
OpArg rn = op >= 2 ? Imm32((CurInstr.Instr >> 6) & 0x7) : MapReg(CurInstr.T_Reg(6));
-
+
Comp_AddCycles_C();
// special case for thumb mov being alias to add rd, rn, #0
diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp
index a89cf99..8451665 100644
--- a/src/ARMJIT_x64/ARMJIT_Branch.cpp
+++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -30,7 +30,7 @@ int squeezePointer(T* ptr)
assert((T*)((u64)truncated) == ptr);
return truncated;
}
-
+
void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
{
// we can simplify constant branches by a lot
diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
index 6fffcec..fa015f4 100644
--- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp
+++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h
index 5046c88..f5817a9 100644
--- a/src/ARMJIT_x64/ARMJIT_Compiler.h
+++ b/src/ARMJIT_x64/ARMJIT_Compiler.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -121,7 +121,7 @@ public:
void A_Comp_Mul_Long();
void A_Comp_CLZ();
-
+
void A_Comp_MemWB();
void A_Comp_MemHalf();
void A_Comp_LDM_STM();
@@ -170,7 +170,7 @@ public:
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
- void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
+ void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
void Comp_ArithTriOpReverse(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
diff --git a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp
index 6d0dd21..0961fdd 100644
--- a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp
+++ b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARMJIT_x64/ARMJIT_Linkage.S b/src/ARMJIT_x64/ARMJIT_Linkage.S
index 650c93b..fe7b143 100644
--- a/src/ARMJIT_x64/ARMJIT_Linkage.S
+++ b/src/ARMJIT_x64/ARMJIT_Linkage.S
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
index 07cade4..7b28e68 100644
--- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
+++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARMJIT_x64/ARMJIT_Offsets.h b/src/ARMJIT_x64/ARMJIT_Offsets.h
index ade28b6..9d2a952 100644
--- a/src/ARMJIT_x64/ARMJIT_Offsets.h
+++ b/src/ARMJIT_x64/ARMJIT_Offsets.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/ARM_InstrInfo.cpp b/src/ARM_InstrInfo.cpp
index afd11da..a7171ba 100644
--- a/src/ARM_InstrInfo.cpp
+++ b/src/ARM_InstrInfo.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/ARM_InstrInfo.h b/src/ARM_InstrInfo.h
index 2f22e8e..0104027 100644
--- a/src/ARM_InstrInfo.h
+++ b/src/ARM_InstrInfo.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -222,7 +222,7 @@ enum
tk_POP,
tk_LDMIA,
tk_STMIA,
-
+
tk_BCOND,
tk_BX,
tk_BLX_REG,
@@ -278,4 +278,4 @@ Info Decode(bool thumb, u32 num, u32 instr);
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/ARM_InstrTable.h b/src/ARM_InstrTable.h
index 5f476f5..e4b2906 100644
--- a/src/ARM_InstrTable.h
+++ b/src/ARM_InstrTable.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d1f47dc..cdb0587 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,142 +1,145 @@
-project(core)
-
set (CMAKE_CXX_STANDARD 17)
+include(FixInterfaceIncludes)
+
add_library(core STATIC
- ARCodeFile.cpp
- AREngine.cpp
- ARM.cpp
- ARM_InstrTable.h
- ARMInterpreter.cpp
- ARMInterpreter_ALU.cpp
- ARMInterpreter_Branch.cpp
- ARMInterpreter_LoadStore.cpp
- CP15.cpp
- CRC32.cpp
- DMA.cpp
- DMA_Timings.h
- DSi.cpp
- DSi_AES.cpp
- DSi_Camera.cpp
- DSi_DSP.cpp
- DSi_I2C.cpp
- DSi_NAND.cpp
- DSi_NDMA.cpp
- DSi_NWifi.cpp
- DSi_SD.cpp
- DSi_SPI_TSC.cpp
- FATStorage.cpp
- FIFO.h
- GBACart.cpp
- GPU.cpp
- GPU2D.cpp
- GPU2D_Soft.cpp
- GPU3D.cpp
- GPU3D_Soft.cpp
- melonDLDI.h
- NDS.cpp
- NDSCart.cpp
- NDSCart_SRAMManager.cpp
- Platform.h
- ROMList.h
- FreeBIOS.h
- RTC.cpp
- Savestate.cpp
- SPI.cpp
- SPU.cpp
- types.h
- version.h
- Wifi.cpp
- WifiAP.cpp
-
- fatfs/diskio.c
- fatfs/ff.c
- fatfs/ffsystem.c
- fatfs/ffunicode.c
- fatfs/ffconf.h
-
- sha1/sha1.c
- tiny-AES-c/aes.c
- xxhash/xxhash.c
-)
+ ARCodeFile.cpp
+ AREngine.cpp
+ ARM.cpp
+ ARM_InstrTable.h
+ ARMInterpreter.cpp
+ ARMInterpreter_ALU.cpp
+ ARMInterpreter_Branch.cpp
+ ARMInterpreter_LoadStore.cpp
+ CP15.cpp
+ CRC32.cpp
+ DMA.cpp
+ DMA_Timings.h
+ DSi.cpp
+ DSi_AES.cpp
+ DSi_Camera.cpp
+ DSi_DSP.cpp
+ DSi_I2C.cpp
+ DSi_NAND.cpp
+ DSi_NDMA.cpp
+ DSi_NWifi.cpp
+ DSi_SD.cpp
+ DSi_SPI_TSC.cpp
+ FATStorage.cpp
+ FIFO.h
+ GBACart.cpp
+ GPU.cpp
+ GPU2D.cpp
+ GPU2D_Soft.cpp
+ GPU3D.cpp
+ GPU3D_Soft.cpp
+ melonDLDI.h
+ NDS.cpp
+ NDSCart.cpp
+ Platform.h
+ ROMList.h
+ FreeBIOS.h
+ RTC.cpp
+ Savestate.cpp
+ SPI.cpp
+ SPU.cpp
+ types.h
+ version.h
+ Wifi.cpp
+ WifiAP.cpp
+
+ fatfs/diskio.c
+ fatfs/ff.c
+ fatfs/ffsystem.c
+ fatfs/ffunicode.c
+ fatfs/ffconf.h
+
+ sha1/sha1.c
+ tiny-AES-c/aes.c
+ xxhash/xxhash.c)
if (ENABLE_OGLRENDERER)
- target_sources(core PRIVATE
- GPU_OpenGL.cpp
- GPU_OpenGL_shaders.h
- GPU3D_OpenGL.cpp
- GPU3D_OpenGL_shaders.h
- OpenGLSupport.cpp
- )
+ target_sources(core PRIVATE
+ GPU_OpenGL.cpp
+ GPU_OpenGL_shaders.h
+ GPU3D_OpenGL.cpp
+ GPU3D_OpenGL_shaders.h
+ OpenGLSupport.cpp)
endif()
if (ENABLE_JIT)
- enable_language(ASM)
-
- target_sources(core PRIVATE
- ARM_InstrInfo.cpp
-
- ARMJIT.cpp
- ARMJIT_Memory.cpp
-
- dolphin/CommonFuncs.cpp
- )
-
- if (ARCHITECTURE STREQUAL x86_64)
- target_sources(core PRIVATE
- dolphin/x64ABI.cpp
- dolphin/x64CPUDetect.cpp
- dolphin/x64Emitter.cpp
-
- ARMJIT_x64/ARMJIT_Compiler.cpp
- ARMJIT_x64/ARMJIT_ALU.cpp
- ARMJIT_x64/ARMJIT_LoadStore.cpp
- ARMJIT_x64/ARMJIT_Branch.cpp
-
- ARMJIT_x64/ARMJIT_Linkage.S
- )
- endif()
- if (ARCHITECTURE STREQUAL ARM64)
- target_sources(core PRIVATE
- dolphin/Arm64Emitter.cpp
- dolphin/MathUtil.cpp
-
- ARMJIT_A64/ARMJIT_Compiler.cpp
- ARMJIT_A64/ARMJIT_ALU.cpp
- ARMJIT_A64/ARMJIT_LoadStore.cpp
- ARMJIT_A64/ARMJIT_Branch.cpp
-
- ARMJIT_A64/ARMJIT_Linkage.S
- )
- endif()
+ enable_language(ASM)
+
+ target_sources(core PRIVATE
+ ARM_InstrInfo.cpp
+
+ ARMJIT.cpp
+ ARMJIT_Memory.cpp
+
+ dolphin/CommonFuncs.cpp)
+
+ if (ARCHITECTURE STREQUAL x86_64)
+ target_sources(core PRIVATE
+ dolphin/x64ABI.cpp
+ dolphin/x64CPUDetect.cpp
+ dolphin/x64Emitter.cpp
+
+ ARMJIT_x64/ARMJIT_Compiler.cpp
+ ARMJIT_x64/ARMJIT_ALU.cpp
+ ARMJIT_x64/ARMJIT_LoadStore.cpp
+ ARMJIT_x64/ARMJIT_Branch.cpp
+
+ ARMJIT_x64/ARMJIT_Linkage.S)
+ endif()
+ if (ARCHITECTURE STREQUAL ARM64)
+ target_sources(core PRIVATE
+ dolphin/Arm64Emitter.cpp
+ dolphin/MathUtil.cpp
+
+ ARMJIT_A64/ARMJIT_Compiler.cpp
+ ARMJIT_A64/ARMJIT_ALU.cpp
+ ARMJIT_A64/ARMJIT_LoadStore.cpp
+ ARMJIT_A64/ARMJIT_Branch.cpp
+
+ ARMJIT_A64/ARMJIT_Linkage.S)
+ endif()
endif()
add_subdirectory(teakra EXCLUDE_FROM_ALL)
-target_link_libraries(core teakra)
+target_link_libraries(core PRIVATE teakra)
+find_library(m MATH_LIBRARY)
+
+if (MATH_LIBRARY)
+ target_link_libraries(core PRIVATE ${MATH_LIBRARY})
+endif()
if (ENABLE_OGLRENDERER)
find_package(PkgConfig REQUIRED)
- pkg_check_modules(EPOXY REQUIRED epoxy)
-
- target_include_directories(core PRIVATE ${EPOXY_INCLUDE_DIRS})
- if (WIN32)
- target_link_libraries(core ole32 comctl32 ws2_32 ${EPOXY_LIBRARIES})
- elseif (APPLE)
- target_link_libraries(core ${EPOXY_LIBRARIES})
- else()
- target_link_libraries(core rt ${EPOXY_LIBRARIES})
+ pkg_check_modules(Epoxy REQUIRED IMPORTED_TARGET epoxy)
+ fix_interface_includes(PkgConfig::Epoxy)
+
+ target_link_libraries(core PUBLIC PkgConfig::Epoxy)
+
+ target_compile_definitions(core PUBLIC OGLRENDERER_ENABLED)
+endif()
+
+if (ENABLE_JIT)
+ target_compile_definitions(core PUBLIC JIT_ENABLED)
+
+
+ if (ENABLE_JIT_PROFILING)
+ include(cmake/FindVTune.cmake)
+ add_definitions(-DJIT_PROFILING_ENABLED)
endif()
-else()
- if (WIN32)
- target_link_libraries(core ole32 comctl32 ws2_32)
- elseif (APPLE)
- target_link_libraries(core)
- else()
- target_link_libraries(core rt)
- endif()
+endif()
+
+if (WIN32)
+ target_link_libraries(core PRIVATE ole32 comctl32 ws2_32)
+elseif(NOT APPLE)
+ target_link_libraries(core PRIVATE rt)
endif()
if (ENABLE_JIT_PROFILING)
- target_link_libraries(core jitprofiling)
+ target_link_libraries(core PRIVATE jitprofiling)
endif()
diff --git a/src/CP15.cpp b/src/CP15.cpp
index e75201b..4fe91cf 100644
--- a/src/CP15.cpp
+++ b/src/CP15.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -134,6 +134,9 @@ void ARMv5::UpdateITCMSetting()
if (CP15Control & (1<<18))
{
ITCMSize = 0x200 << ((ITCMSetting >> 1) & 0x1F);
+#ifdef JIT_ENABLED
+ FastBlockLookupSize = 0;
+#endif
}
else
{
diff --git a/src/CRC32.cpp b/src/CRC32.cpp
index b51b171..c2cc6c0 100644
--- a/src/CRC32.cpp
+++ b/src/CRC32.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -52,7 +52,7 @@ void _inittable()
}
}
-u32 CRC32(u8 *data, int len)
+u32 CRC32(u8 *data, int len, u32 start)
{
if (!tableinited)
{
@@ -60,7 +60,7 @@ u32 CRC32(u8 *data, int len)
tableinited = true;
}
- u32 crc = 0xFFFFFFFF;
+ u32 crc = start ^ 0xFFFFFFFF;
while (len--)
crc = (crc >> 8) ^ crctable[(crc & 0xFF) ^ *data++];
diff --git a/src/CRC32.h b/src/CRC32.h
index 2107533..1be5a8a 100644
--- a/src/CRC32.h
+++ b/src/CRC32.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -21,6 +21,6 @@
#include "types.h"
-u32 CRC32(u8* data, int len);
+u32 CRC32(u8* data, int len, u32 start=0);
#endif // CRC32_H
diff --git a/src/DMA.cpp b/src/DMA.cpp
index e601871..f45efc5 100644
--- a/src/DMA.cpp
+++ b/src/DMA.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/DMA.h b/src/DMA.h
index 17cb618..ad194c1 100644
--- a/src/DMA.h
+++ b/src/DMA.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -20,6 +20,7 @@
#define DMA_H
#include "types.h"
+#include "Savestate.h"
class DMA
{
@@ -96,7 +97,7 @@ private:
bool IsGXFIFODMA;
u32 MRAMBurstCount;
- u8* MRAMBurstTable;
+ const u8* MRAMBurstTable;
};
#endif
diff --git a/src/DMA_Timings.h b/src/DMA_Timings.h
index 1d86750..4281c78 100644
--- a/src/DMA_Timings.h
+++ b/src/DMA_Timings.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,6 +19,8 @@
#ifndef DMA_TIMINGS_H
#define DMA_TIMINGS_H
+#include "types.h"
+
namespace DMATiming
{
@@ -43,9 +45,9 @@ namespace DMATiming
// setting. Timings are such that the nonseq setting only matters for the first
// access, and minor edge cases (like the last of a 0x20000-byte block).
-u8 MRAMDummy[1] = {0};
+constexpr u8 MRAMDummy[1] = {0};
-u8 MRAMRead16Bursts[][256] =
+constexpr u8 MRAMRead16Bursts[][256] =
{
// main RAM to regular 16bit or 32bit bus (similar)
{7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -119,7 +121,7 @@ u8 MRAMRead16Bursts[][256] =
0},
};
-u8 MRAMRead32Bursts[][256] =
+constexpr u8 MRAMRead32Bursts[][256] =
{
// main RAM to regular 16bit bus
{9, 4, 3, 3, 3, 3, 3, 3, 3, 3,
@@ -178,7 +180,7 @@ u8 MRAMRead32Bursts[][256] =
0},
};
-u8 MRAMWrite16Bursts[][256] =
+constexpr u8 MRAMWrite16Bursts[][256] =
{
// regular 16bit or 32bit bus to main RAM (similar)
{8, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -209,7 +211,7 @@ u8 MRAMWrite16Bursts[][256] =
0},
};
-u8 MRAMWrite32Bursts[][256] =
+constexpr u8 MRAMWrite32Bursts[][256] =
{
// regular 16bit bus to main RAM
{9, 4, 4, 4, 4, 4, 4, 4, 4, 4,
diff --git a/src/DSi.cpp b/src/DSi.cpp
index 9267f14..cfce0ac 100644
--- a/src/DSi.cpp
+++ b/src/DSi.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -47,8 +47,6 @@
namespace DSi
{
-u32 BootAddr[2];
-
u16 SCFG_BIOS;
u16 SCFG_Clock9;
u16 SCFG_Clock7;
@@ -79,16 +77,12 @@ DSi_NDMA* NDMAs[8];
DSi_SDHost* SDMMC;
DSi_SDHost* SDIO;
-FILE* SDMMCFile = nullptr;
-
u64 ConsoleID;
u8 eMMC_CID[16];
-u8 ITCMInit[0x8000];
-u8 ARM7Init[0x3C00];
-
void Set_SCFG_Clock9(u16 val);
+void Set_SCFG_MC(u32 val);
bool Init()
@@ -100,6 +94,7 @@ bool Init()
#endif
if (!DSi_I2C::Init()) return false;
+ if (!DSi_CamModule::Init()) return false;
if (!DSi_AES::Init()) return false;
if (!DSi_DSP::Init()) return false;
@@ -127,6 +122,7 @@ void DeInit()
#endif
DSi_I2C::DeInit();
+ DSi_CamModule::DeInit();
DSi_AES::DeInit();
DSi_DSP::DeInit();
@@ -134,8 +130,6 @@ void DeInit()
delete SDMMC;
delete SDIO;
-
- CloseDSiNAND();
}
void Reset()
@@ -144,27 +138,31 @@ void Reset()
//NDS::ARM9->CP15Write(0x911, 0x00000020);
//NDS::ARM9->CP15Write(0x100, NDS::ARM9->CP15Read(0x100) | 0x00050000);
- NDS::ARM9->JumpTo(BootAddr[0]);
- NDS::ARM7->JumpTo(BootAddr[1]);
+ NDS::MapSharedWRAM(3);
NDMACnt[0] = 0; NDMACnt[1] = 0;
for (int i = 0; i < 8; i++) NDMAs[i]->Reset();
- memcpy(NDS::ARM9->ITCM, ITCMInit, 0x8000);
-
DSi_I2C::Reset();
- DSi_AES::Reset();
+ DSi_CamModule::Reset();
DSi_DSP::Reset();
+ SDMMC->CloseHandles();
+ SDIO->CloseHandles();
+
+ LoadNAND();
+
SDMMC->Reset();
SDIO->Reset();
+ DSi_AES::Reset();
+
SCFG_BIOS = 0x0101; // TODO: should be zero when booting from BIOS
SCFG_Clock9 = 0x0187; // CHECKME
SCFG_Clock7 = 0x0187;
SCFG_EXT[0] = 0x8307F100;
SCFG_EXT[1] = 0x93FFFB06;
- SCFG_MC = 0x0010;//0x0011;
+ SCFG_MC = 0x0010 | (~((u32)NDSCart::CartInserted)&1);//0x0011;
SCFG_RST = 0;
DSi_DSP::SetRstLine(false);
@@ -172,22 +170,98 @@ void Reset()
// LCD init flag
GPU::DispStat[0] |= (1<<6);
GPU::DispStat[1] |= (1<<6);
+}
- NDS::MapSharedWRAM(3);
+void Stop()
+{
+ DSi_CamModule::Stop();
+}
- for (u32 i = 0; i < 0x3C00; i+=4)
- ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]);
+void DoSavestate(Savestate* file)
+{
+ file->Section("DSIG");
- u32 eaddr = 0x03FFE6E4;
- ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]);
- ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]);
- ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]);
- ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]);
- ARM7Write16(eaddr+0x2C, 0x0001);
- ARM7Write16(eaddr+0x2E, 0x0001);
- ARM7Write16(eaddr+0x3C, 0x0100);
- ARM7Write16(eaddr+0x3E, 0x40E0);
- ARM7Write16(eaddr+0x42, 0x0001);
+ file->Var16(&SCFG_BIOS);
+ file->Var16(&SCFG_Clock9);
+ file->Var16(&SCFG_Clock7);
+ file->VarArray(&SCFG_EXT[0], sizeof(u32)*2);
+ file->Var32(&SCFG_MC);
+ file->Var16(&SCFG_RST);
+
+ //file->VarArray(ARM9iBIOS, 0x10000);
+ //file->VarArray(ARM7iBIOS, 0x10000);
+
+ if (file->Saving)
+ {
+ file->VarArray(&MBK[0][0], sizeof(u32)*8);
+ file->VarArray(&MBK[1][5], sizeof(u32)*3);
+ file->Var32(&MBK[0][8]);
+ }
+ else
+ {
+ Set_SCFG_Clock9(SCFG_Clock9);
+ Set_SCFG_MC(SCFG_MC);
+ DSi_DSP::SetRstLine(SCFG_RST & 0x0001);
+
+ MBK[0][8] = 0;
+ MBK[1][8] = 0;
+
+ u32 mbk[12];
+ file->VarArray(&mbk, sizeof(u32)*12);
+
+ MapNWRAM_A(0, mbk[0] & 0xFF);
+ MapNWRAM_A(1, (mbk[0] >> 8) & 0xFF);
+ MapNWRAM_A(2, (mbk[0] >> 16) & 0xFF);
+ MapNWRAM_A(3, mbk[0] >> 24);
+
+ MapNWRAM_B(0, mbk[1] & 0xFF);
+ MapNWRAM_B(1, (mbk[1] >> 8) & 0xFF);
+ MapNWRAM_B(2, (mbk[1] >> 16) & 0xFF);
+ MapNWRAM_B(3, mbk[1] >> 24);
+ MapNWRAM_B(4, mbk[2] & 0xFF);
+ MapNWRAM_B(5, (mbk[2] >> 8) & 0xFF);
+ MapNWRAM_B(6, (mbk[2] >> 16) & 0xFF);
+ MapNWRAM_B(7, mbk[2] >> 24);
+
+ MapNWRAM_C(0, mbk[3] & 0xFF);
+ MapNWRAM_C(1, (mbk[3] >> 8) & 0xFF);
+ MapNWRAM_C(2, (mbk[3] >> 16) & 0xFF);
+ MapNWRAM_C(3, mbk[3] >> 24);
+ MapNWRAM_C(4, mbk[4] & 0xFF);
+ MapNWRAM_C(5, (mbk[4] >> 8) & 0xFF);
+ MapNWRAM_C(6, (mbk[4] >> 16) & 0xFF);
+ MapNWRAM_C(7, mbk[4] >> 24);
+
+ MapNWRAMRange(0, 0, mbk[5]);
+ MapNWRAMRange(0, 1, mbk[6]);
+ MapNWRAMRange(0, 2, mbk[7]);
+
+ MapNWRAMRange(1, 0, mbk[8]);
+ MapNWRAMRange(1, 1, mbk[9]);
+ MapNWRAMRange(1, 2, mbk[10]);
+
+ mbk[11] &= 0x00FFFF0F;
+ MBK[0][8] = mbk[11];
+ MBK[1][8] = mbk[11];
+ }
+
+ for (int i = 0; i < 8; i++)
+ NDMAs[i]->DoSavestate(file);
+
+ DSi_AES::DoSavestate(file);
+ DSi_CamModule::DoSavestate(file);
+ DSi_DSP::DoSavestate(file);
+ DSi_I2C::DoSavestate(file);
+ SDMMC->DoSavestate(file);
+ SDIO->DoSavestate(file);
+}
+
+void SetCartInserted(bool inserted)
+{
+ if (inserted)
+ SCFG_MC &= ~1;
+ else
+ SCFG_MC |= 1;
}
void DecryptModcryptArea(u32 offset, u32 size, u8* iv)
@@ -445,7 +519,7 @@ void SetupDirectBoot()
ARM9Write32(0x02FFE000+i, tmp);
}
- if (DSi_NAND::Init(SDMMCFile, &DSi::ARM7iBIOS[0x8308]))
+ if (DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308]))
{
u8 userdata[0x1B0];
DSi_NAND::ReadUserData(userdata);
@@ -544,21 +618,22 @@ void SoftReset()
NDS::ARM9->CP15Reset();
- memcpy(NDS::ARM9->ITCM, ITCMInit, 0x8000);
+ NDS::MapSharedWRAM(3);
- DSi_AES::Reset();
// TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no
// *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus
// the DSP most likely gets reset
DSi_DSP::Reset();
+ SDMMC->CloseHandles();
+ SDIO->CloseHandles();
+
LoadNAND();
SDMMC->Reset();
SDIO->Reset();
- NDS::ARM9->JumpTo(BootAddr[0]);
- NDS::ARM7->JumpTo(BootAddr[1]);
+ DSi_AES::Reset();
SCFG_BIOS = 0x0101; // TODO: should be zero when booting from BIOS
SCFG_Clock9 = 0x0187; // CHECKME
@@ -574,22 +649,6 @@ void SoftReset()
// LCD init flag
GPU::DispStat[0] |= (1<<6);
GPU::DispStat[1] |= (1<<6);
-
- NDS::MapSharedWRAM(3);
-
- for (u32 i = 0; i < 0x3C00; i+=4)
- ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]);
-
- u32 eaddr = 0x03FFE6E4;
- ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]);
- ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]);
- ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]);
- ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]);
- ARM7Write16(eaddr+0x2C, 0x0001);
- ARM7Write16(eaddr+0x2E, 0x0001);
- ARM7Write16(eaddr+0x3C, 0x0100);
- ARM7Write16(eaddr+0x3E, 0x40E0);
- ARM7Write16(eaddr+0x42, 0x0001);
}
bool LoadBIOS()
@@ -650,12 +709,14 @@ bool LoadNAND()
{
printf("Loading DSi NAND\n");
- if (!DSi_NAND::Init(SDMMCFile, &DSi::ARM7iBIOS[0x8308]))
+ if (!DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308]))
{
printf("Failed to load DSi NAND\n");
return false;
}
+ FILE* nand = DSi_NAND::GetFile();
+
// Make sure NWRAM is accessible.
// The Bits are set to the startup values in Reset() and we might
// still have them on default (0) or some bits cleared by the previous
@@ -676,8 +737,8 @@ bool LoadNAND()
memset(NWRAMMask, 0, sizeof(NWRAMMask));
u32 bootparams[8];
- fseek(SDMMCFile, 0x220, SEEK_SET);
- fread(bootparams, 4, 8, SDMMCFile);
+ fseek(nand, 0x220, SEEK_SET);
+ fread(bootparams, 4, 8, nand);
printf("ARM9: offset=%08X size=%08X RAM=%08X size_aligned=%08X\n",
bootparams[0], bootparams[1], bootparams[2], bootparams[3]);
@@ -690,8 +751,8 @@ bool LoadNAND()
MBK[1][8] = 0;
u32 mbk[12];
- fseek(SDMMCFile, 0x380, SEEK_SET);
- fread(mbk, 4, 12, SDMMCFile);
+ fseek(nand, 0x380, SEEK_SET);
+ fread(mbk, 4, 12, nand);
MapNWRAM_A(0, mbk[0] & 0xFF);
MapNWRAM_A(1, (mbk[0] >> 8) & 0xFF);
@@ -745,12 +806,12 @@ bool LoadNAND()
AES_init_ctx_iv(&ctx, boot2key, boot2iv);
- fseek(SDMMCFile, bootparams[0], SEEK_SET);
+ fseek(nand, bootparams[0], SEEK_SET);
dstaddr = bootparams[2];
for (u32 i = 0; i < bootparams[3]; i += 16)
{
u8 data[16];
- fread(data, 16, 1, SDMMCFile);
+ fread(data, 16, 1, nand);
for (int j = 0; j < 16; j++) tmp[j] = data[15-j];
AES_CTR_xcrypt_buffer(&ctx, tmp, 16);
@@ -770,12 +831,12 @@ bool LoadNAND()
AES_init_ctx_iv(&ctx, boot2key, boot2iv);
- fseek(SDMMCFile, bootparams[4], SEEK_SET);
+ fseek(nand, bootparams[4], SEEK_SET);
dstaddr = bootparams[6];
for (u32 i = 0; i < bootparams[7]; i += 16)
{
u8 data[16];
- fread(data, 16, 1, SDMMCFile);
+ fread(data, 16, 1, nand);
for (int j = 0; j < 16; j++) tmp[j] = data[15-j];
AES_CTR_xcrypt_buffer(&ctx, tmp, 16);
@@ -787,11 +848,6 @@ bool LoadNAND()
ARM7Write32(dstaddr, *(u32*)&data[12]); dstaddr += 4;
}
- // repoint the CPUs to the boot2 binaries
-
- BootAddr[0] = bootparams[2];
- BootAddr[1] = bootparams[6];
-
#define printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); }
#define printhex_rev(str, size) { for (int z = (size)-1; z >= 0; z--) printf("%02X", (str)[z]); printf("\n"); }
@@ -800,18 +856,36 @@ bool LoadNAND()
printf("eMMC CID: "); printhex(eMMC_CID, 16);
printf("Console ID: %" PRIx64 "\n", ConsoleID);
- memset(ITCMInit, 0, 0x8000);
- memcpy(&ITCMInit[0x4400], &ARM9iBIOS[0x87F4], 0x400);
- memcpy(&ITCMInit[0x4800], &ARM9iBIOS[0x9920], 0x80);
- memcpy(&ITCMInit[0x4894], &ARM9iBIOS[0x99A0], 0x1048);
- memcpy(&ITCMInit[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048);
+ u32 eaddr = 0x03FFE6E4;
+ ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]);
+ ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]);
+ ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]);
+ ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]);
+ ARM7Write16(eaddr+0x2C, 0x0001);
+ ARM7Write16(eaddr+0x2E, 0x0001);
+ ARM7Write16(eaddr+0x3C, 0x0100);
+ ARM7Write16(eaddr+0x3E, 0x40E0);
+ ARM7Write16(eaddr+0x42, 0x0001);
+
+ memcpy(&NDS::ARM9->ITCM[0x4400], &ARM9iBIOS[0x87F4], 0x400);
+ memcpy(&NDS::ARM9->ITCM[0x4800], &ARM9iBIOS[0x9920], 0x80);
+ memcpy(&NDS::ARM9->ITCM[0x4894], &ARM9iBIOS[0x99A0], 0x1048);
+ memcpy(&NDS::ARM9->ITCM[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048);
+ u8 ARM7Init[0x3C00];
memset(ARM7Init, 0, 0x3C00);
memcpy(&ARM7Init[0x0000], &ARM7iBIOS[0x8188], 0x200);
memcpy(&ARM7Init[0x0200], &ARM7iBIOS[0xB5D8], 0x40);
memcpy(&ARM7Init[0x0254], &ARM7iBIOS[0xC6D0], 0x1048);
memcpy(&ARM7Init[0x129C], &ARM7iBIOS[0xD718], 0x1048);
+ for (u32 i = 0; i < 0x3C00; i+=4)
+ ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]);
+
+ // repoint the CPUs to the boot2 binaries
+ NDS::ARM9->JumpTo(bootparams[2]);
+ NDS::ARM7->JumpTo(bootparams[6]);
+
DSi_NAND::PatchUserData();
DSi_NAND::DeInit();
@@ -819,12 +893,6 @@ bool LoadNAND()
return true;
}
-void CloseDSiNAND()
-{
- if (DSi::SDMMCFile)
- fclose(DSi::SDMMCFile);
- DSi::SDMMCFile = nullptr;
-}
void RunNDMAs(u32 cpu)
{
@@ -1190,7 +1258,6 @@ u8 ARM9Read8(u32 addr)
switch (addr & 0xFF000000)
{
case 0x03000000:
- case 0x03800000:
if (SCFG_EXT[0] & (1 << 25))
{
if (addr >= NWRAMStart[0][0] && addr < NWRAMEnd[0][0])
@@ -1218,6 +1285,9 @@ u8 ARM9Read8(u32 addr)
case 0x09000000:
case 0x0A000000:
return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFF;
+
+ case 0x0C000000:
+ return *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask];
}
return NDS::ARM9Read8(addr);
@@ -1236,7 +1306,6 @@ u16 ARM9Read16(u32 addr)
switch (addr & 0xFF000000)
{
case 0x03000000:
- case 0x03800000:
if (SCFG_EXT[0] & (1 << 25))
{
if (addr >= NWRAMStart[0][0] && addr < NWRAMEnd[0][0])
@@ -1264,6 +1333,9 @@ u16 ARM9Read16(u32 addr)
case 0x09000000:
case 0x0A000000:
return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFF;
+
+ case 0x0C000000:
+ return *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask];
}
return NDS::ARM9Read16(addr);
@@ -1287,7 +1359,6 @@ u32 ARM9Read32(u32 addr)
break;
case 0x03000000:
- case 0x03800000:
if (SCFG_EXT[0] & (1 << 25))
{
if (addr >= NWRAMStart[0][0] && addr < NWRAMEnd[0][0])
@@ -1315,6 +1386,9 @@ u32 ARM9Read32(u32 addr)
case 0x09000000:
case 0x0A000000:
return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFFFFFF;
+
+ case 0x0C000000:
+ return *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask];
}
return NDS::ARM9Read32(addr);
@@ -1325,7 +1399,6 @@ void ARM9Write8(u32 addr, u8 val)
switch (addr & 0xFF000000)
{
case 0x03000000:
- case 0x03800000:
if (SCFG_EXT[0] & (1 << 25))
{
if (addr >= NWRAMStart[0][0] && addr < NWRAMEnd[0][0])
@@ -1413,6 +1486,13 @@ void ARM9Write8(u32 addr, u8 val)
case 0x09000000:
case 0x0A000000:
return;
+
+ case 0x0C000000:
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr);
+#endif
+ *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val;
+ return;
}
return NDS::ARM9Write8(addr, val);
@@ -1423,7 +1503,6 @@ void ARM9Write16(u32 addr, u16 val)
switch (addr & 0xFF000000)
{
case 0x03000000:
- case 0x03800000:
if (SCFG_EXT[0] & (1 << 25))
{
if (addr >= NWRAMStart[0][0] && addr < NWRAMEnd[0][0])
@@ -1497,6 +1576,13 @@ void ARM9Write16(u32 addr, u16 val)
case 0x09000000:
case 0x0A000000:
return;
+
+ case 0x0C000000:
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr);
+#endif
+ *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val;
+ return;
}
return NDS::ARM9Write16(addr, val);
@@ -1507,7 +1593,6 @@ void ARM9Write32(u32 addr, u32 val)
switch (addr & 0xFF000000)
{
case 0x03000000:
- case 0x03800000:
if (SCFG_EXT[0] & (1 << 25))
{
if (addr >= NWRAMStart[0][0] && addr < NWRAMEnd[0][0])
@@ -1581,6 +1666,13 @@ void ARM9Write32(u32 addr, u32 val)
case 0x09000000:
case 0x0A000000:
return;
+
+ case 0x0C000000:
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr);
+#endif
+ *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val;
+ return;
}
return NDS::ARM9Write32(addr, val);
@@ -1591,6 +1683,7 @@ bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region)
switch (addr & 0xFF000000)
{
case 0x02000000:
+ case 0x0C000000:
region->Mem = NDS::MainRAM;
region->Mask = NDS::MainRAMMask;
return true;
@@ -1671,6 +1764,10 @@ u8 ARM7Read8(u32 addr)
case 0x0A000000:
case 0x0A800000:
return (NDS::ExMemCnt[0] & (1<<7)) ? 0xFF : 0;
+
+ case 0x0C000000:
+ case 0x0C800000:
+ return *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask];
}
return NDS::ARM7Read8(addr);
@@ -1724,6 +1821,10 @@ u16 ARM7Read16(u32 addr)
case 0x0A000000:
case 0x0A800000:
return (NDS::ExMemCnt[0] & (1<<7)) ? 0xFFFF : 0;
+
+ case 0x0C000000:
+ case 0x0C800000:
+ return *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask];
}
return NDS::ARM7Read16(addr);
@@ -1777,6 +1878,10 @@ u32 ARM7Read32(u32 addr)
case 0x0A000000:
case 0x0A800000:
return (NDS::ExMemCnt[0] & (1<<7)) ? 0xFFFFFFFF : 0;
+
+ case 0x0C000000:
+ case 0x0C800000:
+ return *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask];
}
return NDS::ARM7Read32(addr);
@@ -1864,6 +1969,14 @@ void ARM7Write8(u32 addr, u8 val)
case 0x0A000000:
case 0x0A800000:
return;
+
+ case 0x0C000000:
+ case 0x0C800000:
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr);
+#endif
+ *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val;
+ return;
}
return NDS::ARM7Write8(addr, val);
@@ -1951,6 +2064,14 @@ void ARM7Write16(u32 addr, u16 val)
case 0x0A000000:
case 0x0A800000:
return;
+
+ case 0x0C000000:
+ case 0x0C800000:
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr);
+#endif
+ *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val;
+ return;
}
return NDS::ARM7Write16(addr, val);
@@ -2038,6 +2159,14 @@ void ARM7Write32(u32 addr, u32 val)
case 0x0A000000:
case 0x0A800000:
return;
+
+ case 0x0C000000:
+ case 0x0C800000:
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr);
+#endif
+ *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val;
+ return;
}
return NDS::ARM7Write32(addr, val);
@@ -2049,6 +2178,8 @@ bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region)
{
case 0x02000000:
case 0x02800000:
+ case 0x0C000000:
+ case 0x0C800000:
region->Mem = NDS::MainRAM;
region->Mask = NDS::MainRAMMask;
return true;
@@ -2107,7 +2238,7 @@ u8 ARM9IORead8(u32 addr)
if ((addr & 0xFFFFFF00) == 0x04004200)
{
if (!(SCFG_EXT[0] & (1<<17))) return 0;
- return DSi_Camera::Read8(addr);
+ return DSi_CamModule::Read8(addr);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
@@ -2139,7 +2270,7 @@ u16 ARM9IORead16(u32 addr)
if ((addr & 0xFFFFFF00) == 0x04004200)
{
if (!(SCFG_EXT[0] & (1<<17))) return 0;
- return DSi_Camera::Read16(addr);
+ return DSi_CamModule::Read16(addr);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
@@ -2201,7 +2332,7 @@ u32 ARM9IORead32(u32 addr)
if ((addr & 0xFFFFFF00) == 0x04004200)
{
if (!(SCFG_EXT[0] & (1<<17))) return 0;
- return DSi_Camera::Read32(addr);
+ return DSi_CamModule::Read32(addr);
}
return NDS::ARM9IORead32(addr);
@@ -2265,7 +2396,7 @@ void ARM9IOWrite8(u32 addr, u8 val)
if ((addr & 0xFFFFFF00) == 0x04004200)
{
if (!(SCFG_EXT[0] & (1<<17))) return;
- return DSi_Camera::Write8(addr, val);
+ return DSi_CamModule::Write8(addr, val);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
@@ -2325,7 +2456,7 @@ void ARM9IOWrite16(u32 addr, u16 val)
if ((addr & 0xFFFFFF00) == 0x04004200)
{
if (!(SCFG_EXT[0] & (1<<17))) return;
- return DSi_Camera::Write16(addr, val);
+ return DSi_CamModule::Write16(addr, val);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
@@ -2475,7 +2606,7 @@ void ARM9IOWrite32(u32 addr, u32 val)
if ((addr & 0xFFFFFF00) == 0x04004200)
{
if (!(SCFG_EXT[0] & (1<<17))) return;
- return DSi_Camera::Write32(addr, val);
+ return DSi_CamModule::Write32(addr, val);
}
return NDS::ARM9IOWrite32(addr, val);
@@ -2512,6 +2643,9 @@ u8 ARM7IORead8(u32 addr)
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 48) & 0xFF;
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 56;
case 0x04004D08: return 0;
+
+ case 0x4004700: return DSi_DSP::SNDExCnt;
+ case 0x4004701: return DSi_DSP::SNDExCnt >> 8;
}
return NDS::ARM7IORead8(addr);
@@ -2544,6 +2678,8 @@ u16 ARM7IORead16(u32 addr)
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFFFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 48;
case 0x04004D08: return 0;
+
+ case 0x4004700: return DSi_DSP::SNDExCnt;
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@@ -2615,6 +2751,10 @@ u32 ARM7IORead32(u32 addr)
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFFFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 32;
case 0x04004D08: return 0;
+
+ case 0x4004700:
+ printf("32-Bit SNDExCnt read? %08X\n", NDS::ARM7->R[15]);
+ return DSi_DSP::SNDExCnt;
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@@ -2662,6 +2802,46 @@ void ARM7IOWrite8(u32 addr, u8 val)
case 0x04004500: DSi_I2C::WriteData(val); return;
case 0x04004501: DSi_I2C::WriteCnt(val); return;
+
+ case 0x4004700:
+ DSi_DSP::WriteSNDExCnt((u16)val | (DSi_DSP::SNDExCnt & 0xFF00));
+ return;
+ case 0x4004701:
+ DSi_DSP::WriteSNDExCnt(((u16)val << 8) | (DSi_DSP::SNDExCnt & 0x00FF));
+ return;
+ }
+
+ if (addr >= 0x04004420 && addr < 0x04004430)
+ {
+ u32 shift = (addr&3)*8;
+ addr -= 0x04004420;
+ addr &= ~3;
+ DSi_AES::WriteIV(addr, (u32)val << shift, 0xFF << shift);
+ return;
+ }
+ if (addr >= 0x04004430 && addr < 0x04004440)
+ {
+ u32 shift = (addr&3)*8;
+ addr -= 0x04004430;
+ addr &= ~3;
+ DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFF << shift);
+ return;
+ }
+ if (addr >= 0x04004440 && addr < 0x04004500)
+ {
+ u32 shift = (addr&3)*8;
+ addr -= 0x04004440;
+ addr &= ~3;
+
+ int n = 0;
+ while (addr >= 0x30) { addr -= 0x30; n++; }
+
+ switch (addr >> 4)
+ {
+ case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return;
+ case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return;
+ case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return;
+ }
}
return NDS::ARM7IOWrite8(addr, val);
@@ -2693,12 +2873,51 @@ void ARM7IOWrite16(u32 addr, u16 val)
case 0x04004062:
if (!(SCFG_EXT[1] & (1 << 31))) /* no access to SCFG Registers if disabled*/
return;
- u32 tmp = MBK[0][8];
- tmp &= ~(0xffff << ((addr % 4) * 8));
- tmp |= (val << ((addr % 4) * 8));
- MBK[0][8] = tmp & 0x00FFFF0F;
- MBK[1][8] = MBK[0][8];
+ {
+ u32 tmp = MBK[0][8];
+ tmp &= ~(0xffff << ((addr % 4) * 8));
+ tmp |= (val << ((addr % 4) * 8));
+ MBK[0][8] = tmp & 0x00FFFF0F;
+ MBK[1][8] = MBK[0][8];
+ }
return;
+
+ case 0x4004700:
+ DSi_DSP::WriteSNDExCnt(val);
+ return;
+ }
+
+ if (addr >= 0x04004420 && addr < 0x04004430)
+ {
+ u32 shift = (addr&1)*16;
+ addr -= 0x04004420;
+ addr &= ~1;
+ DSi_AES::WriteIV(addr, (u32)val << shift, 0xFFFF << shift);
+ return;
+ }
+ if (addr >= 0x04004430 && addr < 0x04004440)
+ {
+ u32 shift = (addr&1)*16;
+ addr -= 0x04004430;
+ addr &= ~1;
+ DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFFFF << shift);
+ return;
+ }
+ if (addr >= 0x04004440 && addr < 0x04004500)
+ {
+ u32 shift = (addr&1)*16;
+ addr -= 0x04004440;
+ addr &= ~1;
+
+ int n = 0;
+ while (addr >= 0x30) { addr -= 0x30; n++; }
+
+ switch (addr >> 4)
+ {
+ case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return;
+ case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return;
+ case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return;
+ }
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@@ -2798,6 +3017,11 @@ void ARM7IOWrite32(u32 addr, u32 val)
case 0x04004400: DSi_AES::WriteCnt(val); return;
case 0x04004404: DSi_AES::WriteBlkCnt(val); return;
case 0x04004408: DSi_AES::WriteInputFIFO(val); return;
+
+ case 0x4004700:
+ printf("32-Bit SNDExCnt write? %08X %08X\n", val, NDS::ARM7->R[15]);
+ DSi_DSP::WriteSNDExCnt(val);
+ return;
}
if (addr >= 0x04004420 && addr < 0x04004430)
diff --git a/src/DSi.h b/src/DSi.h
index 1236527..ef60b30 100644
--- a/src/DSi.h
+++ b/src/DSi.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -39,8 +39,6 @@ extern u64 ConsoleID;
extern DSi_SDHost* SDMMC;
extern DSi_SDHost* SDIO;
-extern FILE* SDMMCFile;
-
const u32 NWRAMSize = 0x40000;
extern u8* NWRAM_A;
@@ -58,6 +56,11 @@ extern u32 NWRAMMask[2][3];
bool Init();
void DeInit();
void Reset();
+void Stop();
+
+void DoSavestate(Savestate* file);
+
+void SetCartInserted(bool inserted);
void SetupDirectBoot();
void SoftReset();
@@ -65,8 +68,6 @@ void SoftReset();
bool LoadBIOS();
bool LoadNAND();
-void CloseDSiNAND();
-
void RunNDMAs(u32 cpu);
void StallNDMAs();
bool NDMAsInMode(u32 cpu, u32 mode);
diff --git a/src/DSiCrypto.cpp b/src/DSiCrypto.cpp
deleted file mode 100644
index 26262e3..0000000
--- a/src/DSiCrypto.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- Copyright 2016-2021 Arisotura
-
- 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/.
-*/
diff --git a/src/DSiCrypto.h b/src/DSiCrypto.h
deleted file mode 100644
index 2828666..0000000
--- a/src/DSiCrypto.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- Copyright 2016-2021 Arisotura
-
- 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 DSICRYPTO_H
-#define DSICRYPTO_H
-
-//
-
-#endif // DSICRYPTO_H
diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp
index 02e4f1c..f84db11 100644
--- a/src/DSi_AES.cpp
+++ b/src/DSi_AES.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -155,6 +155,43 @@ void Reset()
*(u32*)&KeyY[3][8] = 0x202DDD1D;
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("AESi");
+
+ file->Var32(&Cnt);
+
+ file->Var32(&BlkCnt);
+ file->Var32(&RemExtra);
+ file->Var32(&RemBlocks);
+
+ file->Bool32(&OutputFlush);
+
+ file->Var32(&InputDMASize);
+ file->Var32(&OutputDMASize);
+ file->Var32(&AESMode);
+
+ InputFIFO.DoSavestate(file);
+ OutputFIFO.DoSavestate(file);
+
+ file->VarArray(IV, 16);
+
+ file->VarArray(MAC, 16);
+
+ file->VarArray(KeyNormal, 4*16);
+ file->VarArray(KeyX, 4*16);
+ file->VarArray(KeyY, 4*16);
+
+ file->VarArray(CurKey, 16);
+ file->VarArray(CurMAC, 16);
+
+ file->VarArray(OutputMAC, 16);
+ file->Bool32(&OutputMACDue);
+
+ file->VarArray(Ctx.RoundKey, AES_keyExpSize);
+ file->VarArray(Ctx.Iv, AES_BLOCKLEN);
+}
+
void ProcessBlock_CCM_Extra()
{
diff --git a/src/DSi_AES.h b/src/DSi_AES.h
index 27f8e64..d8ef98a 100644
--- a/src/DSi_AES.h
+++ b/src/DSi_AES.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -20,6 +20,7 @@
#define DSI_AES_H
#include "types.h"
+#include "Savestate.h"
namespace DSi_AES
{
@@ -30,6 +31,8 @@ bool Init();
void DeInit();
void Reset();
+void DoSavestate(Savestate* file);
+
u32 ReadCnt();
void WriteCnt(u32 val);
void WriteBlkCnt(u32 val);
diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp
index d8cc335..20f15cf 100644
--- a/src/DSi_Camera.cpp
+++ b/src/DSi_Camera.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -20,17 +20,25 @@
#include <string.h>
#include "DSi.h"
#include "DSi_Camera.h"
+#include "Platform.h"
-DSi_Camera* DSi_Camera0; // 78 / facing outside
-DSi_Camera* DSi_Camera1; // 7A / selfie cam
+namespace DSi_CamModule
+{
+
+Camera* Camera0; // 78 / facing outside
+Camera* Camera1; // 7A / selfie cam
+
+u16 ModuleCnt;
+u16 Cnt;
-u16 DSi_Camera::ModuleCnt;
-u16 DSi_Camera::Cnt;
+u32 CropStart, CropEnd;
-u8 DSi_Camera::FrameBuffer[640*480*4];
-u32 DSi_Camera::FrameLength;
-u32 DSi_Camera::TransferPos;
+// pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are
+u32 DataBuffer[512];
+u32 BufferReadPos, BufferWritePos;
+u32 BufferNumLines;
+Camera* CurCamera;
// note on camera data/etc intervals
// on hardware those are likely affected by several factors
@@ -41,120 +49,383 @@ const u32 kIRQInterval = 1120000; // ~30 FPS
const u32 kTransferStart = 60000;
-bool DSi_Camera::Init()
+bool Init()
{
- DSi_Camera0 = new DSi_Camera(0);
- DSi_Camera1 = new DSi_Camera(1);
+ Camera0 = new Camera(0);
+ Camera1 = new Camera(1);
return true;
}
-void DSi_Camera::DeInit()
+void DeInit()
{
- delete DSi_Camera0;
- delete DSi_Camera1;
+ delete Camera0;
+ delete Camera1;
}
-void DSi_Camera::Reset()
+void Reset()
{
- DSi_Camera0->ResetCam();
- DSi_Camera1->ResetCam();
+ Camera0->Reset();
+ Camera1->Reset();
ModuleCnt = 0; // CHECKME
Cnt = 0;
- memset(FrameBuffer, 0, 640*480*4);
- TransferPos = 0;
- FrameLength = 256*192*2; // TODO: make it check frame size, data type, etc
+ CropStart = 0;
+ CropEnd = 0;
+
+ memset(DataBuffer, 0, 512*sizeof(u32));
+ BufferReadPos = 0;
+ BufferWritePos = 0;
+ BufferNumLines = 0;
+ CurCamera = nullptr;
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
}
+void Stop()
+{
+ Camera0->Stop();
+ Camera1->Stop();
+}
+
+void DoSavestate(Savestate* file)
+{
+ file->Section("CAMi");
+
+ file->Var16(&ModuleCnt);
+ file->Var16(&Cnt);
+
+ /*file->VarArray(FrameBuffer, sizeof(FrameBuffer));
+ file->Var32(&TransferPos);
+ file->Var32(&FrameLength);*/
+
+ Camera0->DoSavestate(file);
+ Camera1->DoSavestate(file);
+}
+
-void DSi_Camera::IRQ(u32 param)
+void IRQ(u32 param)
{
- DSi_Camera* activecam = nullptr;
+ Camera* activecam = nullptr;
- // TODO: check which camera has priority if both are activated
- // (or does it just jumble both data sources together, like it
- // does for, say, overlapping VRAM?)
- if (DSi_Camera0->IsActivated()) activecam = DSi_Camera0;
- else if (DSi_Camera1->IsActivated()) activecam = DSi_Camera1;
+ // TODO: cameras don't have any priority!
+ // activating both together will jumble the image data together
+ if (Camera0->IsActivated()) activecam = Camera0;
+ else if (Camera1->IsActivated()) activecam = Camera1;
if (activecam)
{
- RequestFrame(activecam->Num);
+ activecam->StartTransfer();
if (Cnt & (1<<11))
NDS::SetIRQ(0, NDS::IRQ_DSi_Camera);
if (Cnt & (1<<15))
- NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, Transfer, 0);
+ {
+ BufferReadPos = 0;
+ BufferWritePos = 0;
+ BufferNumLines = 0;
+ CurCamera = activecam;
+ NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, TransferScanline, 0);
+ }
}
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
}
-void DSi_Camera::RequestFrame(u32 cam)
+void TransferScanline(u32 line)
{
- if (!(Cnt & (1<<13))) printf("CAMERA: !! REQUESTING YUV FRAME\n");
+ u32* dstbuf = &DataBuffer[BufferWritePos];
+ int maxlen = 512 - BufferWritePos;
- // TODO: picture size, data type, cropping, etc
- // generate test pattern
- // TODO: get picture from platform (actual camera, video file, whatever source)
- for (u32 y = 0; y < 192; y++)
+ u32 tmpbuf[512];
+ int datalen = CurCamera->TransferScanline(tmpbuf, 512);
+
+ // TODO: must be tweaked such that each block has enough time to transfer
+ u32 delay = datalen*4 + 16;
+
+ int copystart = 0;
+ int copylen = datalen;
+
+ if (Cnt & (1<<14))
{
- for (u32 x = 0; x < 256; x++)
+ // crop picture
+
+ int ystart = (CropStart >> 16) & 0x1FF;
+ int yend = (CropEnd >> 16) & 0x1FF;
+ if (line < ystart || line > yend)
{
- u16* px = (u16*)&FrameBuffer[((y*256) + x) * 2];
+ if (!CurCamera->TransferDone())
+ NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1);
- if ((x & 0x8) ^ (y & 0x8))
- *px = 0x8000;
- else
- *px = 0xFC00 | ((y >> 3) << 5);
+ return;
+ }
+
+ int xstart = (CropStart >> 1) & 0x1FF;
+ int xend = (CropEnd >> 1) & 0x1FF;
+
+ copystart = xstart;
+ copylen = xend+1 - xstart;
+
+ if ((copystart + copylen) > datalen)
+ copylen = datalen - copystart;
+ if (copylen < 0)
+ copylen = 0;
+ }
+
+ if (copylen > maxlen)
+ {
+ copylen = maxlen;
+ Cnt |= (1<<4);
+ }
+
+ if (Cnt & (1<<13))
+ {
+ // convert to RGB
+
+ for (u32 i = 0; i < copylen; i++)
+ {
+ u32 val = tmpbuf[copystart + i];
+
+ int y1 = val & 0xFF;
+ int u = (val >> 8) & 0xFF;
+ int y2 = (val >> 16) & 0xFF;
+ int v = (val >> 24) & 0xFF;
+
+ u -= 128; v -= 128;
+
+ int r1 = y1 + ((v * 91881) >> 16);
+ int g1 = y1 - ((v * 46793) >> 16) - ((u * 22544) >> 16);
+ int b1 = y1 + ((u * 116129) >> 16);
+
+ int r2 = y2 + ((v * 91881) >> 16);
+ int g2 = y2 - ((v * 46793) >> 16) - ((u * 22544) >> 16);
+ int b2 = y2 + ((u * 116129) >> 16);
+
+ r1 = std::clamp(r1, 0, 255); g1 = std::clamp(g1, 0, 255); b1 = std::clamp(b1, 0, 255);
+ r2 = std::clamp(r2, 0, 255); g2 = std::clamp(g2, 0, 255); b2 = std::clamp(b2, 0, 255);
+
+ u32 col1 = (r1 >> 3) | ((g1 >> 3) << 5) | ((b1 >> 3) << 10) | 0x8000;
+ u32 col2 = (r2 >> 3) | ((g2 >> 3) << 5) | ((b2 >> 3) << 10) | 0x8000;
+
+ dstbuf[i] = col1 | (col2 << 16);
}
}
+ else
+ {
+ // return raw data
+
+ memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32));
+ }
+
+ u32 numscan = Cnt & 0x000F;
+ if (BufferNumLines >= numscan)
+ {
+ BufferReadPos = 0; // checkme
+ BufferWritePos = 0;
+ BufferNumLines = 0;
+ DSi::CheckNDMAs(0, 0x0B);
+ }
+ else
+ {
+ BufferWritePos += copylen;
+ if (BufferWritePos > 512) BufferWritePos = 512;
+ BufferNumLines++;
+ }
+
+ if (CurCamera->TransferDone())
+ return;
+
+ NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1);
}
-void DSi_Camera::Transfer(u32 pos)
+
+u8 Read8(u32 addr)
{
- u32 numscan = (Cnt & 0x000F) + 1;
- u32 numpix = numscan * 256; // CHECKME
+ //
- // TODO: present data
- //printf("CAM TRANSFER POS=%d/%d\n", pos, 0x6000*2);
+ printf("unknown DSi cam read8 %08X\n", addr);
+ return 0;
+}
- DSi::CheckNDMAs(0, 0x0B);
+u16 Read16(u32 addr)
+{
+ switch (addr)
+ {
+ case 0x04004200: return ModuleCnt;
+ case 0x04004202: return Cnt;
+ }
+
+ printf("unknown DSi cam read16 %08X\n", addr);
+ return 0;
+}
- pos += numpix;
- if (pos >= 0x6000*2) // HACK
+u32 Read32(u32 addr)
+{
+ switch (addr)
{
- // transfer done
+ case 0x04004204:
+ {
+ u32 ret = DataBuffer[BufferReadPos];
+ if (Cnt & (1<<15))
+ {
+ if (BufferReadPos < 511)
+ BufferReadPos++;
+ // CHECKME!!!!
+ // also presumably we should set bit4 in Cnt if there's no new data to be read
+ }
+
+ return ret;
+ }
+
+ case 0x04004210: return CropStart;
+ case 0x04004214: return CropEnd;
}
- else
+
+ printf("unknown DSi cam read32 %08X\n", addr);
+ return 0;
+}
+
+void Write8(u32 addr, u8 val)
+{
+ //
+
+ printf("unknown DSi cam write8 %08X %02X\n", addr, val);
+}
+
+void Write16(u32 addr, u16 val)
+{
+ switch (addr)
{
- // keep going
+ case 0x04004200:
+ {
+ u16 oldcnt = ModuleCnt;
+ ModuleCnt = val;
+
+ if ((ModuleCnt & (1<<1)) && !(oldcnt & (1<<1)))
+ {
+ // reset shit to zero
+ // CHECKME
+
+ Cnt = 0;
+ }
+
+ if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5)))
+ {
+ // TODO: reset I2C??
+ }
+ }
+ return;
+
+ case 0x04004202:
+ {
+ // TODO: during a transfer, clearing bit15 does not reflect immediately
+ // maybe it needs to finish the trasnfer or atleast the current block
+
+ // checkme
+ u16 oldmask;
+ if (Cnt & 0x8000)
+ {
+ val &= 0x8F20;
+ oldmask = 0x601F;
+ }
+ else
+ {
+ val &= 0xEF2F;
+ oldmask = 0x0010;
+ }
+
+ Cnt = (Cnt & oldmask) | (val & ~0x0020);
+ if (val & (1<<5))
+ {
+ Cnt &= ~(1<<4);
+ BufferReadPos = 0;
+ BufferWritePos = 0;
+ }
- // TODO: must be tweaked such that each block has enough time to transfer
- u32 delay = numpix*2 + 16;
+ if ((val & (1<<15)) && !(Cnt & (1<<15)))
+ {
+ // start transfer
+ //DSi::CheckNDMAs(0, 0x0B);
+ }
+ }
+ return;
- NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, Transfer, pos);
+ case 0x04004210:
+ if (Cnt & (1<<15)) return;
+ CropStart = (CropStart & 0x01FF0000) | (val & 0x03FE);
+ return;
+ case 0x04004212:
+ if (Cnt & (1<<15)) return;
+ CropStart = (CropStart & 0x03FE) | ((val & 0x01FF) << 16);
+ return;
+ case 0x04004214:
+ if (Cnt & (1<<15)) return;
+ CropEnd = (CropEnd & 0x01FF0000) | (val & 0x03FE);
+ return;
+ case 0x04004216:
+ if (Cnt & (1<<15)) return;
+ CropEnd = (CropEnd & 0x03FE) | ((val & 0x01FF) << 16);
+ return;
}
+
+ printf("unknown DSi cam write16 %08X %04X\n", addr, val);
+}
+
+void Write32(u32 addr, u32 val)
+{
+ switch (addr)
+ {
+ case 0x04004210:
+ if (Cnt & (1<<15)) return;
+ CropStart = val & 0x01FF03FE;
+ return;
+ case 0x04004214:
+ if (Cnt & (1<<15)) return;
+ CropEnd = val & 0x01FF03FE;
+ return;
+ }
+
+ printf("unknown DSi cam write32 %08X %08X\n", addr, val);
}
-DSi_Camera::DSi_Camera(u32 num)
+
+Camera::Camera(u32 num)
{
Num = num;
}
-DSi_Camera::~DSi_Camera()
+Camera::~Camera()
{
- //
}
-void DSi_Camera::ResetCam()
+void Camera::DoSavestate(Savestate* file)
+{
+ char magic[5] = "CAMx";
+ magic[3] = '0' + Num;
+ file->Section(magic);
+
+ file->Var32(&DataPos);
+ file->Var32(&RegAddr);
+ file->Var16(&RegData);
+
+ file->Var16(&PLLDiv);
+ file->Var16(&PLLPDiv);
+ file->Var16(&PLLCnt);
+ file->Var16(&ClocksCnt);
+ file->Var16(&StandbyCnt);
+ file->Var16(&MiscCnt);
+
+ file->Var16(&MCUAddr);
+ file->VarArray(MCURegs, 0x8000);
+}
+
+void Camera::Reset()
{
+ Platform::Camera_Stop(Num);
+
DataPos = 0;
RegAddr = 0;
RegData = 0;
@@ -165,9 +436,23 @@ void DSi_Camera::ResetCam()
ClocksCnt = 0;
StandbyCnt = 0x4029; // checkme
MiscCnt = 0;
+
+ MCUAddr = 0;
+ memset(MCURegs, 0, 0x8000);
+
+ // default state is preview mode (checkme)
+ MCURegs[0x2104] = 3;
+
+ TransferY = 0;
+ memset(FrameBuffer, 0, (640*480/2)*sizeof(u32));
}
-bool DSi_Camera::IsActivated()
+void Camera::Stop()
+{
+ Platform::Camera_Stop(Num);
+}
+
+bool Camera::IsActivated()
{
if (StandbyCnt & (1<<14)) return false; // standby
if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled
@@ -176,40 +461,121 @@ bool DSi_Camera::IsActivated()
}
-void DSi_Camera::I2C_Start()
+void Camera::StartTransfer()
{
+ TransferY = 0;
+
+ u8 state = MCURegs[0x2104];
+ if (state == 3) // preview
+ {
+ FrameWidth = *(u16*)&MCURegs[0x2703];
+ FrameHeight = *(u16*)&MCURegs[0x2705];
+ FrameReadMode = *(u16*)&MCURegs[0x2717];
+ FrameFormat = *(u16*)&MCURegs[0x2755];
+ }
+ else if (state == 7) // capture
+ {
+ FrameWidth = *(u16*)&MCURegs[0x2707];
+ FrameHeight = *(u16*)&MCURegs[0x2709];
+ FrameReadMode = *(u16*)&MCURegs[0x272D];
+ FrameFormat = *(u16*)&MCURegs[0x2757];
+ }
+ else
+ {
+ FrameWidth = 0;
+ FrameHeight = 0;
+ FrameReadMode = 0;
+ FrameFormat = 0;
+ }
+
+ Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true);
}
-u8 DSi_Camera::I2C_Read(bool last)
+bool Camera::TransferDone()
{
- u8 ret;
+ return TransferY >= FrameHeight;
+}
- if (DataPos < 2)
+int Camera::TransferScanline(u32* buffer, int maxlen)
+{
+ if (TransferY >= FrameHeight)
+ return 0;
+
+ if (FrameWidth > 640 || FrameHeight > 480 ||
+ FrameWidth < 2 || FrameHeight < 2 ||
+ (FrameWidth & 1))
{
- printf("DSi_Camera: WHAT??\n");
- ret = 0;
+ // TODO work out something for these cases?
+ printf("CAM%d: invalid resolution %dx%d\n", Num, FrameWidth, FrameHeight);
+ //memset(buffer, 0, width*height*sizeof(u16));
+ return 0;
}
- else
+
+ // TODO: non-YUV pixel formats and all
+
+ int retlen = FrameWidth >> 1;
+ int sy = (TransferY * 480) / FrameHeight;
+ if (FrameReadMode & (1<<1))
+ sy = 479 - sy;
+
+ if (FrameReadMode & (1<<0))
{
- if (DataPos & 0x1)
+ for (int dx = 0; dx < retlen; dx++)
{
- ret = RegData & 0xFF;
- RegAddr += 2; // checkme
+ if (dx >= maxlen) break;
+
+ int sx = (dx * 640) / FrameWidth;
+
+ u32 val = FrameBuffer[sy*320 + sx];
+ buffer[dx] = val;
}
- else
+ }
+ else
+ {
+ for (int dx = 0; dx < retlen; dx++)
{
- RegData = I2C_ReadReg(RegAddr);
- ret = RegData >> 8;
+ if (dx >= maxlen) break;
+
+ int sx = 319 - ((dx * 640) / FrameWidth);
+
+ u32 val = FrameBuffer[sy*320 + sx];
+ buffer[dx] = (val & 0xFF00FF00) | ((val >> 16) & 0xFF) | ((val & 0xFF) << 16);
}
}
+ TransferY++;
+
+ return retlen;
+}
+
+
+void Camera::I2C_Start()
+{
+ DataPos = 0;
+}
+
+u8 Camera::I2C_Read(bool last)
+{
+ u8 ret;
+
+ if (DataPos & 0x1)
+ {
+ ret = RegData & 0xFF;
+ RegAddr += 2; // checkme
+ }
+ else
+ {
+ RegData = I2C_ReadReg(RegAddr);
+ ret = RegData >> 8;
+ }
+
if (last) DataPos = 0;
else DataPos++;
return ret;
}
-void DSi_Camera::I2C_Write(u8 val, bool last)
+void Camera::I2C_Write(u8 val, bool last)
{
if (DataPos < 2)
{
@@ -238,7 +604,7 @@ void DSi_Camera::I2C_Write(u8 val, bool last)
else DataPos++;
}
-u16 DSi_Camera::I2C_ReadReg(u16 addr)
+u16 Camera::I2C_ReadReg(u16 addr)
{
switch (addr)
{
@@ -250,6 +616,23 @@ u16 DSi_Camera::I2C_ReadReg(u16 addr)
case 0x0018: return StandbyCnt;
case 0x001A: return MiscCnt;
+ case 0x098C: return MCUAddr;
+ case 0x0990:
+ case 0x0992:
+ case 0x0994:
+ case 0x0996:
+ case 0x0998:
+ case 0x099A:
+ case 0x099C:
+ case 0x099E:
+ {
+ addr -= 0x0990;
+ u16 ret = MCU_Read((MCUAddr & 0x7FFF) + addr);
+ if (!(MCUAddr & (1<<15)))
+ ret |= (MCU_Read((MCUAddr & 0x7FFF) + addr+1) << 8);
+ return ret;
+ }
+
case 0x301A: return ((~StandbyCnt) & 0x4000) >> 12;
}
@@ -257,7 +640,7 @@ u16 DSi_Camera::I2C_ReadReg(u16 addr)
return 0;
}
-void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
+void Camera::I2C_WriteReg(u16 addr, u16 val)
{
switch (addr)
{
@@ -275,18 +658,47 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
return;
case 0x0016:
ClocksCnt = val;
- printf("ClocksCnt=%04X\n", val);
+ //printf("ClocksCnt=%04X\n", val);
return;
case 0x0018:
- // TODO: this shouldn't be instant, but uh
- val &= 0x003F;
- val |= ((val & 0x0001) << 14);
- StandbyCnt = val;
- printf("CAM%d STBCNT=%04X (%04X)\n", Num, StandbyCnt, val);
+ {
+ bool wasactive = IsActivated();
+ // TODO: this shouldn't be instant, but uh
+ val &= 0x003F;
+ val |= ((val & 0x0001) << 14);
+ StandbyCnt = val;
+ //printf("CAM%d STBCNT=%04X (%04X)\n", Num, StandbyCnt, val);
+ bool isactive = IsActivated();
+ if (isactive && !wasactive) Platform::Camera_Start(Num);
+ else if (wasactive && !isactive) Platform::Camera_Stop(Num);
+ }
return;
case 0x001A:
- MiscCnt = val & 0x0B7B;
- printf("CAM%d MISCCNT=%04X (%04X)\n", Num, MiscCnt, val);
+ {
+ bool wasactive = IsActivated();
+ MiscCnt = val & 0x0B7B;
+ //printf("CAM%d MISCCNT=%04X (%04X)\n", Num, MiscCnt, val);
+ bool isactive = IsActivated();
+ if (isactive && !wasactive) Platform::Camera_Start(Num);
+ else if (wasactive && !isactive) Platform::Camera_Stop(Num);
+ }
+ return;
+
+ case 0x098C:
+ MCUAddr = val;
+ return;
+ case 0x0990:
+ case 0x0992:
+ case 0x0994:
+ case 0x0996:
+ case 0x0998:
+ case 0x099A:
+ case 0x099C:
+ case 0x099E:
+ addr -= 0x0990;
+ MCU_Write((MCUAddr & 0x7FFF) + addr, val&0xFF);
+ if (!(MCUAddr & (1<<15)))
+ MCU_Write((MCUAddr & 0x7FFF) + addr+1, val>>8);
return;
}
@@ -294,117 +706,122 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
}
-u8 DSi_Camera::Read8(u32 addr)
+// TODO: not sure at all what is the accessible range
+// or if there is any overlap in the address range
+
+u8 Camera::MCU_Read(u16 addr)
{
- //
+ addr &= 0x7FFF;
- printf("unknown DSi cam read8 %08X\n", addr);
- return 0;
+ return MCURegs[addr];
}
-u16 DSi_Camera::Read16(u32 addr)
+void Camera::MCU_Write(u16 addr, u8 val)
{
+ addr &= 0x7FFF;
+
switch (addr)
{
- case 0x04004200: return ModuleCnt;
- case 0x04004202: return Cnt;
+ case 0x2103: // SEQ_CMD
+ MCURegs[addr] = 0;
+ if (val == 2) MCURegs[0x2104] = 7; // capture mode
+ else if (val == 1) MCURegs[0x2104] = 3; // preview mode
+ else if (val != 5 && val != 6)
+ printf("CAM%d: atypical SEQ_CMD %04X\n", Num, val);
+ return;
+
+ case 0x2104: // SEQ_STATE, read-only
+ return;
}
- printf("unknown DSi cam read16 %08X\n", addr);
- return 0;
+ MCURegs[addr] = val;
}
-u32 DSi_Camera::Read32(u32 addr)
+
+void Camera::InputFrame(u32* data, int width, int height, bool rgb)
{
- switch (addr)
+ // TODO: double-buffering?
+
+ if (width == 640 && height == 480 && !rgb)
{
- case 0x04004204:
+ memcpy(FrameBuffer, data, (640*480/2)*sizeof(u32));
+ return;
+ }
+
+ if (rgb)
+ {
+ for (int dy = 0; dy < 480; dy++)
{
- // TODO
- return 0xFC00801F;
- /*if (!(Cnt & (1<<15))) return 0; // CHECKME
- u32 ret = *(u32*)&FrameBuffer[TransferPos];
- TransferPos += 4;
- if (TransferPos >= FrameLength) TransferPos = 0;
- dorp += 4;
- //if (dorp >= (256*4*2))
- if (TransferPos == 0)
+ int sy = (dy * height) / 480;
+
+ for (int dx = 0; dx < 640; dx+=2)
{
- dorp = 0;
- Cnt &= ~(1<<4);
- }
- return ret;*/
- }
- }
+ int sx;
- printf("unknown DSi cam read32 %08X\n", addr);
- return 0;
-}
+ sx = (dx * width) / 640;
+ u32 pixel1 = data[sy*width + sx];
-void DSi_Camera::Write8(u32 addr, u8 val)
-{
- //
+ sx = ((dx+1) * width) / 640;
+ u32 pixel2 = data[sy*width + sx];
- printf("unknown DSi cam write8 %08X %02X\n", addr, val);
-}
+ int r1 = (pixel1 >> 16) & 0xFF;
+ int g1 = (pixel1 >> 8) & 0xFF;
+ int b1 = pixel1 & 0xFF;
-void DSi_Camera::Write16(u32 addr, u16 val)
-{
- switch (addr)
- {
- case 0x04004200:
- {
- u16 oldcnt = ModuleCnt;
- ModuleCnt = val;
+ int r2 = (pixel2 >> 16) & 0xFF;
+ int g2 = (pixel2 >> 8) & 0xFF;
+ int b2 = pixel2 & 0xFF;
- if ((ModuleCnt & (1<<1)) && !(oldcnt & (1<<1)))
- {
- // reset shit to zero
- // CHECKME
+ int y1 = ((r1 * 19595) + (g1 * 38470) + (b1 * 7471)) >> 16;
+ int u1 = ((b1 - y1) * 32244) >> 16;
+ int v1 = ((r1 - y1) * 57475) >> 16;
- Cnt = 0;
- }
+ int y2 = ((r2 * 19595) + (g2 * 38470) + (b2 * 7471)) >> 16;
+ int u2 = ((b2 - y2) * 32244) >> 16;
+ int v2 = ((r2 - y2) * 57475) >> 16;
- if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5)))
- {
- // TODO: reset I2C??
+ u1 += 128; v1 += 128;
+ u2 += 128; v2 += 128;
+
+ y1 = std::clamp(y1, 0, 255); u1 = std::clamp(u1, 0, 255); v1 = std::clamp(v1, 0, 255);
+ y2 = std::clamp(y2, 0, 255); u2 = std::clamp(u2, 0, 255); v2 = std::clamp(v2, 0, 255);
+
+ // huh
+ u1 = (u1 + u2) >> 1;
+ v1 = (v1 + v2) >> 1;
+
+ FrameBuffer[(dy*640 + dx) / 2] = y1 | (u1 << 8) | (y2 << 16) | (v1 << 24);
}
}
- return;
-
- case 0x04004202:
+ }
+ else
+ {
+ for (int dy = 0; dy < 480; dy++)
{
- // checkme
- u16 oldmask;
- if (Cnt & 0x8000)
- {
- val &= 0x8F20;
- oldmask = 0x601F;
- }
- else
- {
- val &= 0xEF2F;
- oldmask = 0x0010;
- }
-
- Cnt = (Cnt & oldmask) | (val & ~0x0020);
- if (val & (1<<5)) Cnt &= ~(1<<4);
+ int sy = (dy * height) / 480;
- if ((val & (1<<15)) && !(Cnt & (1<<15)))
+ for (int dx = 0; dx < 640; dx+=2)
{
- // start transfer
- //DSi::CheckNDMAs(0, 0x0B);
+ int sx = (dx * width) / 640;
+
+ FrameBuffer[(dy*640 + dx) / 2] = data[(sy*width + sx) / 2];
}
}
- return;
}
+}
- printf("unknown DSi cam write16 %08X %04X\n", addr, val);
}
-void DSi_Camera::Write32(u32 addr, u32 val)
-{
- //
- printf("unknown DSi cam write32 %08X %08X\n", addr, val);
-}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h
index 3f39e3a..bf18e59 100644
--- a/src/DSi_Camera.h
+++ b/src/DSi_Camera.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -20,35 +20,57 @@
#define DSI_CAMERA_H
#include "types.h"
+#include "Savestate.h"
-class DSi_Camera
+namespace DSi_CamModule
{
-public:
- static bool Init();
- static void DeInit();
- static void Reset();
- static void IRQ(u32 param);
- static void RequestFrame(u32 cam);
+class Camera;
+
+extern Camera* Camera0;
+extern Camera* Camera1;
+
+bool Init();
+void DeInit();
+void Reset();
+void Stop();
+
+void DoSavestate(Savestate* file);
- static void Transfer(u32 pos);
+void IRQ(u32 param);
- DSi_Camera(u32 num);
- ~DSi_Camera();
+void TransferScanline(u32 line);
- void ResetCam();
+u8 Read8(u32 addr);
+u16 Read16(u32 addr);
+u32 Read32(u32 addr);
+void Write8(u32 addr, u8 val);
+void Write16(u32 addr, u16 val);
+void Write32(u32 addr, u32 val);
+
+class Camera
+{
+public:
+ Camera(u32 num);
+ ~Camera();
+
+ void DoSavestate(Savestate* file);
+
+ void Reset();
+ void Stop();
bool IsActivated();
+ void StartTransfer();
+ bool TransferDone();
+
+ // lengths in words
+ int TransferScanline(u32* buffer, int maxlen);
+
void I2C_Start();
u8 I2C_Read(bool last);
void I2C_Write(u8 val, bool last);
- static u8 Read8(u32 addr);
- static u16 Read16(u32 addr);
- static u32 Read32(u32 addr);
- static void Write8(u32 addr, u8 val);
- static void Write16(u32 addr, u16 val);
- static void Write32(u32 addr, u32 val);
+ void InputFrame(u32* data, int width, int height, bool rgb);
u32 Num;
@@ -68,20 +90,17 @@ private:
u16 MiscCnt;
u16 MCUAddr;
- u16* MCUData;
-
u8 MCURegs[0x8000];
- static u16 ModuleCnt;
- static u16 Cnt;
+ u8 MCU_Read(u16 addr);
+ void MCU_Write(u16 addr, u8 val);
- static u8 FrameBuffer[640*480*4];
- static u32 TransferPos;
- static u32 FrameLength;
+ u16 FrameWidth, FrameHeight;
+ u16 FrameReadMode, FrameFormat;
+ int TransferY;
+ u32 FrameBuffer[640*480/2]; // YUYV framebuffer, two pixels per word
};
-
-extern DSi_Camera* DSi_Camera0;
-extern DSi_Camera* DSi_Camera1;
+}
#endif // DSI_CAMERA_H
diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp
index 328f01d..0525366 100644
--- a/src/DSi_DSP.cpp
+++ b/src/DSi_DSP.cpp
@@ -27,6 +27,9 @@
namespace DSi_DSP
{
+// not sure whether to not rather put it somewhere else
+u16 SNDExCnt;
+
Teakra::Teakra* TeakraCore;
bool SCFG_RST;
@@ -151,6 +154,8 @@ void Reset()
TeakraCore->Reset();
NDS::CancelEvent(NDS::Event_DSi_DSP);
+
+ SNDExCnt = 0;
}
bool IsRstReleased()
@@ -548,6 +553,21 @@ void Write32(u32 addr, u32 val)
Write16(addr, val & 0xFFFF);
}
+void WriteSNDExCnt(u16 val)
+{
+ // it can be written even in NDS mode
+
+ // mic frequency can only be changed if it was disabled
+ // before the write
+ if (SNDExCnt & 0x8000)
+ {
+ val &= ~0x2000;
+ val |= SNDExCnt & 0x2000;
+ }
+
+ SNDExCnt = val & 0xE00F;
+}
+
void Run(u32 cycles)
{
if (!IsDSPCoreEnabled())
@@ -587,6 +607,8 @@ void DoSavestate(Savestate* file)
file->Var16(&DSP_REP[1]);
file->Var16(&DSP_REP[2]);
file->Var8((u8*)&SCFG_RST);
+
+ // TODO: save the Teakra state!!!
}
}
diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h
index f5264b4..5d3427f 100644
--- a/src/DSi_DSP.h
+++ b/src/DSi_DSP.h
@@ -19,13 +19,17 @@
#ifndef DSI_DSP_H
#define DSI_DSP_H
+#include "types.h"
+#include "Savestate.h"
+
// TODO: for actual sound output
// * audio callbacks
-// * SNDEXCNT
namespace DSi_DSP
{
+extern u16 SNDExCnt;
+
extern u16 DSP_PDATA;
extern u16 DSP_PADR;
extern u16 DSP_PCFG;
@@ -41,10 +45,10 @@ bool Init();
void DeInit();
void Reset();
-// TODO: needs to be called!
-// however, no DSi savestate stuff seems to be actually implemented?!
void DoSavestate(Savestate* file);
+void DSPCatchUpU32(u32 _);
+
// SCFG_RST bit0
bool IsRstReleased();
void SetRstLine(bool release);
@@ -62,6 +66,8 @@ void Write16(u32 addr, u16 val);
u32 Read32(u32 addr);
void Write32(u32 addr, u32 val);
+void WriteSNDExCnt(u16 val);
+
// NOTE: checks SCFG_CLK9
void Run(u32 cycles);
diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp
index 6f5f2e5..5889bef 100644
--- a/src/DSi_I2C.cpp
+++ b/src/DSi_I2C.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -22,6 +22,7 @@
#include "DSi_I2C.h"
#include "DSi_Camera.h"
#include "ARM.h"
+#include "SPI.h"
namespace DSi_BPTWL
@@ -72,8 +73,29 @@ void Reset()
Registers[0x81] = 0x64;
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("I2BP");
+
+ file->VarArray(Registers, 0x100);
+ file->Var32(&CurPos);
+}
+
u8 GetBootFlag() { return Registers[0x70]; }
+bool GetBatteryCharging() { return Registers[0x20] >> 7; }
+void SetBatteryCharging(bool charging)
+{
+ Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F));
+}
+
+u8 GetBatteryLevel() { return Registers[0x20] & 0xF; }
+void SetBatteryLevel(u8 batteryLevel)
+{
+ Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F));
+ SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false);
+}
+
void Start()
{
//printf("BPTWL: start\n");
@@ -147,7 +169,6 @@ u32 Device;
bool Init()
{
if (!DSi_BPTWL::Init()) return false;
- if (!DSi_Camera::Init()) return false;
return true;
}
@@ -155,7 +176,6 @@ bool Init()
void DeInit()
{
DSi_BPTWL::DeInit();
- DSi_Camera::DeInit();
}
void Reset()
@@ -166,12 +186,22 @@ void Reset()
Device = -1;
DSi_BPTWL::Reset();
- DSi_Camera::Reset();
+}
+
+void DoSavestate(Savestate* file)
+{
+ file->Section("I2Ci");
+
+ file->Var8(&Cnt);
+ file->Var8(&Data);
+ file->Var32(&Device);
+
+ DSi_BPTWL::DoSavestate(file);
}
void WriteCnt(u8 val)
{
- //printf("I2C: write CNT %02X, %08X\n", val, NDS::GetPC(1));
+ //printf("I2C: write CNT %02X, %02X, %08X\n", val, Data, NDS::GetPC(1));
// TODO: check ACK flag
// TODO: transfer delay
@@ -190,8 +220,8 @@ void WriteCnt(u8 val)
switch (Device)
{
case 0x4A: Data = DSi_BPTWL::Read(islast); break;
- case 0x78: Data = DSi_Camera0->I2C_Read(islast); break;
- case 0x7A: Data = DSi_Camera1->I2C_Read(islast); break;
+ case 0x78: Data = DSi_CamModule::Camera0->I2C_Read(islast); break;
+ case 0x7A: Data = DSi_CamModule::Camera1->I2C_Read(islast); break;
case 0xA0:
case 0xE0: Data = 0xFF; break;
default:
@@ -216,8 +246,8 @@ void WriteCnt(u8 val)
switch (Device)
{
case 0x4A: DSi_BPTWL::Start(); break;
- case 0x78: DSi_Camera0->I2C_Start(); break;
- case 0x7A: DSi_Camera1->I2C_Start(); break;
+ case 0x78: DSi_CamModule::Camera0->I2C_Start(); break;
+ case 0x7A: DSi_CamModule::Camera1->I2C_Start(); break;
case 0xA0:
case 0xE0: ack = false; break;
default:
@@ -233,8 +263,8 @@ void WriteCnt(u8 val)
switch (Device)
{
case 0x4A: DSi_BPTWL::Write(Data, islast); break;
- case 0x78: DSi_Camera0->I2C_Write(Data, islast); break;
- case 0x7A: DSi_Camera1->I2C_Write(Data, islast); break;
+ case 0x78: DSi_CamModule::Camera0->I2C_Write(Data, islast); break;
+ case 0x7A: DSi_CamModule::Camera1->I2C_Write(Data, islast); break;
case 0xA0:
case 0xE0: ack = false; break;
default:
diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h
index c064366..48c8e88 100644
--- a/src/DSi_I2C.h
+++ b/src/DSi_I2C.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,11 +19,29 @@
#ifndef DSI_I2C_H
#define DSI_I2C_H
+#include "types.h"
+#include "Savestate.h"
+
namespace DSi_BPTWL
{
u8 GetBootFlag();
+bool GetBatteryCharging();
+void SetBatteryCharging(bool charging);
+
+enum
+{
+ batteryLevel_Critical = 0x0,
+ batteryLevel_AlmostEmpty = 0x1,
+ batteryLevel_Low = 0x3,
+ batteryLevel_Half = 0x7,
+ batteryLevel_ThreeQuarters = 0xB,
+ batteryLevel_Full = 0xF
+};
+
+u8 GetBatteryLevel();
+void SetBatteryLevel(u8 batteryLevel);
}
namespace DSi_I2C
@@ -34,7 +52,7 @@ extern u8 Cnt;
bool Init();
void DeInit();
void Reset();
-//void DoSavestate(Savestate* file);
+void DoSavestate(Savestate* file);
void WriteCnt(u8 val);
diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp
index 4366ce6..912fee4 100644
--- a/src/DSi_NAND.cpp
+++ b/src/DSi_NAND.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -49,8 +49,48 @@ UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num);
UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num);
-bool Init(FILE* nandfile, u8* es_keyY)
+bool Init(u8* es_keyY)
{
+ CurFile = nullptr;
+
+ std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath);
+ std::string instnand = nandpath + Platform::InstanceFileSuffix();
+
+ FILE* nandfile = Platform::OpenLocalFile(instnand, "r+b");
+ if ((!nandfile) && (Platform::InstanceID() > 0))
+ {
+ FILE* orig = Platform::OpenLocalFile(nandpath, "rb");
+ if (!orig)
+ {
+ printf("Failed to open DSi NAND\n");
+ return false;
+ }
+
+ fseek(orig, 0, SEEK_END);
+ long len = ftell(orig);
+ fseek(orig, 0, SEEK_SET);
+
+ nandfile = Platform::OpenLocalFile(instnand, "w+b");
+ if (nandfile)
+ {
+ u8* tmpbuf = new u8[0x10000];
+ for (long i = 0; i < len; i+=0x10000)
+ {
+ long blklen = 0x10000;
+ if ((i+blklen) > len) blklen = len-i;
+
+ fread(tmpbuf, blklen, 1, orig);
+ fwrite(tmpbuf, blklen, 1, nandfile);
+ }
+ delete[] tmpbuf;
+ }
+
+ fclose(orig);
+ fclose(nandfile);
+
+ nandfile = Platform::OpenLocalFile(instnand, "r+b");
+ }
+
if (!nandfile)
return false;
@@ -138,10 +178,17 @@ void DeInit()
f_unmount("0:");
ff_disk_close();
+ if (CurFile) fclose(CurFile);
CurFile = nullptr;
}
+FILE* GetFile()
+{
+ return CurFile;
+}
+
+
void GetIDs(u8* emmc_cid, u64& consoleid)
{
memcpy(emmc_cid, eMMC_CID, 16);
@@ -919,29 +966,44 @@ bool CreateSaveFile(const char* path, u32 len)
u32 clustersize, maxfiles, totsec16, fatsz16;
// CHECKME!
- // code inspired from https://github.com/JeffRuLz/TMFH/blob/master/arm9/src/sav.c
- if (len < 573440)
- {
- clustersize = 512;
- maxfiles = 16;
- }
- else if (len < 5472256)
+ // code inspired from https://github.com/Epicpkmn11/NTM/blob/master/arm9/src/sav.c
+ const u16 sectorsize = 0x200;
+
+ // fit maximum sectors for the size
+ const u16 maxsectors = len / sectorsize;
+ u16 tracksize = 1;
+ u16 headcount = 1;
+ u16 totsec16next = 0;
+ while (totsec16next <= maxsectors)
{
- clustersize = 2048;
- maxfiles = 256;
+ totsec16next = tracksize * (headcount + 1) * (headcount + 1);
+ if (totsec16next <= maxsectors)
+ {
+ headcount++;
+ totsec16 = totsec16next;
+
+ tracksize++;
+ totsec16next = tracksize * headcount * headcount;
+ if (totsec16next <= maxsectors)
+ {
+ totsec16 = totsec16next;
+ }
+ }
}
- else
+ totsec16next = (tracksize + 1) * headcount * headcount;
+ if (totsec16next <= maxsectors)
{
- clustersize = 4096;
- maxfiles = 256;
+ tracksize++;
+ totsec16 = totsec16next;
}
- if (len <= 0x4000) fatsz16 = 1;
- else if (len <= 0x200000) fatsz16 = 3;
- else fatsz16 = 6;
+ maxfiles = len < 0x8C000 ? 0x20 : 0x200;
+ clustersize = (totsec16 > (8 << 10)) ? 8 : (totsec16 > (1 << 10) ? 4 : 1);
- if (len == 0x4000) totsec16 = 27;
- else totsec16 = len >> 9;
+ #define ALIGN(v, a) (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
+ u16 totalclusters = ALIGN(totsec16, clustersize) / clustersize;
+ u32 fatbytes = (ALIGN(totalclusters, 2) / 2) * 3; // 2 sectors -> 3 byte
+ fatsz16 = ALIGN(fatbytes, sectorsize) / sectorsize;
FF_FIL file;
FRESULT res;
@@ -960,17 +1022,19 @@ bool CreateSaveFile(const char* path, u32 len)
// create FAT header
data[0x000] = 0xE9;
memcpy(&data[0x003], "MSWIN4.1", 8);
- *(u16*)&data[0x00B] = 512; // bytes per sector
- data[0x00D] = clustersize >> 9;
+ *(u16*)&data[0x00B] = sectorsize; // bytes per sector
+ data[0x00D] = clustersize;
*(u16*)&data[0x00E] = 1; // reserved sectors
data[0x010] = 2; // num FATs
- *(u16*)&data[0x011] = maxfiles << 1;
+ *(u16*)&data[0x011] = maxfiles;
*(u16*)&data[0x013] = totsec16;
data[0x015] = 0xF8;
*(u16*)&data[0x016] = fatsz16;
- data[0x024] = 0x07;
+ *(u16*)&data[0x018] = tracksize;
+ *(u16*)&data[0x01A] = headcount;
+ data[0x024] = 0x05;
data[0x026] = 0x29;
- *(u32*)&data[0x027] = 305419896;
+ *(u32*)&data[0x027] = 0x12345678;
memcpy(&data[0x02B], "VOLUMELABEL", 11);
memcpy(&data[0x036], "FAT12 ", 8);
*(u16*)&data[0x1FE] = 0xAA55;
diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h
index 64c40fa..a23e62f 100644
--- a/src/DSi_NAND.h
+++ b/src/DSi_NAND.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -34,9 +34,11 @@ enum
TitleData_BannerSav,
};
-bool Init(FILE* nand, u8* es_keyY);
+bool Init(u8* es_keyY);
void DeInit();
+FILE* GetFile();
+
void GetIDs(u8* emmc_cid, u64& consoleid);
void ReadHardwareInfo(u8* dataS, u8* dataN);
diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp
index 37d5018..ca834eb 100644
--- a/src/DSi_NDMA.cpp
+++ b/src/DSi_NDMA.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -63,7 +63,31 @@ void DSi_NDMA::Reset()
void DSi_NDMA::DoSavestate(Savestate* file)
{
- // TODO!
+ char magic[5] = "NDMx";
+ magic[3] = '0' + Num + (CPU*4);
+ file->Section(magic);
+
+ file->Var32(&SrcAddr);
+ file->Var32(&DstAddr);
+ file->Var32(&TotalLength);
+ file->Var32(&BlockLength);
+ file->Var32(&SubblockTimer);
+ file->Var32(&FillData);
+ file->Var32(&Cnt);
+
+ file->Var32(&StartMode);
+ file->Var32(&CurSrcAddr);
+ file->Var32(&CurDstAddr);
+ file->Var32(&SubblockLength);
+ file->Var32(&RemCount);
+ file->Var32(&IterCount);
+ file->Var32(&TotalRemCount);
+ file->Var32(&SrcAddrInc);
+ file->Var32(&DstAddrInc);
+
+ file->Var32(&Running);
+ file->Bool32(&InProgress);
+ file->Bool32(&IsGXFIFODMA);
}
void DSi_NDMA::WriteCnt(u32 val)
@@ -99,10 +123,17 @@ void DSi_NDMA::WriteCnt(u32 val)
if ((StartMode & 0x1F) == 0x10)
Start();
+ else if (StartMode == 0x0A)
+ GPU3D::CheckFIFODMA();
- if (StartMode != 0x10 && StartMode != 0x30 &&
- StartMode != 0x04 && StartMode != 0x06 && StartMode != 0x07 && StartMode != 0x08 && StartMode != 0x09 && StartMode != 0x0B &&
- StartMode != 0x24 && StartMode != 0x26 && StartMode != 0x28 && StartMode != 0x29 && StartMode != 0x2A && StartMode != 0x2B)
+ // TODO: unsupported start modes:
+ // * timers (00-03)
+ // * camera (ARM9 0B)
+ // * microphone (ARM7 0C)
+ // * NDS-wifi?? (ARM7 07, likely not working)
+
+ if (StartMode <= 0x03 || StartMode == 0x05 || (StartMode >= 0x0C && StartMode <= 0x0F) ||
+ (StartMode >= 0x20 && StartMode <= 0x23) || StartMode == 0x25 || StartMode == 0x27 || (StartMode >= 0x2C && StartMode <= 0x2F))
printf("UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X LEN=%d BLK=%d CNT=%08X\n",
CPU?7:9, Num, StartMode, SrcAddr, DstAddr, TotalLength, BlockLength, Cnt);
}
@@ -119,8 +150,12 @@ void DSi_NDMA::Start()
RemCount = 0x1000000;
}
- // TODO: how does GXFIFO DMA work with all the block shito?
- IterCount = RemCount;
+ // CHECKME: this is assumed to work the same as the old DMA version
+ // also not really certain how this interacts with the block subdivision system here
+ if (StartMode == 0x0A && RemCount > 112)
+ IterCount = 112;
+ else
+ IterCount = RemCount;
if (((StartMode & 0x1F) != 0x10) && !(Cnt & (1<<29)))
{
diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h
index d4d0a8b..b1ea4c9 100644
--- a/src/DSi_NDMA.h
+++ b/src/DSi_NDMA.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -20,6 +20,7 @@
#define DSI_NDMA_H
#include "types.h"
+#include "Savestate.h"
class DSi_NDMA
{
diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp
index 57bd93c..7f8be83 100644
--- a/src/DSi_NWifi.cpp
+++ b/src/DSi_NWifi.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -223,6 +223,45 @@ void DSi_NWifi::Reset()
NDS::CancelEvent(NDS::Event_DSi_NWifi);
}
+void DSi_NWifi::DoSavestate(Savestate* file)
+{
+ file->Section("NWFi");
+
+ for (int i = 0; i < 9; i++)
+ Mailbox[i].DoSavestate(file);
+
+ file->Var8(&F0_IRQEnable);
+ file->Var8(&F0_IRQStatus);
+
+ file->Var8(&F1_IRQEnable);
+ file->Var8(&F1_IRQEnable_CPU);
+ file->Var8(&F1_IRQEnable_Error);
+ file->Var8(&F1_IRQEnable_Counter);
+ file->Var8(&F1_IRQStatus);
+ file->Var8(&F1_IRQStatus_CPU);
+ file->Var8(&F1_IRQStatus_Error);
+ file->Var8(&F1_IRQStatus_Counter);
+
+ file->Var32(&WindowData);
+ file->Var32(&WindowReadAddr);
+ file->Var32(&WindowWriteAddr);
+
+ file->Var32(&ROMID);
+ file->Var32(&ChipID);
+ file->Var32(&HostIntAddr);
+
+ file->VarArray(EEPROM, 0x400);
+ file->Var32(&EEPROMReady);
+
+ file->Var32(&BootPhase);
+
+ file->Var32(&ErrorMask);
+ file->Var32(&ScanTimer);
+
+ file->Var64(&BeaconTimer);
+ file->Var32(&ConnectionStatus);
+}
+
// CHECKME
// can IRQ status bits be set when the corresponding IRQs are disabled in the enable register?
diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h
index da6597d..ffd5647 100644
--- a/src/DSi_NWifi.h
+++ b/src/DSi_NWifi.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -21,6 +21,7 @@
#include "DSi_SD.h"
#include "FIFO.h"
+#include "Savestate.h"
class DSi_NWifi : public DSi_SDDevice
{
@@ -30,6 +31,8 @@ public:
void Reset();
+ void DoSavestate(Savestate* file);
+
void SendCMD(u8 cmd, u32 param);
void SendACMD(u8 cmd, u32 param);
diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp
index 4c2b085..e603347 100644
--- a/src/DSi_SD.cpp
+++ b/src/DSi_SD.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -51,8 +51,8 @@ DSi_SDHost::DSi_SDHost(u32 num)
{
Num = num;
- Ports[0] = NULL;
- Ports[1] = NULL;
+ Ports[0] = nullptr;
+ Ports[1] = nullptr;
}
DSi_SDHost::~DSi_SDHost()
@@ -61,6 +61,14 @@ DSi_SDHost::~DSi_SDHost()
if (Ports[1]) delete Ports[1];
}
+void DSi_SDHost::CloseHandles()
+{
+ if (Ports[0]) delete Ports[0];
+ if (Ports[1]) delete Ports[1];
+ Ports[0] = nullptr;
+ Ports[1] = nullptr;
+}
+
void DSi_SDHost::Reset()
{
if (Num == 0)
@@ -101,10 +109,7 @@ void DSi_SDHost::Reset()
TXReq = false;
- if (Ports[0]) delete Ports[0];
- if (Ports[1]) delete Ports[1];
- Ports[0] = nullptr;
- Ports[1] = nullptr;
+ CloseHandles();
if (Num == 0)
{
@@ -131,7 +136,10 @@ void DSi_SDHost::Reset()
else
sd = nullptr;
- mmc = new DSi_MMCStorage(this, true, DSi::SDMMCFile);
+ std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath);
+ std::string instnand = nandpath + Platform::InstanceFileSuffix();
+
+ mmc = new DSi_MMCStorage(this, true, instnand);
mmc->SetCID(DSi::eMMC_CID);
Ports[0] = sd;
@@ -150,7 +158,41 @@ void DSi_SDHost::Reset()
void DSi_SDHost::DoSavestate(Savestate* file)
{
- // TODO!
+ file->Section(Num ? "SDIO" : "SDMM");
+
+ file->Var16(&PortSelect);
+ file->Var16(&SoftReset);
+ file->Var16(&SDClock);
+ file->Var16(&SDOption);
+
+ file->Var32(&IRQStatus);
+ file->Var32(&IRQMask);
+
+ file->Var16(&CardIRQStatus);
+ file->Var16(&CardIRQMask);
+ file->Var16(&CardIRQCtl);
+
+ file->Var16(&DataCtl);
+ file->Var16(&Data32IRQ);
+ file->Var32(&DataMode);
+ file->Var16(&BlockCount16);
+ file->Var16(&BlockCount32);
+ file->Var16(&BlockCountInternal);
+ file->Var16(&BlockLen16);
+ file->Var16(&BlockLen32);
+ file->Var16(&StopAction);
+
+ file->Var16(&Command);
+ file->Var32(&Param);
+ file->VarArray(ResponseBuffer, 8);
+
+ file->Var32(&CurFIFO);
+ DataFIFO[0].DoSavestate(file);
+ DataFIFO[1].DoSavestate(file);
+ DataFIFO32.DoSavestate(file);
+
+ if (Ports[0]) Ports[0]->DoSavestate(file);
+ if (Ports[1]) Ports[1]->DoSavestate(file);
}
@@ -438,16 +480,14 @@ u16 DSi_SDHost::Read(u32 addr)
{
if (Ports[0]) // basic check of whether the SD card is inserted
{
- ret |= 0x0030;
+ ret |= 0x0020;
if (!Ports[0]->ReadOnly) ret |= 0x0080;
}
- else
- ret |= 0x0008;
}
else
{
// SDIO wifi is always inserted, I guess
- ret |= 0x00B0;
+ ret |= 0x00A0;
}
return ret;
}
@@ -727,12 +767,15 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard")
-DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file)
+DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename)
: DSi_SDDevice(host)
{
Internal = internal;
- File = file;
+ File = Platform::OpenLocalFile(filename, "r+b");
+
SD = nullptr;
+
+ ReadOnly = false;
}
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir)
@@ -754,6 +797,10 @@ DSi_MMCStorage::~DSi_MMCStorage()
SD->Close();
delete SD;
}
+ if (File)
+ {
+ fclose(File);
+ }
}
void DSi_MMCStorage::Reset()
@@ -781,6 +828,26 @@ void DSi_MMCStorage::Reset()
RWCommand = 0;
}
+void DSi_MMCStorage::DoSavestate(Savestate* file)
+{
+ file->Section(Internal ? "NAND" : "SDCR");
+
+ file->VarArray(CID, 16);
+ file->VarArray(CSD, 16);
+
+ file->Var32(&CSR);
+ file->Var32(&OCR);
+ file->Var32(&RCA);
+ file->VarArray(SCR, 8);
+ file->VarArray(SSR, 64);
+
+ file->Var32(&BlockSize);
+ file->Var64(&RWAddress);
+ file->Var32(&RWCommand);
+
+ // TODO: what about the file contents?
+}
+
void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
{
if (CSR & (1<<5))
diff --git a/src/DSi_SD.h b/src/DSi_SD.h
index 1c0f7ce..5f6dbcd 100644
--- a/src/DSi_SD.h
+++ b/src/DSi_SD.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,10 +19,10 @@
#ifndef DSI_SD_H
#define DSI_SD_H
-#include <string.h>
+#include <cstring>
#include "FIFO.h"
#include "FATStorage.h"
-
+#include "Savestate.h"
class DSi_SDDevice;
@@ -33,6 +33,7 @@ public:
DSi_SDHost(u32 num);
~DSi_SDHost();
+ void CloseHandles();
void Reset();
void DoSavestate(Savestate* file);
@@ -108,6 +109,8 @@ public:
virtual void Reset() = 0;
+ virtual void DoSavestate(Savestate* file) = 0;
+
virtual void SendCMD(u8 cmd, u32 param) = 0;
virtual void ContinueTransfer() = 0;
@@ -122,12 +125,14 @@ protected:
class DSi_MMCStorage : public DSi_SDDevice
{
public:
- DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file);
+ DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename);
DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir);
~DSi_MMCStorage();
void Reset();
+ void DoSavestate(Savestate* file);
+
void SetCID(u8* cid) { memcpy(CID, cid, 16); }
void SendCMD(u8 cmd, u32 param);
diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp
index 061feb1..73d792b 100644
--- a/src/DSi_SPI_TSC.cpp
+++ b/src/DSi_SPI_TSC.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -71,14 +71,15 @@ void Reset()
void DoSavestate(Savestate* file)
{
- /*file->Section("SPTi");
+ file->Section("SPTi");
file->Var32(&DataPos);
- file->Var8(&ControlByte);
+ file->Var8(&Index);
+ file->Var8(&Bank);
file->Var8(&Data);
- file->Var16(&ConvResult);*/
- // TODO!!
+ file->VarArray(Bank3Regs, 0x80);
+ file->Var8(&TSCMode);
}
void SetMode(u8 mode)
diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h
index 13b7883..7a3acf4 100644
--- a/src/DSi_SPI_TSC.h
+++ b/src/DSi_SPI_TSC.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,6 +19,9 @@
#ifndef DSI_SPI_TSC
#define DSI_SPI_TSC
+#include "types.h"
+#include "Savestate.h"
+
namespace DSi_SPI_TSC
{
diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp
index 80bc46e..5764524 100644
--- a/src/FATStorage.cpp
+++ b/src/FATStorage.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/FATStorage.h b/src/FATStorage.h
index 153c95a..29bcbc4 100644
--- a/src/FATStorage.h
+++ b/src/FATStorage.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/FIFO.h b/src/FIFO.h
index 7f71a72..80d9b8a 100644
--- a/src/FIFO.h
+++ b/src/FIFO.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -20,6 +20,7 @@
#define FIFO_H
#include "types.h"
+#include "Savestate.h"
template<typename T, u32 NumEntries>
class FIFO
diff --git a/src/FreeBIOS.h b/src/FreeBIOS.h
index 3d54c78..965d43b 100644
--- a/src/FreeBIOS.h
+++ b/src/FreeBIOS.h
@@ -31,7 +31,7 @@
unsigned char bios_arm7_bin[] = {
0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea,
0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea,
- 0xb8, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
+ 0xe3, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -381,22 +381,22 @@ unsigned char bios_arm7_bin[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea,
- 0x10, 0x50, 0x2d, 0xe9, 0x00, 0x40, 0x4f, 0xe1, 0x04, 0x40, 0x2d, 0xe5,
+ 0x10, 0x50, 0x2d, 0xe9, 0x00, 0x40, 0x4f, 0xe1, 0x10, 0x00, 0x2d, 0xe9,
0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5,
- 0x04, 0xf0, 0x29, 0xe1, 0x04, 0xe0, 0x2d, 0xe5, 0x20, 0x00, 0x5c, 0xe3,
+ 0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3,
0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1,
- 0x28, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
+ 0xd4, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
0x5c, 0x11, 0x00, 0x00, 0x90, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00,
0x4c, 0x11, 0x00, 0x00, 0xbc, 0x11, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x00,
0xe8, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x44, 0x12, 0x00, 0x00,
0xc4, 0x12, 0x00, 0x00, 0x04, 0x13, 0x00, 0x00, 0x50, 0x13, 0x00, 0x00,
0xe8, 0x13, 0x00, 0x00, 0xf0, 0x13, 0x00, 0x00, 0x84, 0x14, 0x00, 0x00,
- 0x84, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x04, 0x15, 0x00, 0x00,
- 0x04, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
- 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0xdc, 0x15, 0x00, 0x00,
- 0xf0, 0x1b, 0x00, 0x00, 0x18, 0x1f, 0x00, 0x00, 0xd8, 0x1e, 0x00, 0x00,
- 0xf4, 0x1e, 0x00, 0x00, 0x04, 0xe0, 0x9d, 0xe4, 0xd3, 0x40, 0xa0, 0xe3,
- 0x04, 0xf0, 0x29, 0xe1, 0x04, 0x40, 0x9d, 0xe4, 0x04, 0xf0, 0x69, 0xe1,
+ 0x00, 0x15, 0x00, 0x00, 0xac, 0x15, 0x00, 0x00, 0xb0, 0x15, 0x00, 0x00,
+ 0xb0, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
+ 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x88, 0x16, 0x00, 0x00,
+ 0x9c, 0x1c, 0x00, 0x00, 0xc4, 0x1f, 0x00, 0x00, 0x84, 0x1f, 0x00, 0x00,
+ 0xa0, 0x1f, 0x00, 0x00, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3,
+ 0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1,
0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
0x01, 0x03, 0xa0, 0xe3, 0x80, 0x20, 0xa0, 0xe3, 0x01, 0x23, 0xc0, 0xe5,
0xf3, 0xff, 0xff, 0xea, 0x01, 0x00, 0x50, 0xe2, 0xfd, 0xff, 0xff, 0xca,
@@ -441,7 +441,7 @@ unsigned char bios_arm7_bin[] = {
0x7e, 0xff, 0xff, 0xea, 0x00, 0x00, 0x01, 0xcc, 0x01, 0xd8, 0x00, 0x14,
0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c,
0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44,
- 0x04, 0x50, 0x2d, 0xe5, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2,
+ 0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2,
0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0,
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
@@ -453,7 +453,7 @@ unsigned char bios_arm7_bin[] = {
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a,
- 0x04, 0x50, 0x9d, 0xe4, 0x50, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3,
+ 0x20, 0x00, 0xbd, 0xe8, 0x50, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3,
0x4e, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1,
0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5,
0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3,
@@ -477,156 +477,170 @@ unsigned char bios_arm7_bin[] = {
0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4,
0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x0d, 0xff, 0xff, 0x0a,
0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea,
- 0x09, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1,
- 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2,
- 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2,
- 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xfe, 0xfe, 0xff, 0x0a,
- 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea,
- 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
- 0x01, 0xc0, 0xc1, 0xe4, 0xf6, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2,
- 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x00, 0x00, 0x24, 0x03,
- 0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f, 0xc8, 0x12, 0xe2, 0x15,
- 0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22, 0x28, 0x25, 0x26, 0x28,
- 0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33, 0xba, 0x36, 0x8c, 0x39,
- 0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44, 0x1c, 0x47, 0xb4, 0x49,
- 0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53, 0xf5, 0x55, 0x42, 0x58,
- 0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60, 0xf1, 0x62, 0xe8, 0x64,
- 0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c, 0xc9, 0x6d, 0x5e, 0x6f,
- 0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75, 0x41, 0x76, 0x6b, 0x77,
- 0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b, 0x29, 0x7c, 0xe3, 0x7c,
- 0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f, 0x61, 0x7f, 0xa6, 0x7f,
- 0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0, 0x8c, 0x10, 0x4f, 0xe2,
- 0xb0, 0x00, 0x91, 0xe1, 0xcf, 0xfe, 0xff, 0xea, 0x00, 0x00, 0x3b, 0x00,
- 0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01, 0x64, 0x01, 0x9f, 0x01,
- 0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02, 0xca, 0x02, 0x05, 0x03,
- 0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03, 0x31, 0x04, 0x6e, 0x04,
- 0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05, 0x9b, 0x05, 0xd8, 0x05,
- 0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06, 0x07, 0x07, 0x43, 0x07,
- 0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08, 0x74, 0x08, 0xb1, 0x08,
- 0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09, 0xe4, 0x09, 0x21, 0x0a,
- 0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b, 0x56, 0x0b, 0x93, 0x0b,
- 0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c, 0xc9, 0x0c, 0x07, 0x0d,
- 0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e, 0x3f, 0x0e, 0x7d, 0x0e,
- 0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f, 0xb6, 0x0f, 0xf5, 0x0f,
- 0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10, 0x30, 0x11, 0x6f, 0x11,
- 0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12, 0xac, 0x12, 0xeb, 0x12,
- 0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13, 0x2a, 0x14, 0x6a, 0x14,
- 0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15, 0xaa, 0x15, 0xea, 0x15,
- 0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16, 0x2c, 0x17, 0x6c, 0x17,
- 0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18, 0xb0, 0x18, 0xf0, 0x18,
- 0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19, 0x36, 0x1a, 0x77, 0x1a,
- 0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b, 0xbe, 0x1b, 0x00, 0x1c,
- 0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d, 0x48, 0x1d, 0x8a, 0x1d,
- 0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e, 0xd5, 0x1e, 0x17, 0x1f,
- 0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20, 0x64, 0x20, 0xa6, 0x20,
- 0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21, 0xf5, 0x21, 0x38, 0x22,
- 0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23, 0x88, 0x23, 0xcb, 0x23,
- 0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24, 0x1d, 0x25, 0x61, 0x25,
- 0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26, 0xb4, 0x26, 0xf8, 0x26,
- 0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28, 0x4e, 0x28, 0x92, 0x28,
- 0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29, 0xea, 0x29, 0x2f, 0x2a,
- 0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b, 0x88, 0x2b, 0xcd, 0x2b,
- 0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c, 0x28, 0x2d, 0x6e, 0x2d,
- 0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e, 0xcb, 0x2e, 0x11, 0x2f,
- 0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30, 0x70, 0x30, 0xb6, 0x30,
- 0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31, 0x17, 0x32, 0x5e, 0x32,
- 0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33, 0xc1, 0x33, 0x08, 0x34,
- 0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35, 0x6c, 0x35, 0xb4, 0x35,
- 0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36, 0x1a, 0x37, 0x62, 0x37,
- 0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38, 0xcb, 0x38, 0x13, 0x39,
- 0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a, 0x7e, 0x3a, 0xc6, 0x3a,
- 0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b, 0x33, 0x3c, 0x7c, 0x3c,
- 0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d, 0xea, 0x3d, 0x34, 0x3e,
- 0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f, 0xa4, 0x3f, 0xee, 0x3f,
- 0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41, 0x61, 0x41, 0xab, 0x41,
- 0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42, 0x1f, 0x43, 0x6a, 0x43,
- 0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44, 0xe1, 0x44, 0x2c, 0x45,
- 0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46, 0xa4, 0x46, 0xf0, 0x46,
- 0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48, 0x6a, 0x48, 0xb6, 0x48,
- 0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49, 0x33, 0x4a, 0x7f, 0x4a,
- 0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b, 0xfe, 0x4b, 0x4a, 0x4c,
- 0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d, 0xcb, 0x4d, 0x18, 0x4e,
- 0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f, 0x9b, 0x4f, 0xe9, 0x4f,
- 0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51, 0x6e, 0x51, 0xbc, 0x51,
- 0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52, 0x43, 0x53, 0x91, 0x53,
- 0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54, 0x1a, 0x55, 0x69, 0x55,
- 0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56, 0xf4, 0x56, 0x44, 0x57,
- 0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58, 0xd1, 0x58, 0x21, 0x59,
- 0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a, 0xb0, 0x5a, 0x01, 0x5b,
- 0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c, 0x92, 0x5c, 0xe3, 0x5c,
- 0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e, 0x77, 0x5e, 0xc8, 0x5e,
- 0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60, 0x5e, 0x60, 0xb0, 0x60,
- 0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61, 0x48, 0x62, 0x9a, 0x62,
- 0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63, 0x34, 0x64, 0x87, 0x64,
- 0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65, 0x24, 0x66, 0x76, 0x66,
- 0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67, 0x15, 0x68, 0x69, 0x68,
- 0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69, 0x0a, 0x6a, 0x5e, 0x6a,
- 0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b, 0x01, 0x6c, 0x55, 0x6c,
- 0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d, 0xfb, 0x6d, 0x50, 0x6e,
- 0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f, 0xf8, 0x6f, 0x4d, 0x70,
- 0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71, 0xf7, 0x71, 0x4d, 0x72,
- 0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73, 0xfa, 0x73, 0x50, 0x74,
- 0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75, 0xff, 0x75, 0x55, 0x76,
- 0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77, 0x07, 0x78, 0x5e, 0x78,
- 0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79, 0x11, 0x7a, 0x69, 0x7a,
- 0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b, 0x1f, 0x7c, 0x77, 0x7c,
- 0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d, 0x2f, 0x7e, 0x88, 0x7e,
- 0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f, 0x42, 0x80, 0x9b, 0x80,
- 0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81, 0x59, 0x82, 0xb2, 0x82,
- 0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84, 0x72, 0x84, 0xcb, 0x84,
- 0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86, 0x8e, 0x86, 0xe8, 0x86,
- 0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88, 0xac, 0x88, 0x07, 0x89,
- 0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a, 0xce, 0x8a, 0x2a, 0x8b,
- 0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c, 0xf3, 0x8c, 0x4f, 0x8d,
- 0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e, 0x1b, 0x8f, 0x77, 0x8f,
- 0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90, 0x46, 0x91, 0xa2, 0x91,
- 0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93, 0x73, 0x93, 0xd1, 0x93,
- 0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95, 0xa4, 0x95, 0x02, 0x96,
- 0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97, 0xd8, 0x97, 0x36, 0x98,
- 0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99, 0x0f, 0x9a, 0x6e, 0x9a,
- 0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b, 0x49, 0x9c, 0xa8, 0x9c,
- 0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e, 0x86, 0x9e, 0xe6, 0x9e,
- 0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0, 0xc6, 0xa0, 0x27, 0xa1,
- 0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2, 0x0a, 0xa3, 0x6b, 0xa3,
- 0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4, 0x50, 0xa5, 0xb2, 0xa5,
- 0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7, 0x9a, 0xa7, 0xfc, 0xa7,
- 0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9, 0xe7, 0xa9, 0x49, 0xaa,
- 0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab, 0x37, 0xac, 0x9a, 0xac,
- 0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae, 0x8a, 0xae, 0xed, 0xae,
- 0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0, 0xe0, 0xb0, 0x45, 0xb1,
- 0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2, 0x3a, 0xb3, 0x9f, 0xb3,
- 0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5, 0x97, 0xb5, 0xfc, 0xb5,
- 0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7, 0xf7, 0xb7, 0x5d, 0xb8,
- 0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9, 0x5b, 0xba, 0xc1, 0xba,
- 0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc, 0xc2, 0xbc, 0x29, 0xbd,
- 0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe, 0x2c, 0xbf, 0x94, 0xbf,
- 0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1, 0x9a, 0xc1, 0x02, 0xc2,
- 0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3, 0x0b, 0xc4, 0x73, 0xc4,
- 0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6, 0x7f, 0xc6, 0xe8, 0xc6,
- 0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8, 0xf7, 0xc8, 0x60, 0xc9,
- 0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb, 0x72, 0xcb, 0xdc, 0xcb,
- 0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd, 0xf1, 0xcd, 0x5b, 0xce,
- 0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0, 0x73, 0xd0, 0xde, 0xd0,
- 0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2, 0xf8, 0xd2, 0x64, 0xd3,
- 0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5, 0x82, 0xd5, 0xee, 0xd5,
- 0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7, 0x0e, 0xd8, 0x7b, 0xd8,
- 0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda, 0x9e, 0xda, 0x0c, 0xdb,
- 0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc, 0x32, 0xdd, 0xa0, 0xdd,
- 0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf, 0xc9, 0xdf, 0x38, 0xe0,
- 0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1, 0x64, 0xe2, 0xd4, 0xe2,
- 0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4, 0x03, 0xe5, 0x73, 0xe5,
- 0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7, 0xa5, 0xe7, 0x16, 0xe8,
- 0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9, 0x4b, 0xea, 0xbc, 0xea,
- 0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec, 0xf5, 0xec, 0x66, 0xed,
- 0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef, 0xa2, 0xef, 0x14, 0xf0,
- 0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1, 0x53, 0xf2, 0xc6, 0xf2,
- 0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4, 0x07, 0xf5, 0x7b, 0xf5,
- 0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7, 0xc0, 0xf7, 0x34, 0xf8,
- 0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa, 0x7c, 0xfa, 0xf1, 0xfa,
- 0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc, 0x3c, 0xfd, 0xb2, 0xfd,
- 0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff, 0xec, 0x15, 0x00, 0x00,
- 0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5, 0xb0, 0x00, 0x91, 0xe1,
- 0x4a, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x07, 0xff, 0xff, 0x0a,
+ 0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4,
+ 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a,
+ 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1,
+ 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2,
+ 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3,
+ 0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11,
+ 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2,
+ 0x20, 0x00, 0xbd, 0x08, 0xf0, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2,
+ 0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a,
+ 0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04,
+ 0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11,
+ 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08,
+ 0xe2, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a,
+ 0xd9, 0xff, 0xff, 0xea, 0xde, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
+ 0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3,
+ 0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4,
+ 0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2,
+ 0xd3, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a,
+ 0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4,
+ 0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xcb, 0xfe, 0xff, 0x0a,
+ 0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea,
+ 0x00, 0x00, 0x24, 0x03, 0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f,
+ 0xc8, 0x12, 0xe2, 0x15, 0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22,
+ 0x28, 0x25, 0x26, 0x28, 0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33,
+ 0xba, 0x36, 0x8c, 0x39, 0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44,
+ 0x1c, 0x47, 0xb4, 0x49, 0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53,
+ 0xf5, 0x55, 0x42, 0x58, 0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60,
+ 0xf1, 0x62, 0xe8, 0x64, 0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c,
+ 0xc9, 0x6d, 0x5e, 0x6f, 0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75,
+ 0x41, 0x76, 0x6b, 0x77, 0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b,
+ 0x29, 0x7c, 0xe3, 0x7c, 0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f,
+ 0x61, 0x7f, 0xa6, 0x7f, 0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0,
+ 0x8c, 0x10, 0x4f, 0xe2, 0xb0, 0x00, 0x91, 0xe1, 0xa4, 0xfe, 0xff, 0xea,
+ 0x00, 0x00, 0x3b, 0x00, 0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01,
+ 0x64, 0x01, 0x9f, 0x01, 0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02,
+ 0xca, 0x02, 0x05, 0x03, 0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03,
+ 0x31, 0x04, 0x6e, 0x04, 0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05,
+ 0x9b, 0x05, 0xd8, 0x05, 0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06,
+ 0x07, 0x07, 0x43, 0x07, 0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08,
+ 0x74, 0x08, 0xb1, 0x08, 0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09,
+ 0xe4, 0x09, 0x21, 0x0a, 0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b,
+ 0x56, 0x0b, 0x93, 0x0b, 0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c,
+ 0xc9, 0x0c, 0x07, 0x0d, 0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e,
+ 0x3f, 0x0e, 0x7d, 0x0e, 0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f,
+ 0xb6, 0x0f, 0xf5, 0x0f, 0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10,
+ 0x30, 0x11, 0x6f, 0x11, 0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12,
+ 0xac, 0x12, 0xeb, 0x12, 0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13,
+ 0x2a, 0x14, 0x6a, 0x14, 0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15,
+ 0xaa, 0x15, 0xea, 0x15, 0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16,
+ 0x2c, 0x17, 0x6c, 0x17, 0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18,
+ 0xb0, 0x18, 0xf0, 0x18, 0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19,
+ 0x36, 0x1a, 0x77, 0x1a, 0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b,
+ 0xbe, 0x1b, 0x00, 0x1c, 0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d,
+ 0x48, 0x1d, 0x8a, 0x1d, 0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e,
+ 0xd5, 0x1e, 0x17, 0x1f, 0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20,
+ 0x64, 0x20, 0xa6, 0x20, 0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21,
+ 0xf5, 0x21, 0x38, 0x22, 0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23,
+ 0x88, 0x23, 0xcb, 0x23, 0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24,
+ 0x1d, 0x25, 0x61, 0x25, 0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26,
+ 0xb4, 0x26, 0xf8, 0x26, 0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28,
+ 0x4e, 0x28, 0x92, 0x28, 0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29,
+ 0xea, 0x29, 0x2f, 0x2a, 0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b,
+ 0x88, 0x2b, 0xcd, 0x2b, 0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c,
+ 0x28, 0x2d, 0x6e, 0x2d, 0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e,
+ 0xcb, 0x2e, 0x11, 0x2f, 0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30,
+ 0x70, 0x30, 0xb6, 0x30, 0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31,
+ 0x17, 0x32, 0x5e, 0x32, 0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33,
+ 0xc1, 0x33, 0x08, 0x34, 0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35,
+ 0x6c, 0x35, 0xb4, 0x35, 0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36,
+ 0x1a, 0x37, 0x62, 0x37, 0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38,
+ 0xcb, 0x38, 0x13, 0x39, 0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a,
+ 0x7e, 0x3a, 0xc6, 0x3a, 0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b,
+ 0x33, 0x3c, 0x7c, 0x3c, 0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d,
+ 0xea, 0x3d, 0x34, 0x3e, 0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f,
+ 0xa4, 0x3f, 0xee, 0x3f, 0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41,
+ 0x61, 0x41, 0xab, 0x41, 0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42,
+ 0x1f, 0x43, 0x6a, 0x43, 0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44,
+ 0xe1, 0x44, 0x2c, 0x45, 0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46,
+ 0xa4, 0x46, 0xf0, 0x46, 0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48,
+ 0x6a, 0x48, 0xb6, 0x48, 0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49,
+ 0x33, 0x4a, 0x7f, 0x4a, 0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b,
+ 0xfe, 0x4b, 0x4a, 0x4c, 0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d,
+ 0xcb, 0x4d, 0x18, 0x4e, 0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f,
+ 0x9b, 0x4f, 0xe9, 0x4f, 0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51,
+ 0x6e, 0x51, 0xbc, 0x51, 0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52,
+ 0x43, 0x53, 0x91, 0x53, 0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54,
+ 0x1a, 0x55, 0x69, 0x55, 0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56,
+ 0xf4, 0x56, 0x44, 0x57, 0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58,
+ 0xd1, 0x58, 0x21, 0x59, 0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a,
+ 0xb0, 0x5a, 0x01, 0x5b, 0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c,
+ 0x92, 0x5c, 0xe3, 0x5c, 0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e,
+ 0x77, 0x5e, 0xc8, 0x5e, 0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60,
+ 0x5e, 0x60, 0xb0, 0x60, 0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61,
+ 0x48, 0x62, 0x9a, 0x62, 0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63,
+ 0x34, 0x64, 0x87, 0x64, 0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65,
+ 0x24, 0x66, 0x76, 0x66, 0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67,
+ 0x15, 0x68, 0x69, 0x68, 0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69,
+ 0x0a, 0x6a, 0x5e, 0x6a, 0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b,
+ 0x01, 0x6c, 0x55, 0x6c, 0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d,
+ 0xfb, 0x6d, 0x50, 0x6e, 0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f,
+ 0xf8, 0x6f, 0x4d, 0x70, 0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71,
+ 0xf7, 0x71, 0x4d, 0x72, 0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73,
+ 0xfa, 0x73, 0x50, 0x74, 0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75,
+ 0xff, 0x75, 0x55, 0x76, 0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77,
+ 0x07, 0x78, 0x5e, 0x78, 0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79,
+ 0x11, 0x7a, 0x69, 0x7a, 0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b,
+ 0x1f, 0x7c, 0x77, 0x7c, 0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d,
+ 0x2f, 0x7e, 0x88, 0x7e, 0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f,
+ 0x42, 0x80, 0x9b, 0x80, 0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81,
+ 0x59, 0x82, 0xb2, 0x82, 0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84,
+ 0x72, 0x84, 0xcb, 0x84, 0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86,
+ 0x8e, 0x86, 0xe8, 0x86, 0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88,
+ 0xac, 0x88, 0x07, 0x89, 0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a,
+ 0xce, 0x8a, 0x2a, 0x8b, 0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c,
+ 0xf3, 0x8c, 0x4f, 0x8d, 0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e,
+ 0x1b, 0x8f, 0x77, 0x8f, 0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90,
+ 0x46, 0x91, 0xa2, 0x91, 0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93,
+ 0x73, 0x93, 0xd1, 0x93, 0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95,
+ 0xa4, 0x95, 0x02, 0x96, 0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97,
+ 0xd8, 0x97, 0x36, 0x98, 0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99,
+ 0x0f, 0x9a, 0x6e, 0x9a, 0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b,
+ 0x49, 0x9c, 0xa8, 0x9c, 0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e,
+ 0x86, 0x9e, 0xe6, 0x9e, 0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0,
+ 0xc6, 0xa0, 0x27, 0xa1, 0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2,
+ 0x0a, 0xa3, 0x6b, 0xa3, 0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4,
+ 0x50, 0xa5, 0xb2, 0xa5, 0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7,
+ 0x9a, 0xa7, 0xfc, 0xa7, 0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9,
+ 0xe7, 0xa9, 0x49, 0xaa, 0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab,
+ 0x37, 0xac, 0x9a, 0xac, 0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae,
+ 0x8a, 0xae, 0xed, 0xae, 0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0,
+ 0xe0, 0xb0, 0x45, 0xb1, 0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2,
+ 0x3a, 0xb3, 0x9f, 0xb3, 0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5,
+ 0x97, 0xb5, 0xfc, 0xb5, 0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7,
+ 0xf7, 0xb7, 0x5d, 0xb8, 0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9,
+ 0x5b, 0xba, 0xc1, 0xba, 0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc,
+ 0xc2, 0xbc, 0x29, 0xbd, 0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe,
+ 0x2c, 0xbf, 0x94, 0xbf, 0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1,
+ 0x9a, 0xc1, 0x02, 0xc2, 0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3,
+ 0x0b, 0xc4, 0x73, 0xc4, 0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6,
+ 0x7f, 0xc6, 0xe8, 0xc6, 0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8,
+ 0xf7, 0xc8, 0x60, 0xc9, 0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb,
+ 0x72, 0xcb, 0xdc, 0xcb, 0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd,
+ 0xf1, 0xcd, 0x5b, 0xce, 0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0,
+ 0x73, 0xd0, 0xde, 0xd0, 0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2,
+ 0xf8, 0xd2, 0x64, 0xd3, 0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5,
+ 0x82, 0xd5, 0xee, 0xd5, 0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7,
+ 0x0e, 0xd8, 0x7b, 0xd8, 0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda,
+ 0x9e, 0xda, 0x0c, 0xdb, 0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc,
+ 0x32, 0xdd, 0xa0, 0xdd, 0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf,
+ 0xc9, 0xdf, 0x38, 0xe0, 0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1,
+ 0x64, 0xe2, 0xd4, 0xe2, 0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4,
+ 0x03, 0xe5, 0x73, 0xe5, 0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7,
+ 0xa5, 0xe7, 0x16, 0xe8, 0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9,
+ 0x4b, 0xea, 0xbc, 0xea, 0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec,
+ 0xf5, 0xec, 0x66, 0xed, 0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef,
+ 0xa2, 0xef, 0x14, 0xf0, 0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1,
+ 0x53, 0xf2, 0xc6, 0xf2, 0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4,
+ 0x07, 0xf5, 0x7b, 0xf5, 0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7,
+ 0xc0, 0xf7, 0x34, 0xf8, 0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa,
+ 0x7c, 0xfa, 0xf1, 0xfa, 0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc,
+ 0x3c, 0xfd, 0xb2, 0xfd, 0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff,
+ 0x98, 0x16, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5,
+ 0xb0, 0x00, 0x91, 0xe1, 0x1f, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
@@ -634,88 +648,74 @@ unsigned char bios_arm7_bin[] = {
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16,
- 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a,
- 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e,
- 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22,
- 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
- 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d,
- 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33,
- 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b,
- 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44,
- 0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
- 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67,
- 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75, 0x76,
- 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20, 0x21, 0x21, 0x21, 0x22,
- 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27,
- 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d,
- 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33,
- 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 0x39, 0x39, 0x3a, 0x3b,
- 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40, 0x41, 0x42, 0x43, 0x43,
- 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d,
- 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x65, 0x66,
- 0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x73, 0x74, 0x75,
- 0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40, 0x41, 0x42, 0x43, 0x43,
- 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4c, 0x4d,
- 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x64, 0x65, 0x66,
- 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70, 0x71, 0x72, 0x74, 0x75,
- 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40, 0x41, 0x42, 0x42, 0x43,
- 0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d,
- 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66,
- 0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75,
- 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x00, 0x00, 0x00, 0x00,
- 0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3, 0xff, 0x20, 0xa0, 0xe3,
- 0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3, 0x05, 0x2c, 0x82, 0xe3,
- 0x8d, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x01, 0x03, 0xc1, 0xe5,
- 0x8a, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3,
- 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
- 0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2, 0x00, 0x00, 0xd1, 0xe7,
- 0x81, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3,
- 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5,
- 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5,
- 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1,
- 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3,
- 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1,
- 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
- 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13,
+ 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+ 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19,
+ 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c,
+ 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20,
+ 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25,
+ 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b,
+ 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31,
+ 0x32, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38,
+ 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41,
+ 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a,
+ 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
+ 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71,
+ 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20,
+ 0x21, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25,
+ 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b,
+ 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31,
+ 0x31, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38,
+ 0x39, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40,
+ 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a,
+ 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70,
+ 0x71, 0x73, 0x74, 0x75, 0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40,
+ 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a,
+ 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
+ 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70,
+ 0x71, 0x72, 0x74, 0x75, 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40,
+ 0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x4b, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
+ 0x62, 0x63, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3,
+ 0xff, 0x20, 0xa0, 0xe3, 0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3,
+ 0x05, 0x2c, 0x82, 0xe3, 0x62, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3,
+ 0x01, 0x03, 0xc1, 0xe5, 0x5f, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9,
+ 0x01, 0x03, 0xa0, 0xe3, 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5,
+ 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2,
+ 0x00, 0x00, 0xd1, 0xe7, 0x56, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03,
+ 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3,
+ 0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a,
+ 0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
+ 0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1,
+ 0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2,
+ 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3,
+ 0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8,
+ 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1401,7 +1401,7 @@ unsigned int bios_arm7_bin_len = 16384;
unsigned char bios_arm9_bin[] = {
0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea,
0x3c, 0x00, 0x00, 0xea, 0x3b, 0x00, 0x00, 0xea, 0x3a, 0x00, 0x00, 0xea,
- 0x82, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
+ 0xad, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1421,22 +1421,22 @@ unsigned char bios_arm9_bin[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea,
- 0x10, 0x50, 0x2d, 0xe9, 0x00, 0x40, 0x4f, 0xe1, 0x04, 0x40, 0x2d, 0xe5,
+ 0x10, 0x50, 0x2d, 0xe9, 0x00, 0x40, 0x4f, 0xe1, 0x10, 0x00, 0x2d, 0xe9,
0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5,
- 0x04, 0xf0, 0x29, 0xe1, 0x04, 0xe0, 0x2d, 0xe5, 0x20, 0x00, 0x5c, 0xe3,
+ 0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3,
0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1,
- 0x4c, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
+ 0xf8, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
0xe0, 0x01, 0xff, 0xff, 0x20, 0x02, 0xff, 0xff, 0x18, 0x02, 0xff, 0xff,
0xd4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
0x4c, 0x02, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xa8, 0x02, 0xff, 0xff,
0x28, 0x03, 0xff, 0xff, 0x68, 0x03, 0xff, 0xff, 0xb4, 0x03, 0xff, 0xff,
0x4c, 0x04, 0xff, 0xff, 0x54, 0x04, 0xff, 0xff, 0xe8, 0x04, 0xff, 0xff,
- 0xe8, 0x04, 0xff, 0xff, 0x64, 0x05, 0xff, 0xff, 0x68, 0x05, 0xff, 0xff,
- 0x68, 0x05, 0xff, 0xff, 0xc0, 0x05, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
- 0xec, 0x05, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
+ 0x64, 0x05, 0xff, 0xff, 0x10, 0x06, 0xff, 0xff, 0x14, 0x06, 0xff, 0xff,
+ 0x14, 0x06, 0xff, 0xff, 0x6c, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
+ 0x98, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
- 0x1c, 0x06, 0xff, 0xff, 0x04, 0xe0, 0x9d, 0xe4, 0xd3, 0x40, 0xa0, 0xe3,
- 0x04, 0xf0, 0x29, 0xe1, 0x04, 0x40, 0x9d, 0xe4, 0x04, 0xf0, 0x69, 0xe1,
+ 0xc8, 0x06, 0xff, 0xff, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3,
+ 0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1,
0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xa0, 0xe3, 0x90, 0x0f, 0x07, 0xee, 0xf4, 0xff, 0xff, 0xea,
0x01, 0x00, 0x50, 0xe2, 0xfd, 0xff, 0xff, 0xca, 0xf1, 0xff, 0xff, 0xea,
@@ -1478,7 +1478,7 @@ unsigned char bios_arm9_bin[] = {
0x87, 0xff, 0xff, 0xea, 0x00, 0x00, 0x01, 0xcc, 0x01, 0xd8, 0x00, 0x14,
0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c,
0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44,
- 0x04, 0x50, 0x2d, 0xe5, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2,
+ 0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2,
0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0,
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
@@ -1490,7 +1490,7 @@ unsigned char bios_arm9_bin[] = {
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a,
- 0x04, 0x50, 0x9d, 0xe4, 0x59, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3,
+ 0x20, 0x00, 0xbd, 0xe8, 0x59, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3,
0x57, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1,
0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5,
0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3,
@@ -1514,47 +1514,47 @@ unsigned char bios_arm9_bin[] = {
0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4,
0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x16, 0xff, 0xff, 0x0a,
0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea,
- 0x12, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1,
- 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2,
- 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2,
- 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0x07, 0xff, 0xff, 0x0a,
- 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea,
- 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
- 0x01, 0xc0, 0xc1, 0xe4, 0xff, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2,
- 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
- 0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1, 0x01, 0xc0, 0xc1, 0xe4,
- 0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
- 0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4, 0xfa, 0xff, 0xff, 0x1a,
- 0xf1, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0xb2, 0xc0, 0xd0, 0xe0,
- 0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0, 0x01, 0x20, 0xc2, 0xe3,
- 0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0, 0x02, 0x20, 0x52, 0xe2,
- 0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0, 0xfa, 0xff, 0xff, 0x1a,
- 0xe5, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x00, 0x03, 0xc1, 0xe5,
- 0xe2, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x11, 0x0f, 0x19, 0xee,
- 0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2, 0x0f, 0xe0, 0xa0, 0xe1,
- 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2,
- 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3,
- 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2,
- 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3,
- 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1,
- 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
- 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1,
- 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2,
- 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x10, 0xff, 0xff, 0x0a,
+ 0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4,
+ 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a,
+ 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1,
+ 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2,
+ 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3,
+ 0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11,
+ 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2,
+ 0x20, 0x00, 0xbd, 0x08, 0xf9, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2,
+ 0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a,
+ 0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04,
+ 0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11,
+ 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08,
+ 0xeb, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a,
+ 0xd9, 0xff, 0xff, 0xea, 0xe7, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
+ 0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3,
+ 0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4,
+ 0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2,
+ 0xdc, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a,
+ 0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4,
+ 0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xd4, 0xfe, 0xff, 0x0a,
+ 0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea,
+ 0x04, 0x30, 0x90, 0xe4, 0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1,
+ 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4,
+ 0x01, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4,
+ 0xfa, 0xff, 0xff, 0x1a, 0xc6, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
+ 0xb2, 0xc0, 0xd0, 0xe0, 0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0,
+ 0x01, 0x20, 0xc2, 0xe3, 0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0,
+ 0x02, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0,
+ 0xfa, 0xff, 0xff, 0x1a, 0xba, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3,
+ 0x00, 0x03, 0xc1, 0xe5, 0xb7, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9,
+ 0x11, 0x0f, 0x19, 0xee, 0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2,
+ 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
+ 0x04, 0xf0, 0x5e, 0xe2, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3,
+ 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5,
+ 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5,
+ 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1,
+ 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3,
+ 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1,
+ 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
+ 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/src/GBACart.cpp b/src/GBACart.cpp
index e5b6e4f..68507bc 100644
--- a/src/GBACart.cpp
+++ b/src/GBACart.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, Raphaël Zumer
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
+#include "NDS.h"
#include "GBACart.h"
#include "CRC32.h"
#include "Platform.h"
@@ -42,7 +43,6 @@ const char SOLAR_SENSOR_GAMECODES[10][5] =
bool CartInserted;
u8* CartROM;
u32 CartROMSize;
-u32 CartCRC;
u32 CartID;
CartCommon* Cart;
@@ -58,16 +58,20 @@ CartCommon::~CartCommon()
{
}
+void CartCommon::Reset()
+{
+}
+
void CartCommon::DoSavestate(Savestate* file)
{
file->Section("GBCS");
}
-void CartCommon::LoadSave(const char* path, u32 type)
+void CartCommon::SetupSave(u32 type)
{
}
-void CartCommon::RelocateSave(const char* path, bool write)
+void CartCommon::LoadSave(const u8* savedata, u32 savelen)
{
}
@@ -100,10 +104,7 @@ CartGame::CartGame(u8* rom, u32 len) : CartCommon()
ROM = rom;
ROMLength = len;
- memset(&GPIO, 0, sizeof(GPIO));
-
SRAM = nullptr;
- SRAMFile = nullptr;
SRAMLength = 0;
SRAMType = S_NULL;
SRAMFlashState = {};
@@ -111,10 +112,23 @@ CartGame::CartGame(u8* rom, u32 len) : CartCommon()
CartGame::~CartGame()
{
- if (SRAMFile) fclose(SRAMFile);
if (SRAM) delete[] SRAM;
}
+u32 CartGame::Checksum()
+{
+ u32 crc = CRC32(ROM, 0xC0, 0);
+
+ // TODO: hash more contents?
+
+ return crc;
+}
+
+void CartGame::Reset()
+{
+ memset(&GPIO, 0, sizeof(GPIO));
+}
+
void CartGame::DoSavestate(Savestate* file)
{
CartCommon::DoSavestate(file);
@@ -123,8 +137,6 @@ void CartGame::DoSavestate(Savestate* file)
file->Var16(&GPIO.data);
file->Var16(&GPIO.direction);
- // logic mostly copied from NDSCart_SRAM
-
u32 oldlen = SRAMLength;
file->Var32(&SRAMLength);
@@ -133,6 +145,7 @@ void CartGame::DoSavestate(Savestate* file)
{
// reallocate save memory
if (oldlen) delete[] SRAM;
+ SRAM = nullptr;
if (SRAMLength) SRAM = new u8[SRAMLength];
}
if (SRAMLength)
@@ -144,9 +157,7 @@ void CartGame::DoSavestate(Savestate* file)
{
// no save data, clear the current state
SRAMType = SaveType::S_NULL;
- if (SRAMFile) fclose(SRAMFile);
SRAM = nullptr;
- SRAMFile = nullptr;
return;
}
@@ -158,27 +169,24 @@ void CartGame::DoSavestate(Savestate* file)
file->Var8(&SRAMFlashState.state);
file->Var8((u8*)&SRAMType);
+
+ if ((!file->Saving) && SRAM)
+ Platform::WriteGBASave(SRAM, SRAMLength, 0, SRAMLength);
}
-void CartGame::LoadSave(const char* path, u32 type)
+void CartGame::SetupSave(u32 type)
{
if (SRAM) delete[] SRAM;
+ SRAM = nullptr;
- strncpy(SRAMPath, path, 1023);
- SRAMPath[1023] = '\0';
- SRAMLength = 0;
+ // TODO: have type be determined from some list, like in NDSCart
+ // and not this gross hack!!
+ SRAMLength = type;
- FILE* f = Platform::OpenFile(SRAMPath, "r+b");
- if (f)
+ if (SRAMLength)
{
- fseek(f, 0, SEEK_END);
- SRAMLength = (u32)ftell(f);
SRAM = new u8[SRAMLength];
-
- fseek(f, 0, SEEK_SET);
- fread(SRAM, SRAMLength, 1, f);
-
- SRAMFile = f;
+ memset(SRAM, 0xFF, SRAMLength);
}
switch (SRAMLength)
@@ -219,26 +227,13 @@ void CartGame::LoadSave(const char* path, u32 type)
}
}
-void CartGame::RelocateSave(const char* path, bool write)
+void CartGame::LoadSave(const u8* savedata, u32 savelen)
{
- if (!write)
- {
- LoadSave(path, 0); // lazy
- return;
- }
-
- strncpy(SRAMPath, path, 1023);
- SRAMPath[1023] = '\0';
-
- FILE *f = Platform::OpenFile(path, "r+b");
- if (!f)
- {
- printf("GBACart_SRAM::RelocateSave: failed to create new file. fuck\n");
- return;
- }
+ if (!SRAM) return;
- SRAMFile = f;
- fwrite(SRAM, SRAMLength, 1, SRAMFile);
+ u32 len = std::min(savelen, SRAMLength);
+ memcpy(SRAM, savedata, len);
+ Platform::WriteGBASave(savedata, len, 0, len);
}
u16 CartGame::ROMRead(u32 addr)
@@ -469,11 +464,7 @@ void CartGame::SRAMWrite_FLASH(u32 addr, u8 val)
u32 start_addr = addr + 0x10000 * SRAMFlashState.bank;
memset((u8*)&SRAM[start_addr], 0xFF, 0x1000);
- if (SRAMFile)
- {
- fseek(SRAMFile, start_addr, SEEK_SET);
- fwrite((u8*)&SRAM[start_addr], 1, 0x1000, SRAMFile);
- }
+ Platform::WriteGBASave(SRAM, SRAMLength, start_addr, 0x1000);
}
SRAMFlashState.state = 0;
SRAMFlashState.cmd = 0;
@@ -531,11 +522,8 @@ void CartGame::SRAMWrite_SRAM(u32 addr, u8 val)
{
*(u8*)&SRAM[addr] = val;
- if (SRAMFile)
- {
- fseek(SRAMFile, addr, SEEK_SET);
- fwrite((u8*)&SRAM[addr], 1, 1, SRAMFile);
- }
+ // TODO: optimize this!!
+ Platform::WriteGBASave(SRAM, SRAMLength, addr, 1);
}
}
@@ -544,16 +532,20 @@ const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 1
CartGameSolarSensor::CartGameSolarSensor(u8* rom, u32 len) : CartGame(rom, len)
{
- LightEdge = false;
- LightCounter = 0;
- LightSample = 0xFF;
- LightLevel = 0;
}
CartGameSolarSensor::~CartGameSolarSensor()
{
}
+void CartGameSolarSensor::Reset()
+{
+ LightEdge = false;
+ LightCounter = 0;
+ LightSample = 0xFF;
+ LightLevel = 0;
+}
+
void CartGameSolarSensor::DoSavestate(Savestate* file)
{
CartGame::DoSavestate(file);
@@ -608,104 +600,163 @@ void CartGameSolarSensor::ProcessGPIO()
}
-bool Init()
+CartRAMExpansion::CartRAMExpansion() : CartCommon()
{
- CartROM = nullptr;
+}
- Cart = nullptr;
+CartRAMExpansion::~CartRAMExpansion()
+{
+}
- return true;
+void CartRAMExpansion::Reset()
+{
+ memset(RAM, 0xFF, sizeof(RAM));
+ RAMEnable = 1;
}
-void DeInit()
+void CartRAMExpansion::DoSavestate(Savestate* file)
{
- if (CartROM) delete[] CartROM;
+ CartCommon::DoSavestate(file);
- if (Cart) delete Cart;
+ file->VarArray(RAM, sizeof(RAM));
+ file->Var16(&RAMEnable);
}
-void Reset()
+u16 CartRAMExpansion::ROMRead(u32 addr)
{
- // Do not reset cartridge ROM.
- // Prefer keeping the inserted cartridge on reset.
- // This allows resetting a DS game without losing GBA state,
- // and resetting to firmware without the slot being emptied.
- // The Stop function will clear the cartridge state via Eject().
+ addr &= 0x01FFFFFF;
+
+ if (addr < 0x01000000)
+ {
+ switch (addr)
+ {
+ case 0xB0: return 0xFFFF;
+ case 0xB2: return 0x0000;
+ case 0xB4: return 0x2400;
+ case 0xB6: return 0x2424;
+ case 0xB8: return 0xFFFF;
+ case 0xBA: return 0xFFFF;
+ case 0xBC: return 0xFFFF;
+ case 0xBE: return 0x7FFF;
+
+ case 0x1FFFC: return 0xFFFF;
+ case 0x1FFFE: return 0x7FFF;
+
+ case 0x240000: return RAMEnable;
+ case 0x240002: return 0x0000;
+ }
+
+ return 0xFFFF;
+ }
+ else if (addr < 0x01800000)
+ {
+ if (!RAMEnable) return 0xFFFF;
+
+ return *(u16*)&RAM[addr & 0x7FFFFF];
+ }
- // OpenBusDecay doesn't need to be reset, either, as it will be set
- // through NDS::SetGBASlotTimings().
+ return 0xFFFF;
}
-void Eject()
+void CartRAMExpansion::ROMWrite(u32 addr, u16 val)
{
- if (CartROM) delete[] CartROM;
+ addr &= 0x01FFFFFF;
- CartInserted = false;
- CartROM = NULL;
- CartROMSize = 0;
- CartCRC = 0;
- CartID = 0;
+ if (addr < 0x01000000)
+ {
+ switch (addr)
+ {
+ case 0x240000:
+ RAMEnable = val & 0x0001;
+ return;
+ }
+ }
+ else if (addr < 0x01800000)
+ {
+ if (!RAMEnable) return;
+
+ *(u16*)&RAM[addr & 0x7FFFFF] = val;
+ }
+}
+
+
+bool Init()
+{
+ CartROM = nullptr;
- if (Cart) delete Cart;
Cart = nullptr;
- Reset();
+ return true;
+}
+
+void DeInit()
+{
+ if (CartROM) delete[] CartROM;
+
+ if (Cart) delete Cart;
+}
+
+void Reset()
+{
+ if (Cart) Cart->Reset();
}
void DoSavestate(Savestate* file)
{
file->Section("GBAC"); // Game Boy Advance Cartridge
- // logic mostly copied from NDSCart
+ // little state here
+ // no need to save OpenBusDecay, it will be set later
- // first we need to reload the cart itself,
- // since unlike with DS, it's not loaded in advance
-
- file->Var32(&CartROMSize);
- if (!CartROMSize) // no GBA cartridge state? nothing to do here
+ u32 carttype = 0;
+ u32 cartchk = 0;
+ if (Cart)
{
- // do eject the cartridge if something is inserted
- Eject();
- return;
+ carttype = Cart->Type();
+ cartchk = Cart->Checksum();
}
- u32 oldCRC = CartCRC;
- file->Var32(&CartCRC);
-
- if (CartCRC != oldCRC)
+ if (file->Saving)
{
- // delete and reallocate ROM so that it is zero-padded to its full length
- if (CartROM) delete[] CartROM;
- CartROM = new u8[CartROMSize];
+ file->Var32(&carttype);
+ file->Var32(&cartchk);
}
+ else
+ {
+ u32 savetype;
+ file->Var32(&savetype);
+ if (savetype != carttype) return;
- // only save/load the cartridge header
- //
- // GBA connectivity on DS mainly involves identifying the title currently
- // inserted, reading save data, and issuing commands intercepted here
- // (e.g. solar sensor signals). we don't know of any case where GBA ROM is
- // read directly from DS software. therefore, it is more practical, both
- // from the development and user experience perspectives, to avoid dealing
- // with file dependencies, and store a small portion of ROM data that should
- // satisfy the needs of all known software that reads from the GBA slot.
- //
- // note: in case of a state load, only the cartridge header is restored, but
- // the rest of the ROM data is only cleared (zero-initialized) if the CRC
- // differs. Therefore, loading the GBA cartridge associated with the save state
- // in advance will maintain access to the full ROM contents.
- file->VarArray(CartROM, 192);
-
- CartInserted = true; // known, because CartROMSize > 0
- file->Var32(&CartCRC);
- file->Var32(&CartID);
-
- // now do the rest
+ u32 savechk;
+ file->Var32(&savechk);
+ if (savechk != cartchk) return;
+ }
if (Cart) Cart->DoSavestate(file);
}
-void LoadROMCommon(const char *sram)
+bool LoadROM(const u8* romdata, u32 romlen)
{
+ if (CartInserted)
+ EjectCart();
+
+ CartROMSize = 0x200;
+ while (CartROMSize < romlen)
+ CartROMSize <<= 1;
+
+ try
+ {
+ CartROM = new u8[CartROMSize];
+ }
+ catch (const std::bad_alloc& e)
+ {
+ printf("GBACart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize);
+ return false;
+ }
+
+ memset(CartROM, 0, CartROMSize);
+ memcpy(CartROM, romdata, romlen);
+
char gamecode[5] = { '\0' };
memcpy(&gamecode, CartROM + 0xAC, 4);
printf("GBA game code: %s\n", gamecode);
@@ -722,9 +773,6 @@ void LoadROMCommon(const char *sram)
printf("GBA solar sensor support detected!\n");
}
- CartCRC = CRC32(CartROM, CartROMSize);
- printf("GBA ROM CRC32: %08X\n", CartCRC);
-
CartInserted = true;
if (solarsensor)
@@ -732,61 +780,61 @@ void LoadROMCommon(const char *sram)
else
Cart = new CartGame(CartROM, CartROMSize);
+ if (Cart)
+ Cart->Reset();
+
// save
- printf("GBA save file: %s\n", sram);
+ //printf("GBA save file: %s\n", sram);
// TODO: have a list of sorts like in NDSCart? to determine the savemem type
- if (Cart) Cart->LoadSave(sram, 0);
+ //if (Cart) Cart->LoadSave(sram, 0);
+
+ // TODO: setup cart save here! from a list or something
+
+ return true;
}
-bool LoadROM(const char* path, const char* sram)
+void LoadSave(const u8* savedata, u32 savelen)
{
- FILE* f = Platform::OpenFile(path, "rb");
- if (!f)
+ if (Cart)
{
- return false;
- }
+ // gross hack
+ Cart->SetupSave(savelen);
- if (CartInserted)
- {
- Reset();
+ Cart->LoadSave(savedata, savelen);
}
-
- fseek(f, 0, SEEK_END);
- u32 len = (u32)ftell(f);
-
- CartROMSize = 0x200;
- while (CartROMSize < len)
- CartROMSize <<= 1;
-
- CartROM = new u8[CartROMSize];
- memset(CartROM, 0, CartROMSize);
- fseek(f, 0, SEEK_SET);
- fread(CartROM, 1, len, f);
- fclose(f);
-
- LoadROMCommon(sram);
-
- return true;
}
-bool LoadROM(const u8* romdata, u32 filelength, const char *sram)
+void LoadAddon(int type)
{
- CartROMSize = 0x200;
- while (CartROMSize < filelength)
- CartROMSize <<= 1;
+ CartROMSize = 0;
+ CartROM = nullptr;
- CartROM = new u8[CartROMSize];
- memcpy(CartROM, romdata, filelength);
+ switch (type)
+ {
+ case NDS::GBAAddon_RAMExpansion:
+ Cart = new CartRAMExpansion();
+ break;
- LoadROMCommon(sram);
+ default:
+ printf("GBACart: !! invalid addon type %d\n", type);
+ return;
+ }
- return true;
+ CartInserted = true;
}
-void RelocateSave(const char* path, bool write)
+void EjectCart()
{
- if (Cart) Cart->RelocateSave(path, write);
+ if (Cart) delete Cart;
+ Cart = nullptr;
+
+ if (CartROM) delete[] CartROM;
+
+ CartInserted = false;
+ CartROM = nullptr;
+ CartROMSize = 0;
+ CartID = 0;
}
diff --git a/src/GBACart.h b/src/GBACart.h
index 8698e25..8610714 100644
--- a/src/GBACart.h
+++ b/src/GBACart.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, Raphaël Zumer
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -32,10 +32,15 @@ public:
CartCommon();
virtual ~CartCommon();
+ virtual u32 Type() { return 0x001; }
+ virtual u32 Checksum() { return 0; }
+
+ virtual void Reset();
+
virtual void DoSavestate(Savestate* file);
- virtual void LoadSave(const char* path, u32 type);
- virtual void RelocateSave(const char* path, bool write);
+ virtual void SetupSave(u32 type);
+ virtual void LoadSave(const u8* savedata, u32 savelen);
virtual int SetInput(int num, bool pressed);
@@ -53,10 +58,15 @@ public:
CartGame(u8* rom, u32 len);
virtual ~CartGame() override;
+ virtual u32 Type() override { return 0x101; }
+ virtual u32 Checksum() override;
+
+ virtual void Reset() override;
+
virtual void DoSavestate(Savestate* file) override;
- virtual void LoadSave(const char* path, u32 type) override;
- virtual void RelocateSave(const char* path, bool write) override;
+ virtual void SetupSave(u32 type) override;
+ virtual void LoadSave(const u8* savedata, u32 savelen) override;
virtual u16 ROMRead(u32 addr) override;
virtual void ROMWrite(u32 addr, u16 val) override;
@@ -107,11 +117,8 @@ protected:
} SRAMFlashState;
u8* SRAM;
- FILE* SRAMFile;
u32 SRAMLength;
SaveType SRAMType;
-
- char SRAMPath[1024];
};
// CartGameSolarSensor -- Boktai game cart
@@ -121,6 +128,10 @@ public:
CartGameSolarSensor(u8* rom, u32 len);
virtual ~CartGameSolarSensor() override;
+ virtual u32 Type() override { return 0x102; }
+
+ virtual void Reset() override;
+
virtual void DoSavestate(Savestate* file) override;
virtual int SetInput(int num, bool pressed) override;
@@ -136,6 +147,27 @@ private:
u8 LightLevel;
};
+// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
+class CartRAMExpansion : public CartCommon
+{
+public:
+ CartRAMExpansion();
+ ~CartRAMExpansion() override;
+
+ virtual u32 Type() override { return 0x201; }
+
+ void Reset() override;
+
+ void DoSavestate(Savestate* file) override;
+
+ u16 ROMRead(u32 addr) override;
+ void ROMWrite(u32 addr, u16 val) override;
+
+private:
+ u8 RAM[0x800000];
+ u16 RAMEnable;
+};
+
// possible inputs for GBA carts that might accept user input
enum
{
@@ -146,17 +178,23 @@ enum
extern bool CartInserted;
extern u8* CartROM;
extern u32 CartROMSize;
-extern u32 CartCRC;
bool Init();
void DeInit();
void Reset();
-void Eject();
void DoSavestate(Savestate* file);
-bool LoadROM(const char* path, const char* sram);
-bool LoadROM(const u8* romdata, u32 filelength, const char *sram);
-void RelocateSave(const char* path, bool write);
+
+bool LoadROM(const u8* romdata, u32 romlen);
+void LoadSave(const u8* savedata, u32 savelen);
+
+void LoadAddon(int type);
+
+void EjectCart();
+
+//bool LoadROM(const char* path, const char* sram);
+//bool LoadROM(const u8* romdata, u32 filelength, const char *sram);
+//void RelocateSave(const char* path, bool write);
// TODO: make more flexible, support nonbinary inputs
int SetInput(int num, bool pressed);
diff --git a/src/GPU.cpp b/src/GPU.cpp
index de229b3..f54d771 100644
--- a/src/GPU.cpp
+++ b/src/GPU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -21,6 +21,10 @@
#include "NDS.h"
#include "GPU.h"
+#ifdef JIT_ENABLED
+#include "ARMJIT.h"
+#endif
+
#include "GPU2D_Soft.h"
namespace GPU
@@ -529,6 +533,8 @@ u8* GetUniqueBankPtr(u32 mask, u32 offset)
void MapVRAM_AB(u32 bank, u8 cnt)
{
+ cnt &= 0x9B;
+
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
@@ -587,6 +593,8 @@ void MapVRAM_AB(u32 bank, u8 cnt)
void MapVRAM_CD(u32 bank, u8 cnt)
{
+ cnt &= 0x9F;
+
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
@@ -649,6 +657,9 @@ void MapVRAM_CD(u32 bank, u8 cnt)
VRAMMap_ARM7[ofs] |= bankmask;
memset(VRAMDirty[bank].Data, 0xFF, sizeof(VRAMDirty[bank].Data));
VRAMSTAT |= (1 << (bank-2));
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidateWVRAM(ofs);
+#endif
break;
case 3: // texture
@@ -671,6 +682,8 @@ void MapVRAM_CD(u32 bank, u8 cnt)
void MapVRAM_E(u32 bank, u8 cnt)
{
+ cnt &= 0x87;
+
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
@@ -733,6 +746,8 @@ void MapVRAM_E(u32 bank, u8 cnt)
void MapVRAM_FG(u32 bank, u8 cnt)
{
+ cnt &= 0x9F;
+
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
@@ -831,6 +846,8 @@ void MapVRAM_FG(u32 bank, u8 cnt)
void MapVRAM_H(u32 bank, u8 cnt)
{
+ cnt &= 0x83;
+
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
@@ -891,6 +908,8 @@ void MapVRAM_H(u32 bank, u8 cnt)
void MapVRAM_I(u32 bank, u8 cnt)
{
+ cnt &= 0x83;
+
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
diff --git a/src/GPU.h b/src/GPU.h
index 1432bdf..9686704 100644
--- a/src/GPU.h
+++ b/src/GPU.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp
index 2dd606a..eda3e32 100644
--- a/src/GPU2D.cpp
+++ b/src/GPU2D.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -521,8 +521,6 @@ void Unit::Write32(u32 addr, u32 val)
return;
case 0x064:
- // TODO: check what happens when writing to it during display
- // esp. if a capture is happening
CaptureCnt = val & 0xEF3F1F1F;
return;
@@ -534,31 +532,32 @@ void Unit::Write32(u32 addr, u32 val)
return;
}
- if (!Enabled) return;
-
- switch (addr & 0x00000FFF)
+ if (Enabled)
{
- case 0x028:
- if (val & 0x08000000) val |= 0xF0000000;
- BGXRef[0] = val;
- if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0];
- return;
- case 0x02C:
- if (val & 0x08000000) val |= 0xF0000000;
- BGYRef[0] = val;
- if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0];
- return;
-
- case 0x038:
- if (val & 0x08000000) val |= 0xF0000000;
- BGXRef[1] = val;
- if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1];
- return;
- case 0x03C:
- if (val & 0x08000000) val |= 0xF0000000;
- BGYRef[1] = val;
- if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1];
- return;
+ switch (addr & 0x00000FFF)
+ {
+ case 0x028:
+ if (val & 0x08000000) val |= 0xF0000000;
+ BGXRef[0] = val;
+ if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0];
+ return;
+ case 0x02C:
+ if (val & 0x08000000) val |= 0xF0000000;
+ BGYRef[0] = val;
+ if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0];
+ return;
+
+ case 0x038:
+ if (val & 0x08000000) val |= 0xF0000000;
+ BGXRef[1] = val;
+ if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1];
+ return;
+ case 0x03C:
+ if (val & 0x08000000) val |= 0xF0000000;
+ BGYRef[1] = val;
+ if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1];
+ return;
+ }
}
Write16(addr, val&0xFFFF);
@@ -721,4 +720,4 @@ void Unit::GetOBJVRAM(u8*& data, u32& mask)
}
}
-} \ No newline at end of file
+}
diff --git a/src/GPU2D.h b/src/GPU2D.h
index 5a387fd..5edc0a2 100644
--- a/src/GPU2D.h
+++ b/src/GPU2D.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp
index f7adc53..66095e1 100644
--- a/src/GPU2D_Soft.cpp
+++ b/src/GPU2D_Soft.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -38,9 +38,9 @@ SoftRenderer::SoftRenderer()
u32 SoftRenderer::ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb)
{
- u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb)) >> 4;
- u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb)) >> 4) & 0x007F00;
- u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb)) >> 4) & 0x7F0000;
+ u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4;
+ u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00;
+ u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000;
if (r > 0x00003F) r = 0x00003F;
if (g > 0x003F00) g = 0x003F00;
@@ -56,16 +56,9 @@ u32 SoftRenderer::ColorBlend5(u32 val1, u32 val2)
if (eva == 32) return val1;
- u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb)) >> 5;
- u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb)) >> 5) & 0x007F00;
- u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb)) >> 5) & 0x7F0000;
-
- if (eva <= 16)
- {
- r += 0x000001;
- g += 0x000100;
- b += 0x010000;
- }
+ u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5;
+ u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00;
+ u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000;
if (r > 0x00003F) r = 0x00003F;
if (g > 0x003F00) g = 0x003F00;
@@ -74,24 +67,24 @@ u32 SoftRenderer::ColorBlend5(u32 val1, u32 val2)
return r | g | b | 0xFF000000;
}
-u32 SoftRenderer::ColorBrightnessUp(u32 val, u32 factor)
+u32 SoftRenderer::ColorBrightnessUp(u32 val, u32 factor, u32 bias)
{
u32 rb = val & 0x3F003F;
u32 g = val & 0x003F00;
- rb += ((((0x3F003F - rb) * factor) >> 4) & 0x3F003F);
- g += ((((0x003F00 - g) * factor) >> 4) & 0x003F00);
+ rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
+ g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00);
return rb | g | 0xFF000000;
}
-u32 SoftRenderer::ColorBrightnessDown(u32 val, u32 factor)
+u32 SoftRenderer::ColorBrightnessDown(u32 val, u32 factor, u32 bias)
{
u32 rb = val & 0x3F003F;
u32 g = val & 0x003F00;
- rb -= (((rb * factor) >> 4) & 0x3F003F);
- g -= (((g * factor) >> 4) & 0x003F00);
+ rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
+ g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00);
return rb | g | 0xFF000000;
}
@@ -160,8 +153,8 @@ u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2)
{
case 0: return val1;
case 1: return ColorBlend4(val1, val2, eva, evb);
- case 2: return ColorBrightnessUp(val1, CurUnit->EVY);
- case 3: return ColorBrightnessDown(val1, CurUnit->EVY);
+ case 2: return ColorBrightnessUp(val1, CurUnit->EVY, 0x8);
+ case 3: return ColorBrightnessDown(val1, CurUnit->EVY, 0x7);
case 4: return ColorBlend5(val1, val2);
}
@@ -335,7 +328,7 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
for (int i = 0; i < 256; i++)
{
- dst[i] = ColorBrightnessUp(dst[i], factor);
+ dst[i] = ColorBrightnessUp(dst[i], factor, 0x0);
}
}
else if ((masterBrightness >> 14) == 2)
@@ -346,7 +339,7 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
for (int i = 0; i < 256; i++)
{
- dst[i] = ColorBrightnessDown(dst[i], factor);
+ dst[i] = ColorBrightnessDown(dst[i], factor, 0xF);
}
}
}
@@ -452,8 +445,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
u32 evy = (val3 >> 8) & 0x1F;
val1 = _3dval;
- if (compmode == 2) val1 = ColorBrightnessUp(val1, evy);
- else if (compmode == 3) val1 = ColorBrightnessDown(val1, evy);
+ if (compmode == 2) val1 = ColorBrightnessUp(val1, evy, 0x8);
+ else if (compmode == 3) val1 = ColorBrightnessDown(val1, evy, 0x7);
}
else
val1 = val2;
@@ -561,9 +554,9 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
u32 bB = (val >> 10) & 0x1F;
u32 aB = val >> 15;
- u32 rD = ((rA * aA * eva) + (rB * aB * evb)) >> 4;
- u32 gD = ((gA * aA * eva) + (gB * aB * evb)) >> 4;
- u32 bD = ((bA * aA * eva) + (bB * aB * evb)) >> 4;
+ u32 rD = ((rA * aA * eva) + (rB * aB * evb) + 8) >> 4;
+ u32 gD = ((gA * aA * eva) + (gB * aB * evb) + 8) >> 4;
+ u32 bD = ((bA * aA * eva) + (bB * aB * evb) + 8) >> 4;
u32 aD = (eva>0 ? aA : 0) | (evb>0 ? aB : 0);
if (rD > 0x1F) rD = 0x1F;
@@ -588,9 +581,9 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
u32 bA = (val >> 17) & 0x1F;
u32 aA = ((val >> 24) != 0) ? 1 : 0;
- u32 rD = (rA * aA * eva) >> 4;
- u32 gD = (gA * aA * eva) >> 4;
- u32 bD = (bA * aA * eva) >> 4;
+ u32 rD = ((rA * aA * eva) + 8) >> 4;
+ u32 gD = ((gA * aA * eva) + 8) >> 4;
+ u32 bD = ((bA * aA * eva) + 8) >> 4;
u32 aD = (eva>0 ? aA : 0);
dst[dstaddr] = rD | (gD << 5) | (bD << 10) | (aD << 15);
@@ -2258,4 +2251,4 @@ void SoftRenderer::DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s
}
}
-} \ No newline at end of file
+}
diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h
index b7043ee..d7a417b 100644
--- a/src/GPU2D_Soft.h
+++ b/src/GPU2D_Soft.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -49,8 +49,8 @@ private:
u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb);
u32 ColorBlend5(u32 val1, u32 val2);
- u32 ColorBrightnessUp(u32 val, u32 factor);
- u32 ColorBrightnessDown(u32 val, u32 factor);
+ u32 ColorBrightnessUp(u32 val, u32 factor, u32 bias);
+ u32 ColorBrightnessDown(u32 val, u32 factor, u32 bias);
u32 ColorComposite(int i, u32 val1, u32 val2);
template<u32 bgmode> void DrawScanlineBGMode(u32 line);
@@ -78,4 +78,4 @@ private:
void DoCapture(u32 line, u32 width);
};
-} \ No newline at end of file
+}
diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp
index 7fa04b3..b4b9eb2 100644
--- a/src/GPU3D.cpp
+++ b/src/GPU3D.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU3D.h b/src/GPU3D.h
index 23f55eb..4a7bfdc 100644
--- a/src/GPU3D.h
+++ b/src/GPU3D.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp
index df7fc0b..ebabd51 100644
--- a/src/GPU3D_OpenGL.cpp
+++ b/src/GPU3D_OpenGL.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h
index 0752cf1..a9dd62c 100644
--- a/src/GPU3D_OpenGL.h
+++ b/src/GPU3D_OpenGL.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU3D_OpenGL_shaders.h b/src/GPU3D_OpenGL_shaders.h
index db15df3..6e17c35 100644
--- a/src/GPU3D_OpenGL_shaders.h
+++ b/src/GPU3D_OpenGL_shaders.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp
index fa22afa..0f8c602 100644
--- a/src/GPU3D_Soft.cpp
+++ b/src/GPU3D_Soft.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h
index 61bf368..c792f13 100644
--- a/src/GPU3D_Soft.h
+++ b/src/GPU3D_Soft.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp
index 0dd25f0..79adf7a 100644
--- a/src/GPU_OpenGL.cpp
+++ b/src/GPU_OpenGL.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -153,14 +153,14 @@ void GLCompositor::SetRenderSettings(RenderSettings& settings)
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// fill the padding
- u8 zeroPixels[ScreenW*2*scale*4];
- memset(zeroPixels, 0, sizeof(zeroPixels));
+ u8* zeroPixels = (u8*) calloc(1, ScreenW*2*scale*4);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192*scale, ScreenW, 2*scale, GL_RGBA, GL_UNSIGNED_BYTE, zeroPixels);
GLenum fbassign[] = {GL_COLOR_ATTACHMENT0};
glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB[i]);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CompScreenOutputTex[i], 0);
glDrawBuffers(1, fbassign);
+ free(zeroPixels);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h
index 08ea4a6..d27ae20 100644
--- a/src/GPU_OpenGL.h
+++ b/src/GPU_OpenGL.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/GPU_OpenGL_shaders.h b/src/GPU_OpenGL_shaders.h
index ba3b4bd..b1460cd 100644
--- a/src/GPU_OpenGL_shaders.h
+++ b/src/GPU_OpenGL_shaders.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -82,8 +82,7 @@ void main()
eva = (_3dpix.a & 0x1F) + 1;
evb = 32 - eva;
- val1 = ((_3dpix * eva) + (val1 * evb)) >> 5;
- if (eva <= 16) val1 += ivec4(1,1,1,0);
+ val1 = ((_3dpix * eva) + (val1 * evb) + 0x10) >> 5;
val1 = min(val1, 0x3F);
}
else
@@ -103,7 +102,7 @@ void main()
eva = val3.g;
evb = val3.b;
- val1 = ((val1 * eva) + (_3dpix * evb)) >> 4;
+ val1 = ((val1 * eva) + (_3dpix * evb) + 0x8) >> 4;
val1 = min(val1, 0x3F);
}
else
@@ -123,8 +122,8 @@ void main()
evy = val3.g;
val1 = _3dpix;
- if (compmode == 2) val1 += ((ivec4(0x3F,0x3F,0x3F,0) - val1) * evy) >> 4;
- else if (compmode == 3) val1 -= (val1 * evy) >> 4;
+ if (compmode == 2) val1 += (((0x3F - val1) * evy) + 0x8) >> 4;
+ else if (compmode == 3) val1 -= ((val1 * evy) + 0x7) >> 4;
}
else
val1 = val2;
@@ -142,7 +141,7 @@ void main()
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
- pixel += ((ivec4(0x3F,0x3F,0x3F,0) - pixel) * evy) >> 4;
+ pixel += ((0x3F - pixel) * evy) >> 4;
}
else if (brightmode == 2)
{
@@ -150,7 +149,7 @@ void main()
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
- pixel -= (pixel * evy) >> 4;
+ pixel -= ((pixel * evy) + 0xF) >> 4;
}
}
diff --git a/src/NDS.cpp b/src/NDS.cpp
index e809313..966b252 100644
--- a/src/NDS.cpp
+++ b/src/NDS.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -32,7 +32,6 @@
#include "Wifi.h"
#include "AREngine.h"
#include "Platform.h"
-#include "NDSCart_SRAMManager.h"
#include "FreeBIOS.h"
#ifdef JIT_ENABLED
@@ -42,6 +41,9 @@
#include "DSi.h"
#include "DSi_SPI_TSC.h"
+#include "DSi_NWifi.h"
+#include "DSi_Camera.h"
+#include "DSi_DSP.h"
namespace NDS
@@ -174,6 +176,7 @@ bool RunningGame;
void DivDone(u32 param);
void SqrtDone(u32 param);
void RunTimer(u32 tid, s32 cycles);
+void UpdateWifiTimings();
void SetWifiWaitCnt(u16 val);
void SetGBASlotTimings();
@@ -200,7 +203,6 @@ bool Init()
DMAs[6] = new DMA(1, 2);
DMAs[7] = new DMA(1, 3);
- if (!NDSCart_SRAMManager::Init()) return false;
if (!NDSCart::Init()) return false;
if (!GBACart::Init()) return false;
if (!GPU::Init()) return false;
@@ -228,7 +230,6 @@ void DeInit()
for (int i = 0; i < 8; i++)
delete DMAs[i];
- NDSCart_SRAMManager::DeInit();
NDSCart::DeInit();
GBACart::DeInit();
GPU::DeInit();
@@ -353,7 +354,28 @@ void InitTimings()
// handled later: GBA slot, wifi
}
-void SetupDirectBoot()
+bool NeedsDirectBoot()
+{
+ if (ConsoleType == 1)
+ {
+ // for now, DSi mode requires original BIOS/NAND
+ return false;
+ }
+ else
+ {
+ // internal BIOS does not support direct boot
+ if (!Platform::GetConfigBool(Platform::ExternalBIOSEnable))
+ return true;
+
+ // DSi/3DS firmwares aren't bootable
+ if (SPI_Firmware::GetFirmwareLength() == 0x20000)
+ return true;
+
+ return false;
+ }
+}
+
+void SetupDirectBoot(std::string romname)
{
if (ConsoleType == 1)
{
@@ -444,6 +466,8 @@ void SetupDirectBoot()
ARM9->CP15Write(0x911, 0x00000020);
}
+ NDSCart::SetupDirectBoot(romname);
+
ARM9->R[12] = NDSCart::Header.ARM9EntryAddress;
ARM9->R[13] = 0x03002F7C;
ARM9->R[14] = NDSCart::Header.ARM9EntryAddress;
@@ -542,7 +566,6 @@ void Reset()
if (ConsoleType == 1)
{
DSi::LoadBIOS();
- DSi::LoadNAND();
ARM9ClockShift = 2;
MainRAMMask = 0xFFFFFF;
@@ -658,6 +681,11 @@ void Reset()
AREngine::Reset();
}
+void Start()
+{
+ Running = true;
+}
+
void Stop()
{
printf("Stopping: shutdown\n");
@@ -665,6 +693,9 @@ void Stop()
Platform::StopEmu();
GPU::Stop();
SPU::Stop();
+
+ if (ConsoleType == 1)
+ DSi::Stop();
}
bool DoSavestate_Scheduler(Savestate* file)
@@ -692,7 +723,14 @@ bool DoSavestate_Scheduler(Savestate* file)
DivDone,
SqrtDone,
- NULL
+ DSi_SDHost::FinishRX,
+ DSi_SDHost::FinishTX,
+ DSi_NWifi::MSTimer,
+ DSi_CamModule::IRQ,
+ DSi_CamModule::TransferScanline,
+ DSi_DSP::DSPCatchUpU32,
+
+ nullptr
};
int len = Event_MAX;
@@ -702,7 +740,7 @@ bool DoSavestate_Scheduler(Savestate* file)
{
SchedEvent* evt = &SchedList[i];
- u32 funcid = -1;
+ u32 funcid = 0xFFFFFFFF;
if (evt->Func)
{
for (int j = 0; eventfuncs[j]; j++)
@@ -749,7 +787,7 @@ bool DoSavestate_Scheduler(Savestate* file)
evt->Func = eventfuncs[funcid];
}
else
- evt->Func = NULL;
+ evt->Func = nullptr;
file->Var64(&evt->Timestamp);
file->Var32(&evt->Param);
@@ -763,15 +801,26 @@ bool DoSavestate(Savestate* file)
{
file->Section("NDSG");
- // TODO:
- // * do something for bool's (sizeof=1)
- // * do something for 'loading DSi-mode savestate in DS mode' and vice-versa
- // * add IE2/IF2 there
+ if (file->Saving)
+ {
+ u32 console = ConsoleType;
+ file->Var32(&console);
+ }
+ else
+ {
+ u32 console;
+ file->Var32(&console);
+ if (console != ConsoleType)
+ return false;
+ }
- file->VarArray(MainRAM, 0x400000);
- file->VarArray(SharedWRAM, 0x8000);
+ file->VarArray(MainRAM, MainRAMMaxSize);
+ file->VarArray(SharedWRAM, SharedWRAMSize);
file->VarArray(ARM7WRAM, ARM7WRAMSize);
+ //file->VarArray(ARM9BIOS, 0x1000);
+ //file->VarArray(ARM7BIOS, 0x4000);
+
file->VarArray(ExMemCnt, 2*sizeof(u16));
file->VarArray(ROMSeed0, 2*8);
file->VarArray(ROMSeed1, 2*8);
@@ -781,6 +830,8 @@ bool DoSavestate(Savestate* file)
file->VarArray(IME, 2*sizeof(u32));
file->VarArray(IE, 2*sizeof(u32));
file->VarArray(IF, 2*sizeof(u32));
+ file->Var32(&IE2);
+ file->Var32(&IF2);
file->Var8(&PostFlag9);
file->Var8(&PostFlag7);
@@ -825,11 +876,8 @@ bool DoSavestate(Savestate* file)
file->Var64(&LastSysClockCycles);
file->Var64(&FrameStartTimestamp);
file->Var32(&NumFrames);
- if (file->IsAtleastVersion(7, 1))
- {
- file->Var32(&NumLagFrames);
- file->Bool32(&LagFrameFlag);
- }
+ file->Var32(&NumLagFrames);
+ file->Bool32(&LagFrameFlag);
// TODO: save KeyInput????
file->Var16(&KeyCnt);
@@ -848,9 +896,7 @@ bool DoSavestate(Savestate* file)
InitTimings();
SetGBASlotTimings();
- u16 tmp = WifiWaitCnt;
- WifiWaitCnt = 0xFFFF;
- SetWifiWaitCnt(tmp); // force timing table update
+ UpdateWifiTimings();
}
for (int i = 0; i < 8; i++)
@@ -860,16 +906,23 @@ bool DoSavestate(Savestate* file)
ARM7->DoSavestate(file);
NDSCart::DoSavestate(file);
- GBACart::DoSavestate(file);
+ if (ConsoleType == 0)
+ GBACart::DoSavestate(file);
GPU::DoSavestate(file);
SPU::DoSavestate(file);
SPI::DoSavestate(file);
RTC::DoSavestate(file);
Wifi::DoSavestate(file);
+ if (ConsoleType == 1)
+ DSi::DoSavestate(file);
+
if (!file->Saving)
{
GPU::SetPowerCnt(PowerControl9);
+
+ SPU::SetPowerCnt(PowerControl7 & 0x0001);
+ Wifi::SetPowerCnt(PowerControl7 & 0x0002);
}
#ifdef JIT_ENABLED
@@ -888,72 +941,58 @@ void SetConsoleType(int type)
ConsoleType = type;
}
-bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct)
+bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
{
- if (NDSCart::LoadROM(romdata, filelength, sram, direct))
- {
- Running = true;
- return true;
- }
- else
- {
- printf("Failed to load ROM from archive\n");
+ if (!NDSCart::LoadROM(romdata, romlen))
return false;
- }
+
+ if (savedata && savelen)
+ NDSCart::LoadSave(savedata, savelen);
+
+ return true;
}
-bool LoadROM(const char* path, const char* sram, bool direct)
+void LoadSave(const u8* savedata, u32 savelen)
{
- if (NDSCart::LoadROM(path, sram, direct))
- {
- Running = true;
- return true;
- }
- else
- {
- printf("Failed to load ROM %s\n", path);
- return false;
- }
+ if (savedata && savelen)
+ NDSCart::LoadSave(savedata, savelen);
}
-bool LoadGBAROM(const char* path, const char* sram)
+void EjectCart()
{
- if (GBACart::LoadROM(path, sram))
- {
- return true;
- }
- else
- {
- printf("Failed to load ROM %s\n", path);
- return false;
- }
+ NDSCart::EjectCart();
}
-bool LoadGBAROM(const u8* romdata, u32 filelength, const char *filename, const char *sram)
+bool CartInserted()
{
- if (GBACart::LoadROM(romdata, filelength, sram))
- {
- return true;
- }
- else
- {
- printf("Failed to load ROM %s from archive\n", filename);
+ return NDSCart::CartInserted;
+}
+
+bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
+{
+ if (!GBACart::LoadROM(romdata, romlen))
return false;
- }
+
+ if (savedata && savelen)
+ GBACart::LoadSave(savedata, savelen);
+
+ return true;
}
-void LoadBIOS()
+void LoadGBAAddon(int type)
{
- Reset();
- Running = true;
+ GBACart::LoadAddon(type);
}
-void RelocateSave(const char* path, bool write)
+void EjectGBACart()
{
- printf("SRAM: relocating to %s (write=%s)\n", path, write?"true":"false");
- NDSCart::RelocateSave(path, write);
+ GBACart::EjectCart();
}
+void LoadBIOS()
+{
+ Reset();
+}
u64 NextTarget()
@@ -1094,8 +1133,6 @@ u32 RunFrame()
GPU3D::Timestamp-SysTimestamp);
#endif
SPU::TransferOutput();
-
- NDSCart::FlushSRAMFile();
}
// In the context of TASes, frame count is traditionally the primary measure of emulated time,
@@ -1166,6 +1203,25 @@ void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 para
Reschedule(evt->Timestamp);
}
+void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param)
+{
+ if (SchedListMask & (1<<id))
+ {
+ printf("!! EVENT %d ALREADY SCHEDULED\n", id);
+ return;
+ }
+
+ SchedEvent* evt = &SchedList[id];
+
+ evt->Timestamp = timestamp;
+ evt->Func = func;
+ evt->Param = param;
+
+ SchedListMask |= (1<<id);
+
+ Reschedule(evt->Timestamp);
+}
+
void CancelEvent(u32 id)
{
SchedListMask &= ~(1<<id);
@@ -1229,15 +1285,30 @@ void SetLidClosed(bool closed)
}
}
+void CamInputFrame(int cam, u32* data, int width, int height, bool rgb)
+{
+ // TODO: support things like the GBA-slot camera addon
+ // whenever these are emulated
+
+ if (ConsoleType == 1)
+ {
+ switch (cam)
+ {
+ case 0: return DSi_CamModule::Camera0->InputFrame(data, width, height, rgb);
+ case 1: return DSi_CamModule::Camera1->InputFrame(data, width, height, rgb);
+ }
+ }
+}
+
void MicInputFrame(s16* data, int samples)
{
return SPI_TSC::MicInputFrame(data, samples);
}
-int ImportSRAM(u8* data, u32 length)
+/*int ImportSRAM(u8* data, u32 length)
{
return NDSCart::ImportSRAM(data, length);
-}
+}*/
void Halt()
@@ -1291,15 +1362,29 @@ void MapSharedWRAM(u8 val)
}
+void UpdateWifiTimings()
+{
+ if (PowerControl7 & 0x0002)
+ {
+ const int ntimings[4] = {10, 8, 6, 18};
+ u16 val = WifiWaitCnt;
+
+ SetARM7RegionTimings(0x04800, 0x04808, Mem7_Wifi0, 16, ntimings[val & 0x3], (val & 0x4) ? 4 : 6);
+ SetARM7RegionTimings(0x04808, 0x04810, Mem7_Wifi1, 16, ntimings[(val>>3) & 0x3], (val & 0x20) ? 4 : 10);
+ }
+ else
+ {
+ SetARM7RegionTimings(0x04800, 0x04808, Mem7_Wifi0, 32, 1, 1);
+ SetARM7RegionTimings(0x04808, 0x04810, Mem7_Wifi1, 32, 1, 1);
+ }
+}
+
void SetWifiWaitCnt(u16 val)
{
if (WifiWaitCnt == val) return;
WifiWaitCnt = val;
-
- const int ntimings[4] = {10, 8, 6, 18};
- SetARM7RegionTimings(0x04800, 0x04808, Mem7_Wifi0, 16, ntimings[val & 0x3], (val & 0x4) ? 4 : 6);
- SetARM7RegionTimings(0x04808, 0x04810, Mem7_Wifi1, 16, ntimings[(val>>3) & 0x3], (val & 0x20) ? 4 : 10);
+ UpdateWifiTimings();
}
void SetGBASlotTimings()
@@ -1909,8 +1994,8 @@ void debug(u32 param)
//for (int i = 0; i < 9; i++)
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
- /*FILE*
- shit = fopen("debug/construct.bin", "wb");
+ FILE*
+ shit = fopen("debug/inazuma.bin", "wb");
fwrite(ARM9->ITCM, 0x8000, 1, shit);
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
{
@@ -1922,9 +2007,14 @@ void debug(u32 param)
u32 val = ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
- fclose(shit);*/
+ for (u32 i = 0x06000000; i < 0x06040000; i+=4)
+ {
+ u32 val = ARM7Read32(i);
+ fwrite(&val, 4, 1, shit);
+ }
+ fclose(shit);
- FILE*
+ /*FILE*
shit = fopen("debug/directboot9.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
@@ -1932,13 +2022,13 @@ void debug(u32 param)
fwrite(&val, 4, 1, shit);
}
fclose(shit);
- shit = fopen("debug/directboot7.bin", "wb");
+ shit = fopen("debug/camera7.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
u32 val = DSi::ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
- fclose(shit);
+ fclose(shit);*/
}
@@ -2364,6 +2454,7 @@ u8 ARM7Read8(u32 addr)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return 0;
if (addr & 0x1) return Wifi::Read(addr-1) >> 8;
return Wifi::Read(addr) & 0xFF;
}
@@ -2428,6 +2519,7 @@ u16 ARM7Read16(u32 addr)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return 0;
return Wifi::Read(addr);
}
break;
@@ -2491,6 +2583,7 @@ u32 ARM7Read32(u32 addr)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return 0;
return Wifi::Read(addr) | (Wifi::Read(addr+2) << 16);
}
break;
@@ -2582,7 +2675,8 @@ void ARM7Write8(u32 addr, u8 val)
return;
}
- if (ARM7->R[15] > 0x00002F30) // ARM7 BIOS bug
+ //if (ARM7->R[15] > 0x00002F30) // ARM7 BIOS bug
+ if (addr >= 0x01000000)
printf("unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7->R[15]);
}
@@ -2630,6 +2724,7 @@ void ARM7Write16(u32 addr, u16 val)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return;
Wifi::Write(addr, val);
return;
}
@@ -2659,7 +2754,8 @@ void ARM7Write16(u32 addr, u16 val)
return;
}
- printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]);
+ if (addr >= 0x01000000)
+ printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]);
}
void ARM7Write32(u32 addr, u32 val)
@@ -2706,6 +2802,7 @@ void ARM7Write32(u32 addr, u32 val)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return;
Wifi::Write(addr, val & 0xFFFF);
Wifi::Write(addr+2, val >> 16);
return;
@@ -2739,7 +2836,8 @@ void ARM7Write32(u32 addr, u32 val)
return;
}
- printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]);
+ if (addr >= 0x01000000)
+ printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]);
}
bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
@@ -2899,7 +2997,8 @@ u8 ARM9IORead8(u32 addr)
return (u8)(emuID[idx]);
}
- printf("unknown ARM9 IO read8 %08X %08X\n", addr, ARM9->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM9 IO read8 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -3045,7 +3144,8 @@ u16 ARM9IORead16(u32 addr)
return GPU3D::Read16(addr);
}
- printf("unknown ARM9 IO read16 %08X %08X\n", addr, ARM9->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM9 IO read16 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -3188,7 +3288,8 @@ u32 ARM9IORead32(u32 addr)
return GPU3D::Read32(addr);
}
- printf("unknown ARM9 IO read32 %08X %08X\n", addr, ARM9->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM9 IO read32 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -3716,6 +3817,7 @@ u8 ARM7IORead8(u32 addr)
case 0x04000241: return WRAMCnt;
case 0x04000300: return PostFlag7;
+ case 0x04000304: return PowerControl7;
}
if (addr >= 0x04000400 && addr < 0x04000520)
@@ -3723,7 +3825,8 @@ u8 ARM7IORead8(u32 addr)
return SPU::Read8(addr);
}
- printf("unknown ARM7 IO read8 %08X %08X\n", addr, ARM7->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM7 IO read8 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -3798,7 +3901,9 @@ u16 ARM7IORead16(u32 addr)
case 0x040001C2: return SPI::ReadData();
case 0x04000204: return ExMemCnt[1];
- case 0x04000206: return WifiWaitCnt;
+ case 0x04000206:
+ if (!(PowerControl7 & (1<<1))) return 0;
+ return WifiWaitCnt;
case 0x04000208: return IME[1];
case 0x04000210: return IE[1] & 0xFFFF;
@@ -3814,7 +3919,8 @@ u16 ARM7IORead16(u32 addr)
return SPU::Read16(addr);
}
- printf("unknown ARM7 IO read16 %08X %08X\n", addr, ARM7->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM7 IO read16 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -3880,6 +3986,7 @@ u32 ARM7IORead32(u32 addr)
case 0x04000210: return IE[1];
case 0x04000214: return IF[1];
+ case 0x04000304: return PowerControl7;
case 0x04000308: return ARM7BIOSProt;
case 0x04100000:
@@ -3913,7 +4020,8 @@ u32 ARM7IORead32(u32 addr)
return SPU::Read32(addr);
}
- printf("unknown ARM7 IO read32 %08X %08X\n", addr, ARM7->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM7 IO read32 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -4108,6 +4216,7 @@ void ARM7IOWrite16(u32 addr, u16 val)
return;
}
case 0x04000206:
+ if (!(PowerControl7 & (1<<1))) return;
SetWifiWaitCnt(val);
return;
@@ -4123,7 +4232,15 @@ void ARM7IOWrite16(u32 addr, u16 val)
PostFlag7 = val & 0x01;
return;
- case 0x04000304: PowerControl7 = val; return;
+ case 0x04000304:
+ {
+ u16 change = PowerControl7 ^ val;
+ PowerControl7 = val & 0x0003;
+ SPU::SetPowerCnt(val & 0x0001);
+ Wifi::SetPowerCnt(val & 0x0002);
+ if (change & 0x0002) UpdateWifiTimings();
+ }
+ return;
case 0x04000308:
if (ARM7BIOSProt == 0)
@@ -4245,7 +4362,15 @@ void ARM7IOWrite32(u32 addr, u32 val)
case 0x04000210: IE[1] = val; UpdateIRQ(1); return;
case 0x04000214: IF[1] &= ~val; UpdateIRQ(1); return;
- case 0x04000304: PowerControl7 = val & 0xFFFF; return;
+ case 0x04000304:
+ {
+ u16 change = PowerControl7 ^ val;
+ PowerControl7 = val & 0x0003;
+ SPU::SetPowerCnt(val & 0x0001);
+ Wifi::SetPowerCnt(val & 0x0002);
+ if (change & 0x0002) UpdateWifiTimings();
+ }
+ return;
case 0x04000308:
if (ARM7BIOSProt == 0)
diff --git a/src/NDS.h b/src/NDS.h
index 29fc4ef..824c2bc 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,6 +19,8 @@
#ifndef NDS_H
#define NDS_H
+#include <string>
+
#include "Savestate.h"
#include "types.h"
@@ -48,8 +50,6 @@ enum
Event_DSi_NWifi,
Event_DSi_CamIRQ,
Event_DSi_CamTransfer,
-
- Event_DSi_RAMSizeChange,
Event_DSi_DSP,
Event_MAX
@@ -162,6 +162,12 @@ struct MemRegion
u32 Mask;
};
+// supported GBA slot addon types
+enum
+{
+ GBAAddon_RAMExpansion = 1,
+};
+
#ifdef JIT_ENABLED
extern bool EnableJIT;
#endif
@@ -219,6 +225,7 @@ extern u8* ARM7WRAM;
bool Init();
void DeInit();
void Reset();
+void Start();
void Stop();
bool DoSavestate(Savestate* file);
@@ -229,13 +236,19 @@ void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth,
// 0=DS 1=DSi
void SetConsoleType(int type);
-bool LoadROM(const char* path, const char* sram, bool direct);
-bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct);
-bool LoadGBAROM(const char* path, const char* sram);
-bool LoadGBAROM(const u8* romdata, u32 filelength, const char *filename, const char *sram);
void LoadBIOS();
-void SetupDirectBoot();
-void RelocateSave(const char* path, bool write);
+
+bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen);
+void LoadSave(const u8* savedata, u32 savelen);
+void EjectCart();
+bool CartInserted();
+
+bool NeedsDirectBoot();
+void SetupDirectBoot(std::string romname);
+
+bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen);
+void LoadGBAAddon(int type);
+void EjectGBACart();
u32 RunFrame();
@@ -247,11 +260,11 @@ void SetKeyMask(u32 mask);
bool IsLidClosed();
void SetLidClosed(bool closed);
+void CamInputFrame(int cam, u32* data, int width, int height, bool rgb);
void MicInputFrame(s16* data, int samples);
-int ImportSRAM(u8* data, u32 length);
-
void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param);
+void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param);
void CancelEvent(u32 id);
void debug(u32 p);
diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index 3d231ac..cdc26ef 100644
--- a/src/NDSCart.cpp
+++ b/src/NDSCart.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -22,11 +22,11 @@
#include "DSi.h"
#include "NDSCart.h"
#include "ARM.h"
+#include "CRC32.h"
#include "DSi_AES.h"
#include "Platform.h"
#include "ROMList.h"
#include "melonDLDI.h"
-#include "NDSCart_SRAMManager.h"
namespace NDSCart
@@ -51,12 +51,9 @@ u32 TransferDir;
u8 TransferCmd[8];
bool CartInserted;
-char CartName[256];
u8* CartROM;
u32 CartROMSize;
u32 CartID;
-bool CartIsHomebrew;
-bool CartIsDSi;
NDSHeader Header;
NDSBanner Banner;
@@ -135,13 +132,32 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod)
}
}
-void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod)
+void Key1_LoadKeyBuf(bool dsi)
{
- // TODO: source the key data from different possible places
- if (dsi && NDS::ConsoleType==1)
- memcpy(Key1_KeyBuf, &DSi::ARM7iBIOS[0xC6D0], 0x1048); // hax
+ // it is possible that this gets called before the BIOSes are loaded
+ // so we will read from the BIOS files directly
+
+ if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
+ {
+ std::string path = Platform::GetConfigString(dsi ? Platform::DSi_BIOS7Path : Platform::BIOS7Path);
+ FILE* f = Platform::OpenLocalFile(path, "rb");
+ if (f)
+ {
+ fseek(f, dsi ? 0xC6D0 : 0x0030, SEEK_SET);
+ fread(Key1_KeyBuf, 0x1048, 1, f);
+ fclose(f);
+ }
+ }
else
- memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax
+ {
+ // well
+ memset(Key1_KeyBuf, 0, 0x1048);
+ }
+}
+
+void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod)
+{
+ Key1_LoadKeyBuf(dsi);
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
@@ -191,6 +207,22 @@ CartCommon::~CartCommon()
{
}
+u32 CartCommon::Checksum()
+{
+ u32 crc = CRC32(ROM, 0x40);
+
+ crc = CRC32(&ROM[Header.ARM9ROMOffset], Header.ARM9Size, crc);
+ crc = CRC32(&ROM[Header.ARM7ROMOffset], Header.ARM7Size, crc);
+
+ if (IsDSi)
+ {
+ crc = CRC32(&ROM[Header.DSiARM9iROMOffset], Header.DSiARM9iSize, crc);
+ crc = CRC32(&ROM[Header.DSiARM7iROMOffset], Header.DSiARM7iSize, crc);
+ }
+
+ return crc;
+}
+
void CartCommon::Reset()
{
CmdEncMode = 0;
@@ -198,11 +230,11 @@ void CartCommon::Reset()
DSiMode = false;
}
-void CartCommon::SetupDirectBoot()
+void CartCommon::SetupDirectBoot(std::string romname)
{
CmdEncMode = 2;
DataEncMode = 2;
- DSiMode = IsDSi && NDS::ConsoleType==1;
+ DSiMode = IsDSi && (NDS::ConsoleType==1);
}
void CartCommon::DoSavestate(Savestate* file)
@@ -214,20 +246,11 @@ void CartCommon::DoSavestate(Savestate* file)
file->Bool32(&DSiMode);
}
-void CartCommon::LoadSave(const char* path, u32 type)
-{
-}
-
-void CartCommon::RelocateSave(const char* path, bool write)
+void CartCommon::SetupSave(u32 type)
{
}
-int CartCommon::ImportSRAM(const u8* data, u32 length)
-{
- return 0;
-}
-
-void CartCommon::FlushSRAMFile()
+void CartCommon::LoadSave(const u8* savedata, u32 savelen)
{
}
@@ -392,11 +415,7 @@ void CartRetail::DoSavestate(Savestate* file)
CartCommon::DoSavestate(file);
// we reload the SRAM contents.
- // it should be the same file (as it should be the same ROM, duh)
- // but the contents may change
-
- //if (!file->Saving && SRAMLength)
- // delete[] SRAM;
+ // it should be the same file, but the contents may change
u32 oldlen = SRAMLength;
@@ -407,13 +426,11 @@ void CartRetail::DoSavestate(Savestate* file)
printf("oh well. loading it anyway. adsfgdsf\n");
if (oldlen) delete[] SRAM;
+ SRAM = nullptr;
if (SRAMLength) SRAM = new u8[SRAMLength];
}
if (SRAMLength)
{
- //if (!file->Saving)
- // SRAM = new u8[SRAMLength];
-
file->VarArray(SRAM, SRAMLength);
}
@@ -423,20 +440,14 @@ void CartRetail::DoSavestate(Savestate* file)
file->Var32(&SRAMAddr);
file->Var8(&SRAMStatus);
- // SRAMManager might now have an old buffer (or one from the future or alternate timeline!)
- if (!file->Saving)
- {
- SRAMFileDirty = false;
- NDSCart_SRAMManager::RequestFlush();
- }
+ if ((!file->Saving) && SRAM)
+ Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength);
}
-void CartRetail::LoadSave(const char* path, u32 type)
+void CartRetail::SetupSave(u32 type)
{
if (SRAM) delete[] SRAM;
-
- strncpy(SRAMPath, path, 1023);
- SRAMPath[1023] = '\0';
+ SRAM = nullptr;
if (type > 10) type = 0;
int sramlen[] =
@@ -455,18 +466,6 @@ void CartRetail::LoadSave(const char* path, u32 type)
memset(SRAM, 0xFF, SRAMLength);
}
- FILE* f = Platform::OpenFile(path, "rb");
- if (f)
- {
- fseek(f, 0, SEEK_SET);
- fread(SRAM, 1, SRAMLength, f);
-
- fclose(f);
- }
-
- SRAMFileDirty = false;
- NDSCart_SRAMManager::Setup(path, SRAM, SRAMLength);
-
switch (type)
{
case 1: SRAMType = 1; break; // EEPROM, small
@@ -483,47 +482,13 @@ void CartRetail::LoadSave(const char* path, u32 type)
}
}
-void CartRetail::RelocateSave(const char* path, bool write)
-{
- if (!write)
- {
- LoadSave(path, 0); // lazy
- return;
- }
-
- strncpy(SRAMPath, path, 1023);
- SRAMPath[1023] = '\0';
-
- FILE* f = Platform::OpenFile(path, "wb");
- if (!f)
- {
- printf("NDSCart_SRAM::RelocateSave: failed to create new file. fuck\n");
- return;
- }
-
- fwrite(SRAM, SRAMLength, 1, f);
- fclose(f);
-}
-
-int CartRetail::ImportSRAM(const u8* data, u32 length)
-{
- memcpy(SRAM, data, std::min(length, SRAMLength));
- FILE* f = Platform::OpenFile(SRAMPath, "wb");
- if (f)
- {
- fwrite(SRAM, SRAMLength, 1, f);
- fclose(f);
- }
-
- return length - SRAMLength;
-}
-
-void CartRetail::FlushSRAMFile()
+void CartRetail::LoadSave(const u8* savedata, u32 savelen)
{
- if (!SRAMFileDirty) return;
+ if (!SRAM) return;
- SRAMFileDirty = false;
- NDSCart_SRAMManager::RequestFlush();
+ u32 len = std::min(savelen, SRAMLength);
+ memcpy(SRAM, savedata, len);
+ Platform::WriteNDSSave(savedata, len, 0, len);
}
int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len)
@@ -564,17 +529,17 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)
{
case 0x04: // write disable
SRAMStatus &= ~(1<<1);
- break;
+ return 0;
case 0x06: // write enable
SRAMStatus |= (1<<1);
- break;
+ return 0;
default:
SRAMCmd = val;
SRAMAddr = 0;
}
- return 0;
+ return 0xFF;
}
switch (SRAMType)
@@ -582,7 +547,7 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)
case 1: return SRAMWrite_EEPROMTiny(val, pos, last);
case 2: return SRAMWrite_EEPROM(val, pos, last);
case 3: return SRAMWrite_FLASH(val, pos, last);
- default: return 0;
+ default: return 0xFF;
}
}
@@ -624,6 +589,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
if (pos < 2)
{
SRAMAddr = val;
+ SRAMFirstAddr = SRAMAddr;
}
else
{
@@ -631,11 +597,15 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
if (SRAMStatus & (1<<1))
{
SRAM[(SRAMAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF] = val;
- SRAMFileDirty |= last;
}
SRAMAddr++;
}
- if (last) SRAMStatus &= ~(1<<1);
+ if (last)
+ {
+ SRAMStatus &= ~(1<<1);
+ Platform::WriteNDSSave(SRAM, SRAMLength,
+ (SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr);
+ }
return 0;
case 0x03: // read low
@@ -658,7 +628,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
default:
if (pos == 1)
printf("unknown tiny EEPROM save command %02X\n", SRAMCmd);
- return 0;
+ return 0xFF;
}
}
@@ -683,6 +653,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)
{
SRAMAddr <<= 8;
SRAMAddr |= val;
+ SRAMFirstAddr = SRAMAddr;
}
else
{
@@ -690,11 +661,15 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)
if (SRAMStatus & (1<<1))
{
SRAM[SRAMAddr & (SRAMLength-1)] = val;
- SRAMFileDirty |= last;
}
SRAMAddr++;
}
- if (last) SRAMStatus &= ~(1<<1);
+ if (last)
+ {
+ SRAMStatus &= ~(1<<1);
+ Platform::WriteNDSSave(SRAM, SRAMLength,
+ SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
+ }
return 0;
case 0x03: // read
@@ -719,7 +694,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)
default:
if (pos == 1)
printf("unknown EEPROM save command %02X\n", SRAMCmd);
- return 0;
+ return 0xFF;
}
}
@@ -735,6 +710,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
{
SRAMAddr <<= 8;
SRAMAddr |= val;
+ SRAMFirstAddr = SRAMAddr;
}
else
{
@@ -742,11 +718,15 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
{
// CHECKME: should it be &=~val ??
SRAM[SRAMAddr & (SRAMLength-1)] = 0;
- SRAMFileDirty |= last;
}
SRAMAddr++;
}
- if (last) SRAMStatus &= ~(1<<1);
+ if (last)
+ {
+ SRAMStatus &= ~(1<<1);
+ Platform::WriteNDSSave(SRAM, SRAMLength,
+ SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
+ }
return 0;
case 0x03: // read
@@ -768,17 +748,22 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
{
SRAMAddr <<= 8;
SRAMAddr |= val;
+ SRAMFirstAddr = SRAMAddr;
}
else
{
if (SRAMStatus & (1<<1))
{
SRAM[SRAMAddr & (SRAMLength-1)] = val;
- SRAMFileDirty |= last;
}
SRAMAddr++;
}
- if (last) SRAMStatus &= ~(1<<1);
+ if (last)
+ {
+ SRAMStatus &= ~(1<<1);
+ Platform::WriteNDSSave(SRAM, SRAMLength,
+ SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
+ }
return 0;
case 0x0B: // fast read
@@ -809,6 +794,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
{
SRAMAddr <<= 8;
SRAMAddr |= val;
+ SRAMFirstAddr = SRAMAddr;
}
if ((pos == 3) && (SRAMStatus & (1<<1)))
{
@@ -817,9 +803,13 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
SRAM[SRAMAddr & (SRAMLength-1)] = 0;
SRAMAddr++;
}
- SRAMFileDirty = true;
}
- if (last) SRAMStatus &= ~(1<<1);
+ if (last)
+ {
+ SRAMStatus &= ~(1<<1);
+ Platform::WriteNDSSave(SRAM, SRAMLength,
+ SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
+ }
return 0;
case 0xDB: // page erase
@@ -827,6 +817,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
{
SRAMAddr <<= 8;
SRAMAddr |= val;
+ SRAMFirstAddr = SRAMAddr;
}
if ((pos == 3) && (SRAMStatus & (1<<1)))
{
@@ -835,15 +826,19 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
SRAM[SRAMAddr & (SRAMLength-1)] = 0;
SRAMAddr++;
}
- SRAMFileDirty = true;
}
- if (last) SRAMStatus &= ~(1<<1);
+ if (last)
+ {
+ SRAMStatus &= ~(1<<1);
+ Platform::WriteNDSSave(SRAM, SRAMLength,
+ SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
+ }
return 0;
default:
if (pos == 1)
printf("unknown FLASH save command %02X\n", SRAMCmd);
- return 0;
+ return 0xFF;
}
}
@@ -884,19 +879,12 @@ void CartRetailNAND::DoSavestate(Savestate* file)
BuildSRAMID();
}
-void CartRetailNAND::LoadSave(const char* path, u32 type)
+void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen)
{
- CartRetail::LoadSave(path, type);
+ CartRetail::LoadSave(savedata, savelen);
BuildSRAMID();
}
-int CartRetailNAND::ImportSRAM(const u8* data, u32 length)
-{
- int ret = CartRetail::ImportSRAM(data, length);
- BuildSRAMID();
- return ret;
-}
-
int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len)
{
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len);
@@ -926,7 +914,7 @@ int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len)
if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000))
{
memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800);
- SRAMFileDirty = true;
+ Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800);
}
SRAMAddr = 0;
@@ -1164,8 +1152,30 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)
{
+ SD = nullptr;
+}
+
+CartHomebrew::~CartHomebrew()
+{
+ if (SD)
+ {
+ SD->Close();
+ delete SD;
+ }
+}
+
+void CartHomebrew::Reset()
+{
+ CartCommon::Reset();
+
ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly);
+ if (SD)
+ {
+ SD->Close();
+ delete SD;
+ }
+
if (Platform::GetConfigBool(Platform::DLDI_Enable))
{
std::string folderpath;
@@ -1185,29 +1195,15 @@ CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len,
SD = nullptr;
}
-CartHomebrew::~CartHomebrew()
-{
- if (SD)
- {
- SD->Close();
- delete SD;
- }
-}
-
-void CartHomebrew::Reset()
-{
- CartCommon::Reset();
-}
-
-void CartHomebrew::SetupDirectBoot()
+void CartHomebrew::SetupDirectBoot(std::string romname)
{
- CartCommon::SetupDirectBoot();
+ CartCommon::SetupDirectBoot(romname);
if (SD)
{
// add the ROM to the SD volume
- if (!SD->InjectFile(CartName, CartROM, CartROMSize))
+ if (!SD->InjectFile(romname, CartROM, CartROMSize))
return;
// setup argv command line
@@ -1216,7 +1212,7 @@ void CartHomebrew::SetupDirectBoot()
int argvlen;
strncpy(argv, "fat:/", 511);
- strncat(argv, CartName, 511);
+ strncat(argv, romname.c_str(), 511);
argvlen = strlen(argv);
void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32;
@@ -1295,40 +1291,8 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
}
}
-void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
+void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly)
{
- u32 offset = *(u32*)&ROM[0x20];
- u32 size = *(u32*)&ROM[0x2C];
-
- u8* binary = &ROM[offset];
- u32 dldioffset = 0;
-
- for (u32 i = 0; i < size; i++)
- {
- if (*(u32*)&binary[i ] == 0xBF8DA5ED &&
- *(u32*)&binary[i+4] == 0x69684320 &&
- *(u32*)&binary[i+8] == 0x006D6873)
- {
- dldioffset = i;
- break;
- }
- }
-
- if (!dldioffset)
- {
- return;
- }
-
- printf("DLDI structure found at %08X (%08X)\n", dldioffset, offset+dldioffset);
-
- if (*(u32*)&patch[0] != 0xBF8DA5ED ||
- *(u32*)&patch[4] != 0x69684320 ||
- *(u32*)&patch[8] != 0x006D6873)
- {
- printf("bad DLDI patch\n");
- return;
- }
-
if (patch[0x0D] > binary[dldioffset+0x0F])
{
printf("DLDI driver ain't gonna fit, sorry\n");
@@ -1425,7 +1389,37 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
*(u32*)&binary[writesec_addr+0x04] = 0xE12FFF1E; // bx lr
}
- printf("applied DLDI patch\n");
+ printf("applied DLDI patch at %08X\n", dldioffset);
+}
+
+void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
+{
+ if (*(u32*)&patch[0] != 0xBF8DA5ED ||
+ *(u32*)&patch[4] != 0x69684320 ||
+ *(u32*)&patch[8] != 0x006D6873)
+ {
+ printf("bad DLDI patch\n");
+ return;
+ }
+
+ u32 offset = *(u32*)&ROM[0x20];
+ u32 size = *(u32*)&ROM[0x2C];
+
+ u8* binary = &ROM[offset];
+
+ for (u32 i = 0; i < size; )
+ {
+ if (*(u32*)&binary[i ] == 0xBF8DA5ED &&
+ *(u32*)&binary[i+4] == 0x69684320 &&
+ *(u32*)&binary[i+8] == 0x006D6873)
+ {
+ printf("DLDI structure found at %08X (%08X)\n", i, offset+i);
+ ApplyDLDIPatchAt(binary, i, patch, patchlen, readonly);
+ i += patchlen;
+ }
+ else
+ i++;
+ }
}
void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
@@ -1441,6 +1435,7 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
bool Init()
{
+ CartInserted = false;
CartROM = nullptr;
Cart = nullptr;
@@ -1455,17 +1450,6 @@ void DeInit()
void Reset()
{
- CartInserted = false;
- if (CartROM) delete[] CartROM;
- CartROM = nullptr;
- CartROMSize = 0;
- CartID = 0;
- CartIsHomebrew = false;
- CartIsDSi = false;
-
- if (Cart) delete Cart;
- Cart = nullptr;
-
ResetCart();
}
@@ -1494,6 +1478,30 @@ void DoSavestate(Savestate* file)
// (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)
+ u32 carttype = 0;
+ u32 cartchk = 0;
+ if (Cart)
+ {
+ carttype = Cart->Type();
+ cartchk = Cart->Checksum();
+ }
+
+ if (file->Saving)
+ {
+ file->Var32(&carttype);
+ file->Var32(&cartchk);
+ }
+ else
+ {
+ u32 savetype;
+ file->Var32(&savetype);
+ if (savetype != carttype) return;
+
+ u32 savechk;
+ file->Var32(&savechk);
+ if (savechk != cartchk) return;
+ }
+
if (Cart) Cart->DoSavestate(file);
}
@@ -1542,11 +1550,6 @@ bool ReadROMParams(u32 gamecode, ROMListEntry* params)
void DecryptSecureArea(u8* out)
{
- // TODO: source decryption data from different possible sources
- // * original DS-mode ARM7 BIOS has the key data at 0x30
- // * .srl ROMs (VC dumps) have encrypted secure areas but have precomputed
- // decryption data at 0x1000 (and at the beginning of the DSi region if any)
-
u32 gamecode = (u32)Header.GameCode[3] << 24 |
(u32)Header.GameCode[2] << 16 |
(u32)Header.GameCode[1] << 8 |
@@ -1576,10 +1579,41 @@ void DecryptSecureArea(u8* out)
}
}
-bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
+bool LoadROM(const u8* romdata, u32 romlen)
{
+ if (CartInserted)
+ EjectCart();
+
+ memset(&Header, 0, sizeof(Header));
+ memset(&Banner, 0, sizeof(Banner));
+
+ CartROMSize = 0x200;
+ while (CartROMSize < romlen)
+ CartROMSize <<= 1;
+
+ try
+ {
+ CartROM = new u8[CartROMSize];
+ }
+ catch (const std::bad_alloc& e)
+ {
+ printf("NDSCart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize);
+ return false;
+ }
+
+ memset(CartROM, 0, CartROMSize);
+ memcpy(CartROM, romdata, romlen);
+
memcpy(&Header, CartROM, sizeof(Header));
- memcpy(&Banner, CartROM + Header.BannerOffset, sizeof(Banner));
+
+ u8 unitcode = Header.UnitCode;
+ bool dsi = (unitcode & 0x02) != 0;
+
+ size_t bannersize = dsi ? 0x23C0 : 0xA40;
+ if (Header.BannerOffset >= 0x200 && Header.BannerOffset < (CartROMSize - bannersize))
+ {
+ memcpy(&Banner, CartROM + Header.BannerOffset, bannersize);
+ }
printf("Game code: %.4s\n", Header.GameCode);
@@ -1588,8 +1622,8 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
- u8 unitcode = Header.UnitCode;
- CartIsDSi = (unitcode & 0x02) != 0;
+ u32 arm9base = Header.ARM9ROMOffset;
+ bool homebrew = (arm9base < 0x4000) || (gamecode == 0x23232323);
ROMListEntry romparams;
if (!ReadROMParams(gamecode, &romparams))
@@ -1599,7 +1633,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
romparams.GameCode = gamecode;
romparams.ROMSize = CartROMSize;
- if (*(u32*)&CartROM[0x20] < 0x4000)
+ if (homebrew)
romparams.SaveMemType = 0; // no saveRAM for homebrew
else
romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME)
@@ -1607,7 +1641,8 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
else
printf("ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
- if (romparams.ROMSize != filelength) printf("!! bad ROM size %d (expected %d) rounded to %d\n", filelength, romparams.ROMSize, CartROMSize);
+ if (romparams.ROMSize != romlen)
+ printf("!! bad ROM size %d (expected %d) rounded to %d\n", romlen, romparams.ROMSize, CartROMSize);
// generate a ROM ID
// note: most games don't check the actual value
@@ -1622,7 +1657,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
if (romparams.SaveMemType >= 8 && romparams.SaveMemType <= 10)
CartID |= 0x08000000; // NAND flag
- if (CartIsDSi)
+ if (dsi)
CartID |= 0x40000000;
// cart ID for Jam with the Band
@@ -1633,35 +1668,26 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
printf("Cart ID: %08X\n", CartID);
- u32 arm9base = *(u32*)&CartROM[0x20];
-
- if (arm9base < 0x8000)
+ if (arm9base >= 0x4000 && arm9base < 0x8000)
{
- if (arm9base >= 0x4000)
+ // reencrypt secure area if needed
+ if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF && *(u32*)&CartROM[arm9base+0x10] != 0xE7FFDEFF)
{
- // reencrypt secure area if needed
- if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF && *(u32*)&CartROM[arm9base+0x10] != 0xE7FFDEFF)
- {
- printf("Re-encrypting cart secure area\n");
+ printf("Re-encrypting cart secure area\n");
- strncpy((char*)&CartROM[arm9base], "encryObj", 8);
+ strncpy((char*)&CartROM[arm9base], "encryObj", 8);
- Key1_InitKeycode(false, gamecode, 3, 2);
- for (u32 i = 0; i < 0x800; i += 8)
- Key1_Encrypt((u32*)&CartROM[arm9base + i]);
+ Key1_InitKeycode(false, gamecode, 3, 2);
+ for (u32 i = 0; i < 0x800; i += 8)
+ Key1_Encrypt((u32*)&CartROM[arm9base + i]);
- Key1_InitKeycode(false, gamecode, 2, 2);
- Key1_Encrypt((u32*)&CartROM[arm9base]);
- }
+ Key1_InitKeycode(false, gamecode, 2, 2);
+ Key1_Encrypt((u32*)&CartROM[arm9base]);
}
}
- if ((arm9base < 0x4000) || (gamecode == 0x23232323))
- {
- CartIsHomebrew = true;
- }
-
CartInserted = true;
+ DSi::SetCartInserted(true);
u32 irversion = 0;
if ((gamecode & 0xFF) == 'I')
@@ -1672,7 +1698,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
irversion = 2; // Pokémon HG/SS, B/W, B2/W2
}
- if (CartIsHomebrew)
+ if (homebrew)
Cart = new CartHomebrew(CartROM, CartROMSize, CartID);
else if (CartID & 0x08000000)
Cart = new CartRetailNAND(CartROM, CartROMSize, CartID);
@@ -1684,99 +1710,46 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
Cart = new CartRetail(CartROM, CartROMSize, CartID);
if (Cart)
- {
Cart->Reset();
- if (direct)
- {
- NDS::SetupDirectBoot();
- Cart->SetupDirectBoot();
- }
- }
-
- // encryption
- Key1_InitKeycode(false, gamecode, 2, 2);
- // save
- printf("Save file: %s\n", sram);
- if (Cart) Cart->LoadSave(sram, romparams.SaveMemType);
+ if (Cart && romparams.SaveMemType > 0)
+ Cart->SetupSave(romparams.SaveMemType);
return true;
}
-bool LoadROM(const char* path, const char* sram, bool direct)
+void LoadSave(const u8* savedata, u32 savelen)
{
- // TODO: streaming mode? for really big ROMs or systems with limited RAM
- // for now we're lazy
- // also TODO: validate what we're loading!!
-
- FILE* f = Platform::OpenFile(path, "rb");
- if (!f)
- {
- return false;
- }
-
- NDS::Reset();
-
- char* romname = strrchr((char*)path, '/');
- if (!romname)
- {
- romname = strrchr((char*)path, '\\');
- if (!romname)
- romname = (char*)&path[-1];
- }
- romname++;
- strncpy(CartName, romname, 255); CartName[255] = '\0';
-
- fseek(f, 0, SEEK_END);
- u32 len = (u32)ftell(f);
-
- CartROMSize = 0x200;
- while (CartROMSize < len)
- CartROMSize <<= 1;
-
- CartROM = new u8[CartROMSize];
- memset(CartROM, 0, CartROMSize);
- fseek(f, 0, SEEK_SET);
- fread(CartROM, 1, len, f);
-
- fclose(f);
-
- return LoadROMCommon(len, sram, direct);
+ if (Cart)
+ Cart->LoadSave(savedata, savelen);
}
-bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct)
+void SetupDirectBoot(std::string romname)
{
- NDS::Reset();
-
- // TODO: make it more meaningful?
- strncpy(CartName, "rom.nds", 256);
+ if (Cart)
+ Cart->SetupDirectBoot(romname);
+}
- u32 len = filelength;
- CartROMSize = 0x200;
- while (CartROMSize < len)
- CartROMSize <<= 1;
+void EjectCart()
+{
+ if (!CartInserted) return;
- CartROM = new u8[CartROMSize];
- memset(CartROM, 0, CartROMSize);
- memcpy(CartROM, romdata, filelength);
+ // ejecting the cart triggers the gamecard IRQ
+ NDS::SetIRQ(0, NDS::IRQ_CartIREQMC);
+ NDS::SetIRQ(1, NDS::IRQ_CartIREQMC);
- return LoadROMCommon(filelength, sram, direct);
-}
+ if (Cart) delete Cart;
+ Cart = nullptr;
-void RelocateSave(const char* path, bool write)
-{
- if (Cart) Cart->RelocateSave(path, write);
-}
+ CartInserted = false;
+ if (CartROM) delete[] CartROM;
+ CartROM = nullptr;
+ CartROMSize = 0;
+ CartID = 0;
-void FlushSRAMFile()
-{
- if (Cart) Cart->FlushSRAMFile();
-}
+ DSi::SetCartInserted(false);
-int ImportSRAM(const u8* data, u32 length)
-{
- if (Cart) return Cart->ImportSRAM(data, length);
- return 0;
+ // CHECKME: does an eject imply anything for the ROM/SPI transfer registers?
}
void ResetCart()
@@ -1840,9 +1813,8 @@ void ROMPrepareData(u32 param)
void WriteROMCnt(u32 val)
{
- ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x00800000);
-
- if (!(SPICnt & (1<<15))) return;
+ u32 xferstart = (val & ~ROMCnt) & (1<<31);
+ ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x20800000);
// all this junk would only really be useful if melonDS was interfaced to
// a DS cart reader
@@ -1866,7 +1838,11 @@ void WriteROMCnt(u32 val)
printf("key2 Y: %02X%08X\n", (u32)(Key2_Y>>32), (u32)Key2_Y);
}
- if (!(ROMCnt & (1<<31))) return;
+ // transfers will only start when bit31 changes from 0 to 1
+ // and if AUXSPICNT is configured correctly
+ if (!(SPICnt & (1<<15))) return;
+ if (SPICnt & (1<<13)) return;
+ if (!xferstart) return;
u32 datasize = (ROMCnt >> 24) & 0x7;
if (datasize == 7)
@@ -1880,6 +1856,8 @@ void WriteROMCnt(u32 val)
*(u32*)&TransferCmd[0] = *(u32*)&ROMCommand[0];
*(u32*)&TransferCmd[4] = *(u32*)&ROMCommand[4];
+ memset(TransferData, 0xFF, TransferLen);
+
/*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n",
SPICnt, ROMCnt,
TransferCmd[0], TransferCmd[1], TransferCmd[2], TransferCmd[3],
@@ -1903,6 +1881,7 @@ void WriteROMCnt(u32 val)
// thus a command would take 8 cycles to be transferred
// and it would take 4 cycles to receive a word of data
// TODO: advance read position if bit28 is set
+ // TODO: during a write transfer, bit23 is set immediately when beginning the transfer(?)
u32 xfercycle = (ROMCnt & (1<<27)) ? 8 : 5;
u32 cmddelay = 8;
@@ -1983,6 +1962,10 @@ void WriteSPICnt(u16 val)
}
SPICnt = (SPICnt & 0x0080) | (val & 0xE043);
+
+ // AUXSPICNT can be changed during a transfer
+ // in this case, the transfer continues until the end, even if bit13 or bit15 are cleared
+ // if the transfer speed is changed, the transfer continues at the new speed (TODO)
if (SPICnt & (1<<7))
printf("!! CHANGING AUXSPICNT DURING TRANSFER: %04X\n", val);
}
@@ -2005,8 +1988,7 @@ void WriteSPIData(u8 val)
{
if (!(SPICnt & (1<<15))) return;
if (!(SPICnt & (1<<13))) return;
-
- if (SPICnt & (1<<7)) printf("!! WRITING AUXSPIDATA DURING PENDING TRANSFER\n");
+ if (SPICnt & (1<<7)) return;
SPICnt |= (1<<7);
diff --git a/src/NDSCart.h b/src/NDSCart.h
index 9f39988..10286ac 100644
--- a/src/NDSCart.h
+++ b/src/NDSCart.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,7 +19,10 @@
#ifndef NDSCART_H
#define NDSCART_H
+#include <string>
+
#include "types.h"
+#include "Savestate.h"
#include "NDS_Header.h"
#include "FATStorage.h"
@@ -33,15 +36,16 @@ public:
CartCommon(u8* rom, u32 len, u32 chipid);
virtual ~CartCommon();
+ virtual u32 Type() { return 0x001; }
+ virtual u32 Checksum();
+
virtual void Reset();
- virtual void SetupDirectBoot();
+ virtual void SetupDirectBoot(std::string romname);
virtual void DoSavestate(Savestate* file);
- virtual void LoadSave(const char* path, u32 type);
- virtual void RelocateSave(const char* path, bool write);
- virtual int ImportSRAM(const u8* data, u32 length);
- virtual void FlushSRAMFile();
+ virtual void SetupSave(u32 type);
+ virtual void LoadSave(const u8* savedata, u32 savelen);
virtual int ROMCommandStart(u8* cmd, u8* data, u32 len);
virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len);
@@ -71,14 +75,14 @@ public:
CartRetail(u8* rom, u32 len, u32 chipid);
virtual ~CartRetail() override;
+ virtual u32 Type() override { return 0x101; }
+
virtual void Reset() override;
virtual void DoSavestate(Savestate* file) override;
- virtual void LoadSave(const char* path, u32 type) override;
- virtual void RelocateSave(const char* path, bool write) override;
- virtual int ImportSRAM(const u8* data, u32 length) override;
- virtual void FlushSRAMFile() override;
+ virtual void SetupSave(u32 type) override;
+ virtual void LoadSave(const u8* savedata, u32 savelen) override;
virtual int ROMCommandStart(u8* cmd, u8* data, u32 len) override;
@@ -95,11 +99,9 @@ protected:
u32 SRAMLength;
u32 SRAMType;
- char SRAMPath[1024];
- bool SRAMFileDirty;
-
u8 SRAMCmd;
u32 SRAMAddr;
+ u32 SRAMFirstAddr;
u8 SRAMStatus;
};
@@ -110,12 +112,13 @@ public:
CartRetailNAND(u8* rom, u32 len, u32 chipid);
~CartRetailNAND() override;
+ virtual u32 Type() override { return 0x102; }
+
void Reset() override;
void DoSavestate(Savestate* file) override;
- void LoadSave(const char* path, u32 type) override;
- int ImportSRAM(const u8* data, u32 length) override;
+ void LoadSave(const u8* savedata, u32 savelen) override;
int ROMCommandStart(u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
@@ -139,6 +142,8 @@ public:
CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion);
~CartRetailIR() override;
+ virtual u32 Type() override { return 0x103; }
+
void Reset() override;
void DoSavestate(Savestate* file) override;
@@ -157,6 +162,8 @@ public:
CartRetailBT(u8* rom, u32 len, u32 chipid);
~CartRetailBT() override;
+ virtual u32 Type() override { return 0x104; }
+
void Reset() override;
void DoSavestate(Savestate* file) override;
@@ -171,8 +178,10 @@ public:
CartHomebrew(u8* rom, u32 len, u32 chipid);
~CartHomebrew() override;
+ virtual u32 Type() override { return 0x201; }
+
void Reset() override;
- void SetupDirectBoot() override;
+ void SetupDirectBoot(std::string romname) override;
void DoSavestate(Savestate* file) override;
@@ -180,6 +189,7 @@ public:
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
private:
+ void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly);
void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly);
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
@@ -192,6 +202,7 @@ extern u32 ROMCnt;
extern u8 ROMCommand[8];
+extern bool CartInserted;
extern u8* CartROM;
extern u32 CartROMSize;
@@ -207,14 +218,12 @@ void Reset();
void DoSavestate(Savestate* file);
void DecryptSecureArea(u8* out);
-bool LoadROM(const char* path, const char* sram, bool direct);
-bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct);
-
-void FlushSRAMFile();
-void RelocateSave(const char* path, bool write);
+bool LoadROM(const u8* romdata, u32 romlen);
+void LoadSave(const u8* savedata, u32 savelen);
+void SetupDirectBoot(std::string romname);
-int ImportSRAM(const u8* data, u32 length);
+void EjectCart();
void ResetCart();
diff --git a/src/NDSCart_SRAMManager.cpp b/src/NDSCart_SRAMManager.cpp
deleted file mode 100644
index addd122..0000000
--- a/src/NDSCart_SRAMManager.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- Copyright 2016-2021 Arisotura
-
- 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 <string.h>
-#include <unistd.h>
-#include <time.h>
-#include <atomic>
-#include "NDSCart_SRAMManager.h"
-#include "Platform.h"
-
-namespace NDSCart_SRAMManager
-{
-
-Platform::Thread* FlushThread;
-std::atomic_bool FlushThreadRunning;
-Platform::Mutex* SecondaryBufferLock;
-
-char Path[1024];
-
-u8* Buffer;
-u32 Length;
-
-u8* SecondaryBuffer;
-u32 SecondaryBufferLength;
-
-time_t TimeAtLastFlushRequest;
-
-// We keep versions in case the user closes the application before
-// a flush cycle is finished.
-u32 PreviousFlushVersion;
-u32 FlushVersion;
-
-void FlushThreadFunc();
-
-bool Init()
-{
- SecondaryBufferLock = Platform::Mutex_Create();
-
- return true;
-}
-
-void DeInit()
-{
- if (FlushThreadRunning)
- {
- FlushThreadRunning = false;
- Platform::Thread_Wait(FlushThread);
- Platform::Thread_Free(FlushThread);
- FlushSecondaryBuffer();
- }
-
- if (SecondaryBuffer) delete[] SecondaryBuffer;
- SecondaryBuffer = NULL;
-
- Platform::Mutex_Free(SecondaryBufferLock);
-}
-
-void Setup(const char* path, u8* buffer, u32 length)
-{
- // Flush SRAM in case there is unflushed data from previous state.
- FlushSecondaryBuffer();
-
- Platform::Mutex_Lock(SecondaryBufferLock);
-
- strncpy(Path, path, 1023);
- Path[1023] = '\0';
-
- Buffer = buffer;
- Length = length;
-
- if(SecondaryBuffer) delete[] SecondaryBuffer; // Delete secondary buffer, there might be previous state.
-
- SecondaryBuffer = new u8[length];
- SecondaryBufferLength = length;
-
- FlushVersion = 0;
- PreviousFlushVersion = 0;
- TimeAtLastFlushRequest = 0;
-
- Platform::Mutex_Unlock(SecondaryBufferLock);
-
- if (path[0] != '\0' && !FlushThreadRunning)
- {
- FlushThread = Platform::Thread_Create(FlushThreadFunc);
- FlushThreadRunning = true;
- }
- else if (path[0] == '\0' && FlushThreadRunning)
- {
- FlushThreadRunning = false;
- Platform::Thread_Wait(FlushThread);
- Platform::Thread_Free(FlushThread);
- }
-}
-
-void RequestFlush()
-{
- Platform::Mutex_Lock(SecondaryBufferLock);
- printf("NDS SRAM: Flush requested\n");
- memcpy(SecondaryBuffer, Buffer, Length);
- FlushVersion++;
- TimeAtLastFlushRequest = time(NULL);
- Platform::Mutex_Unlock(SecondaryBufferLock);
-}
-
-void FlushThreadFunc()
-{
- for (;;)
- {
- Platform::Sleep(100 * 1000); // 100ms
-
- if (!FlushThreadRunning) return;
-
- // We debounce for two seconds after last flush request to ensure that writing has finished.
- if (TimeAtLastFlushRequest == 0 || difftime(time(NULL), TimeAtLastFlushRequest) < 2)
- {
- continue;
- }
-
- FlushSecondaryBuffer();
- }
-}
-
-void FlushSecondaryBuffer(u8* dst, s32 dstLength)
-{
- // When flushing to a file, there's no point in re-writing the exact same data.
- if (!dst && !NeedsFlush()) return;
- // When flushing to memory, we don't know if dst already has any data so we only check that we CAN flush.
- if (dst && dstLength < SecondaryBufferLength) return;
-
- Platform::Mutex_Lock(SecondaryBufferLock);
- if (dst)
- {
- memcpy(dst, SecondaryBuffer, SecondaryBufferLength);
- }
- else
- {
- FILE* f = Platform::OpenFile(Path, "wb");
- if (f)
- {
- printf("NDS SRAM: Written\n");
- fwrite(SecondaryBuffer, SecondaryBufferLength, 1, f);
- fclose(f);
- }
- }
- PreviousFlushVersion = FlushVersion;
- TimeAtLastFlushRequest = 0;
- Platform::Mutex_Unlock(SecondaryBufferLock);
-}
-
-bool NeedsFlush()
-{
- return FlushVersion != PreviousFlushVersion;
-}
-
-void UpdateBuffer(u8* src, s32 srcLength)
-{
- if (!src || srcLength != Length) return;
-
- // should we create a lock for the primary buffer? this method is not intended to be called from a secondary thread in the way Flush is
- memcpy(Buffer, src, srcLength);
- Platform::Mutex_Lock(SecondaryBufferLock);
- memcpy(SecondaryBuffer, src, srcLength);
- Platform::Mutex_Unlock(SecondaryBufferLock);
-
- PreviousFlushVersion = FlushVersion;
-}
-
-}
diff --git a/src/NDS_Header.h b/src/NDS_Header.h
index c2079bf..2c94f4c 100644
--- a/src/NDS_Header.h
+++ b/src/NDS_Header.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, WaluigiWare64
+ Copyright 2016-2022 melonDS team, WaluigiWare64
This file is part of melonDS.
diff --git a/src/NonStupidBitfield.h b/src/NonStupidBitfield.h
index 45b160e..badefaf 100644
--- a/src/NonStupidBitfield.h
+++ b/src/NonStupidBitfield.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, RSDuck
+ Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp
index 9df814b..aee107a 100644
--- a/src/OpenGLSupport.cpp
+++ b/src/OpenGLSupport.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h
index cbbb34e..d40246c 100644
--- a/src/OpenGLSupport.h
+++ b/src/OpenGLSupport.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/Platform.h b/src/Platform.h
index bbdc245..f2997ef 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -32,6 +32,10 @@ void DeInit();
void StopEmu();
+// instance ID, for local multiplayer
+int InstanceID();
+std::string InstanceFileSuffix();
+
// configuration values
enum ConfigEntry
@@ -77,7 +81,6 @@ enum ConfigEntry
Firm_Color,
Firm_Message,
Firm_MAC,
- Firm_RandomizeMAC,
AudioBitrate,
};
@@ -144,13 +147,30 @@ void Mutex_Lock(Mutex* mutex);
void Mutex_Unlock(Mutex* mutex);
bool Mutex_TryLock(Mutex* mutex);
+void Sleep(u64 usecs);
+
+
+// functions called when the NDS or GBA save files need to be written back to storage
+// savedata and savelen are always the entire save memory buffer and its full length
+// writeoffset and writelen indicate which part of the memory was altered
+void WriteNDSSave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen);
+void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen);
+
// local multiplayer comm interface
// packet type: DS-style TX header (12 bytes) + original 802.11 frame
bool MP_Init();
void MP_DeInit();
-int MP_SendPacket(u8* data, int len);
-int MP_RecvPacket(u8* data, bool block);
+void MP_Begin();
+void MP_End();
+int MP_SendPacket(u8* data, int len, u64 timestamp);
+int MP_RecvPacket(u8* data, u64* timestamp);
+int MP_SendCmd(u8* data, int len, u64 timestamp);
+int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid);
+int MP_SendAck(u8* data, int len, u64 timestamp);
+int MP_RecvHostPacket(u8* data, u64* timestamp);
+u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask);
+
// LAN comm interface
// packet type: Ethernet (802.3)
@@ -159,7 +179,15 @@ void LAN_DeInit();
int LAN_SendPacket(u8* data, int len);
int LAN_RecvPacket(u8* data);
-void Sleep(u64 usecs);
+
+// interface for camera emulation
+// camera numbers:
+// 0 = DSi outer camera
+// 1 = DSi inner camera
+// other values reserved for future camera addon emulation
+void Camera_Start(int num);
+void Camera_Stop(int num);
+void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv);
}
diff --git a/src/ROMList.h b/src/ROMList.h
index 2f168e5..ab23112 100644
--- a/src/ROMList.h
+++ b/src/ROMList.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,6 +19,8 @@
#ifndef ROMLIST_H
#define ROMLIST_H
+#include "types.h"
+
struct ROMListEntry
{
u32 GameCode;
diff --git a/src/RTC.cpp b/src/RTC.cpp
index 905a8d8..1c02cbb 100644
--- a/src/RTC.cpp
+++ b/src/RTC.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/RTC.h b/src/RTC.h
index 89a5940..0fed9fd 100644
--- a/src/RTC.h
+++ b/src/RTC.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/SPI.cpp b/src/SPI.cpp
index 80ef336..e990b3a 100644
--- a/src/SPI.cpp
+++ b/src/SPI.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -127,8 +127,8 @@ void LoadDefaultFirmware()
if (NDS::ConsoleType == 1)
{
Firmware[0x1D] = 0x57; // DSi
- Firmware[0x2F] = 0x18;
- Firmware[0x1FD] = 0x02;
+ Firmware[0x2F] = 0x0F;
+ Firmware[0x1FD] = 0x01;
Firmware[0x1FE] = 0x20;
}
else
@@ -215,41 +215,52 @@ void LoadDefaultFirmware()
// wifi access points
// TODO: WFC ID??
- u32 apdata = userdata - 0x400;
- memset(&Firmware[apdata], 0, 0x300);
-
- strcpy((char*)&Firmware[apdata+0x40], "melonAP");
- if (NDS::ConsoleType == 1) *(u16*)&Firmware[apdata+0xEA] = 1400;
- Firmware[apdata+0xEF] = 0x01;
- *(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
-
- apdata += 0x100;
- Firmware[apdata+0xE7] = 0xFF;
- Firmware[apdata+0xEF] = 0x01;
- *(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
-
- apdata += 0x100;
- Firmware[apdata+0xE7] = 0xFF;
- Firmware[apdata+0xEF] = 0x01;
- *(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
-
- if (NDS::ConsoleType == 1)
+ FILE* f = Platform::OpenLocalFile("wfcsettings.bin"+Platform::InstanceFileSuffix(), "rb");
+ if (!f) f = Platform::OpenLocalFile("wfcsettings.bin", "rb");
+ if (f)
{
- apdata = userdata - 0xA00;
- Firmware[apdata+0xE7] = 0xFF;
+ u32 apdata = userdata - 0xA00;
+ fread(&Firmware[apdata], 0x900, 1, f);
+ fclose(f);
+ }
+ else
+ {
+ u32 apdata = userdata - 0x400;
+ memset(&Firmware[apdata], 0, 0x300);
+
+ strcpy((char*)&Firmware[apdata+0x40], "melonAP");
+ if (NDS::ConsoleType == 1) *(u16*)&Firmware[apdata+0xEA] = 1400;
+ Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
- apdata += 0x200;
+ apdata += 0x100;
Firmware[apdata+0xE7] = 0xFF;
+ Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
- apdata += 0x200;
+ apdata += 0x100;
Firmware[apdata+0xE7] = 0xFF;
+ Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
+
+ if (NDS::ConsoleType == 1)
+ {
+ apdata = userdata - 0xA00;
+ Firmware[apdata+0xE7] = 0xFF;
+ *(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
+
+ apdata += 0x200;
+ Firmware[apdata+0xE7] = 0xFF;
+ *(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
+
+ apdata += 0x200;
+ Firmware[apdata+0xE7] = 0xFF;
+ *(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
+ }
}
}
-void LoadFirmwareFromFile(FILE* f)
+void LoadFirmwareFromFile(FILE* f, bool makecopy)
{
fseek(f, 0, SEEK_END);
@@ -261,7 +272,9 @@ void LoadFirmwareFromFile(FILE* f)
fread(Firmware, 1, FirmwareLength, f);
// take a backup
- std::string fwBackupPath = FirmwarePath + ".bak";
+ std::string fwBackupPath;
+ if (!makecopy) fwBackupPath = FirmwarePath + ".bak";
+ else fwBackupPath = FirmwarePath;
FILE* bf = Platform::OpenLocalFile(fwBackupPath, "rb");
if (!bf)
{
@@ -323,15 +336,24 @@ void Reset()
else
FirmwarePath = Platform::GetConfigString(Platform::FirmwarePath);
+ bool makecopy = false;
+ std::string origpath = FirmwarePath;
+ FirmwarePath += Platform::InstanceFileSuffix();
+
FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb");
if (!f)
{
+ f = Platform::OpenLocalFile(origpath, "rb");
+ makecopy = true;
+ }
+ if (!f)
+ {
printf("Firmware not found! Generating default firmware.\n");
FirmwarePath = "";
}
else
{
- LoadFirmwareFromFile(f);
+ LoadFirmwareFromFile(f, makecopy);
fclose(f);
}
}
@@ -375,28 +397,28 @@ void Reset()
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
- if (firmoverride)
+ //if (firmoverride)
{
u8 mac[6];
- bool rep;
+ bool rep = false;
+
+ memcpy(mac, &Firmware[0x36], 6);
+
+ if (firmoverride)
+ rep = Platform::GetConfigArray(Platform::Firm_MAC, mac);
- if (Platform::GetConfigBool(Platform::Firm_RandomizeMAC))
+ int inst = Platform::InstanceID();
+ if (inst > 0)
{
- mac[0] = 0x00;
- mac[1] = 0x09;
- mac[2] = 0xBF;
- mac[3] = rand()&0xFF;
- mac[4] = rand()&0xFF;
- mac[5] = rand()&0xFF;
rep = true;
- }
- else
- {
- rep = Platform::GetConfigArray(Platform::Firm_MAC, mac);
+ mac[3] += inst;
+ mac[4] += inst*0x44;
+ mac[5] += inst*0x10;
}
if (rep)
{
+ mac[0] &= 0xFC; // ensure the MAC isn't a broadcast MAC
memcpy(&Firmware[0x36], mac, 6);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
@@ -463,6 +485,7 @@ void SetupDirectBoot(bool dsi)
}
}
+u32 GetFirmwareLength() { return FirmwareLength; }
u8 GetConsoleType() { return Firmware[0x1D]; }
u8 GetWifiVersion() { return Firmware[0x2F]; }
u8 GetNWifiVersion() { return Firmware[0x1FD]; } // for DSi; will return 0xFF on a DS
@@ -563,6 +586,7 @@ void Write(u8 val, u32 hold)
default:
printf("unknown firmware SPI command %02X\n", CurCmd);
+ Data = 0xFF;
break;
}
@@ -573,12 +597,27 @@ void Write(u8 val, u32 hold)
FILE* f = Platform::OpenLocalFile(FirmwarePath, "r+b");
if (f)
{
- u32 cutoff = 0x7FA00 & FirmwareMask;
+ u32 cutoff = ((NDS::ConsoleType==1) ? 0x7F400 : 0x7FA00) & FirmwareMask;
fseek(f, cutoff, SEEK_SET);
fwrite(&Firmware[cutoff], FirmwareLength-cutoff, 1, f);
fclose(f);
}
}
+ else
+ {
+ char wfcfile[50] = {0};
+ int inst = Platform::InstanceID();
+ if (inst > 0) snprintf(wfcfile, 49, "wfcsettings.bin", Platform::InstanceID());
+ else strncpy(wfcfile, "wfcsettings.bin", 49);
+
+ FILE* f = Platform::OpenLocalFile(wfcfile, "wb");
+ if (f)
+ {
+ u32 cutoff = 0x7F400 & FirmwareMask;
+ fwrite(&Firmware[cutoff], 0x900, 1, f);
+ fclose(f);
+ }
+ }
}
}
@@ -617,12 +656,15 @@ void Reset()
Registers[4] = 0x40;
RegMasks[0] = 0x7F;
- RegMasks[1] = 0x01;
+ RegMasks[1] = 0x00;
RegMasks[2] = 0x01;
RegMasks[3] = 0x03;
RegMasks[4] = 0x0F;
}
+bool GetBatteryLevelOkay() { return !Registers[1]; }
+void SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; }
+
void DoSavestate(Savestate* file)
{
file->Section("SPPW");
@@ -659,6 +701,7 @@ void Write(u8 val, u32 hold)
if (DataPos == 1)
{
+ // TODO: DSi-specific registers in DSi mode
u32 regid = Index & 0x07;
if (Index & 0x80)
@@ -891,6 +934,8 @@ void WriteCnt(u16 val)
}
}
+ // TODO: presumably the transfer speed can be changed during a transfer
+ // like with the NDSCart SPI interface
Cnt = (Cnt & 0x0080) | (val & 0xCF03);
if (val & 0x0400) printf("!! CRAPOED 16BIT SPI MODE\n");
if (Cnt & (1<<7)) printf("!! CHANGING SPICNT DURING TRANSFER: %04X\n", val);
@@ -925,8 +970,7 @@ u8 ReadData()
void WriteData(u8 val)
{
if (!(Cnt & (1<<15))) return;
-
- if (Cnt & (1<<7)) printf("!! WRITING AUXSPIDATA DURING PENDING TRANSFER\n");
+ if (Cnt & (1<<7)) return;
Cnt |= (1<<7);
switch (Cnt & 0x0300)
diff --git a/src/SPI.h b/src/SPI.h
index d82485b..8ac3948 100644
--- a/src/SPI.h
+++ b/src/SPI.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -28,6 +28,7 @@ void SetupDirectBoot(bool dsi);
u32 FixFirmwareLength(u32 originalLength);
+u32 GetFirmwareLength();
u8 GetConsoleType();
u8 GetWifiVersion();
u8 GetNWifiVersion();
@@ -36,6 +37,14 @@ u8* GetWifiMAC();
}
+namespace SPI_Powerman
+{
+
+bool GetBatteryLevelOkay();
+void SetBatteryLevelOkay(bool okay);
+
+}
+
namespace SPI_TSC
{
diff --git a/src/SPU.cpp b/src/SPU.cpp
index 1de6a26..9f245a2 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -184,6 +184,12 @@ void DoSavestate(Savestate* file)
}
+void SetPowerCnt(u32 val)
+{
+ // TODO
+}
+
+
void SetInterpolation(int type)
{
InterpType = type;
diff --git a/src/SPU.h b/src/SPU.h
index 22c87a7..1f28c2f 100644
--- a/src/SPU.h
+++ b/src/SPU.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -31,6 +31,8 @@ void Stop();
void DoSavestate(Savestate* file);
+void SetPowerCnt(u32 val);
+
// 0=none 1=linear 2=cosine 3=cubic
void SetInterpolation(int type);
diff --git a/src/Savestate.cpp b/src/Savestate.cpp
index 1bfe937..9128939 100644
--- a/src/Savestate.cpp
+++ b/src/Savestate.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -43,7 +43,10 @@
* different minor means adjustments may have to be made
*/
-Savestate::Savestate(const char* filename, bool save)
+// TODO: buffering system! or something of that sort
+// repeated fread/fwrite is slow on Switch
+
+Savestate::Savestate(std::string filename, bool save)
{
const char* magic = "MELN";
@@ -55,7 +58,7 @@ Savestate::Savestate(const char* filename, bool save)
file = Platform::OpenLocalFile(filename, "wb");
if (!file)
{
- printf("savestate: file %s doesn't exist\n", filename);
+ printf("savestate: file %s doesn't exist\n", filename.c_str());
Error = true;
return;
}
@@ -74,7 +77,7 @@ Savestate::Savestate(const char* filename, bool save)
file = Platform::OpenFile(filename, "rb");
if (!file)
{
- printf("savestate: file %s doesn't exist\n", filename);
+ printf("savestate: file %s doesn't exist\n", filename.c_str());
Error = true;
return;
}
diff --git a/src/Savestate.h b/src/Savestate.h
index 60d34cc..275b402 100644
--- a/src/Savestate.h
+++ b/src/Savestate.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,6 +19,7 @@
#ifndef SAVESTATE_H
#define SAVESTATE_H
+#include <string>
#include <stdio.h>
#include "types.h"
@@ -28,7 +29,7 @@
class Savestate
{
public:
- Savestate(const char* filename, bool save);
+ Savestate(std::string filename, bool save);
~Savestate();
bool Error;
diff --git a/src/Wifi.cpp b/src/Wifi.cpp
index e6fdeed..c2614e7 100644
--- a/src/Wifi.cpp
+++ b/src/Wifi.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -23,6 +23,8 @@
#include "Wifi.h"
#include "WifiAP.h"
#include "Platform.h"
+#include "ARM.h"
+#include "GPU.h"
namespace Wifi
@@ -31,37 +33,52 @@ namespace Wifi
//#define WIFI_LOG printf
#define WIFI_LOG(...) {}
+#define PRINT_MAC(pf, mac) printf("%s: %02X:%02X:%02X:%02X:%02X:%02X\n", pf, (mac)[0], (mac)[1], (mac)[2], (mac)[3], (mac)[4], (mac)[5]);
+
u8 RAM[0x2000];
u16 IO[0x1000>>1];
#define IOPORT(x) IO[(x)>>1]
+#define IOPORT8(x) ((u8*)IO)[x]
+
+// destination MACs for MP frames
+const u8 MPCmdMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x00};
+const u8 MPReplyMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x10};
+const u8 MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03};
+
+const int kTimerInterval = 8;
+const u32 kTimeCheckMask = ~(kTimerInterval - 1);
+
+bool Enabled;
+bool PowerOn;
+
+s32 TimerError;
u16 Random;
+// general, always-on microsecond counter
+u64 USTimestamp;
+
u64 USCounter;
u64 USCompare;
bool BlockBeaconIRQ14;
u32 CmdCounter;
-u16 BBCnt;
-u8 BBWrite;
u8 BBRegs[0x100];
u8 BBRegsRO[0x100];
u8 RFVersion;
-u16 RFCnt;
-u16 RFData1;
-u16 RFData2;
u32 RFRegs[0x40];
struct TXSlot
{
+ bool Valid;
u16 Addr;
u16 Length;
u8 Rate;
u8 CurPhase;
- u32 CurPhaseTime;
+ int CurPhaseTime;
u32 HalfwordTimeMask;
};
@@ -69,21 +86,28 @@ TXSlot TXSlots[6];
u8 RXBuffer[2048];
u32 RXBufferPtr;
-u32 RXTime;
+int RXTime;
u32 RXHalfwordTimeMask;
-u16 RXEndAddr;
u32 ComStatus; // 0=waiting for packets 1=receiving 2=sending
u32 TXCurSlot;
u32 RXCounter;
int MPReplyTimer;
-int MPNumReplies;
+u16 MPClientMask, MPClientFail;
+
+u8 MPClientReplies[15*1024];
bool MPInited;
bool LANInited;
+int USUntilPowerOn;
+bool ForcePowerOn;
+// MULTIPLAYER SYNC APPARATUS
+bool IsMPClient;
+u64 NextSync; // for clients: timestamp for next sync point
+u64 RXTimestamp;
// multiplayer host TX sequence:
// 1. preamble
@@ -110,21 +134,26 @@ bool LANInited;
// 4 = switching from TX to RX
// 5 = MP host data sent, waiting for replies (RFPINS=0x0084)
// 6 = RX
-// 7 = ??
+// 7 = switching from RX reply to TX ack
// 8 = MP client sending reply, MP host sending ack (RFPINS=0x0046)
// 9 = idle
// wifi TODO:
-// * power saving
-// * RXSTAT, multiplay reply errors
+// * RXSTAT
// * TX errors (if applicable)
bool Init()
{
- MPInited = false;
- LANInited = false;
+ //MPInited = false;
+ //LANInited = false;
+
+ Platform::MP_Init();
+ MPInited = true;
+
+ Platform::LAN_Init();
+ LANInited = true;
WifiAP::Init();
@@ -146,6 +175,9 @@ void Reset()
memset(RAM, 0, 0x2000);
memset(IO, 0, 0x1000);
+ Enabled = false;
+ PowerOn = false;
+
Random = 1;
memset(BBRegs, 0, 0x100);
@@ -198,19 +230,39 @@ void Reset()
memset(&IOPORT(0x018), 0xFF, 6);
memset(&IOPORT(0x020), 0xFF, 6);
+ // TODO: find out what the initial values are
+ IOPORT(W_PowerUS) = 0x0001;
+
+ USTimestamp = 0;
+
USCounter = 0;
USCompare = 0;
BlockBeaconIRQ14 = false;
+ memset(TXSlots, 0, sizeof(TXSlots));
ComStatus = 0;
TXCurSlot = -1;
RXCounter = 0;
+ memset(RXBuffer, 0, sizeof(RXBuffer));
+ RXBufferPtr = 0;
+ RXTime = 0;
+ RXHalfwordTimeMask = 0xFFFFFFFF;
+
MPReplyTimer = 0;
- MPNumReplies = 0;
+ MPClientMask = 0;
+ MPClientFail = 0;
+ memset(MPClientReplies, 0, sizeof(MPClientReplies));
CmdCounter = 0;
+ USUntilPowerOn = 0;
+ ForcePowerOn = false;
+
+ IsMPClient = false;
+ NextSync = 0;
+ RXTimestamp = 0;
+
WifiAP::Reset();
}
@@ -226,8 +278,13 @@ void DoSavestate(Savestate* file)
file->VarArray(RAM, 0x2000);
file->VarArray(IO, 0x1000);
+ file->Bool32(&Enabled);
+ file->Bool32(&PowerOn);
+
file->Var16(&Random);
+ file->Var32((u32*)&TimerError);
+
file->VarArray(BBRegs, 0x100);
file->VarArray(BBRegsRO, 0x100);
@@ -238,17 +295,107 @@ void DoSavestate(Savestate* file)
file->Var64(&USCompare);
file->Bool32(&BlockBeaconIRQ14);
+ file->Var32(&CmdCounter);
+
+ file->Var64(&USTimestamp);
+
+ for (int i = 0; i < 6; i++)
+ {
+ TXSlot* slot = &TXSlots[i];
+
+ file->Bool32(&slot->Valid);
+ file->Var16(&slot->Addr);
+ file->Var16(&slot->Length);
+ file->Var8(&slot->Rate);
+ file->Var8(&slot->CurPhase);
+ file->Var32((u32*)&slot->CurPhaseTime);
+ file->Var32(&slot->HalfwordTimeMask);
+ }
+
+ file->VarArray(RXBuffer, sizeof(RXBuffer));
+ file->Var32(&RXBufferPtr);
+ file->Var32((u32*)&RXTime);
+ file->Var32(&RXHalfwordTimeMask);
+
file->Var32(&ComStatus);
file->Var32(&TXCurSlot);
file->Var32(&RXCounter);
file->Var32((u32*)&MPReplyTimer);
- file->Var32((u32*)&MPNumReplies);
+ file->Var16(&MPClientMask);
+ file->Var16(&MPClientFail);
- file->Var32(&CmdCounter);
+ file->VarArray(MPClientReplies, sizeof(MPClientReplies));
+
+ file->Var32((u32*)&USUntilPowerOn);
+ file->Bool32(&ForcePowerOn);
+
+ file->Bool32(&IsMPClient);
+ file->Var64(&NextSync);
+ file->Var64(&RXTimestamp);
}
+void ScheduleTimer(bool first)
+{
+ if (first) TimerError = 0;
+
+ s32 cycles = 33513982 * kTimerInterval;
+ cycles -= TimerError;
+ s32 delay = (cycles + 999999) / 1000000;
+ TimerError = (delay * 1000000) - cycles;
+
+ NDS::ScheduleEvent(NDS::Event_Wifi, !first, delay, USTimer, 0);
+}
+
+void UpdatePowerOn()
+{
+ bool on = Enabled;
+
+ if (NDS::ConsoleType == 1)
+ {
+ // TODO for DSi:
+ // * W_POWER_US doesn't work (atleast on DWM-W024)
+ // * other registers like GPIO_WIFI may also control wifi power/clock
+ // * turning wifi off via POWCNT2 while sending breaks further attempts at sending frames
+ }
+ else
+ {
+ on = on && ((IOPORT(W_PowerUS) & 0x1) == 0);
+ }
+
+ if (on == PowerOn)
+ return;
+
+ PowerOn = on;
+ if (on)
+ {
+ printf("WIFI: ON\n");
+
+ ScheduleTimer(true);
+
+ Platform::MP_Begin();
+ }
+ else
+ {
+ printf("WIFI: OFF\n");
+
+ NDS::CancelEvent(NDS::Event_Wifi);
+
+ Platform::MP_End();
+ }
+}
+
+void SetPowerCnt(u32 val)
+{
+ Enabled = val & (1<<1);
+ UpdatePowerOn();
+}
+
+
+void PowerDown();
+void StartTX_Beacon();
+
void SetIRQ(u32 irq)
{
u32 oldflags = IOPORT(W_IF) & IOPORT(W_IE);
@@ -267,7 +414,8 @@ void SetIRQ13()
if (!(IOPORT(W_PowerTX) & 0x0002))
{
IOPORT(0x034) = 0x0002;
- // TODO: 03C
+ //PowerDown();
+ // FIXME!!
IOPORT(W_RFPins) = 0x0046;
IOPORT(W_RFStatus) = 9;
}
@@ -316,20 +464,32 @@ void SetIRQ15()
void SetStatus(u32 status)
{
- // TODO, eventually: states 2/4, also find out what state 7 is
+ // TODO, eventually: states 2/4/7
u16 rfpins[10] = {0x04, 0x84, 0, 0x46, 0, 0x84, 0x87, 0, 0x46, 0x04};
IOPORT(W_RFStatus) = status;
IOPORT(W_RFPins) = rfpins[status];
}
-bool MACEqual(u8* a, u8* b)
+void PowerDown()
{
- return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]);
+ IOPORT(W_TXReqRead) &= ~0x000F;
+ IOPORT(W_PowerState) |= 0x0200;
+
+ // if the RF hardware is powered down while still sending or receiving,
+ // the current frame is completed before going idle
+ if (!ComStatus)
+ {
+ SetStatus(9);
+ }
}
-// TODO: set RFSTATUS/RFPINS
+bool MACEqual(const u8* a, const u8* b)
+{
+ return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]);
+}
+
int PreambleLen(int rate)
{
@@ -338,6 +498,16 @@ int PreambleLen(int rate)
return 192;
}
+u32 NumClients(u16 bitmask)
+{
+ u32 ret = 0;
+ for (int i = 1; i < 16; i++)
+ {
+ if (bitmask & (1<<i)) ret++;
+ }
+ return ret;
+}
+
void IncrementTXCount(TXSlot* slot)
{
u8 cnt = RAM[slot->Addr + 0x4];
@@ -345,6 +515,19 @@ void IncrementTXCount(TXSlot* slot)
*(u16*)&RAM[slot->Addr + 0x4] = cnt;
}
+void ReportMPReplyErrors(u16 clientfail)
+{
+ // TODO: do these trigger any IRQ?
+
+ for (int i = 1; i < 16; i++)
+ {
+ if (!(clientfail & (1<<i)))
+ continue;
+
+ IOPORT8(W_CMDStat0 + i)++;
+ }
+}
+
void StartTX_LocN(int nslot, int loc)
{
TXSlot* slot = &TXSlots[nslot];
@@ -369,7 +552,8 @@ void StartTX_Cmd()
// TODO: cancel the transfer if there isn't enough time left (check CMDCOUNT)
- if (IOPORT(W_TXSlotCmd) & 0x7000) printf("wifi: !! unusual TXSLOT_CMD bits set %04X\n", IOPORT(W_TXSlotCmd));
+ if (IOPORT(W_TXSlotCmd) & 0x3000)
+ printf("wifi: !! unusual TXSLOT_CMD bits set %04X\n", IOPORT(W_TXSlotCmd));
slot->Addr = (IOPORT(W_TXSlotCmd) & 0x0FFF) << 1;
slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
@@ -399,9 +583,11 @@ void StartTX_Beacon()
IOPORT(W_TXBusy) |= 0x0010;
}
-// TODO eventually: there is a small delay to firing TX
void FireTX()
{
+ if (!(IOPORT(W_RXCnt) & 0x8000))
+ return;
+
u16 txbusy = IOPORT(W_TXBusy);
u16 txreq = IOPORT(W_TXReqRead);
@@ -441,24 +627,89 @@ void FireTX()
}
}
+void SendMPDefaultReply()
+{
+ u8 reply[12 + 32];
+
+ *(u16*)&reply[0xA] = 28; // length
+
+ // rate
+ //if (TXSlots[1].Rate == 2) reply[0x8] = 0x14;
+ //else reply[0x8] = 0xA;
+ // TODO
+ reply[0x8] = 0x14;
+
+ *(u16*)&reply[0xC + 0x00] = 0x0158;
+ *(u16*)&reply[0xC + 0x02] = 0x00F0;//0; // TODO??
+ *(u16*)&reply[0xC + 0x04] = IOPORT(W_BSSID0);
+ *(u16*)&reply[0xC + 0x06] = IOPORT(W_BSSID1);
+ *(u16*)&reply[0xC + 0x08] = IOPORT(W_BSSID2);
+ *(u16*)&reply[0xC + 0x0A] = IOPORT(W_MACAddr0);
+ *(u16*)&reply[0xC + 0x0C] = IOPORT(W_MACAddr1);
+ *(u16*)&reply[0xC + 0x0E] = IOPORT(W_MACAddr2);
+ *(u16*)&reply[0xC + 0x10] = 0x0903;
+ *(u16*)&reply[0xC + 0x12] = 0x00BF;
+ *(u16*)&reply[0xC + 0x14] = 0x1000;
+ *(u16*)&reply[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
+ *(u32*)&reply[0xC + 0x18] = 0;
+
+ int txlen = Platform::MP_SendReply(reply, 12+28, USTimestamp, IOPORT(W_AIDLow));
+ WIFI_LOG("wifi: sent %d/40 bytes of MP default reply\n", txlen);
+}
+
void SendMPReply(u16 clienttime, u16 clientmask)
{
TXSlot* slot = &TXSlots[5];
// mark the last packet as success. dunno what the MSB is, it changes.
+ //if (slot->Valid)
if (IOPORT(W_TXSlotReply2) & 0x8000)
*(u16*)&RAM[slot->Addr] = 0x0001;
+ // CHECKME!!
+ // can the transfer rate for MP replies be set, or is it determined from the CMD transfer rate?
+ // how does it work for default empty replies?
+ slot->Rate = 2;
+
IOPORT(W_TXSlotReply2) = IOPORT(W_TXSlotReply1);
IOPORT(W_TXSlotReply1) = 0;
+ if (!(IOPORT(W_TXSlotReply2) & 0x8000))
+ {
+ slot->Valid = false;
+ }
+ else
+ {
+ slot->Valid = true;
+
+ slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
+ slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
+
+ // the packet is entirely ignored if it lasts longer than the maximum reply time
+ u32 duration = PreambleLen(slot->Rate) + (slot->Length * (slot->Rate==2 ? 4:8));
+ if (duration > clienttime)
+ slot->Valid = false;
+ }
+
+ //if (RAM[slot->Addr+4] > 0)
+ // printf("REPLY RETRY COUNTER %d (%04X)\n", RAM[slot->Addr+4], IOPORT(W_TXSlotReply2));
+
// this seems to be set upon IRQ0
// TODO: how does it behave if the packet addr is changed before it gets sent? (maybe just not possible)
- if (IOPORT(W_TXSlotReply2) & 0x8000)
+ if (slot->Valid)
{
- slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
//*(u16*)&RAM[slot->Addr + 0x4] = 0x0001;
IncrementTXCount(slot);
+
+ slot->CurPhase = 0;
+ int txlen = Platform::MP_SendReply(&RAM[slot->Addr], 12 + slot->Length, USTimestamp, IOPORT(W_AIDLow));
+ WIFI_LOG("wifi: sent %d/%d bytes of MP reply\n", txlen, 12 + slot->Length);
+ }
+ else
+ {
+ slot->CurPhase = 10;
+
+ SendMPDefaultReply();
}
u16 clientnum = 0;
@@ -468,43 +719,12 @@ void SendMPReply(u16 clienttime, u16 clientmask)
clientnum++;
}
- slot->CurPhase = 0;
- slot->CurPhaseTime = 16 + ((clienttime + 10) * clientnum);
+ slot->CurPhaseTime = 16 + ((clienttime + 10) * clientnum) + PreambleLen(slot->Rate);
IOPORT(W_TXBusy) |= 0x0080;
}
-void SendMPDefaultReply()
-{
- u8 reply[12 + 32];
-
- *(u16*)&reply[0xA] = 28; // length
-
- // rate
- //if (TXSlots[1].Rate == 2) reply[0x8] = 0x14;
- //else reply[0x8] = 0xA;
- // TODO
- reply[0x8] = 0x14;
-
- *(u16*)&reply[0xC + 0x00] = 0x0158;
- *(u16*)&reply[0xC + 0x02] = 0x00F0;//0; // TODO??
- *(u16*)&reply[0xC + 0x04] = IOPORT(W_BSSID0);
- *(u16*)&reply[0xC + 0x06] = IOPORT(W_BSSID1);
- *(u16*)&reply[0xC + 0x08] = IOPORT(W_BSSID2);
- *(u16*)&reply[0xC + 0x0A] = IOPORT(W_MACAddr0);
- *(u16*)&reply[0xC + 0x0C] = IOPORT(W_MACAddr1);
- *(u16*)&reply[0xC + 0x0E] = IOPORT(W_MACAddr2);
- *(u16*)&reply[0xC + 0x10] = 0x0903;
- *(u16*)&reply[0xC + 0x12] = 0x00BF;
- *(u16*)&reply[0xC + 0x14] = 0x1000;
- *(u16*)&reply[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
- *(u32*)&reply[0xC + 0x18] = 0;
-
- int txlen = Platform::MP_SendPacket(reply, 12+28);
- WIFI_LOG("wifi: sent %d/40 bytes of MP default reply\n", txlen);
-}
-
-void SendMPAck()
+void SendMPAck(u16 clientfail)
{
u8 ack[12 + 32];
@@ -514,41 +734,32 @@ void SendMPAck()
if (TXSlots[1].Rate == 2) ack[0x8] = 0x14;
else ack[0x8] = 0xA;
- *(u16*)&ack[0xC + 0x00] = 0x0218;
- *(u16*)&ack[0xC + 0x02] = 0;
- *(u16*)&ack[0xC + 0x04] = 0x0903;
- *(u16*)&ack[0xC + 0x06] = 0x00BF;
- *(u16*)&ack[0xC + 0x08] = 0x0300;
- *(u16*)&ack[0xC + 0x0A] = IOPORT(W_BSSID0);
- *(u16*)&ack[0xC + 0x0C] = IOPORT(W_BSSID1);
- *(u16*)&ack[0xC + 0x0E] = IOPORT(W_BSSID2);
- *(u16*)&ack[0xC + 0x10] = IOPORT(W_MACAddr0);
- *(u16*)&ack[0xC + 0x12] = IOPORT(W_MACAddr1);
- *(u16*)&ack[0xC + 0x14] = IOPORT(W_MACAddr2);
- *(u16*)&ack[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
- *(u16*)&ack[0xC + 0x18] = 0x0033; // ???
- *(u16*)&ack[0xC + 0x1A] = 0;
- *(u32*)&ack[0xC + 0x1C] = 0;
-
- int txlen = Platform::MP_SendPacket(ack, 12+32);
- WIFI_LOG("wifi: sent %d/44 bytes of MP ack, %d %d\n", txlen, ComStatus, RXTime);
-}
-
-u32 NumClients(u16 bitmask)
-{
- u32 ret = 0;
- for (int i = 1; i < 16; i++)
- {
- if (bitmask & (1<<i)) ret++;
- }
- return ret;
+ *(u16*)&ack[0xC + 0x00] = 0x0218;
+ *(u16*)&ack[0xC + 0x02] = 0;
+ *(u16*)&ack[0xC + 0x04] = 0x0903;
+ *(u16*)&ack[0xC + 0x06] = 0x00BF;
+ *(u16*)&ack[0xC + 0x08] = 0x0300;
+ *(u16*)&ack[0xC + 0x0A] = IOPORT(W_BSSID0);
+ *(u16*)&ack[0xC + 0x0C] = IOPORT(W_BSSID1);
+ *(u16*)&ack[0xC + 0x0E] = IOPORT(W_BSSID2);
+ *(u16*)&ack[0xC + 0x10] = IOPORT(W_MACAddr0);
+ *(u16*)&ack[0xC + 0x12] = IOPORT(W_MACAddr1);
+ *(u16*)&ack[0xC + 0x14] = IOPORT(W_MACAddr2);
+ *(u16*)&ack[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
+ *(u16*)&ack[0xC + 0x18] = 0x0033; // ???
+ *(u16*)&ack[0xC + 0x1A] = clientfail;
+ *(u32*)&ack[0xC + 0x1C] = 0;
+
+ int txlen = Platform::MP_SendAck(ack, 12+32, USTimestamp);
+ WIFI_LOG("wifi: sent %d/44 bytes of MP ack, %d %d\n", txlen, ComStatus, RXTime);
}
-bool CheckRX(bool block);
+bool CheckRX(int type);
+void MPClientReplyRX(int client);
bool ProcessTX(TXSlot* slot, int num)
{
- slot->CurPhaseTime--;
+ slot->CurPhaseTime -= kTimerInterval;
if (slot->CurPhaseTime > 0)
{
if (slot->CurPhase == 1)
@@ -558,19 +769,28 @@ bool ProcessTX(TXSlot* slot, int num)
}
else if (slot->CurPhase == 2)
{
- MPReplyTimer--;
- if (MPReplyTimer == 0 && MPNumReplies > 0)
+ MPReplyTimer -= kTimerInterval;
+ if (MPReplyTimer <= 0 && MPClientMask != 0)
{
- if (CheckRX(true))
+ int nclient = 1;
+ while (!(MPClientMask & (1 << nclient))) nclient++;
+
+ u32 curclient = 1 << nclient;
+
+ /*if (CheckRX(1))
{
- ComStatus |= 0x1;
- }
+ // we received a reply, mark it as such
+ // TODO: is any received packet considered a good reply?
+ // hardware probably requires a specific frame-control and/or destination MAC
- // TODO: properly handle reply errors
- // also, if the reply is too big to fit within its window, what happens?
+ MPClientFail &= ~curclient;
+ }
+ else printf("REPLY %04X NOT RECEIVED\n");*/
+ if (!(MPClientFail & curclient))
+ MPClientReplyRX(nclient);
- MPReplyTimer = 10 + IOPORT(W_CmdReplyTime);
- MPNumReplies--;
+ MPReplyTimer += 10 + IOPORT(W_CmdReplyTime);
+ MPClientMask &= ~curclient;
}
}
@@ -590,31 +810,16 @@ bool ProcessTX(TXSlot* slot, int num)
SetStatus(8);
- // if no reply is configured, send a default empty reply
- if (!(IOPORT(W_TXSlotReply2) & 0x8000))
- {
- SendMPDefaultReply();
-
- slot->Addr = 0;
- slot->Length = 28;
- slot->Rate = 2; // TODO
- slot->CurPhase = 4;
- slot->CurPhaseTime = 28*4;
- slot->HalfwordTimeMask = 0xFFFFFFFF;
- IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
- break;
- }
+ //slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
+ //slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
- slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
- slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
+ /*u8 rate = RAM[slot->Addr + 0x8];
+ if (rate == 0x14) slot->Rate = 2;
+ else slot->Rate = 1;*/
// TODO: duration should be set by hardware
// doesn't seem to be important
//RAM[slot->Addr + 0xC + 2] = 0x00F0;
-
- u8 rate = RAM[slot->Addr + 0x8];
- if (rate == 0x14) slot->Rate = 2;
- else slot->Rate = 1;
}
else
SetStatus(3);
@@ -623,17 +828,32 @@ bool ProcessTX(TXSlot* slot, int num)
if (slot->Rate == 2)
{
len *= 4;
- slot->HalfwordTimeMask = 0x7;
+ slot->HalfwordTimeMask = 0x7 & kTimeCheckMask;
}
else
{
len *= 8;
- slot->HalfwordTimeMask = 0xF;
+ slot->HalfwordTimeMask = 0xF & kTimeCheckMask;
}
slot->CurPhase = 1;
slot->CurPhaseTime = len;
+ u16 framectl = *(u16*)&RAM[slot->Addr + 0xC];
+ if (framectl & (1<<14))
+ {
+ // WEP frame
+ // TODO: what happens when sending a WEP frame while WEP processing is off?
+ // TODO: some form of actual WEP processing?
+ // for now we just set the WEP FCS to a nonzero value, because some games require it
+
+ if (IOPORT(W_WEPCnt) & (1<<15))
+ {
+ u32 wep_fcs = (slot->Addr + 0xC + slot->Length - 7) & ~0x1;
+ *(u32*)&RAM[wep_fcs] = 0x22334466;
+ }
+ }
+
u64 oldts;
if (num == 4)
{
@@ -642,28 +862,66 @@ bool ProcessTX(TXSlot* slot, int num)
*(u64*)&RAM[slot->Addr + 0xC + 24] = USCounter;
}
- //u32 noseqno = 0;
- //if (num == 1) noseqno = (IOPORT(W_TXSlotCmd) & 0x4000);
+ u32 noseqno = 0;
+ if (num == 1) noseqno = (IOPORT(W_TXSlotCmd) & 0x4000);
- //if (!noseqno)
+ if (!noseqno)
{
*(u16*)&RAM[slot->Addr + 0xC + 22] = IOPORT(W_TXSeqNo) << 4;
IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
}
+ if ((num != 5) && (RAM[slot->Addr+4] > 0))
+ printf("SLOT %d RETRY COUNTER %d\n", RAM[slot->Addr+4]);
+
// set TX addr
IOPORT(W_RXTXAddr) = slot->Addr >> 1;
- // send
- int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length);
- WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
- txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
- *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);
+ if (num == 1)
+ {
+ // send
+ int txlen = Platform::MP_SendCmd(&RAM[slot->Addr], 12 + slot->Length, USTimestamp);
+ WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
+ txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
+ *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);
+ }
+ else if (num == 5)
+ {
+ // send
+ /*int txlen = Platform::MP_SendReply(&RAM[slot->Addr], 12 + slot->Length, USTimestamp, IOPORT(W_AIDLow));
+ WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
+ txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
+ *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);*/
+ }
+ else //if (num != 5)
+ {
+ // send
+ int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length, USTimestamp);
+ WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
+ txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
+ *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);
+ }
// if the packet is being sent via LOC1..3, send it to the AP
// any packet sent via CMD/REPLY/BEACON isn't going to have much use outside of local MP
if (num == 0 || num == 2 || num == 3)
+ {
+ if ((framectl & 0x00FF) == 0x0010)
+ {
+ u16 aid = *(u16*)&RAM[slot->Addr + 0xC + 24 + 4];
+ if (aid) printf("[HOST] syncing client %04X, sync=%016llX\n", aid, USTimestamp);
+ }
+ else if ((framectl & 0x00FF) == 0x00C0)
+ {
+ if (IsMPClient)
+ {
+ printf("[CLIENT] deauth\n");
+ IsMPClient = false;
+ }
+ }
+
WifiAP::SendPacket(&RAM[slot->Addr], 12 + slot->Length);
+ }
if (num == 4)
{
@@ -672,10 +930,25 @@ bool ProcessTX(TXSlot* slot, int num)
}
break;
+ case 10: // preamble done (default empty MP reply)
+ {
+ SetIRQ(7);
+ SetStatus(8);
+
+ //SendMPDefaultReply();
+
+ //slot->Addr = 0;
+ //slot->Length = 28;
+ slot->CurPhase = 4;
+ slot->CurPhaseTime = 28*4;
+ slot->HalfwordTimeMask = 0xFFFFFFFF;
+ }
+ break;
+
case 1: // transmit done
{
- // for the MP reply slot, this is set later
- if (num != 5)
+ // for the MP CMD and reply slots, this is set later
+ if (num != 1 && num != 5)
*(u16*)&RAM[slot->Addr] = 0x0001;
RAM[slot->Addr + 5] = 0;
@@ -688,12 +961,21 @@ bool ProcessTX(TXSlot* slot, int num)
}
SetStatus(5);
- u16 clientmask = *(u16*)&RAM[slot->Addr + 12 + 24 + 2];
- MPNumReplies = NumClients(clientmask);
- MPReplyTimer = 16;
+ u16 clientmask = *(u16*)&RAM[slot->Addr + 12 + 24 + 2] & 0xFFFE;
+ //MPNumReplies = NumClients(clientmask);
+ MPReplyTimer = 16 + PreambleLen(slot->Rate);
+ MPClientMask = clientmask;
+ MPClientFail = clientmask;
+ u16 res = 0;
+ if (clientmask)
+ res = Platform::MP_RecvReplies(MPClientReplies, USTimestamp, clientmask);
+ MPClientFail &= ~res;
+
+ // TODO: 112 likely includes the ack preamble, which needs adjusted
+ // for long-preamble settings
slot->CurPhase = 2;
- slot->CurPhaseTime = 112 + ((10 + IOPORT(W_CmdReplyTime)) * MPNumReplies);
+ slot->CurPhaseTime = 112 + ((10 + IOPORT(W_CmdReplyTime)) * NumClients(clientmask));
break;
}
@@ -748,7 +1030,10 @@ bool ProcessTX(TXSlot* slot, int num)
if (slot->Rate == 2) slot->CurPhaseTime = 32 * 4;
else slot->CurPhaseTime = 32 * 8;
- SendMPAck();
+ ReportMPReplyErrors(MPClientFail);
+
+ // send
+ SendMPAck(MPClientFail);
slot->CurPhase = 3;
}
@@ -760,11 +1045,15 @@ bool ProcessTX(TXSlot* slot, int num)
IOPORT(W_TXBusy) &= ~(1<<1);
IOPORT(W_TXSlotCmd) &= 0x7FFF; // confirmed
- // seems this is set to indicate which clients failed to reply
- *(u16*)&RAM[slot->Addr + 0x2] = 0;
+ if (!MPClientFail)
+ *(u16*)&RAM[slot->Addr] = 0x0001;
+ else
+ *(u16*)&RAM[slot->Addr] = 0x0005;
+
+ // this is set to indicate which clients failed to reply
+ *(u16*)&RAM[slot->Addr + 0x2] = MPClientFail;
IncrementTXCount(slot);
- SetIRQ(12);
IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
if (IOPORT(W_TXStatCnt) & 0x2000)
@@ -774,12 +1063,19 @@ bool ProcessTX(TXSlot* slot, int num)
}
SetStatus(1);
+ // TODO: retry the whole cycle if some clients failed to respond
+ // AND if there is enough time left in CMDCOUNT
+ // (games seem to always configure CMDCOUNT such that there is no time for retries)
+ SetIRQ(12);
+
FireTX();
}
return true;
case 4: // MP default reply transfer finished
{
+ IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
+
IOPORT(W_TXBusy) &= ~0x80;
SetStatus(1);
FireTX();
@@ -802,98 +1098,403 @@ inline void IncrementRXAddr(u16& addr, u16 inc = 2)
}
}
-bool CheckRX(bool block)
+void StartRX()
+{
+ u16 framelen = *(u16*)&RXBuffer[8];
+ RXTime = framelen;
+
+ u16 txrate = *(u16*)&RXBuffer[6];
+ if (txrate == 0x14)
+ {
+ RXTime *= 4;
+ RXHalfwordTimeMask = 0x7 & kTimeCheckMask;
+ }
+ else
+ {
+ RXTime *= 8;
+ RXHalfwordTimeMask = 0xF & kTimeCheckMask;
+ }
+
+ u16 addr = IOPORT(W_RXBufWriteCursor) << 1;
+ IncrementRXAddr(addr, 12);
+ IOPORT(W_RXTXAddr) = addr >> 1;
+
+ RXBufferPtr = 12;
+
+ SetIRQ(6);
+ SetStatus(6);
+ ComStatus |= 1;
+}
+
+void FinishRX()
{
+ ComStatus &= ~0x1;
+ RXCounter = 0;
+
+ if (!ComStatus)
+ {
+ if (IOPORT(W_PowerState) & 0x0300)
+ SetStatus(9);
+ else
+ SetStatus(1);
+ }
+
+ // TODO: RX stats
+
+ u16 framectl = *(u16*)&RXBuffer[12];
+
+ // check the frame's destination address
+ // note: the hardware always checks the first address field, regardless of the frame type/etc
+ // similarly, the second address field is used to send acks to non-broadcast frames
+
+ u8* dstmac = &RXBuffer[12 + 4];
+ if (!(dstmac[0] & 0x01))
+ {
+ if (!MACEqual(dstmac, (u8*)&IOPORT(W_MACAddr0)))
+ return;
+ }
+
+ // reject the frame if it's a WEP frame and WEP is off
+ // TODO: check if sending WEP frames with WEP off works at all?
+
+ if (framectl & (1<<14))
+ {
+ if (!(IOPORT(W_WEPCnt) & (1<<15)))
+ return;
+ }
+
+ // apply RX filtering
+ // TODO:
+ // * RXFILTER bits 0, 9, 10, 12 not fully understood
+ // * port 0D8 also affects reception of frames
+ // * MP CMD frames with a duplicate sequence number are ignored
+
+ u16 rxflags = 0x0010;
+
+ switch ((framectl >> 2) & 0x3)
+ {
+ case 0: // management
+ {
+ u8* bssid = &RXBuffer[12 + 16];
+ if (MACEqual(bssid, (u8*)&IOPORT(W_BSSID0)))
+ rxflags |= 0x8000;
+
+ u16 subtype = (framectl >> 4) & 0xF;
+ if (subtype == 0x8) // beacon
+ {
+ if (!(rxflags & 0x8000))
+ {
+ if (!(IOPORT(W_RXFilter) & (1<<0)))
+ return;
+ }
+
+ rxflags |= 0x0001;
+ }
+ else if ((subtype <= 0x5) ||
+ (subtype >= 0xA && subtype <= 0xC))
+ {
+ if (!(rxflags & 0x8000))
+ {
+ // CHECKME!
+ if (!(IOPORT(W_RXFilter) & (3<<9)))
+ return;
+ }
+ }
+ }
+ break;
+
+ case 1: // control
+ {
+ if ((framectl & 0xF0) == 0xA0) // PS-poll
+ {
+ u8* bssid = &RXBuffer[12 + 4];
+ if (MACEqual(bssid, (u8*)&IOPORT(W_BSSID0)))
+ rxflags |= 0x8000;
+
+ if (!(rxflags & 0x8000))
+ {
+ if (!(IOPORT(W_RXFilter) & (1<<11)))
+ return;
+ }
+
+ rxflags |= 0x0005;
+ }
+ else
+ return;
+ }
+ break;
+
+ case 2: // data
+ {
+ u16 fromto = (framectl >> 8) & 0x3;
+ if (IOPORT(W_RXFilter2) & (1<<fromto))
+ return;
+
+ int bssidoffset[4] = {16, 4, 10, 0};
+ if (bssidoffset[fromto])
+ {
+ u8* bssid = &RXBuffer[12 + bssidoffset[fromto]];
+ if (MACEqual(bssid, (u8*)&IOPORT(W_BSSID0)))
+ rxflags |= 0x8000;
+ }
+
+ u16 rxfilter = IOPORT(W_RXFilter);
+
+ if (!(rxflags & 0x8000))
+ {
+ if (!(rxfilter & (1<<11)))
+ return;
+ }
+
+ if (framectl & (1<<11)) // retransmit
+ {
+ if (!(rxfilter & (1<<0)))
+ return;
+ }
+
+ // check for MP frames
+ // the hardware simply checks for these specific MAC addresses
+ // the reply check has priority over the other checks
+ // TODO: it seems to be impossible to receive a MP reply outside of a CMD transfer's reply timeframe
+ // if the framectl subtype field is 1 or 5
+ // maybe one of the unknown registers controls that?
+ // maybe it is impossible to receive CF-Ack frames outside of a CF-Poll period?
+ // TODO: GBAtek says frame type F is for all empty packets?
+ // my hardware tests say otherwise
+
+ if (MACEqual(&RXBuffer[12 + 16], MPReplyMAC))
+ {
+ if ((framectl & 0xF0) == 0x50)
+ rxflags |= 0x000F;
+ else
+ rxflags |= 0x000E;
+ }
+ else if (MACEqual(&RXBuffer[12 + 4], MPCmdMAC))
+ {
+ rxflags |= 0x000C;
+ }
+ else if (MACEqual(&RXBuffer[12 + 4], MPAckMAC))
+ {
+ rxflags |= 0x000D;
+ }
+ else
+ {
+ rxflags |= 0x0008;
+ }
+
+ switch ((framectl >> 4) & 0xF)
+ {
+ case 0x0: break;
+
+ case 0x1:
+ if ((rxflags & 0xF) == 0xD)
+ {
+ if (!(rxfilter & (1<<7))) return;
+ }
+ else if ((rxflags & 0xF) != 0xE)
+ {
+ if (!(rxfilter & (1<<1))) return;
+ }
+ break;
+
+ case 0x2:
+ if ((rxflags & 0xF) != 0xC)
+ {
+ if (!(rxfilter & (1<<2))) return;
+ }
+ break;
+
+ case 0x3:
+ if (!(rxfilter & (1<<3))) return;
+ break;
+
+ case 0x4: break;
+
+ case 0x5:
+ if ((rxflags & 0xF) == 0xF)
+ {
+ if (!(rxfilter & (1<<8))) return;
+ }
+ else
+ {
+ if (!(rxfilter & (1<<4))) return;
+ }
+ break;
+
+ case 0x6:
+ if (!(rxfilter & (1<<5))) return;
+ break;
+
+ case 0x7:
+ if (!(rxfilter & (1<<6))) return;
+ break;
+
+ default:
+ return;
+ }
+ }
+ break;
+ }
+
+ // build the RX header
+
+ u16 headeraddr = IOPORT(W_RXBufWriteCursor) << 1;
+ *(u16*)&RAM[headeraddr] = rxflags;
+ IncrementRXAddr(headeraddr);
+ *(u16*)&RAM[headeraddr] = 0x0040; // ???
+ IncrementRXAddr(headeraddr, 4);
+ *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[6]; // TX rate
+ IncrementRXAddr(headeraddr);
+ *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[8]; // frame length
+ IncrementRXAddr(headeraddr);
+ *(u16*)&RAM[headeraddr] = 0x4080; // RSSI
+
+ // signal successful reception
+
+ u16 addr = IOPORT(W_RXTXAddr) << 1;
+ if (addr & 0x2) IncrementRXAddr(addr);
+ IOPORT(W_RXBufWriteCursor) = (addr & ~0x3) >> 1;
+
+ SetIRQ(0);
+
+ if ((rxflags & 0x800F) == 0x800C)
+ {
+ // reply to CMD frames
+
+ u16 clientmask = *(u16*)&RXBuffer[0xC + 26];
+ if (IOPORT(W_AIDLow) && (clientmask & (1 << IOPORT(W_AIDLow))))
+ {
+ SendMPReply(*(u16*)&RXBuffer[0xC + 24], clientmask);
+ }
+ else
+ {
+ // send a blank
+ // this is just so the host can have something to receive, instead of hitting a timeout
+ // in the case this client wasn't ready to send a reply
+ // TODO: also send this if we have RX disabled
+
+ Platform::MP_SendReply(nullptr, 0, USTimestamp, 0);
+ }
+ }
+ else if ((rxflags & 0x800F) == 0x8001)
+ {
+ // when receiving a beacon with the right BSSID, the beacon's timestamp
+ // is copied to USCOUNTER
+
+ u32 len = *(u16*)&RXBuffer[8];
+ u16 txrate = *(u16*)&RXBuffer[6];
+ len *= ((txrate==0x14) ? 4 : 8);
+ len -= 76; // CHECKME: is this offset fixed?
+
+ u64 timestamp = *(u64*)&RXBuffer[12 + 24];
+ timestamp += (u64)len;
+
+ USCounter = timestamp;
+ }
+}
+
+void MPClientReplyRX(int client)
+{
+ if (IOPORT(W_PowerState) & 0x0300)
+ return;
+
+ if (!(IOPORT(W_RXCnt) & 0x8000))
+ return;
+
+ if (IOPORT(W_RXBufBegin) == IOPORT(W_RXBufEnd))
+ return;
+
+ int framelen;
+ u8 txrate;
+
+ u8* reply = &MPClientReplies[(client-1)*1024];
+ framelen = *(u16*)&reply[10];
+
+ txrate = reply[8];
+
+ // TODO: what are the maximum crop values?
+ u16 framectl = *(u16*)&reply[12];
+ if (framectl & (1<<14))
+ {
+ framelen -= (IOPORT(W_RXLenCrop) >> 7) & 0x1FE;
+ if (framelen > 24) memmove(&RXBuffer[12+24], &RXBuffer[12+28], framelen);
+ }
+ else
+ framelen -= (IOPORT(W_RXLenCrop) << 1) & 0x1FE;
+
+ if (framelen < 0) framelen = 0;
+
+ // TODO rework RX system so we don't need this (by reading directly into MPClientReplies)
+ memcpy(RXBuffer, reply, 12+framelen);
+
+ *(u16*)&RXBuffer[6] = txrate;
+ *(u16*)&RXBuffer[8] = framelen;
+
+ RXTimestamp = 0;
+ StartRX();
+}
+
+bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames
+{
+ if (IOPORT(W_PowerState) & 0x0300)
+ return false;
+
if (!(IOPORT(W_RXCnt) & 0x8000))
return false;
if (IOPORT(W_RXBufBegin) == IOPORT(W_RXBufEnd))
return false;
- u16 framelen;
+ int rxlen;
+ int framelen;
u16 framectl;
u8 txrate;
- bool bssidmatch;
- u16 rxflags;
+ u64 timestamp;
for (;;)
{
- int rxlen = Platform::MP_RecvPacket(RXBuffer, block);
- if (rxlen == 0) rxlen = WifiAP::RecvPacket(RXBuffer);
- if (rxlen == 0) return false;
+ timestamp = 0;
+
+ if (type == 0)
+ {
+ rxlen = Platform::MP_RecvPacket(RXBuffer, &timestamp);
+ if (rxlen <= 0)
+ rxlen = WifiAP::RecvPacket(RXBuffer);
+ }
+ else
+ {
+ rxlen = Platform::MP_RecvHostPacket(RXBuffer, &timestamp);
+ if (rxlen < 0)
+ {
+ // host is gone
+ // TODO: make this more resilient
+ IsMPClient = false;
+ }
+ }
+
+ if (rxlen <= 0) return false;
if (rxlen < 12+24) continue;
framelen = *(u16*)&RXBuffer[10];
if (framelen != rxlen-12)
{
- printf("bad frame length\n");
+ printf("bad frame length %d/%d\n", framelen, rxlen-12);
continue;
}
- framelen -= 4;
framectl = *(u16*)&RXBuffer[12+0];
txrate = RXBuffer[8];
- u32 a_src, a_dst, a_bss;
- rxflags = 0x0010;
- switch (framectl & 0x000C)
+ // TODO: what are the maximum crop values?
+ if (framectl & (1<<14))
{
- case 0x0000: // management
- a_src = 10;
- a_dst = 4;
- a_bss = 16;
- if ((framectl & 0x00F0) == 0x0080)
- rxflags |= 0x0001;
- break;
-
- case 0x0004: // control
- printf("blarg\n");
- continue;
-
- case 0x0008: // data
- switch (framectl & 0x0300)
- {
- case 0x0000: // STA to STA
- a_src = 10;
- a_dst = 4;
- a_bss = 16;
- break;
- case 0x0100: // STA to DS
- a_src = 10;
- a_dst = 16;
- a_bss = 4;
- break;
- case 0x0200: // DS to STA
- a_src = 16;
- a_dst = 4;
- a_bss = 10;
- break;
- case 0x0300: // DS to DS
- printf("blarg\n");
- continue;
- }
- // TODO: those also trigger on other framectl values
- // like 0208 -> C
- framectl &= 0xE7FF;
- if (framectl == 0x0228) rxflags |= 0x000C; // MP host frame
- else if (framectl == 0x0218) rxflags |= 0x000D; // MP ack frame
- else if (framectl == 0x0118) rxflags |= 0x000E; // MP reply frame
- else if (framectl == 0x0158) rxflags |= 0x000F; // empty MP reply frame
- else rxflags |= 0x0008;
- break;
+ framelen -= (IOPORT(W_RXLenCrop) >> 7) & 0x1FE;
+ if (framelen > 24) memmove(&RXBuffer[12+24], &RXBuffer[12+28], framelen);
}
+ else
+ framelen -= (IOPORT(W_RXLenCrop) << 1) & 0x1FE;
- if (MACEqual(&RXBuffer[12 + a_src], (u8*)&IOPORT(W_MACAddr0)))
- continue; // oops. we received a packet we just sent.
-
- bssidmatch = MACEqual(&RXBuffer[12 + a_bss], (u8*)&IOPORT(W_BSSID0));
- //if (!(IOPORT(W_BSSID0) & 0x0001) && !(RXBuffer[12 + a_bss] & 0x01) &&
- if (!MACEqual(&RXBuffer[12 + a_dst], (u8*)&IOPORT(W_MACAddr0)) &&
- !(RXBuffer[12 + a_dst] & 0x01))
- {
- printf("received packet %04X but it didn't pass the MAC check\n", framectl);
- continue;
- }
+ if (framelen < 0) framelen = 0;
break;
}
@@ -901,37 +1502,64 @@ bool CheckRX(bool block)
WIFI_LOG("wifi: received packet FC:%04X SN:%04X CL:%04X RXT:%d CMT:%d\n",
framectl, *(u16*)&RXBuffer[12+4+6+6+6], *(u16*)&RXBuffer[12+4+6+6+6+2+2], framelen*4, IOPORT(W_CmdReplyTime));
- // make RX header
-
- if (bssidmatch) rxflags |= 0x8000;
-
- *(u16*)&RXBuffer[0] = rxflags;
- *(u16*)&RXBuffer[2] = 0x0040; // ???
*(u16*)&RXBuffer[6] = txrate;
*(u16*)&RXBuffer[8] = framelen;
- *(u16*)&RXBuffer[10] = 0x4080; // min/max RSSI. dunno
- RXTime = framelen;
+ bool macgood = (RXBuffer[12 + 4] & 0x01) || MACEqual(&RXBuffer[12 + 4], (u8*)&IOPORT(W_MACAddr0));
- if (txrate == 0x14)
+ if (((framectl & 0x00FF) == 0x0010) && timestamp && macgood)
{
- RXTime *= 4;
- RXHalfwordTimeMask = 0x7;
+ // if receiving an association response: get the sync value from the host
+
+ u16 aid = *(u16*)&RXBuffer[12+24+4];
+
+ if (aid)
+ {
+ printf("[CLIENT %01X] host sync=%016llX\n", aid&0xF, timestamp);
+
+ IsMPClient = true;
+ USTimestamp = timestamp;
+ NextSync = RXTimestamp + (framelen * (txrate==0x14 ? 4:8));
+ }
+
+ RXTimestamp = 0;
+ StartRX();
}
- else
+ else if (((framectl & 0x00FF) == 0x00C0) && timestamp && macgood && IsMPClient)
{
- RXTime *= 8;
- RXHalfwordTimeMask = 0xF;
+ IsMPClient = false;
+ NextSync = 0;
+
+ RXTimestamp = 0;
+ StartRX();
}
+ else if (macgood && IsMPClient)
+ {
+ // if we are being a MP client, we need to delay this frame until we reach the
+ // timestamp it came with
+ // we also need to determine how far we can run after having received this frame
- u16 addr = IOPORT(W_RXBufWriteCursor) << 1;
- IncrementRXAddr(addr, 12);
- IOPORT(W_RXTXAddr) = addr >> 1;
+ RXTimestamp = timestamp;
+ if (RXTimestamp < USTimestamp) RXTimestamp = USTimestamp;
+ NextSync = RXTimestamp + (framelen * (txrate==0x14 ? 4:8));
- RXBufferPtr = 12;
+ if (MACEqual(&RXBuffer[12 + 4], MPCmdMAC))
+ {
+ u16 clienttime = *(u16*)&RXBuffer[12+24];
+ u16 clientmask = *(u16*)&RXBuffer[12+26];
+
+ // include the MP reply time window
+ NextSync += 112 + ((clienttime + 10) * NumClients(clientmask));
+ }
+ }
+ else
+ {
+ // otherwise, just start receiving this frame now
+
+ RXTimestamp = 0;
+ StartRX();
+ }
- SetIRQ(6);
- SetStatus(6);
return true;
}
@@ -940,7 +1568,7 @@ void MSTimer()
{
if (IOPORT(W_USCompareCnt))
{
- if (USCounter == USCompare)
+ if ((USCounter & ~0x3FF) == USCompare)
{
BlockBeaconIRQ14 = false;
SetIRQ14(0);
@@ -962,55 +1590,108 @@ void MSTimer()
void USTimer(u32 param)
{
- WifiAP::USTimer();
+ USTimestamp += kTimerInterval;
+
+ if (IsMPClient && (!ComStatus))
+ {
+ if (RXTimestamp && (USTimestamp >= RXTimestamp))
+ {
+ RXTimestamp = 0;
+ StartRX();
+ }
+
+ if (USTimestamp >= NextSync)
+ {
+ // TODO: not do this every tick if it fails to receive a frame!
+ CheckRX(2);
+ }
+ }
+
+ if (!(USTimestamp & 0x3FF & kTimeCheckMask))
+ WifiAP::MSTimer();
+
+ bool switchOffPowerSaving = false;
+ if (USUntilPowerOn < 0)
+ {
+ USUntilPowerOn += kTimerInterval;
+
+ switchOffPowerSaving = (USUntilPowerOn >= 0) && (IOPORT(W_PowerUnk) & 0x0001 || ForcePowerOn);
+ }
+ if ((USUntilPowerOn >= 0) && (IOPORT(W_PowerState) & 0x0002 || switchOffPowerSaving))
+ {
+ IOPORT(W_PowerState) = 0;
+ IOPORT(W_RFPins) = 1;
+ IOPORT(W_RFPins) = 0x0084;
+ SetIRQ(11);
+ }
if (IOPORT(W_USCountCnt))
{
- USCounter++;
+ USCounter += kTimerInterval;
u32 uspart = (USCounter & 0x3FF);
if (IOPORT(W_USCompareCnt))
{
u32 beaconus = (IOPORT(W_BeaconCount1) << 10) | (0x3FF - uspart);
- if (beaconus == IOPORT(W_PreBeacon)) SetIRQ15();
+ if ((beaconus & kTimeCheckMask) == (IOPORT(W_PreBeacon) & kTimeCheckMask))
+ SetIRQ15();
}
- if (!uspart) MSTimer();
+ if (!(uspart & kTimeCheckMask))
+ MSTimer();
}
if (IOPORT(W_CmdCountCnt) & 0x0001)
{
if (CmdCounter > 0)
{
- CmdCounter--;
+ if (CmdCounter < kTimerInterval)
+ CmdCounter = 0;
+ else
+ CmdCounter -= kTimerInterval;
}
}
if (IOPORT(W_ContentFree) != 0)
- IOPORT(W_ContentFree)--;
+ {
+ if (IOPORT(W_ContentFree) < kTimerInterval)
+ IOPORT(W_ContentFree) = 0;
+ else
+ IOPORT(W_ContentFree) -= kTimerInterval;
+ }
if (ComStatus == 0)
{
u16 txbusy = IOPORT(W_TXBusy);
if (txbusy)
{
- ComStatus = 0x2;
- if (txbusy & 0x0080) TXCurSlot = 5;
- else if (txbusy & 0x0010) TXCurSlot = 4;
- else if (txbusy & 0x0008) TXCurSlot = 3;
- else if (txbusy & 0x0004) TXCurSlot = 2;
- else if (txbusy & 0x0002) TXCurSlot = 1;
- else if (txbusy & 0x0001) TXCurSlot = 0;
+ if (IOPORT(W_PowerState) & 0x0300)
+ {
+ ComStatus = 0;
+ TXCurSlot = -1;
+ }
+ else
+ {
+ ComStatus = 0x2;
+ if (txbusy & 0x0080) TXCurSlot = 5;
+ else if (txbusy & 0x0010) TXCurSlot = 4;
+ else if (txbusy & 0x0008) TXCurSlot = 3;
+ else if (txbusy & 0x0004) TXCurSlot = 2;
+ else if (txbusy & 0x0002) TXCurSlot = 1;
+ else if (txbusy & 0x0001) TXCurSlot = 0;
+ }
}
else
{
- if ((!(RXCounter & 0x1FF)))
+ if ((!IsMPClient) || (USTimestamp > NextSync))
{
- if (CheckRX(false))
- ComStatus = 0x1;
+ if ((!(RXCounter & 0x1FF & kTimeCheckMask)) && (!ComStatus))
+ {
+ CheckRX(0);
+ }
}
- RXCounter++;
+ RXCounter += kTimerInterval;
}
}
@@ -1019,6 +1700,12 @@ void USTimer(u32 param)
bool finished = ProcessTX(&TXSlots[TXCurSlot], TXCurSlot);
if (finished)
{
+ if (IOPORT(W_PowerState) & 0x0300)
+ {
+ IOPORT(W_TXBusy) = 0;
+ SetStatus(9);
+ }
+
// transfer finished, see if there's another slot to do
// checkme: priority order of beacon/reply
// TODO: for CMD, check CMDCOUNT
@@ -1039,50 +1726,31 @@ void USTimer(u32 param)
}
if (ComStatus & 0x1)
{
- RXTime--;
+ RXTime -= kTimerInterval;
if (!(RXTime & RXHalfwordTimeMask))
{
u16 addr = IOPORT(W_RXTXAddr) << 1;
if (addr < 0x1FFF) *(u16*)&RAM[addr] = *(u16*)&RXBuffer[RXBufferPtr];
IncrementRXAddr(addr);
+ IOPORT(W_RXTXAddr) = addr >> 1;
RXBufferPtr += 2;
- if (RXTime == 0) // finished receiving
+ if (RXTime <= 0) // finished receiving
{
- if (addr & 0x2) IncrementRXAddr(addr);
-
- // copy the RX header
- u16 headeraddr = IOPORT(W_RXBufWriteCursor) << 1;
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[0]; IncrementRXAddr(headeraddr);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[2]; IncrementRXAddr(headeraddr, 4);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[6]; IncrementRXAddr(headeraddr);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[8]; IncrementRXAddr(headeraddr);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[10];
-
- IOPORT(W_RXBufWriteCursor) = (addr & ~0x3) >> 1;
-
- SetIRQ(0);
- SetStatus(1);
-
- WIFI_LOG("wifi: finished receiving packet %04X\n", *(u16*)&RXBuffer[12]);
-
- ComStatus &= ~0x1;
- RXCounter = 0;
-
- if ((RXBuffer[0] & 0x0F) == 0x0C)
- {
- u16 clientmask = *(u16*)&RXBuffer[0xC + 26];
- if (IOPORT(W_AIDLow) && (RXBuffer[0xC + 4] & 0x01) && (clientmask & (1 << IOPORT(W_AIDLow))))
- {
- SendMPReply(*(u16*)&RXBuffer[0xC + 24], *(u16*)&RXBuffer[0xC + 26]);
- }
- }
+ FinishRX();
}
-
- if (addr == (IOPORT(W_RXBufReadCursor) << 1))
+ else if (addr == (IOPORT(W_RXBufReadCursor) << 1))
{
- printf("wifi: RX buffer full\n");
+ // TODO: properly check the crossing of the read cursor
+ // (for example, if it is outside of the RX buffer)
+
+ printf("wifi: RX buffer full (buf=%04X/%04X rd=%04X wr=%04X rxtx=%04X power=%04X com=%d rxcnt=%04X filter=%04X/%04X frame=%04X/%04X len=%d)\n",
+ (IOPORT(W_RXBufBegin)>>1)&0xFFF, (IOPORT(W_RXBufEnd)>>1)&0xFFF,
+ IOPORT(W_RXBufReadCursor), IOPORT(W_RXBufWriteCursor),
+ IOPORT(W_RXTXAddr), IOPORT(W_PowerState), ComStatus,
+ IOPORT(W_RXCnt), IOPORT(W_RXFilter), IOPORT(W_RXFilter2),
+ *(u16*)&RXBuffer[0], *(u16*)&RXBuffer[12], *(u16*)&RXBuffer[8]);
RXTime = 0;
SetStatus(1);
if (TXCurSlot == 0xFFFFFFFF)
@@ -1091,15 +1759,15 @@ void USTimer(u32 param)
RXCounter = 0;
}
// TODO: proper error management
+ if ((!ComStatus) && (IOPORT(W_PowerState) & 0x0300))
+ {
+ SetStatus(9);
+ }
}
-
- IOPORT(W_RXTXAddr) = addr >> 1;
}
}
- // TODO: make it more accurate, eventually
- // in the DS, the wifi system has its own 22MHz clock and doesn't use the system clock
- NDS::ScheduleEvent(NDS::Event_Wifi, true, 33, USTimer, 0);
+ ScheduleTimer(false);
}
@@ -1137,15 +1805,13 @@ void RFTransfer_Type3()
}
-// TODO: wifi waitstates
-
u16 Read(u32 addr)
-{//printf("WIFI READ %08X\n", addr);
+{
if (addr >= 0x04810000)
return 0;
addr &= 0x7FFE;
- //printf("WIFI: read %08X\n", addr);
+
if (addr >= 0x4000 && addr < 0x6000)
{
return *(u16*)&RAM[addr & 0x1FFE];
@@ -1193,7 +1859,6 @@ u16 Read(u32 addr)
if (activeread)
{
u32 rdaddr = IOPORT(W_RXBufReadAddr);
-
u16 ret = *(u16*)&RAM[rdaddr];
rdaddr += 2;
@@ -1223,6 +1888,20 @@ u16 Read(u32 addr)
case W_TXBusy:
return IOPORT(W_TXBusy) & 0x001F; // no bit for MP replies. odd
+
+ case W_CMDStat0:
+ case W_CMDStat1:
+ case W_CMDStat2:
+ case W_CMDStat3:
+ case W_CMDStat4:
+ case W_CMDStat5:
+ case W_CMDStat6:
+ case W_CMDStat7:
+ {
+ u16 ret = IOPORT(addr&0xFFF);
+ IOPORT(addr&0xFFF) = 0;
+ return ret;
+ }
}
//printf("WIFI: read %08X\n", addr);
@@ -1230,12 +1909,12 @@ u16 Read(u32 addr)
}
void Write(u32 addr, u16 val)
-{//printf("WIFI WRITE %08X %04X\n", addr, val);
+{
if (addr >= 0x04810000)
return;
addr &= 0x7FFE;
- //printf("WIFI: write %08X %04X\n", addr, val);
+
if (addr >= 0x4000 && addr < 0x6000)
{
*(u16*)&RAM[addr & 0x1FFE] = val;
@@ -1252,15 +1931,25 @@ void Write(u32 addr, u16 val)
if (!(oldval & 0x0001) && (val & 0x0001))
{
- IOPORT(0x034) = 0x0002;
- IOPORT(W_RFPins) = 0x0046;
- IOPORT(W_RFStatus) = 9;
- IOPORT(0x27C) = 0x0005;
- // TODO: 02A2??
+ if (!(USUntilPowerOn < 0 && ForcePowerOn))
+ {
+ //printf("mode reset power on %08x\n", NDS::ARM7->R[15]);
+ IOPORT(0x034) = 0x0002;
+ IOPORT(0x27C) = 0x0005;
+ // TODO: 02A2??
+
+ if (IOPORT(W_PowerUnk) & 0x0002)
+ {
+ USUntilPowerOn = -2048;
+ IOPORT(W_PowerState) |= 0x100;
+ }
+ }
}
else if ((oldval & 0x0001) && !(val & 0x0001))
{
+ //printf("mode reset shutdown %08x\n", NDS::ARM7->R[15]);
IOPORT(0x27C) = 0x000A;
+ PowerDown();
}
if (val & 0x2000)
@@ -1306,6 +1995,11 @@ void Write(u32 addr, u16 val)
case W_ModeWEP:
val &= 0x007F;
+ //printf("writing mode web %x\n", val);
+ if ((val & 0x7) == 1)
+ IOPORT(W_PowerUnk) |= 0x0002;
+ if ((val & 0x7) == 2)
+ IOPORT(W_PowerUnk) = 0x0003;
break;
case W_IF:
@@ -1316,54 +2010,74 @@ void Write(u32 addr, u16 val)
printf("wifi: force-setting IF %04X\n", val);
return;
+ case W_AIDLow:
+ IOPORT(W_AIDLow) = val & 0x000F;
+ return;
+ case W_AIDFull:
+ IOPORT(W_AIDFull) = val & 0x07FF;
+ return;
+
case W_PowerState:
- if (val & 0x0002)
- {
- // TODO: delay for this
- SetIRQ(11);
- IOPORT(W_PowerState) = 0x0000;
+ //printf("writing power state %x %08x\n", val, NDS::ARM7->R[15]);
+ IOPORT(W_PowerState) |= val & 0x0002;
- // checkme
- IOPORT(W_RFPins) = 0x00C6;
- IOPORT(W_RFStatus) = 9;
+ if (IOPORT(W_ModeReset) & 0x0001 && IOPORT(W_PowerState) & 0x0002)
+ {
+ /*if (IOPORT(W_PowerState) & 0x100)
+ {
+ AlwaysPowerOn = true;
+ USUntilPowerOn = -1;
+ }
+ else */
+ if (IOPORT(W_PowerForce) == 1)
+ {
+ //printf("power on\n");
+ IOPORT(W_PowerState) |= 0x100;
+ USUntilPowerOn = -2048;
+ ForcePowerOn = false;
+ }
}
return;
case W_PowerForce:
- if ((val&0x8001)==0x8000) printf("WIFI: forcing power %04X\n", val);
+ //if ((val&0x8001)==0x8000) printf("WIFI: forcing power %04X\n", val);
+
val &= 0x8001;
+ //printf("writing power force %x %08x\n", val, NDS::ARM7->R[15]);
if (val == 0x8001)
{
+ //printf("force power off\n");
IOPORT(0x034) = 0x0002;
IOPORT(W_PowerState) = 0x0200;
IOPORT(W_TXReqRead) = 0;
- IOPORT(W_RFPins) = 0x0046;
- IOPORT(W_RFStatus) = 9;
+ PowerDown();
}
- break;
- case W_PowerUS:
- // schedule timer event when the clock is enabled
- // TODO: check whether this resets USCOUNT (and also which other events can reset it)
- if ((IOPORT(W_PowerUS) & 0x0001) && !(val & 0x0001))
+ if (val == 1 && IOPORT(W_PowerState) & 0x0002)
{
- printf("WIFI ON\n");
- NDS::ScheduleEvent(NDS::Event_Wifi, false, 33, USTimer, 0);
- if (!MPInited)
- {
- Platform::MP_Init();
- MPInited = true;
- }
- if (!LANInited)
- {
- Platform::LAN_Init();
- LANInited = true;
- }
+ //printf("power on\n");
+ IOPORT(W_PowerState) |= 0x100;
+ USUntilPowerOn = -2048;
+ ForcePowerOn = false;
}
- else if (!(IOPORT(W_PowerUS) & 0x0001) && (val & 0x0001))
+ if (val == 0x8000)
{
- printf("WIFI OFF\n");
- NDS::CancelEvent(NDS::Event_Wifi);
+ //printf("force power on\n");
+ IOPORT(W_PowerState) |= 0x100;
+ USUntilPowerOn = -2048;
+ ForcePowerOn = true;
}
break;
+ case W_PowerUS:
+ IOPORT(W_PowerUS) = val & 0x0003;
+ UpdatePowerOn();
+ return;
+ case W_PowerUnk:
+ val &= 0x0003;
+ //printf("writing power unk %x\n", val);
+ if ((IOPORT(W_ModeWEP) & 0x7) == 1)
+ val |= 2;
+ else if ((IOPORT(W_ModeWEP) & 0x7) == 2)
+ val = 3;
+ break;
case W_USCountCnt: val &= 0x0001; break;
case W_USCompareCnt:
@@ -1416,6 +2130,10 @@ void Write(u32 addr, u16 val)
IOPORT(W_TXSlotReply2) = IOPORT(W_TXSlotReply1);
IOPORT(W_TXSlotReply1) = 0;
}
+ if (val & 0x8000)
+ {
+ FireTX();
+ }
val &= 0xFF0E;
if (val & 0x7FFF) printf("wifi: unknown RXCNT bits set %04X\n", val);
break;
@@ -1500,6 +2218,7 @@ void Write(u32 addr, u16 val)
case W_TXSlotCmd:
// checkme: is it possible to cancel a queued transfer that hasn't started yet
// by clearing bit15 here?
+ // TODO: "W_TXBUF_CMD.Bit15 can be set ONLY while W_CMD_COUNT is non-zero."
IOPORT(addr&0xFFF) = val;
FireTX();
return;
@@ -1527,9 +2246,12 @@ void Write(u32 addr, u16 val)
case 0x214:
case 0x268:
return;
+
+ default:
+ //printf("WIFI unk: write %08X %04X\n", addr, val);
+ break;
}
- //printf("WIFI: write %08X %04X\n", addr, val);
IOPORT(addr&0xFFF) = val;
}
diff --git a/src/Wifi.h b/src/Wifi.h
index eda5348..b9594f4 100644
--- a/src/Wifi.h
+++ b/src/Wifi.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -51,6 +51,7 @@ enum
W_PowerTX = 0x038,
W_PowerState = 0x03C,
W_PowerForce = 0x040,
+ W_PowerUnk = 0x48,
W_Random = 0x044,
@@ -92,6 +93,7 @@ enum
W_CmdTotalTime = 0x0C0,
W_CmdReplyTime = 0x0C4,
W_RXFilter = 0x0D0,
+ W_RXLenCrop = 0x0DA,
W_RXFilter2 = 0x0E0,
W_USCountCnt = 0x0E8,
@@ -135,12 +137,43 @@ enum
W_TXErrorCount = 0x1C0,
W_RXCount = 0x1C4,
+ W_CMDStat0 = 0x1D0,
+ W_CMDStat1 = 0x1D2,
+ W_CMDStat2 = 0x1D4,
+ W_CMDStat3 = 0x1D6,
+ W_CMDStat4 = 0x1D8,
+ W_CMDStat5 = 0x1DA,
+ W_CMDStat6 = 0x1DC,
+ W_CMDStat7 = 0x1DE,
+
W_TXSeqNo = 0x210,
W_RFStatus = 0x214,
W_IFSet = 0x21C,
W_RXTXAddr = 0x268,
};
+enum
+{
+ Event_RXCheck = 0,
+ Event_IRQ15,
+ Event_MSTimer,
+ Event_RFWakeup,
+ Event_RX,
+ Event_TX,
+ Event_MPClientSync,
+ Event_RF,
+ Event_BB,
+
+ Event_MAX
+};
+
+struct SchedEvent
+{
+ void (*Func)(u32 param);
+ u64 Timestamp;
+ u32 Param;
+};
+
extern bool MPInited;
@@ -150,7 +183,7 @@ void DeInit();
void Reset();
void DoSavestate(Savestate* file);
-void StartTX_Beacon();
+void SetPowerCnt(u32 val);
void USTimer(u32 param);
diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp
index 2f3804b..5351639 100644
--- a/src/WifiAP.cpp
+++ b/src/WifiAP.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -91,7 +91,7 @@ void DeInit()
void Reset()
{
// random starting point for the counter
- USCounter = 0x428888017ULL;
+ USCounter = 0x428888000ULL;
SeqNo = 0x0120;
BeaconDue = false;
@@ -115,18 +115,6 @@ bool MACIsBroadcast(u8* a)
}
-void USTimer()
-{
- USCounter++;
-
- u32 chk = (u32)USCounter;
- if (!(chk & 0x1FFFF))
- {
- // send beacon every 128ms
- BeaconDue = true;
- }
-}
-
void MSTimer()
{
USCounter += 0x400;
diff --git a/src/WifiAP.h b/src/WifiAP.h
index 67ea424..e5ca1ed 100644
--- a/src/WifiAP.h
+++ b/src/WifiAP.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,6 +19,8 @@
#ifndef WIFIAP_H
#define WIFIAP_H
+#include "types.h"
+
namespace WifiAP
{
@@ -31,7 +33,6 @@ bool Init();
void DeInit();
void Reset();
-void USTimer();
void MSTimer();
// packet format: 12-byte TX header + original 802.11 frame
diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h
index f52dced..91f5d88 100644
--- a/src/frontend/FrontendUtil.h
+++ b/src/frontend/FrontendUtil.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -21,100 +21,12 @@
#include "types.h"
+#include <string>
#include <vector>
namespace Frontend
{
-enum
-{
- ROMSlot_NDS = 0,
- ROMSlot_GBA,
-
- ROMSlot_MAX
-};
-
-enum
-{
- Load_OK = 0,
-
- Load_BIOS9Missing,
- Load_BIOS9Bad,
-
- Load_BIOS7Missing,
- Load_BIOS7Bad,
-
- Load_FirmwareMissing,
- Load_FirmwareBad,
- Load_FirmwareNotBootable,
-
- Load_DSiBIOS9Missing,
- Load_DSiBIOS9Bad,
-
- Load_DSiBIOS7Missing,
- Load_DSiBIOS7Bad,
-
- Load_DSiNANDMissing,
- Load_DSiNANDBad,
-
- // TODO: more precise errors for ROM loading
- Load_ROMLoadError,
-};
-
-extern char ROMPath [ROMSlot_MAX][1024];
-extern char SRAMPath[ROMSlot_MAX][1024];
-extern bool SavestateLoaded;
-
-// Stores type of nds rom i.e. nds/srl/dsi. Should be updated everytime an NDS rom is loaded from an archive
-extern char NDSROMExtension[4];
-
-// initialize the ROM handling utility
-void Init_ROM();
-
-// deinitialize the ROM handling utility
-void DeInit_ROM();
-
-// load the BIOS/firmware and boot from it
-int LoadBIOS();
-
-// load a ROM file to the specified cart slot
-// note: loading a ROM to the NDS slot resets emulation
-int LoadROM(const char* file, int slot);
-int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const char *romfilename, const char *sramfilename, int slot);
-
-// unload the ROM loaded in the specified cart slot
-// simulating ejection of the cartridge
-void UnloadROM(int slot);
-
-void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef);
-void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef);
-
-// reset execution of the current ROM
-int Reset();
-
-// get the filename associated with the given savestate slot (1-8)
-void GetSavestateName(int slot, char* filename, int len);
-
-// determine whether the given savestate slot does contain a savestate
-bool SavestateExists(int slot);
-
-// load the given savestate file
-// if successful, emulation will continue from the savestate's point
-bool LoadState(const char* filename);
-
-// save the current emulator state to the given file
-bool SaveState(const char* filename);
-
-// undo the latest savestate load
-void UndoStateLoad();
-
-// imports savedata from an external file. Returns the difference between the filesize and the SRAM size
-int ImportSRAM(const char* filename);
-
-// enable or disable cheats
-void EnableCheats(bool enable);
-
-
// setup the display layout based on the provided display size and parameters
// * screenWidth/screenHeight: size of the host display
// * screenLayout: how the DS screens are laid out
diff --git a/src/frontend/SharedConfig.h b/src/frontend/SharedConfig.h
deleted file mode 100644
index 3598e74..0000000
--- a/src/frontend/SharedConfig.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- Copyright 2016-2021 Arisotura
-
- 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 SHAREDCONFIG_H
-#define SHAREDCONFIG_H
-
-namespace Config
-{
-
-extern int ConsoleType;
-extern int DirectBoot;
-extern int SavestateRelocSRAM;
-
-extern int ExternalBIOSEnable;
-
-extern char BIOS9Path[1024];
-extern char BIOS7Path[1024];
-extern char FirmwarePath[1024];
-
-extern char DSiBIOS9Path[1024];
-extern char DSiBIOS7Path[1024];
-extern char DSiFirmwarePath[1024];
-extern char DSiNANDPath[1024];
-
-}
-
-#endif
diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp
index cad2318..fc0edeb 100644
--- a/src/frontend/Util_Audio.cpp
+++ b/src/frontend/Util_Audio.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp
deleted file mode 100644
index 4d23c70..0000000
--- a/src/frontend/Util_ROM.cpp
+++ /dev/null
@@ -1,845 +0,0 @@
-/*
- Copyright 2016-2021 Arisotura
-
- 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 <string.h>
-
-#include <utility>
-
-#ifdef ARCHIVE_SUPPORT_ENABLED
-#include "ArchiveUtil.h"
-#endif
-#include "FrontendUtil.h"
-#include "SharedConfig.h"
-#include "Platform.h"
-
-#include "NDS.h"
-#include "DSi.h"
-#include "GBACart.h"
-
-#include "AREngine.h"
-
-
-namespace Frontend
-{
-
-char ROMPath [ROMSlot_MAX][1024];
-char SRAMPath [ROMSlot_MAX][1024];
-char PrevSRAMPath[ROMSlot_MAX][1024]; // for savestate 'undo load'
-
-char NDSROMExtension[4];
-
-bool SavestateLoaded;
-
-ARCodeFile* CheatFile;
-bool CheatsOn;
-
-
-void Init_ROM()
-{
- SavestateLoaded = false;
-
- memset(ROMPath[ROMSlot_NDS], 0, 1024);
- memset(ROMPath[ROMSlot_GBA], 0, 1024);
- memset(SRAMPath[ROMSlot_NDS], 0, 1024);
- memset(SRAMPath[ROMSlot_GBA], 0, 1024);
- memset(PrevSRAMPath[ROMSlot_NDS], 0, 1024);
- memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024);
-
- CheatFile = nullptr;
- CheatsOn = false;
-}
-
-void DeInit_ROM()
-{
- if (CheatFile)
- {
- delete CheatFile;
- CheatFile = nullptr;
- }
-}
-
-// TODO: currently, when failing to load a ROM for whatever reason, we attempt
-// to revert to the previous state and resume execution; this may not be a very
-// good thing, depending on what state the core was left in.
-// should we do a better state revert (via the savestate system)? completely stop?
-
-void SetupSRAMPath(int slot)
-{
- strncpy(SRAMPath[slot], ROMPath[slot], 1023);
- SRAMPath[slot][1023] = '\0';
- strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3);
-}
-
-int VerifyDSBIOS()
-{
- FILE* f;
- long len;
-
- if (!Config::ExternalBIOSEnable) return Load_OK;
-
- f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
- if (!f) return Load_BIOS9Missing;
-
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- if (len != 0x1000)
- {
- fclose(f);
- return Load_BIOS9Bad;
- }
-
- fclose(f);
-
- f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
- if (!f) return Load_BIOS7Missing;
-
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- if (len != 0x4000)
- {
- fclose(f);
- return Load_BIOS7Bad;
- }
-
- fclose(f);
-
- return Load_OK;
-}
-
-int VerifyDSiBIOS()
-{
- FILE* f;
- long len;
-
- // TODO: check the first 32 bytes
-
- f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb");
- if (!f) return Load_DSiBIOS9Missing;
-
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- if (len != 0x10000)
- {
- fclose(f);
- return Load_DSiBIOS9Bad;
- }
-
- fclose(f);
-
- f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
- if (!f) return Load_DSiBIOS7Missing;
-
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- if (len != 0x10000)
- {
- fclose(f);
- return Load_DSiBIOS7Bad;
- }
-
- fclose(f);
-
- return Load_OK;
-}
-
-int VerifyDSFirmware()
-{
- FILE* f;
- long len;
-
- if (!Config::ExternalBIOSEnable) return Load_FirmwareNotBootable;
-
- f = Platform::OpenLocalFile(Config::FirmwarePath, "rb");
- if (!f) return Load_FirmwareNotBootable;
-
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- if (len == 0x20000)
- {
- // 128KB firmware, not bootable
- fclose(f);
- return Load_FirmwareNotBootable;
- }
- else if (len != 0x40000 && len != 0x80000)
- {
- fclose(f);
- return Load_FirmwareBad;
- }
-
- fclose(f);
-
- return Load_OK;
-}
-
-int VerifyDSiFirmware()
-{
- FILE* f;
- long len;
-
- f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb");
- if (!f) return Load_FirmwareMissing;
-
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- if (len != 0x20000)
- {
- // not 128KB
- // TODO: check whether those work
- fclose(f);
- return Load_FirmwareBad;
- }
-
- fclose(f);
-
- return Load_OK;
-}
-
-int SetupDSiNAND()
-{
- FILE* f;
- long len;
-
- f = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b");
- if (!f) return Load_DSiNANDMissing;
-
- // TODO: some basic checks
- // check that it has the nocash footer, and all
-
- DSi::SDMMCFile = f;
-
- return Load_OK;
-}
-
-void LoadCheats()
-{
- if (CheatFile)
- {
- delete CheatFile;
- CheatFile = nullptr;
- }
-
- char filename[1024];
- if (ROMPath[ROMSlot_NDS][0] != '\0')
- {
- strncpy(filename, ROMPath[ROMSlot_NDS], 1023);
- filename[1023] = '\0';
- strncpy(filename + strlen(ROMPath[ROMSlot_NDS]) - 3, "mch", 3);
- }
- else
- {
- strncpy(filename, "firmware.mch", 1023);
- }
-
- // TODO: check for error (malformed cheat file, ...)
- CheatFile = new ARCodeFile(filename);
-
- AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
-}
-
-int LoadBIOS()
-{
- DSi::CloseDSiNAND();
-
- int res;
-
- res = VerifyDSBIOS();
- if (res != Load_OK) return res;
-
- if (Config::ConsoleType == 1)
- {
- res = VerifyDSiBIOS();
- if (res != Load_OK) return res;
-
- res = VerifyDSiFirmware();
- if (res != Load_OK) return res;
-
- res = SetupDSiNAND();
- if (res != Load_OK) return res;
- }
- else
- {
- res = VerifyDSFirmware();
- if (res != Load_OK) return res;
- }
-
- // TODO:
- // original code in the libui frontend called NDS::LoadGBAROM() if needed
- // should this be carried over here?
- // is that behavior consistent with that of LoadROM() below?
-
- ROMPath[ROMSlot_NDS][0] = '\0';
- SRAMPath[ROMSlot_NDS][0] = '\0';
-
- NDS::SetConsoleType(Config::ConsoleType);
- NDS::LoadBIOS();
-
- SavestateLoaded = false;
-
- LoadCheats();
-
- return Load_OK;
-}
-
-int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const char *romfilename, const char *sramfilename, int slot)
-{
- int res;
- bool directboot = Config::DirectBoot != 0;
-
- if (Config::ConsoleType == 1 && slot == 1)
- {
- // cannot load a GBA ROM into a DSi
- return Load_ROMLoadError;
- }
-
- res = VerifyDSBIOS();
- if (res != Load_OK) return res;
-
- if (Config::ConsoleType == 1)
- {
- res = VerifyDSiBIOS();
- if (res != Load_OK) return res;
-
- res = VerifyDSiFirmware();
- if (res != Load_OK) return res;
-
- res = SetupDSiNAND();
- if (res != Load_OK) return res;
-
- GBACart::Eject();
- ROMPath[ROMSlot_GBA][0] = '\0';
- }
- else
- {
- res = VerifyDSFirmware();
- if (res != Load_OK)
- {
- if (res == Load_FirmwareNotBootable)
- directboot = true;
- else
- return res;
- }
- }
-
- char oldpath[1024];
- char oldsram[1024];
- strncpy(oldpath, ROMPath[slot], 1024);
- strncpy(oldsram, SRAMPath[slot], 1024);
-
- strncpy(SRAMPath[slot], sramfilename, 1024);
- strncpy(ROMPath[slot], archivefilename, 1024);
-
- NDS::SetConsoleType(Config::ConsoleType);
-
- if (slot == ROMSlot_NDS && NDS::LoadROM(romdata, romlength, SRAMPath[slot], directboot))
- {
- SavestateLoaded = false;
-
- LoadCheats();
-
- // Reload the inserted GBA cartridge (if any)
- // TODO: report failure there??
- //if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]);
-
- strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
- return Load_OK;
- }
- else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(romdata, romlength, romfilename, SRAMPath[slot]))
- {
- SavestateLoaded = false; // checkme??
-
- strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
- return Load_OK;
- }
- else
- {
- strncpy(ROMPath[slot], oldpath, 1024);
- strncpy(SRAMPath[slot], oldsram, 1024);
- return Load_ROMLoadError;
- }
-}
-
-int LoadROM(const char* file, int slot)
-{
- DSi::CloseDSiNAND();
-
- int res;
- bool directboot = Config::DirectBoot != 0;
-
- if (Config::ConsoleType == 1 && slot == 1)
- {
- // cannot load a GBA ROM into a DSi
- return Load_ROMLoadError;
- }
-
- res = VerifyDSBIOS();
- if (res != Load_OK) return res;
-
- if (Config::ConsoleType == 1)
- {
- res = VerifyDSiBIOS();
- if (res != Load_OK) return res;
-
- res = VerifyDSiFirmware();
- if (res != Load_OK) return res;
-
- res = SetupDSiNAND();
- if (res != Load_OK) return res;
-
- GBACart::Eject();
- ROMPath[ROMSlot_GBA][0] = '\0';
- }
- else
- {
- res = VerifyDSFirmware();
- if (res != Load_OK)
- {
- if (res == Load_FirmwareNotBootable)
- directboot = true;
- else
- return res;
- }
- }
-
- char oldpath[1024];
- char oldsram[1024];
- strncpy(oldpath, ROMPath[slot], 1024);
- strncpy(oldsram, SRAMPath[slot], 1024);
-
- strncpy(ROMPath[slot], file, 1023);
- ROMPath[slot][1023] = '\0';
-
- SetupSRAMPath(0);
- SetupSRAMPath(1);
-
- NDS::SetConsoleType(Config::ConsoleType);
-
- if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot))
- {
- SavestateLoaded = false;
-
- LoadCheats();
-
- // Reload the inserted GBA cartridge (if any)
- // TODO: report failure there??
- if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]);
-
- strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
- return Load_OK;
- }
- else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot]))
- {
- SavestateLoaded = false; // checkme??
-
- strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
- return Load_OK;
- }
- else
- {
- strncpy(ROMPath[slot], oldpath, 1024);
- strncpy(SRAMPath[slot], oldsram, 1024);
- return Load_ROMLoadError;
- }
-}
-
-void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef)
-{
- int index = 0;
- for (int i = 0; i < 4; i++)
- {
- for (int j = 0; j < 4; j++)
- {
- for (int k = 0; k < 8; k++)
- {
- for (int l = 0; l < 8; l++)
- {
- u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F;
- u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31;
- u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31;
- u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31;
- u8 a = pal_index ? 255: 0;
- u32* row = &iconRef[256 * i + 32 * k + 8 * j];
- row[l] = (a << 24) | (r << 16) | (g << 8) | b;
- index++;
- }
- }
- }
- }
-}
-
-#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15)
-#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14)
-#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11)
-#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8)
-#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0)
-
-void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef)
-{
- for (int i = 0; i < 64; i++)
- {
- if (!sequence[i])
- break;
- u32* frame = &animatedTexRef[32 * 32 * i];
- ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame);
-
- if (SEQ_FLIPH(sequence[i]))
- {
- for (int x = 0; x < 32; x++)
- {
- for (int y = 0; y < 32/2; y++)
- {
- std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]);
- }
- }
- }
- if (SEQ_FLIPV(sequence[i]))
- {
- for (int x = 0; x < 32/2; x++)
- {
- for (int y = 0; y < 32; y++)
- {
- std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]);
- }
- }
- }
-
- for (int j = 0; j < SEQ_DUR(sequence[i]); j++)
- animatedSequenceRef.push_back(i);
- }
-}
-
-void UnloadROM(int slot)
-{
- if (slot == ROMSlot_NDS)
- {
- // TODO!
- }
- else if (slot == ROMSlot_GBA)
- {
- GBACart::Eject();
- }
-
- ROMPath[slot][0] = '\0';
-
- DSi::CloseDSiNAND();
-}
-
-int Reset()
-{
- DSi::CloseDSiNAND();
-
- int res;
- bool directboot = Config::DirectBoot != 0;
-
- res = VerifyDSBIOS();
- if (res != Load_OK) return res;
-
- if (Config::ConsoleType == 1)
- {
- res = VerifyDSiBIOS();
- if (res != Load_OK) return res;
-
- res = VerifyDSiFirmware();
- if (res != Load_OK) return res;
-
- res = SetupDSiNAND();
- if (res != Load_OK) return res;
-
- GBACart::Eject();
- ROMPath[ROMSlot_GBA][0] = '\0';
- }
- else
- {
- res = VerifyDSFirmware();
- if (res != Load_OK)
- {
- if (res == Load_FirmwareNotBootable)
- directboot = true;
- else
- return res;
- }
- }
-
- SavestateLoaded = false;
-
- NDS::SetConsoleType(Config::ConsoleType);
-
- if (ROMPath[ROMSlot_NDS][0] == '\0')
- {
- NDS::LoadBIOS();
- }
- else
- {
- char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_NDS]);
- strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4);
-
- if(!strncasecmp(ext, ".nds", 4) || !strncasecmp(ext, ".srl", 4) || !strncasecmp(ext, ".dsi", 4))
- {
- SetupSRAMPath(0);
- if (!NDS::LoadROM(ROMPath[ROMSlot_NDS], SRAMPath[ROMSlot_NDS], directboot))
- return Load_ROMLoadError;
- }
-#ifdef ARCHIVE_SUPPORT_ENABLED
- else
- {
- u8 *romdata = nullptr; u32 romlen;
- char romfilename[1024] = {0}, sramfilename[1024];
- strncpy(sramfilename, SRAMPath[ROMSlot_NDS], 1024); // Use existing SRAMPath
-
- int pos = strlen(sramfilename) - 1;
- while(pos > 0 && sramfilename[pos] != '/' && sramfilename[pos] != '\\')
- --pos;
-
- strncpy(romfilename, &sramfilename[pos + 1], 1024);
- strncpy(&romfilename[strlen(romfilename) - 3], NDSROMExtension, 3); // extension could be nds, srl or dsi
- printf("RESET loading from archive : %s\n", romfilename);
- romlen = Archive::ExtractFileFromArchive(ROMPath[ROMSlot_NDS], romfilename, &romdata);
- if(!romdata)
- return Load_ROMLoadError;
-
- bool ok = NDS::LoadROM(romdata, romlen, sramfilename, directboot);
- delete romdata;
- if(!ok)
- return Load_ROMLoadError;
- }
-#endif
- }
-
- if (ROMPath[ROMSlot_GBA][0] != '\0')
- {
- char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_GBA]);
- strncpy(ext, ROMPath[ROMSlot_GBA] + _len - 4, 4);
-
- if(!strncasecmp(ext, ".gba", 4))
- {
- SetupSRAMPath(1);
- if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]))
- return Load_ROMLoadError;
- }
-#ifdef ARCHIVE_SUPPORT_ENABLED
- else
- {
- u8 *romdata = nullptr; u32 romlen;
- char romfilename[1024] = {0}, sramfilename[1024];
- strncpy(sramfilename, SRAMPath[ROMSlot_GBA], 1024); // Use existing SRAMPath
-
- int pos = strlen(sramfilename) - 1;
- while(pos > 0 && sramfilename[pos] != '/' && sramfilename[pos] != '\\')
- --pos;
-
- strncpy(romfilename, &sramfilename[pos + 1], 1024);
- strncpy(&romfilename[strlen(romfilename) - 3], "gba", 3);
- printf("RESET loading from archive : %s\n", romfilename);
- romlen = Archive::ExtractFileFromArchive(ROMPath[ROMSlot_GBA], romfilename, &romdata);
- if(!romdata)
- return Load_ROMLoadError;
-
- bool ok = NDS::LoadGBAROM(romdata, romlen, romfilename, SRAMPath[ROMSlot_GBA]);
- delete romdata;
- if(!ok)
- return Load_ROMLoadError;
- }
-#endif
- }
-
- LoadCheats();
-
- return Load_OK;
-}
-
-
-// SAVESTATE TODO
-// * configurable paths. not everyone wants their ROM directory to be polluted, I guess.
-
-void GetSavestateName(int slot, char* filename, int len)
-{
- int pos;
-
- if (ROMPath[ROMSlot_NDS][0] == '\0') // running firmware, no ROM
- {
- strcpy(filename, "firmware");
- pos = 8;
- }
- else
- {
- char *rompath;
- char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_NDS]);
- strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4);
-
- if(!strncasecmp(ext, ".nds", 4) || !strncasecmp(ext, ".srl", 4) || !strncasecmp(ext, ".dsi", 4))
- rompath = ROMPath[ROMSlot_NDS];
- else
- rompath = SRAMPath[ROMSlot_NDS]; // If archive, construct ssname from sram file
-
- 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';
-}
-
-bool SavestateExists(int slot)
-{
- char ssfile[1024];
- GetSavestateName(slot, ssfile, 1024);
- return Platform::FileExists(ssfile);
-}
-
-bool LoadState(const char* filename)
-{
- u32 oldGBACartCRC = GBACart::CartCRC;
-
- // backup
- Savestate* backup = new Savestate("timewarp.mln", true);
- NDS::DoSavestate(backup);
- delete backup;
-
- bool failed = false;
-
- 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);
- failed = true;
- }
-
- NDS::DoSavestate(state);
- delete state;
-
- if (!failed)
- {
- if (Config::SavestateRelocSRAM && ROMPath[ROMSlot_NDS][0]!='\0')
- {
- strncpy(PrevSRAMPath[ROMSlot_NDS], SRAMPath[0], 1024);
-
- strncpy(SRAMPath[ROMSlot_NDS], filename, 1019);
- int len = strlen(SRAMPath[ROMSlot_NDS]);
- strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav");
- SRAMPath[ROMSlot_NDS][len+4] = '\0';
-
- NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false);
- }
-
- bool loadedPartialGBAROM = false;
-
- // in case we have a GBA cart inserted, and the GBA ROM changes
- // due to having loaded a save state, we do not want to reload
- // the previous cartridge on reset, or commit writes to any
- // loaded save file. therefore, their paths are "nulled".
- if (GBACart::CartInserted && GBACart::CartCRC != oldGBACartCRC)
- {
- ROMPath[ROMSlot_GBA][0] = '\0';
- SRAMPath[ROMSlot_GBA][0] = '\0';
- loadedPartialGBAROM = true;
- }
-
- // TODO forward this to user in a meaningful way!!
- /*char msg[64];
- if (slot > 0) sprintf(msg, "State loaded from slot %d%s",
- slot, loadedPartialGBAROM ? " (GBA ROM header only)" : "");
- else sprintf(msg, "State loaded from file%s",
- loadedPartialGBAROM ? " (GBA ROM header only)" : "");
- OSD::AddMessage(0, msg);*/
-
- SavestateLoaded = true;
- }
-
- return !failed;
-}
-
-bool SaveState(const char* filename)
-{
- Savestate* state = new Savestate(filename, true);
- if (state->Error)
- {
- delete state;
- return false;
- }
- else
- {
- NDS::DoSavestate(state);
- delete state;
-
- if (Config::SavestateRelocSRAM && ROMPath[ROMSlot_NDS][0]!='\0')
- {
- strncpy(SRAMPath[ROMSlot_NDS], filename, 1019);
- int len = strlen(SRAMPath[ROMSlot_NDS]);
- strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav");
- SRAMPath[ROMSlot_NDS][len+4] = '\0';
-
- NDS::RelocateSave(SRAMPath[ROMSlot_NDS], true);
- }
- }
-
- return true;
-}
-
-void UndoStateLoad()
-{
- if (!SavestateLoaded) return;
-
- // 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[ROMSlot_NDS][0]!='\0')
- {
- strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024);
- NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false);
- }
-}
-
-int ImportSRAM(const char* filename)
-{
- FILE* file = fopen(filename, "rb");
- fseek(file, 0, SEEK_END);
- u32 size = ftell(file);
- u8* importData = new u8[size];
- rewind(file);
- fread(importData, size, 1, file);
- fclose(file);
-
- int diff = NDS::ImportSRAM(importData, size);
- delete[] importData;
- return diff;
-}
-
-void EnableCheats(bool enable)
-{
- CheatsOn = enable;
- if (CheatFile)
- AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
-}
-
-}
diff --git a/src/frontend/Util_Video.cpp b/src/frontend/Util_Video.cpp
index 33f0183..778c299 100644
--- a/src/frontend/Util_Video.cpp
+++ b/src/frontend/Util_Video.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/mic_blow.h b/src/frontend/mic_blow.h
index fcce5aa..0e1069b 100644
--- a/src/frontend/mic_blow.h
+++ b/src/frontend/mic_blow.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp
index 6919d48..fff1a94 100644
--- a/src/frontend/qt_sdl/ArchiveUtil.cpp
+++ b/src/frontend/qt_sdl/ArchiveUtil.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, WaluigiWare64
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -17,43 +17,66 @@
*/
#include "ArchiveUtil.h"
+#include "Platform.h"
namespace Archive
{
-QVector<QString> ListArchive(const char* path)
+#ifdef __WIN32__
+#define melon_archive_open(a, f, b) archive_read_open_filename_w(a, (const wchar_t*)f.utf16(), b)
+#else
+#define melon_archive_open(a, f, b) archive_read_open_filename(a, f.toUtf8().constData(), b)
+#endif // __WIN32__
+
+bool compareCI(const QString& s1, const QString& s2)
+{
+ return s1.toLower() < s2.toLower();
+}
+
+QVector<QString> ListArchive(QString path)
{
struct archive *a;
struct archive_entry *entry;
int r;
- QVector<QString> fileList = {"OK"};
-
+ QVector<QString> fileList;
+
a = archive_read_new();
+
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
- r = archive_read_open_filename(a, path, 10240);
+
+ //r = archive_read_open_filename(a, path, 10240);
+ r = melon_archive_open(a, path, 10240);
if (r != ARCHIVE_OK)
{
return QVector<QString> {"Err"};
}
-
- while (archive_read_next_header(a, &entry) == ARCHIVE_OK)
+
+ while (archive_read_next_header(a, &entry) == ARCHIVE_OK)
{
- fileList.push_back(archive_entry_pathname(entry));
- archive_read_data_skip(a);
+ if (archive_entry_filetype(entry) != AE_IFREG)
+ continue;
+
+ fileList.push_back(archive_entry_pathname_utf8(entry));
+ archive_read_data_skip(a);
}
+
archive_read_close(a);
- archive_read_free(a);
+ archive_read_free(a);
+
if (r != ARCHIVE_OK)
{
return QVector<QString> {"Err"};
}
-
+
+ std::stable_sort(fileList.begin(), fileList.end(), compareCI);
+ fileList.prepend("OK");
+
return fileList;
}
-QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer)
+QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer)
{
struct archive *a = archive_read_new();
struct archive_entry *entry;
@@ -61,8 +84,9 @@ QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
-
- r = archive_read_open_filename(a, path, 10240);
+
+ //r = archive_read_open_filename(a, path, 10240);
+ r = melon_archive_open(a, path, 10240);
if (r != ARCHIVE_OK)
{
return QVector<QString> {"Err"};
@@ -70,7 +94,7 @@ QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile
while (archive_read_next_header(a, &entry) == ARCHIVE_OK)
{
- if (strcmp(wantedFile, archive_entry_pathname(entry)) == 0)
+ if (strcmp(wantedFile.toUtf8().constData(), archive_entry_pathname_utf8(entry)) == 0)
{
break;
}
@@ -92,7 +116,45 @@ QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile
}
-u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata)
+u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize)
+{
+ struct archive *a = archive_read_new();
+ struct archive_entry *entry;
+ int r;
+
+ if (!filedata) return -1;
+
+ archive_read_support_format_all(a);
+ archive_read_support_filter_all(a);
+
+ //r = archive_read_open_filename(a, path, 10240);
+ r = melon_archive_open(a, path, 10240);
+ if (r != ARCHIVE_OK)
+ {
+ return -1;
+ }
+
+ while (archive_read_next_header(a, &entry) == ARCHIVE_OK)
+ {
+ if (strcmp(wantedFile.toUtf8().constData(), archive_entry_pathname_utf8(entry)) == 0)
+ {
+ break;
+ }
+ }
+
+ size_t bytesToRead = archive_entry_size(entry);
+ if (filesize) *filesize = bytesToRead;
+ *filedata = new u8[bytesToRead];
+ ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead);
+
+ archive_read_close(a);
+ archive_read_free(a);
+
+ return (u32)bytesRead;
+
+}
+
+/*u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata)
{
QByteArray romBuffer;
QVector<QString> extractResult = ExtractFileFromArchive(path, wantedFile, &romBuffer);
@@ -107,6 +169,6 @@ u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdat
memcpy(*romdata, romBuffer.data(), len);
return len;
-}
+}*/
}
diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h
index a8a4a14..761f542 100644
--- a/src/frontend/qt_sdl/ArchiveUtil.h
+++ b/src/frontend/qt_sdl/ArchiveUtil.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, WaluigiWare64
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -34,10 +34,11 @@
namespace Archive
{
-
-QVector<QString> ListArchive(const char* path);
-QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer);
-u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata);
+
+QVector<QString> ListArchive(QString path);
+u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize);
+//QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer);
+//u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata);
}
diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp
index d4ce678..4beefaf 100644
--- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -62,11 +62,25 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(
connect(grpMicMode, SIGNAL(buttonClicked(int)), this, SLOT(onChangeMicMode(int)));
grpMicMode->button(Config::MicInputType)->setChecked(true);
- ui->txtMicWavPath->setText(Config::MicWavPath);
+ ui->txtMicWavPath->setText(QString::fromStdString(Config::MicWavPath));
bool iswav = (Config::MicInputType == 3);
ui->txtMicWavPath->setEnabled(iswav);
ui->btnMicWavBrowse->setEnabled(iswav);
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ {
+ ui->lblInstanceNum->setText(QString("Configuring settings for instance %1").arg(inst+1));
+ ui->cbInterpolation->setEnabled(false);
+ ui->cbBitrate->setEnabled(false);
+ for (QAbstractButton* btn : grpMicMode->buttons())
+ btn->setEnabled(false);
+ ui->txtMicWavPath->setEnabled(false);
+ ui->btnMicWavBrowse->setEnabled(false);
+ }
+ else
+ ui->lblInstanceNum->hide();
}
AudioSettingsDialog::~AudioSettingsDialog()
@@ -77,7 +91,7 @@ AudioSettingsDialog::~AudioSettingsDialog()
void AudioSettingsDialog::on_AudioSettingsDialog_accepted()
{
Config::MicInputType = grpMicMode->checkedId();
- strncpy(Config::MicWavPath, ui->txtMicWavPath->text().toStdString().c_str(), 1023); Config::MicWavPath[1023] = '\0';
+ Config::MicWavPath = ui->txtMicWavPath->text().toStdString();
Config::Save();
closeDlg();
diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h
index 0bb32c6..498c152 100644
--- a/src/frontend/qt_sdl/AudioSettingsDialog.h
+++ b/src/frontend/qt_sdl/AudioSettingsDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.ui b/src/frontend/qt_sdl/AudioSettingsDialog.ui
index d7cfadd..8fc38d9 100644
--- a/src/frontend/qt_sdl/AudioSettingsDialog.ui
+++ b/src/frontend/qt_sdl/AudioSettingsDialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>482</width>
- <height>256</height>
+ <height>301</height>
</rect>
</property>
<property name="sizePolicy">
@@ -24,6 +24,13 @@
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
+ <widget class="QLabel" name="lblInstanceNum">
+ <property name="text">
+ <string>Configuring settings for instance X</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Audio output</string>
@@ -76,7 +83,7 @@
<item row="1" column="1">
<widget class="QComboBox" name="cbBitrate">
<property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The bitrate of audio playback. If set to &quot;Automatic&quot; this will be 10-bit for DS mode and 16-bit for DSi mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The bitrate of audio playback. If set to &quot;Automatic&quot; this will be 10-bit for DS mode and 16-bit for DSi mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt
index ad38e37..a8d6e4b 100644
--- a/src/frontend/qt_sdl/CMakeLists.txt
+++ b/src/frontend/qt_sdl/CMakeLists.txt
@@ -1,139 +1,123 @@
-project(qt_sdl)
+include(CMakeDependentOption)
-SET(SOURCES_QT_SDL
+include(FixInterfaceIncludes)
+
+set(SOURCES_QT_SDL
main.cpp
main_shaders.h
CheatsDialog.cpp
- Config.cpp
+ Config.cpp
EmuSettingsDialog.cpp
+ PowerManagement/PowerManagementDialog.cpp
+ PowerManagement/resources/battery.qrc
InputConfig/InputConfigDialog.cpp
InputConfig/MapButton.h
InputConfig/resources/ds.qrc
VideoSettingsDialog.cpp
+ CameraSettingsDialog.cpp
AudioSettingsDialog.cpp
FirmwareSettingsDialog.cpp
+ PathSettingsDialog.cpp
+ MPSettingsDialog.cpp
WifiSettingsDialog.cpp
InterfaceSettingsDialog.cpp
ROMInfoDialog.cpp
+ RAMInfoDialog.cpp
TitleManagerDialog.cpp
Input.cpp
LAN_PCap.cpp
LAN_Socket.cpp
+ LocalMP.cpp
OSD.cpp
OSD_shaders.h
font.h
Platform.cpp
QPathInput.h
+ ROMManager.cpp
+ SaveManager.cpp
+ CameraManager.cpp
ArchiveUtil.h
ArchiveUtil.cpp
- ../Util_ROM.cpp
../Util_Video.cpp
../Util_Audio.cpp
../FrontendUtil.h
../mic_blow.h
- ../SharedConfig.h
${CMAKE_SOURCE_DIR}/res/melon.qrc
-)
+ )
+
+if (APPLE)
+ option(USE_QT6 "Build using Qt 6 instead of 5" ON)
+else()
+ option(USE_QT6 "Build using Qt 6 instead of 5" OFF)
+endif()
-option(USE_QT6 "Build using Qt 6 instead of 5" OFF)
if (WIN32)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -i <SOURCE> -o <OBJECT>")
endif()
if (USE_QT6)
- if (BUILD_STATIC AND QT6_STATIC_DIR)
- set(QT6_STATIC_BASE ${QT6_STATIC_DIR}/lib/cmake/Qt6)
- set(Qt6_DIR ${QT6_STATIC_BASE})
- set(Qt6Core_DIR ${QT6_STATIC_BASE}Core)
- set(Qt6Gui_DIR ${QT6_STATIC_BASE}Gui)
- set(Qt6Widgets_DIR ${QT6_STATIC_BASE}Widgets)
- set(Qt6Network_DIR ${QT6_STATIC_BASE}Network)
- set(Qt6OpenGL_DIR ${QT6_STATIC_BASE}OpenGL)
- set(Qt6OpenGLWidgets_DIR ${QT6_STATIC_BASE}OpenGLWidgets)
- endif()
- find_package(Qt6 COMPONENTS Core Gui Widgets Network OpenGL OpenGLWidgets REQUIRED)
- set(QT_LINK_LIBS Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::OpenGL Qt6::OpenGLWidgets)
+ find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia OpenGL OpenGLWidgets REQUIRED)
+ set(QT_LINK_LIBS Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Multimedia Qt6::OpenGL Qt6::OpenGLWidgets)
else()
- if (BUILD_STATIC AND QT5_STATIC_DIR)
- set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
- set(Qt5_DIR ${QT5_STATIC_BASE})
- set(Qt5Core_DIR ${QT5_STATIC_BASE}Core)
- set(Qt5Gui_DIR ${QT5_STATIC_BASE}Gui)
- set(Qt5Widgets_DIR ${QT5_STATIC_BASE}Widgets)
- set(Qt5Network_DIR ${QT5_STATIC_BASE}Network)
- endif()
- find_package(Qt5 COMPONENTS Core Gui Widgets Network REQUIRED)
- set(QT_LINK_LIBS Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
+ find_package(Qt5 COMPONENTS Core Gui Widgets Network Multimedia REQUIRED)
+ set(QT_LINK_LIBS Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network Qt5::Multimedia)
endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
+if (BUILD_STATIC)
+ list(APPEND PKG_CONFIG_EXECUTABLE "--static")
+endif()
+
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
-find_package(Iconv REQUIRED)
-pkg_check_modules(SDL2 REQUIRED sdl2)
-pkg_check_modules(SLIRP REQUIRED slirp)
-pkg_check_modules(LIBARCHIVE REQUIRED libarchive)
-add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
+pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2)
+pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp)
+pkg_check_modules(LibArchive REQUIRED IMPORTED_TARGET libarchive)
-if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL Release))
- add_executable(melonDS WIN32 ${SOURCES_QT_SDL})
-else()
- add_executable(melonDS ${SOURCES_QT_SDL})
-endif()
+fix_interface_includes(PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive)
-target_link_libraries(melonDS ${CMAKE_THREAD_LIBS_INIT})
+add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
-target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_PREFIX}/include ${SLIRP_INCLUDE_DIRS} ${LIBARCHIVE_INCLUDE_DIRS})
-target_link_directories(melonDS PRIVATE ${SDL2_LIBRARY_DIRS} ${SLIRP_LIBRARY_DIRS})
-target_link_directories(melonDS PRIVATE ${LIBARCHIVE_LIBRARY_DIRS})
-
-target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
-target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
-target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
-target_link_libraries(melonDS core)
+add_executable(melonDS ${SOURCES_QT_SDL})
if (BUILD_STATIC)
- target_link_libraries(melonDS -static ${SDL2_STATIC_LIBRARIES} ${SLIRP_STATIC_LIBRARIES} ${LIBARCHIVE_STATIC_LIBRARIES})
qt_import_plugins(melonDS INCLUDE Qt::QSvgPlugin)
-else()
- target_link_libraries(melonDS ${SDL2_LIBRARIES} ${SLIRP_LIBRARIES} ${LIBARCHIVE_LIBRARIES})
+ target_link_options(melonDS PRIVATE -static)
endif()
-if (NOT Iconv_IS_BUILT_IN)
- target_link_libraries(melonDS ${Iconv_LIBRARIES})
-endif()
+target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
+target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
+target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
+target_link_libraries(melonDS PRIVATE core)
+target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive)
+target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS})
if (UNIX)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
- target_link_libraries(melonDS ${QT_LINK_LIBS})
- if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- target_link_libraries(melonDS dl)
- endif()
elseif (WIN32)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" ON)
+
configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_SOURCE_DIR}/melon.rc")
target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc")
- target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32)
- if (BUILD_STATIC)
- target_link_libraries(melonDS imm32 winmm version setupapi -static z zstd ${QT_LINK_LIBS})
- else()
- target_link_libraries(melonDS ${QT_LINK_LIBS})
- endif()
+ target_link_libraries(melonDS PRIVATE ws2_32 iphlpapi)
+ set_target_properties(melonDS PROPERTIES LINK_FLAGS_DEBUG "-mconsole")
endif()
if (PORTABLE)
- add_definitions(-DPORTABLE)
+ target_compile_definitions(melonDS PRIVATE PORTABLE)
endif()
if (APPLE)
+ target_sources(melonDS PRIVATE sem_timedwait.cpp)
+
# Copy icon into the bundle
set(RESOURCE_FILES "${CMAKE_SOURCE_DIR}/res/melon.icns")
target_sources(melonDS PUBLIC "${RESOURCE_FILES}")
@@ -143,14 +127,7 @@ if (APPLE)
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/melon.plist.in
OUTPUT_NAME melonDS
RESOURCE "${RESOURCE_FILES}")
-
- # Qt 6 requires macOS 10.15 if building on 10.15 or greater
- if(CMAKE_SYSTEM_VERSION VERSION_GREATER_EQUAL 19.0.0)
- if (USE_QT6)
- set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE)
- endif()
- endif()
option(MACOS_BUNDLE_LIBS "Bundle libraries with the app on macOS" OFF)
option(MACOS_BUILD_DMG "Build DMG image of the macOS application bundle" OFF)
@@ -168,8 +145,8 @@ endif()
if (UNIX AND NOT APPLE)
foreach(SIZE 16 32 48 64 128 256)
install(FILES ${CMAKE_SOURCE_DIR}/res/icon/melon_${SIZE}x${SIZE}.png
- DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/${SIZE}x${SIZE}/apps
- RENAME net.kuribo64.melonDS.png)
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/${SIZE}x${SIZE}/apps
+ RENAME net.kuribo64.melonDS.png)
endforeach()
install(FILES ${CMAKE_SOURCE_DIR}/res/net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp
new file mode 100644
index 0000000..19cf8d4
--- /dev/null
+++ b/src/frontend/qt_sdl/CameraManager.cpp
@@ -0,0 +1,612 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 "CameraManager.h"
+#include "Config.h"
+
+
+#if QT_VERSION >= 0x060000
+
+CameraFrameDumper::CameraFrameDumper(QObject* parent) : QVideoSink(parent)
+{
+ cam = (CameraManager*)parent;
+
+ connect(this, &CameraFrameDumper::videoFrameChanged, this, &CameraFrameDumper::present);
+}
+
+void CameraFrameDumper::present(const QVideoFrame& _frame)
+{
+ QVideoFrame frame(_frame);
+ if (!frame.map(QVideoFrame::ReadOnly))
+ return;
+ if (!frame.isReadable())
+ {
+ frame.unmap();
+ return;
+ }
+
+ switch (frame.pixelFormat())
+ {
+ case QVideoFrameFormat::Format_XRGB8888:
+ case QVideoFrameFormat::Format_YUYV:
+ cam->feedFrame((u32*)frame.bits(0), frame.width(), frame.height(), frame.pixelFormat() == QVideoFrameFormat::Format_YUYV);
+ break;
+
+ case QVideoFrameFormat::Format_UYVY:
+ cam->feedFrame_UYVY((u32*)frame.bits(0), frame.width(), frame.height());
+ break;
+
+ case QVideoFrameFormat::Format_NV12:
+ cam->feedFrame_NV12((u8*)frame.bits(0), (u8*)frame.bits(1), frame.width(), frame.height());
+ break;
+ }
+
+ frame.unmap();
+}
+
+#else
+
+CameraFrameDumper::CameraFrameDumper(QObject* parent) : QAbstractVideoSurface(parent)
+{
+ cam = (CameraManager*)parent;
+}
+
+bool CameraFrameDumper::present(const QVideoFrame& _frame)
+{
+ QVideoFrame frame(_frame);
+ if (!frame.map(QAbstractVideoBuffer::ReadOnly))
+ return false;
+ if (!frame.isReadable())
+ {
+ frame.unmap();
+ return false;
+ }
+
+ switch (frame.pixelFormat())
+ {
+ case QVideoFrame::Format_RGB32:
+ case QVideoFrame::Format_YUYV:
+ cam->feedFrame((u32*)frame.bits(0), frame.width(), frame.height(), frame.pixelFormat() == QVideoFrame::Format_YUYV);
+ break;
+
+ case QVideoFrame::Format_UYVY:
+ cam->feedFrame_UYVY((u32*)frame.bits(0), frame.width(), frame.height());
+ break;
+
+ case QVideoFrame::Format_NV12:
+ cam->feedFrame_NV12((u8*)frame.bits(0), (u8*)frame.bits(1), frame.width(), frame.height());
+ break;
+ }
+
+ frame.unmap();
+
+ return true;
+}
+
+QList<QVideoFrame::PixelFormat> CameraFrameDumper::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
+{
+ QList<QVideoFrame::PixelFormat> ret;
+
+ ret.append(QVideoFrame::Format_RGB32);
+ ret.append(QVideoFrame::Format_YUYV);
+ ret.append(QVideoFrame::Format_UYVY);
+ ret.append(QVideoFrame::Format_NV12);
+
+ return ret;
+}
+
+#endif
+
+
+CameraManager::CameraManager(int num, int width, int height, bool yuv) : QObject()
+{
+ this->num = num;
+
+ startNum = 0;
+
+ // QCamera needs to be controlled from the UI thread, hence this
+ connect(this, SIGNAL(camStartSignal()), this, SLOT(camStart()));
+ connect(this, SIGNAL(camStopSignal()), this, SLOT(camStop()));
+
+ frameWidth = width;
+ frameHeight = height;
+ frameFormatYUV = yuv;
+
+ int fbsize = frameWidth * frameHeight;
+ if (yuv) fbsize /= 2;
+ frameBuffer = new u32[fbsize];
+ tempFrameBuffer = new u32[fbsize];
+
+ inputType = -1;
+ xFlip = false;
+ init();
+}
+
+CameraManager::~CameraManager()
+{
+ deInit();
+
+ // save settings here?
+
+ delete[] frameBuffer;
+}
+
+void CameraManager::init()
+{
+ if (inputType != -1)
+ deInit();
+
+ startNum = 0;
+
+ inputType = Config::Camera[num].InputType;
+ imagePath = QString::fromStdString(Config::Camera[num].ImagePath);
+ camDeviceName = QString::fromStdString(Config::Camera[num].CamDeviceName);
+
+ camDevice = nullptr;
+
+ {
+ // fill the framebuffer with black
+
+ int total = frameWidth * frameHeight;
+ u32 fill = 0;
+ if (frameFormatYUV)
+ {
+ total /= 2;
+ fill = 0x80008000;
+ }
+
+ for (int i = 0; i < total; i++)
+ frameBuffer[i] = fill;
+ }
+
+ if (inputType == 1)
+ {
+ // still image
+
+ QImage img(imagePath);
+ if (!img.isNull())
+ {
+ QImage imgconv = img.convertToFormat(QImage::Format_RGB32);
+ if (frameFormatYUV)
+ {
+ copyFrame_RGBtoYUV((u32*)img.bits(), img.width(), img.height(),
+ frameBuffer, frameWidth, frameHeight,
+ false);
+ }
+ else
+ {
+ copyFrame_Straight((u32*)img.bits(), img.width(), img.height(),
+ frameBuffer, frameWidth, frameHeight,
+ false, false);
+ }
+ }
+ }
+ else if (inputType == 2)
+ {
+ // physical camera
+
+#if QT_VERSION >= 0x060000
+ const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
+ for (const QCameraDevice& cam : cameras)
+ {
+ if (QString(cam.id()) == camDeviceName)
+ {
+ camDevice = new QCamera(cam);
+ break;
+ }
+ }
+
+ if (camDevice)
+ {
+ const QList<QCameraFormat> supported = camDevice->cameraDevice().videoFormats();
+ bool good = false;
+ for (const QCameraFormat& item : supported)
+ {
+ if (item.pixelFormat() != QVideoFrameFormat::Format_YUYV &&
+ item.pixelFormat() != QVideoFrameFormat::Format_UYVY &&
+ item.pixelFormat() != QVideoFrameFormat::Format_NV12 &&
+ item.pixelFormat() != QVideoFrameFormat::Format_XRGB8888)
+ continue;
+
+ if (item.resolution().width() != 640 && item.resolution().height() != 480)
+ continue;
+
+ camDevice->setCameraFormat(item);
+ good = true;
+ break;
+ }
+
+ if (!good)
+ {
+ delete camDevice;
+ camDevice = nullptr;
+ }
+ else
+ {
+ camDumper = new CameraFrameDumper(this);
+
+ camSession = new QMediaCaptureSession(this);
+ camSession->setCamera(camDevice);
+ camSession->setVideoOutput(camDumper);
+ }
+ }
+#else
+ camDevice = new QCamera(camDeviceName.toUtf8());
+ if (camDevice->error() != QCamera::NoError)
+ {
+ delete camDevice;
+ camDevice = nullptr;
+ }
+
+ if (camDevice)
+ {
+ camDevice->load();
+
+ const QList<QCameraViewfinderSettings> supported = camDevice->supportedViewfinderSettings();
+ bool good = false;
+ for (const QCameraViewfinderSettings& item : supported)
+ {
+ if (item.pixelFormat() != QVideoFrame::Format_YUYV &&
+ item.pixelFormat() != QVideoFrame::Format_UYVY &&
+ item.pixelFormat() != QVideoFrame::Format_NV12 &&
+ item.pixelFormat() != QVideoFrame::Format_RGB32)
+ continue;
+
+ if (item.resolution().width() != 640 && item.resolution().height() != 480)
+ continue;
+
+ camDevice->setViewfinderSettings(item);
+ good = true;
+ break;
+ }
+
+ camDevice->unload();
+
+ if (!good)
+ {
+ delete camDevice;
+ camDevice = nullptr;
+ }
+ else
+ {
+ camDumper = new CameraFrameDumper(this);
+ camDevice->setViewfinder(camDumper);
+ }
+ }
+#endif
+ }
+}
+
+void CameraManager::deInit()
+{
+ if (inputType == 2)
+ {
+ if (camDevice)
+ {
+ camDevice->stop();
+ delete camDevice;
+ delete camDumper;
+#if QT_VERSION >= 0x060000
+ delete camSession;
+#endif
+ }
+ }
+
+ camDevice = nullptr;
+ inputType = -1;
+}
+
+void CameraManager::start()
+{
+ if (startNum == 1) return;
+ startNum = 1;
+
+ if (inputType == 2)
+ {
+ emit camStartSignal();
+ }
+}
+
+void CameraManager::stop()
+{
+ if (startNum == 0) return;
+ startNum = 0;
+
+ if (inputType == 2)
+ {
+ emit camStopSignal();
+ }
+}
+
+bool CameraManager::isStarted()
+{
+ return startNum != 0;
+}
+
+void CameraManager::camStart()
+{
+ if (camDevice)
+ camDevice->start();
+}
+
+void CameraManager::camStop()
+{
+ if (camDevice)
+ camDevice->stop();
+}
+
+void CameraManager::setXFlip(bool flip)
+{
+ xFlip = flip;
+}
+
+void CameraManager::captureFrame(u32* frame, int width, int height, bool yuv)
+{
+ frameMutex.lock();
+
+ if ((width == frameWidth) &&
+ (height == frameHeight) &&
+ (yuv == frameFormatYUV) &&
+ (!xFlip))
+ {
+ int len = width * height;
+ if (yuv) len /= 2;
+ memcpy(frame, frameBuffer, len * sizeof(u32));
+ }
+ else
+ {
+ if (yuv == frameFormatYUV)
+ {
+ copyFrame_Straight(frameBuffer, frameWidth, frameHeight,
+ frame, width, height,
+ xFlip, yuv);
+ }
+ else if (yuv)
+ {
+ copyFrame_RGBtoYUV(frameBuffer, frameWidth, frameHeight,
+ frame, width, height,
+ xFlip);
+ }
+ else
+ {
+ copyFrame_YUVtoRGB(frameBuffer, frameWidth, frameHeight,
+ frame, width, height,
+ xFlip);
+ }
+ }
+
+ frameMutex.unlock();
+}
+
+void CameraManager::feedFrame(u32* frame, int width, int height, bool yuv)
+{
+ frameMutex.lock();
+
+ if (width == frameWidth && height == frameHeight && yuv == frameFormatYUV)
+ {
+ int len = width * height;
+ if (yuv) len /= 2;
+ memcpy(frameBuffer, frame, len * sizeof(u32));
+ }
+ else
+ {
+ if (yuv == frameFormatYUV)
+ {
+ copyFrame_Straight(frame, width, height,
+ frameBuffer, frameWidth, frameHeight,
+ false, yuv);
+ }
+ else if (yuv)
+ {
+ copyFrame_RGBtoYUV(frame, width, height,
+ frameBuffer, frameWidth, frameHeight,
+ false);
+ }
+ else
+ {
+ copyFrame_YUVtoRGB(frame, width, height,
+ frameBuffer, frameWidth, frameHeight,
+ false);
+ }
+ }
+
+ frameMutex.unlock();
+}
+
+void CameraManager::feedFrame_UYVY(u32* frame, int width, int height)
+{
+ for (int y = 0; y < frameHeight; y++)
+ {
+ int sy = (y * height) / frameHeight;
+
+ for (int x = 0; x < frameWidth; x+=2)
+ {
+ int sx = (x * width) / frameWidth;
+
+ u32 val = frame[((sy*width) + sx) >> 1];
+
+ val = ((val & 0xFF00FF00) >> 8) | ((val & 0x00FF00FF) << 8);
+
+ tempFrameBuffer[((y*frameWidth) + x) >> 1] = val;
+ }
+ }
+
+ feedFrame(tempFrameBuffer, frameWidth, frameHeight, true);
+}
+
+void CameraManager::feedFrame_NV12(u8* planeY, u8* planeUV, int width, int height)
+{
+ for (int y = 0; y < frameHeight; y++)
+ {
+ int sy = (y * height) / frameHeight;
+
+ for (int x = 0; x < frameWidth; x+=2)
+ {
+ int sx1 = (x * width) / frameWidth;
+ int sx2 = ((x+1) * width) / frameWidth;
+
+ u32 val;
+
+ u8 y1 = planeY[(sy*width) + sx1];
+ u8 y2 = planeY[(sy*width) + sx2];
+
+ int uvpos = (((sy>>1)*(width>>1)) + (sx1>>1));
+ u8 u = planeUV[uvpos << 1];
+ u8 v = planeUV[(uvpos << 1) + 1];
+
+ val = y1 | (u << 8) | (y2 << 16) | (v << 24);
+ tempFrameBuffer[((y*frameWidth) + x) >> 1] = val;
+ }
+ }
+
+ feedFrame(tempFrameBuffer, frameWidth, frameHeight, true);
+}
+
+void CameraManager::copyFrame_Straight(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip, bool yuv)
+{
+ if (yuv)
+ {
+ swidth /= 2;
+ dwidth /= 2;
+ }
+
+ for (int dy = 0; dy < dheight; dy++)
+ {
+ int sy = (dy * sheight) / dheight;
+
+ for (int dx = 0; dx < dwidth; dx++)
+ {
+ int sx = (dx * swidth) / dwidth;
+ if (xflip) sx = swidth-1 - sx;
+
+ u32 val = src[(sy * swidth) + sx];
+
+ if (yuv)
+ {
+ if (xflip)
+ val = (val & 0xFF00FF00) |
+ ((val >> 16) & 0xFF) |
+ ((val & 0xFF) << 16);
+ }
+ else
+ val |= 0xFF000000;
+
+ dst[(dy * dwidth) + dx] = val;
+ }
+ }
+}
+
+void CameraManager::copyFrame_RGBtoYUV(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip)
+{
+ for (int dy = 0; dy < dheight; dy++)
+ {
+ int sy = (dy * sheight) / dheight;
+
+ for (int dx = 0; dx < dwidth; dx+=2)
+ {
+ int sx;
+
+ sx = (dx * swidth) / dwidth;
+ if (xflip) sx = swidth-1 - sx;
+
+ u32 pixel1 = src[sy*swidth + sx];
+
+ sx = ((dx+1) * swidth) / dwidth;
+ if (xflip) sx = swidth-1 - sx;
+
+ u32 pixel2 = src[sy*swidth + sx];
+
+ int r1 = (pixel1 >> 16) & 0xFF;
+ int g1 = (pixel1 >> 8) & 0xFF;
+ int b1 = pixel1 & 0xFF;
+
+ int r2 = (pixel2 >> 16) & 0xFF;
+ int g2 = (pixel2 >> 8) & 0xFF;
+ int b2 = pixel2 & 0xFF;
+
+ int y1 = ((r1 * 19595) + (g1 * 38470) + (b1 * 7471)) >> 16;
+ int u1 = ((b1 - y1) * 32244) >> 16;
+ int v1 = ((r1 - y1) * 57475) >> 16;
+
+ int y2 = ((r2 * 19595) + (g2 * 38470) + (b2 * 7471)) >> 16;
+ int u2 = ((b2 - y2) * 32244) >> 16;
+ int v2 = ((r2 - y2) * 57475) >> 16;
+
+ u1 += 128; v1 += 128;
+ u2 += 128; v2 += 128;
+
+ y1 = std::clamp(y1, 0, 255); u1 = std::clamp(u1, 0, 255); v1 = std::clamp(v1, 0, 255);
+ y2 = std::clamp(y2, 0, 255); u2 = std::clamp(u2, 0, 255); v2 = std::clamp(v2, 0, 255);
+
+ // huh
+ u1 = (u1 + u2) >> 1;
+ v1 = (v1 + v2) >> 1;
+
+ dst[(dy*dwidth + dx) / 2] = y1 | (u1 << 8) | (y2 << 16) | (v1 << 24);
+ }
+ }
+}
+
+void CameraManager::copyFrame_YUVtoRGB(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip)
+{
+ for (int dy = 0; dy < dheight; dy++)
+ {
+ int sy = (dy * sheight) / dheight;
+
+ for (int dx = 0; dx < dwidth; dx+=2)
+ {
+ int sx = (dx * swidth) / dwidth;
+ if (xflip) sx = swidth-2 - sx;
+
+ u32 val = src[(sy*swidth + sx) / 2];
+
+ int y1, y2;
+ if (xflip)
+ {
+ y1 = (val >> 16) & 0xFF;
+ y2 = val & 0xFF;
+ }
+ else
+ {
+ y1 = val & 0xFF;
+ y2 = (val >> 16) & 0xFF;
+ }
+ int u = (val >> 8) & 0xFF;
+ int v = (val >> 24) & 0xFF;
+
+ u -= 128; v -= 128;
+
+ int r1 = y1 + ((v * 91881) >> 16);
+ int g1 = y1 - ((v * 46793) >> 16) - ((u * 22544) >> 16);
+ int b1 = y1 + ((u * 116129) >> 16);
+
+ int r2 = y2 + ((v * 91881) >> 16);
+ int g2 = y2 - ((v * 46793) >> 16) - ((u * 22544) >> 16);
+ int b2 = y2 + ((u * 116129) >> 16);
+
+ r1 = std::clamp(r1, 0, 255); g1 = std::clamp(g1, 0, 255); b1 = std::clamp(b1, 0, 255);
+ r2 = std::clamp(r2, 0, 255); g2 = std::clamp(g2, 0, 255); b2 = std::clamp(b2, 0, 255);
+
+ u32 col1 = 0xFF000000 | (r1 << 16) | (g1 << 8) | b1;
+ u32 col2 = 0xFF000000 | (r2 << 16) | (g2 << 8) | b2;
+
+ dst[dy*dwidth + dx ] = col1;
+ dst[dy*dwidth + dx+1] = col2;
+ }
+ }
+}
diff --git a/src/frontend/qt_sdl/CameraManager.h b/src/frontend/qt_sdl/CameraManager.h
new file mode 100644
index 0000000..6743d19
--- /dev/null
+++ b/src/frontend/qt_sdl/CameraManager.h
@@ -0,0 +1,134 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 CAMERAMANAGER_H
+#define CAMERAMANAGER_H
+
+#include <QCamera>
+#if QT_VERSION >= 0x060000
+ #include <QMediaDevices>
+ #include <QCameraDevice>
+ #include <QMediaCaptureSession>
+ #include <QVideoSink>
+#else
+ #include <QCameraInfo>
+ #include <QAbstractVideoSurface>
+ #include <QVideoSurfaceFormat>
+#endif
+#include <QMutex>
+
+#include "types.h"
+
+class CameraManager;
+
+
+#if QT_VERSION >= 0x060000
+
+class CameraFrameDumper : public QVideoSink
+{
+ Q_OBJECT
+
+public:
+ CameraFrameDumper(QObject* parent = nullptr);
+
+public slots:
+ void present(const QVideoFrame& frame);
+
+private:
+ CameraManager* cam;
+};
+
+#else
+
+class CameraFrameDumper : public QAbstractVideoSurface
+{
+ Q_OBJECT
+
+public:
+ CameraFrameDumper(QObject* parent = nullptr);
+
+ bool present(const QVideoFrame& frame) override;
+ QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override;
+
+private:
+ CameraManager* cam;
+};
+
+#endif
+
+
+class CameraManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ CameraManager(int num, int width, int height, bool yuv);
+ ~CameraManager();
+
+ void init();
+ void deInit();
+
+ void start();
+ void stop();
+ bool isStarted();
+
+ void setXFlip(bool flip);
+
+ void captureFrame(u32* frame, int width, int height, bool yuv);
+
+ void feedFrame(u32* frame, int width, int height, bool yuv);
+ void feedFrame_UYVY(u32* frame, int width, int height);
+ void feedFrame_NV12(u8* planeY, u8* planeUV, int width, int height);
+
+signals:
+ void camStartSignal();
+ void camStopSignal();
+
+private slots:
+ void camStart();
+ void camStop();
+
+private:
+ int num;
+
+ int startNum;
+
+ int inputType;
+ QString imagePath;
+ QString camDeviceName;
+
+ QCamera* camDevice;
+ CameraFrameDumper* camDumper;
+#if QT_VERSION >= 0x060000
+ QMediaCaptureSession* camSession;
+#endif
+
+ int frameWidth, frameHeight;
+ bool frameFormatYUV;
+ u32* frameBuffer;
+ u32* tempFrameBuffer;
+ QMutex frameMutex;
+
+ bool xFlip;
+
+ void copyFrame_Straight(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip, bool yuv);
+ void copyFrame_RGBtoYUV(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip);
+ void copyFrame_YUVtoRGB(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip);
+};
+
+#endif // CAMERAMANAGER_H
diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.cpp b/src/frontend/qt_sdl/CameraSettingsDialog.cpp
new file mode 100644
index 0000000..1844e0f
--- /dev/null
+++ b/src/frontend/qt_sdl/CameraSettingsDialog.cpp
@@ -0,0 +1,304 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 <QFileDialog>
+#include <QPaintEvent>
+#include <QPainter>
+
+#include "types.h"
+
+#include "CameraSettingsDialog.h"
+#include "ui_CameraSettingsDialog.h"
+
+
+CameraSettingsDialog* CameraSettingsDialog::currentDlg = nullptr;
+
+extern std::string EmuDirectory;
+
+extern CameraManager* camManager[2];
+
+
+CameraPreviewPanel::CameraPreviewPanel(QWidget* parent) : QWidget(parent)
+{
+ currentCam = nullptr;
+ updateTimer = startTimer(50);
+}
+
+CameraPreviewPanel::~CameraPreviewPanel()
+{
+ killTimer(updateTimer);
+}
+
+void CameraPreviewPanel::paintEvent(QPaintEvent* event)
+{
+ QPainter painter(this);
+
+ if (!currentCam)
+ {
+ painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0));
+ return;
+ }
+
+ QImage picture(256, 192, QImage::Format_RGB32);
+ currentCam->captureFrame((u32*)picture.bits(), 256, 192, false);
+ painter.drawImage(0, 0, picture);
+}
+
+
+CameraSettingsDialog::CameraSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::CameraSettingsDialog)
+{
+ previewPanel = nullptr;
+ currentCfg = nullptr;
+ currentCam = nullptr;
+
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ for (int i = 0; i < 2; i++)
+ {
+ oldCamSettings[i] = Config::Camera[i];
+ }
+
+ ui->cbCameraSel->addItem("DSi outer camera");
+ ui->cbCameraSel->addItem("DSi inner camera");
+
+#if QT_VERSION >= 0x060000
+ const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
+ for (const QCameraDevice &cameraInfo : cameras)
+ {
+ QString name = cameraInfo.description();
+ QCameraDevice::Position pos = cameraInfo.position();
+ if (pos != QCameraDevice::UnspecifiedPosition)
+ {
+ name += " (";
+ if (pos == QCameraDevice::FrontFace)
+ name += "inner camera";
+ else if (pos == QCameraDevice::BackFace)
+ name += "outer camera";
+ name += ")";
+ }
+
+ ui->cbPhysicalCamera->addItem(name, QString(cameraInfo.id()));
+ }
+#else
+ const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
+ for (const QCameraInfo &cameraInfo : cameras)
+ {
+ QString name = cameraInfo.description();
+ QCamera::Position pos = cameraInfo.position();
+ if (pos != QCamera::UnspecifiedPosition)
+ {
+ name += " (";
+ if (pos == QCamera::FrontFace)
+ name += "inner camera";
+ else if (pos == QCamera::BackFace)
+ name += "outer camera";
+ name += ")";
+ }
+
+ ui->cbPhysicalCamera->addItem(name, cameraInfo.deviceName());
+ }
+#endif
+ ui->rbPictureCamera->setEnabled(ui->cbPhysicalCamera->count() > 0);
+
+ grpInputType = new QButtonGroup(this);
+ grpInputType->addButton(ui->rbPictureNone, 0);
+ grpInputType->addButton(ui->rbPictureImg, 1);
+ grpInputType->addButton(ui->rbPictureCamera, 2);
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ connect(grpInputType, SIGNAL(buttonClicked(int)), this, SLOT(onChangeInputType(int)));
+#else
+ connect(grpInputType, SIGNAL(idClicked(int)), this, SLOT(onChangeInputType(int)));
+#endif
+
+ previewPanel = new CameraPreviewPanel(this);
+ QVBoxLayout* previewLayout = new QVBoxLayout();
+ previewLayout->addWidget(previewPanel);
+ ui->grpPreview->setLayout(previewLayout);
+ previewPanel->setMinimumSize(256, 192);
+ previewPanel->setMaximumSize(256, 192);
+
+ on_cbCameraSel_currentIndexChanged(ui->cbCameraSel->currentIndex());
+}
+
+CameraSettingsDialog::~CameraSettingsDialog()
+{
+ delete ui;
+}
+
+void CameraSettingsDialog::on_CameraSettingsDialog_accepted()
+{
+ for (int i = 0; i < 2; i++)
+ {
+ camManager[i]->stop();
+ }
+
+ Config::Save();
+
+ closeDlg();
+}
+
+void CameraSettingsDialog::on_CameraSettingsDialog_rejected()
+{
+ for (int i = 0; i < 2; i++)
+ {
+ camManager[i]->stop();
+ camManager[i]->deInit();
+ Config::Camera[i] = oldCamSettings[i];
+ camManager[i]->init();
+ }
+
+ closeDlg();
+}
+
+void CameraSettingsDialog::on_cbCameraSel_currentIndexChanged(int id)
+{
+ if (!previewPanel) return;
+
+ if (currentCam)
+ {
+ currentCam->stop();
+ }
+
+ currentId = id;
+ currentCfg = &Config::Camera[id];
+ //currentCam = camManager[id];
+ currentCam = nullptr;
+ populateCamControls(id);
+ currentCam = camManager[id];
+ previewPanel->setCurrentCam(currentCam);
+
+ currentCam->start();
+}
+
+void CameraSettingsDialog::onChangeInputType(int type)
+{
+ if (!currentCfg) return;
+
+ if (currentCam)
+ {
+ currentCam->stop();
+ currentCam->deInit();
+ }
+
+ currentCfg->InputType = type;
+
+ ui->txtSrcImagePath->setEnabled(type == 1);
+ ui->btnSrcImageBrowse->setEnabled(type == 1);
+ ui->cbPhysicalCamera->setEnabled((type == 2) && (ui->cbPhysicalCamera->count()>0));
+
+ currentCfg->ImagePath = ui->txtSrcImagePath->text().toStdString();
+
+ if (ui->cbPhysicalCamera->count() > 0)
+ currentCfg->CamDeviceName = ui->cbPhysicalCamera->currentData().toString().toStdString();
+
+ if (currentCam)
+ {
+ currentCam->init();
+ currentCam->start();
+ }
+}
+
+void CameraSettingsDialog::on_txtSrcImagePath_textChanged()
+{
+ if (!currentCfg) return;
+
+ if (currentCam)
+ {
+ currentCam->stop();
+ currentCam->deInit();
+ }
+
+ currentCfg->ImagePath = ui->txtSrcImagePath->text().toStdString();
+
+ if (currentCam)
+ {
+ currentCam->init();
+ currentCam->start();
+ }
+}
+
+void CameraSettingsDialog::on_btnSrcImageBrowse_clicked()
+{
+ QString file = QFileDialog::getOpenFileName(this,
+ "Select image file...",
+ QString::fromStdString(EmuDirectory),
+ "Image files (*.png *.jpg *.jpeg *.bmp);;Any file (*.*)");
+
+ if (file.isEmpty()) return;
+
+ ui->txtSrcImagePath->setText(file);
+}
+
+void CameraSettingsDialog::on_cbPhysicalCamera_currentIndexChanged(int id)
+{
+ if (!currentCfg) return;
+
+ if (currentCam)
+ {
+ currentCam->stop();
+ currentCam->deInit();
+ }
+
+ currentCfg->CamDeviceName = ui->cbPhysicalCamera->itemData(id).toString().toStdString();
+
+ if (currentCam)
+ {
+ currentCam->init();
+ currentCam->start();
+ }
+}
+
+void CameraSettingsDialog::populateCamControls(int id)
+{
+ Config::CameraConfig& cfg = Config::Camera[id];
+
+ int type = cfg.InputType;
+ if (type < 0 || type >= grpInputType->buttons().count()) type = 0;
+ grpInputType->button(type)->setChecked(true);
+
+ ui->txtSrcImagePath->setText(QString::fromStdString(cfg.ImagePath));
+
+ bool deviceset = false;
+ QString device = QString::fromStdString(cfg.CamDeviceName);
+ for (int i = 0; i < ui->cbPhysicalCamera->count(); i++)
+ {
+ QString itemdev = ui->cbPhysicalCamera->itemData(i).toString();
+ if (itemdev == device)
+ {
+ ui->cbPhysicalCamera->setCurrentIndex(i);
+ deviceset = true;
+ break;
+ }
+ }
+ if (!deviceset)
+ ui->cbPhysicalCamera->setCurrentIndex(0);
+
+ onChangeInputType(type);
+
+ ui->chkFlipPicture->setChecked(cfg.XFlip);
+}
+
+void CameraSettingsDialog::on_chkFlipPicture_clicked()
+{
+ if (!currentCfg) return;
+
+ currentCfg->XFlip = ui->chkFlipPicture->isChecked();
+ if (currentCam) currentCam->setXFlip(currentCfg->XFlip);
+}
diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.h b/src/frontend/qt_sdl/CameraSettingsDialog.h
new file mode 100644
index 0000000..8572ac4
--- /dev/null
+++ b/src/frontend/qt_sdl/CameraSettingsDialog.h
@@ -0,0 +1,108 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 CAMERASETTINGSDIALOG_H
+#define CAMERASETTINGSDIALOG_H
+
+#include <QDialog>
+#include <QButtonGroup>
+
+#include "Config.h"
+#include "CameraManager.h"
+
+namespace Ui { class CameraSettingsDialog; }
+class CameraSettingsDialog;
+
+class CameraPreviewPanel : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CameraPreviewPanel(QWidget* parent);
+ ~CameraPreviewPanel();
+
+ void setCurrentCam(CameraManager* cam)
+ {
+ currentCam = cam;
+ }
+
+protected:
+ void paintEvent(QPaintEvent* event) override;
+ void timerEvent(QTimerEvent* event) override
+ {
+ repaint();
+ }
+
+private:
+ int updateTimer;
+ CameraManager* currentCam;
+};
+
+class CameraSettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit CameraSettingsDialog(QWidget* parent);
+ ~CameraSettingsDialog();
+
+ static CameraSettingsDialog* currentDlg;
+ static CameraSettingsDialog* openDlg(QWidget* parent)
+ {
+ if (currentDlg)
+ {
+ currentDlg->activateWindow();
+ return currentDlg;
+ }
+
+ currentDlg = new CameraSettingsDialog(parent);
+ currentDlg->open();
+ return currentDlg;
+ }
+ static void closeDlg()
+ {
+ currentDlg = nullptr;
+ }
+
+private slots:
+ void on_CameraSettingsDialog_accepted();
+ void on_CameraSettingsDialog_rejected();
+
+ void on_cbCameraSel_currentIndexChanged(int id);
+ void onChangeInputType(int type);
+ void on_txtSrcImagePath_textChanged();
+ void on_btnSrcImageBrowse_clicked();
+ void on_cbPhysicalCamera_currentIndexChanged(int id);
+ void on_chkFlipPicture_clicked();
+
+private:
+ Ui::CameraSettingsDialog* ui;
+
+ QButtonGroup* grpInputType;
+ CameraPreviewPanel* previewPanel;
+
+ int currentId;
+ Config::CameraConfig* currentCfg;
+ CameraManager* currentCam;
+
+ Config::CameraConfig oldCamSettings[2];
+
+ void populateCamControls(int id);
+};
+
+#endif // CAMERASETTINGSDIALOG_H
diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.ui b/src/frontend/qt_sdl/CameraSettingsDialog.ui
new file mode 100644
index 0000000..bbaf45b
--- /dev/null
+++ b/src/frontend/qt_sdl/CameraSettingsDialog.ui
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CameraSettingsDialog</class>
+ <widget class="QDialog" name="CameraSettingsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>605</width>
+ <height>341</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Camera settings - melonDS</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Configure emulated camera:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="cbCameraSel"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Picture source</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="2">
+ <widget class="QLineEdit" name="txtSrcImagePath"/>
+ </item>
+ <item row="0" column="0" colspan="4">
+ <widget class="QRadioButton" name="rbPictureNone">
+ <property name="text">
+ <string>None (blank)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QPushButton" name="btnSrcImageBrowse">
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="rbPictureCamera">
+ <property name="text">
+ <string>Physical camera:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QRadioButton" name="rbPictureImg">
+ <property name="text">
+ <string>Image file:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" colspan="2">
+ <widget class="QComboBox" name="cbPhysicalCamera"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Picture settings</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="chkFlipPicture">
+ <property name="text">
+ <string>Flip horizontally</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="2" rowspan="3">
+ <widget class="QGroupBox" name="grpPreview">
+ <property name="title">
+ <string>Preview</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CameraSettingsDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>CameraSettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/frontend/qt_sdl/CheatsDialog.cpp b/src/frontend/qt_sdl/CheatsDialog.cpp
index afa0805..4537df3 100644
--- a/src/frontend/qt_sdl/CheatsDialog.cpp
+++ b/src/frontend/qt_sdl/CheatsDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -24,6 +24,7 @@
#include "types.h"
#include "Platform.h"
#include "Config.h"
+#include "ROMManager.h"
#include "CheatsDialog.h"
#include "ui_CheatsDialog.h"
@@ -33,15 +34,13 @@ CheatsDialog* CheatsDialog::currentDlg = nullptr;
extern std::string EmuDirectory;
-namespace Frontend { extern ARCodeFile* CheatFile; }
-
CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::CheatsDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
- codeFile = Frontend::CheatFile;
+ codeFile = ROMManager::GetCheatFile();
QStandardItemModel* model = new QStandardItemModel();
ui->tvCodeList->setModel(model);
@@ -55,7 +54,7 @@ CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Cheats
{
ARCodeCat& cat = *i;
- QStandardItem* catitem = new QStandardItem(cat.Name);
+ QStandardItem* catitem = new QStandardItem(QString::fromStdString(cat.Name));
catitem->setEditable(true);
catitem->setData(QVariant::fromValue(i));
root->appendRow(catitem);
@@ -64,7 +63,7 @@ CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Cheats
{
ARCode& code = *j;
- QStandardItem* codeitem = new QStandardItem(code.Name);
+ QStandardItem* codeitem = new QStandardItem(QString::fromStdString(code.Name));
codeitem->setEditable(true);
codeitem->setCheckable(true);
codeitem->setCheckState(code.Enabled ? Qt::Checked : Qt::Unchecked);
@@ -113,13 +112,12 @@ void CheatsDialog::on_btnNewCat_clicked()
ARCodeCat cat;
cat.Codes.clear();
- memset(cat.Name, 0, 128);
- strncpy(cat.Name, "(new category)", 127);
+ cat.Name = "(new category)";
codeFile->Categories.push_back(cat);
ARCodeCatList::iterator id = codeFile->Categories.end(); id--;
- QStandardItem* catitem = new QStandardItem(cat.Name);
+ QStandardItem* catitem = new QStandardItem(QString::fromStdString(cat.Name));
catitem->setEditable(true);
catitem->setData(QVariant::fromValue(id));
root->appendRow(catitem);
@@ -160,8 +158,7 @@ void CheatsDialog::on_btnNewARCode_clicked()
ARCodeCat& cat = *it_cat;
ARCode code;
- memset(code.Name, 0, 128);
- strncpy(code.Name, "(new AR code)", 127);
+ code.Name = "(new AR code)";
code.Enabled = true;
code.CodeLen = 0;
memset(code.Code, 0, sizeof(code.Code));
@@ -169,7 +166,7 @@ void CheatsDialog::on_btnNewARCode_clicked()
cat.Codes.push_back(code);
ARCodeList::iterator id = cat.Codes.end(); id--;
- QStandardItem* codeitem = new QStandardItem(code.Name);
+ QStandardItem* codeitem = new QStandardItem(QString::fromStdString(code.Name));
codeitem->setEditable(true);
codeitem->setCheckable(true);
codeitem->setCheckState(code.Enabled ? Qt::Checked : Qt::Unchecked);
@@ -275,13 +272,12 @@ void CheatsDialog::onCheatEntryModified(QStandardItem* item)
if (item->text().isEmpty())
{
- QString oldname = QString(cat.Name);
+ QString oldname = QString::fromStdString(cat.Name);
item->setText(oldname.isEmpty() ? "(blank category name?)" : oldname);
}
else
{
- strncpy(cat.Name, item->text().toStdString().c_str(), 127);
- cat.Name[127] = '\0';
+ cat.Name = item->text().toStdString();
}
}
else if (data.canConvert<ARCodeList::iterator>())
@@ -290,13 +286,12 @@ void CheatsDialog::onCheatEntryModified(QStandardItem* item)
if (item->text().isEmpty())
{
- QString oldname = QString(code.Name);
+ QString oldname = QString::fromStdString(code.Name);
item->setText(oldname.isEmpty() ? "(blank code name?)" : oldname);
}
else
{
- strncpy(code.Name, item->text().toStdString().c_str(), 127);
- code.Name[127] = '\0';
+ code.Name = item->text().toStdString();
}
code.Enabled = (item->checkState() == Qt::Checked);
diff --git a/src/frontend/qt_sdl/CheatsDialog.h b/src/frontend/qt_sdl/CheatsDialog.h
index 926657c..c5a7e13 100644
--- a/src/frontend/qt_sdl/CheatsDialog.h
+++ b/src/frontend/qt_sdl/CheatsDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp
index 30babaf..8b2f3d4 100644
--- a/src/frontend/qt_sdl/Config.cpp
+++ b/src/frontend/qt_sdl/Config.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -36,290 +36,315 @@ int JoystickID;
int WindowWidth;
int WindowHeight;
-int WindowMaximized;
+bool WindowMaximized;
int ScreenRotation;
int ScreenGap;
int ScreenLayout;
-int ScreenSwap;
+bool ScreenSwap;
int ScreenSizing;
-int IntegerScaling;
+bool IntegerScaling;
int ScreenAspectTop;
int ScreenAspectBot;
-int ScreenFilter;
+bool ScreenFilter;
-int ScreenUseGL;
-int ScreenVSync;
+bool ScreenUseGL;
+bool ScreenVSync;
int ScreenVSyncInterval;
int _3DRenderer;
-int Threaded3D;
+bool Threaded3D;
int GL_ScaleFactor;
-int GL_BetterPolygons;
+bool GL_BetterPolygons;
-int LimitFPS;
-int AudioSync;
-int ShowOSD;
+bool LimitFPS;
+bool AudioSync;
+bool ShowOSD;
int ConsoleType;
-int DirectBoot;
+bool DirectBoot;
#ifdef JIT_ENABLED
-int JIT_Enable = false;
+bool JIT_Enable = false;
int JIT_MaxBlockSize = 32;
-int JIT_BranchOptimisations = true;
-int JIT_LiteralOptimisations = true;
-int JIT_FastMemory = true;
+bool JIT_BranchOptimisations = true;
+bool JIT_LiteralOptimisations = true;
+bool JIT_FastMemory = true;
#endif
-int ExternalBIOSEnable;
+bool ExternalBIOSEnable;
-char BIOS9Path[1024];
-char BIOS7Path[1024];
-char FirmwarePath[1024];
+std::string BIOS9Path;
+std::string BIOS7Path;
+std::string FirmwarePath;
-char DSiBIOS9Path[1024];
-char DSiBIOS7Path[1024];
-char DSiFirmwarePath[1024];
-char DSiNANDPath[1024];
+std::string DSiBIOS9Path;
+std::string DSiBIOS7Path;
+std::string DSiFirmwarePath;
+std::string DSiNANDPath;
-int DLDIEnable;
-char DLDISDPath[1024];
+bool DLDIEnable;
+std::string DLDISDPath;
int DLDISize;
-int DLDIReadOnly;
-int DLDIFolderSync;
-char DLDIFolderPath[1024];
+bool DLDIReadOnly;
+bool DLDIFolderSync;
+std::string DLDIFolderPath;
-int DSiSDEnable;
-char DSiSDPath[1024];
+bool DSiSDEnable;
+std::string DSiSDPath;
int DSiSDSize;
-int DSiSDReadOnly;
-int DSiSDFolderSync;
-char DSiSDFolderPath[1024];
+bool DSiSDReadOnly;
+bool DSiSDFolderSync;
+std::string DSiSDFolderPath;
-int FirmwareOverrideSettings;
-char FirmwareUsername[64];
+bool FirmwareOverrideSettings;
+std::string FirmwareUsername;
int FirmwareLanguage;
int FirmwareBirthdayMonth;
int FirmwareBirthdayDay;
int FirmwareFavouriteColour;
-char FirmwareMessage[1024];
-char FirmwareMAC[18];
-int RandomizeMAC;
+std::string FirmwareMessage;
+std::string FirmwareMAC;
-int SocketBindAnyAddr;
-char LANDevice[128];
-int DirectLAN;
+int MPAudioMode;
+int MPRecvTimeout;
-int SavestateRelocSRAM;
+std::string LANDevice;
+bool DirectLAN;
+
+bool SavestateRelocSRAM;
int AudioInterp;
int AudioBitrate;
int AudioVolume;
int MicInputType;
-char MicWavPath[1024];
+std::string MicWavPath;
+
+std::string LastROMFolder;
-char LastROMFolder[1024];
+std::string RecentROMList[10];
-char RecentROMList[10][1024];
+std::string SaveFilePath;
+std::string SavestatePath;
+std::string CheatFilePath;
-int EnableCheats;
+bool EnableCheats;
-int MouseHide;
+bool MouseHide;
int MouseHideSeconds;
-int PauseLostFocus;
+bool PauseLostFocus;
+
+bool DSBatteryLevelOkay;
+int DSiBatteryLevel;
+bool DSiBatteryCharging;
+
+CameraConfig Camera[2];
const char* kConfigFile = "melonDS.ini";
+const char* kUniqueConfigFile = "melonDS.%d.ini";
ConfigEntry ConfigFile[] =
{
- {"Key_A", 0, &KeyMapping[0], -1, NULL, 0},
- {"Key_B", 0, &KeyMapping[1], -1, NULL, 0},
- {"Key_Select", 0, &KeyMapping[2], -1, NULL, 0},
- {"Key_Start", 0, &KeyMapping[3], -1, NULL, 0},
- {"Key_Right", 0, &KeyMapping[4], -1, NULL, 0},
- {"Key_Left", 0, &KeyMapping[5], -1, NULL, 0},
- {"Key_Up", 0, &KeyMapping[6], -1, NULL, 0},
- {"Key_Down", 0, &KeyMapping[7], -1, NULL, 0},
- {"Key_R", 0, &KeyMapping[8], -1, NULL, 0},
- {"Key_L", 0, &KeyMapping[9], -1, NULL, 0},
- {"Key_X", 0, &KeyMapping[10], -1, NULL, 0},
- {"Key_Y", 0, &KeyMapping[11], -1, NULL, 0},
-
- {"Joy_A", 0, &JoyMapping[0], -1, NULL, 0},
- {"Joy_B", 0, &JoyMapping[1], -1, NULL, 0},
- {"Joy_Select", 0, &JoyMapping[2], -1, NULL, 0},
- {"Joy_Start", 0, &JoyMapping[3], -1, NULL, 0},
- {"Joy_Right", 0, &JoyMapping[4], -1, NULL, 0},
- {"Joy_Left", 0, &JoyMapping[5], -1, NULL, 0},
- {"Joy_Up", 0, &JoyMapping[6], -1, NULL, 0},
- {"Joy_Down", 0, &JoyMapping[7], -1, NULL, 0},
- {"Joy_R", 0, &JoyMapping[8], -1, NULL, 0},
- {"Joy_L", 0, &JoyMapping[9], -1, NULL, 0},
- {"Joy_X", 0, &JoyMapping[10], -1, NULL, 0},
- {"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0},
-
- {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, NULL, 0},
- {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, NULL, 0},
- {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
- {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
- {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1, NULL, 0},
- {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
- {"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1, NULL, 0},
- {"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1, NULL, 0},
- {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
- {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
- {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, NULL, 0},
-
- {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
- {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
- {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, NULL, 0},
- {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0},
- {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0},
- {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0},
- {"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1, NULL, 0},
- {"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1, NULL, 0},
- {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
- {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
- {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, NULL, 0},
-
- {"JoystickID", 0, &JoystickID, 0, NULL, 0},
-
- {"WindowWidth", 0, &WindowWidth, 256, NULL, 0},
- {"WindowHeight", 0, &WindowHeight, 384, NULL, 0},
- {"WindowMax", 0, &WindowMaximized, 0, NULL, 0},
-
- {"ScreenRotation", 0, &ScreenRotation, 0, NULL, 0},
- {"ScreenGap", 0, &ScreenGap, 0, NULL, 0},
- {"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0},
- {"ScreenSwap", 0, &ScreenSwap, 0, NULL, 0},
- {"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0},
- {"IntegerScaling", 0, &IntegerScaling, 0, NULL, 0},
- {"ScreenAspectTop",0, &ScreenAspectTop,0, NULL, 0},
- {"ScreenAspectBot",0, &ScreenAspectBot,0, NULL, 0},
- {"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0},
-
- {"ScreenUseGL", 0, &ScreenUseGL, 0, NULL, 0},
- {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
- {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, NULL, 0},
-
- {"3DRenderer", 0, &_3DRenderer, 0, NULL, 0},
- {"Threaded3D", 0, &Threaded3D, 1, NULL, 0},
-
- {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0},
- {"GL_BetterPolygons", 0, &GL_BetterPolygons, 0, NULL, 0},
-
- {"LimitFPS", 0, &LimitFPS, 1, NULL, 0},
- {"AudioSync", 0, &AudioSync, 0, NULL, 0},
- {"ShowOSD", 0, &ShowOSD, 1, NULL, 0},
-
- {"ConsoleType", 0, &ConsoleType, 0, NULL, 0},
- {"DirectBoot", 0, &DirectBoot, 1, NULL, 0},
+ {"Key_A", 0, &KeyMapping[0], -1, true},
+ {"Key_B", 0, &KeyMapping[1], -1, true},
+ {"Key_Select", 0, &KeyMapping[2], -1, true},
+ {"Key_Start", 0, &KeyMapping[3], -1, true},
+ {"Key_Right", 0, &KeyMapping[4], -1, true},
+ {"Key_Left", 0, &KeyMapping[5], -1, true},
+ {"Key_Up", 0, &KeyMapping[6], -1, true},
+ {"Key_Down", 0, &KeyMapping[7], -1, true},
+ {"Key_R", 0, &KeyMapping[8], -1, true},
+ {"Key_L", 0, &KeyMapping[9], -1, true},
+ {"Key_X", 0, &KeyMapping[10], -1, true},
+ {"Key_Y", 0, &KeyMapping[11], -1, true},
+
+ {"Joy_A", 0, &JoyMapping[0], -1, true},
+ {"Joy_B", 0, &JoyMapping[1], -1, true},
+ {"Joy_Select", 0, &JoyMapping[2], -1, true},
+ {"Joy_Start", 0, &JoyMapping[3], -1, true},
+ {"Joy_Right", 0, &JoyMapping[4], -1, true},
+ {"Joy_Left", 0, &JoyMapping[5], -1, true},
+ {"Joy_Up", 0, &JoyMapping[6], -1, true},
+ {"Joy_Down", 0, &JoyMapping[7], -1, true},
+ {"Joy_R", 0, &JoyMapping[8], -1, true},
+ {"Joy_L", 0, &JoyMapping[9], -1, true},
+ {"Joy_X", 0, &JoyMapping[10], -1, true},
+ {"Joy_Y", 0, &JoyMapping[11], -1, true},
+
+ {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, true},
+ {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, true},
+ {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, true},
+ {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, true},
+ {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1, true},
+ {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, true},
+ {"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1, true},
+ {"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1, true},
+ {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, true},
+ {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, true},
+ {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, true},
+
+ {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, true},
+ {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, true},
+ {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, true},
+ {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, true},
+ {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, true},
+ {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, true},
+ {"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1, true},
+ {"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1, true},
+ {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, true},
+ {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, true},
+ {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, true},
+
+ {"JoystickID", 0, &JoystickID, 0, true},
+
+ {"WindowWidth", 0, &WindowWidth, 256, true},
+ {"WindowHeight", 0, &WindowHeight, 384, true},
+ {"WindowMax", 1, &WindowMaximized, false, true},
+
+ {"ScreenRotation", 0, &ScreenRotation, 0, true},
+ {"ScreenGap", 0, &ScreenGap, 0, true},
+ {"ScreenLayout", 0, &ScreenLayout, 0, true},
+ {"ScreenSwap", 1, &ScreenSwap, false, true},
+ {"ScreenSizing", 0, &ScreenSizing, 0, true},
+ {"IntegerScaling", 1, &IntegerScaling, false, true},
+ {"ScreenAspectTop",0, &ScreenAspectTop,0, true},
+ {"ScreenAspectBot",0, &ScreenAspectBot,0, true},
+ {"ScreenFilter", 1, &ScreenFilter, true, true},
+
+ {"ScreenUseGL", 1, &ScreenUseGL, false, false},
+ {"ScreenVSync", 1, &ScreenVSync, false, false},
+ {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, false},
+
+ {"3DRenderer", 0, &_3DRenderer, 0, false},
+ {"Threaded3D", 1, &Threaded3D, true, false},
+
+ {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, false},
+ {"GL_BetterPolygons", 1, &GL_BetterPolygons, false, false},
+
+ {"LimitFPS", 1, &LimitFPS, true, false},
+ {"AudioSync", 1, &AudioSync, false},
+ {"ShowOSD", 1, &ShowOSD, true, false},
+
+ {"ConsoleType", 0, &ConsoleType, 0, false},
+ {"DirectBoot", 1, &DirectBoot, true, false},
#ifdef JIT_ENABLED
- {"JIT_Enable", 0, &JIT_Enable, 0, NULL, 0},
- {"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, NULL, 0},
- {"JIT_BranchOptimisations", 0, &JIT_BranchOptimisations, 1, NULL, 0},
- {"JIT_LiteralOptimisations", 0, &JIT_LiteralOptimisations, 1, NULL, 0},
+ {"JIT_Enable", 1, &JIT_Enable, false, false},
+ {"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, false},
+ {"JIT_BranchOptimisations", 1, &JIT_BranchOptimisations, true, false},
+ {"JIT_LiteralOptimisations", 1, &JIT_LiteralOptimisations, true, false},
#ifdef __APPLE__
- {"JIT_FastMemory", 0, &JIT_FastMemory, 0, NULL, 0},
+ {"JIT_FastMemory", 1, &JIT_FastMemory, false, false},
#else
- {"JIT_FastMemory", 0, &JIT_FastMemory, 1, NULL, 0},
+ {"JIT_FastMemory", 1, &JIT_FastMemory, true, false},
#endif
#endif
- {"ExternalBIOSEnable", 0, &ExternalBIOSEnable, 0, NULL, 0},
-
- {"BIOS9Path", 1, BIOS9Path, 0, "", 1023},
- {"BIOS7Path", 1, BIOS7Path, 0, "", 1023},
- {"FirmwarePath", 1, FirmwarePath, 0, "", 1023},
-
- {"DSiBIOS9Path", 1, DSiBIOS9Path, 0, "", 1023},
- {"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023},
- {"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023},
- {"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023},
-
- {"DLDIEnable", 0, &DLDIEnable, 0, NULL, 0},
- {"DLDISDPath", 1, DLDISDPath, 0, "dldi.bin", 1023},
- {"DLDISize", 0, &DLDISize, 0, NULL, 0},
- {"DLDIReadOnly", 0, &DLDIReadOnly, 0, NULL, 0},
- {"DLDIFolderSync", 0, &DLDIFolderSync, 0, NULL, 0},
- {"DLDIFolderPath", 1, DLDIFolderPath, 0, "", 1023},
-
- {"DSiSDEnable", 0, &DSiSDEnable, 0, NULL, 0},
- {"DSiSDPath", 1, DSiSDPath, 0, "dsisd.bin", 1023},
- {"DSiSDSize", 0, &DSiSDSize, 0, NULL, 0},
- {"DSiSDReadOnly", 0, &DSiSDReadOnly, 0, NULL, 0},
- {"DSiSDFolderSync", 0, &DSiSDFolderSync, 0, NULL, 0},
- {"DSiSDFolderPath", 1, DSiSDFolderPath, 0, "", 1023},
-
- {"FirmwareOverrideSettings", 0, &FirmwareOverrideSettings, false, NULL, 0},
- {"FirmwareUsername", 1, FirmwareUsername, 0, "melonDS", 63},
- {"FirmwareLanguage", 0, &FirmwareLanguage, 1, NULL, 0},
- {"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 0, NULL, 0},
- {"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 0, NULL, 0},
- {"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0, NULL, 0},
- {"FirmwareMessage", 1, FirmwareMessage, 0, "", 1023},
- {"FirmwareMAC", 1, FirmwareMAC, 0, "", 17},
- {"RandomizeMAC", 0, &RandomizeMAC, 0, NULL, 0},
-
- {"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},
- {"LANDevice", 1, LANDevice, 0, "", 127},
- {"DirectLAN", 0, &DirectLAN, 0, NULL, 0},
-
- {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 0, NULL, 0},
-
- {"AudioInterp", 0, &AudioInterp, 0, NULL, 0},
- {"AudioBitrate", 0, &AudioBitrate, 0, NULL, 0},
- {"AudioVolume", 0, &AudioVolume, 256, NULL, 0},
- {"MicInputType", 0, &MicInputType, 1, NULL, 0},
- {"MicWavPath", 1, MicWavPath, 0, "", 1023},
-
- {"LastROMFolder", 1, LastROMFolder, 0, "", 1023},
-
- {"RecentROM_0", 1, RecentROMList[0], 0, "", 1023},
- {"RecentROM_1", 1, RecentROMList[1], 0, "", 1023},
- {"RecentROM_2", 1, RecentROMList[2], 0, "", 1023},
- {"RecentROM_3", 1, RecentROMList[3], 0, "", 1023},
- {"RecentROM_4", 1, RecentROMList[4], 0, "", 1023},
- {"RecentROM_5", 1, RecentROMList[5], 0, "", 1023},
- {"RecentROM_6", 1, RecentROMList[6], 0, "", 1023},
- {"RecentROM_7", 1, RecentROMList[7], 0, "", 1023},
- {"RecentROM_8", 1, RecentROMList[8], 0, "", 1023},
- {"RecentROM_9", 1, RecentROMList[9], 0, "", 1023},
-
- {"EnableCheats", 0, &EnableCheats, 0, NULL, 0},
-
- {"MouseHide", 0, &MouseHide, 0, NULL, 0},
- {"MouseHideSeconds", 0, &MouseHideSeconds, 5, NULL, 0},
- {"PauseLostFocus", 0, &PauseLostFocus, 0, NULL, 0},
-
- {"", -1, NULL, 0, NULL, 0}
+ {"ExternalBIOSEnable", 1, &ExternalBIOSEnable, false, false},
+
+ {"BIOS9Path", 2, &BIOS9Path, (std::string)"", false},
+ {"BIOS7Path", 2, &BIOS7Path, (std::string)"", false},
+ {"FirmwarePath", 2, &FirmwarePath, (std::string)"", false},
+
+ {"DSiBIOS9Path", 2, &DSiBIOS9Path, (std::string)"", false},
+ {"DSiBIOS7Path", 2, &DSiBIOS7Path, (std::string)"", false},
+ {"DSiFirmwarePath", 2, &DSiFirmwarePath, (std::string)"", false},
+ {"DSiNANDPath", 2, &DSiNANDPath, (std::string)"", false},
+
+ {"DLDIEnable", 1, &DLDIEnable, false, false},
+ {"DLDISDPath", 2, &DLDISDPath, (std::string)"dldi.bin", false},
+ {"DLDISize", 0, &DLDISize, 0, false},
+ {"DLDIReadOnly", 1, &DLDIReadOnly, false, false},
+ {"DLDIFolderSync", 1, &DLDIFolderSync, false, false},
+ {"DLDIFolderPath", 2, &DLDIFolderPath, (std::string)"", false},
+
+ {"DSiSDEnable", 1, &DSiSDEnable, false, false},
+ {"DSiSDPath", 2, &DSiSDPath, (std::string)"dsisd.bin", false},
+ {"DSiSDSize", 0, &DSiSDSize, 0, false},
+ {"DSiSDReadOnly", 1, &DSiSDReadOnly, false, false},
+ {"DSiSDFolderSync", 1, &DSiSDFolderSync, false, false},
+ {"DSiSDFolderPath", 2, &DSiSDFolderPath, (std::string)"", false},
+
+ {"FirmwareOverrideSettings", 1, &FirmwareOverrideSettings, false, true},
+ {"FirmwareUsername", 2, &FirmwareUsername, (std::string)"melonDS", true},
+ {"FirmwareLanguage", 0, &FirmwareLanguage, 1, true},
+ {"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 1, true},
+ {"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 1, true},
+ {"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0, true},
+ {"FirmwareMessage", 2, &FirmwareMessage, (std::string)"", true},
+ {"FirmwareMAC", 2, &FirmwareMAC, (std::string)"", true},
+
+ {"MPAudioMode", 0, &MPAudioMode, 1, false},
+ {"MPRecvTimeout", 0, &MPRecvTimeout, 25, false},
+
+ {"LANDevice", 2, &LANDevice, (std::string)"", false},
+ {"DirectLAN", 1, &DirectLAN, false, false},
+
+ {"SavStaRelocSRAM", 1, &SavestateRelocSRAM, false, false},
+
+ {"AudioInterp", 0, &AudioInterp, 0, false},
+ {"AudioBitrate", 0, &AudioBitrate, 0, false},
+ {"AudioVolume", 0, &AudioVolume, 256, true},
+ {"MicInputType", 0, &MicInputType, 1, false},
+ {"MicWavPath", 2, &MicWavPath, (std::string)"", false},
+
+ {"LastROMFolder", 2, &LastROMFolder, (std::string)"", true},
+
+ {"RecentROM_0", 2, &RecentROMList[0], (std::string)"", true},
+ {"RecentROM_1", 2, &RecentROMList[1], (std::string)"", true},
+ {"RecentROM_2", 2, &RecentROMList[2], (std::string)"", true},
+ {"RecentROM_3", 2, &RecentROMList[3], (std::string)"", true},
+ {"RecentROM_4", 2, &RecentROMList[4], (std::string)"", true},
+ {"RecentROM_5", 2, &RecentROMList[5], (std::string)"", true},
+ {"RecentROM_6", 2, &RecentROMList[6], (std::string)"", true},
+ {"RecentROM_7", 2, &RecentROMList[7], (std::string)"", true},
+ {"RecentROM_8", 2, &RecentROMList[8], (std::string)"", true},
+ {"RecentROM_9", 2, &RecentROMList[9], (std::string)"", true},
+
+ {"SaveFilePath", 2, &SaveFilePath, (std::string)"", true},
+ {"SavestatePath", 2, &SavestatePath, (std::string)"", true},
+ {"CheatFilePath", 2, &CheatFilePath, (std::string)"", true},
+
+ {"EnableCheats", 1, &EnableCheats, false, true},
+
+ {"MouseHide", 1, &MouseHide, false, false},
+ {"MouseHideSeconds", 0, &MouseHideSeconds, 5, false},
+ {"PauseLostFocus", 1, &PauseLostFocus, false, false},
+
+ {"DSBatteryLevelOkay", 1, &DSBatteryLevelOkay, true, true},
+ {"DSiBatteryLevel", 0, &DSiBatteryLevel, 0xF, true},
+ {"DSiBatteryCharging", 1, &DSiBatteryCharging, true, true},
+
+ // TODO!!
+ // we need a more elegant way to deal with this
+ {"Camera0_InputType", 0, &Camera[0].InputType, 0, false},
+ {"Camera0_ImagePath", 2, &Camera[0].ImagePath, (std::string)"", false},
+ {"Camera0_CamDeviceName", 2, &Camera[0].CamDeviceName, (std::string)"", false},
+ {"Camera0_XFlip", 1, &Camera[0].XFlip, false, false},
+ {"Camera1_InputType", 0, &Camera[1].InputType, 0, false},
+ {"Camera1_ImagePath", 2, &Camera[1].ImagePath, (std::string)"", false},
+ {"Camera1_CamDeviceName", 2, &Camera[1].CamDeviceName, (std::string)"", false},
+ {"Camera1_XFlip", 1, &Camera[1].XFlip, false, false},
+
+ {"", -1, nullptr, 0, false}
};
-void Load()
+void LoadFile(int inst)
{
- ConfigEntry* entry = &ConfigFile[0];
- for (;;)
+ FILE* f;
+ if (inst > 0)
{
- if (!entry->Value) break;
-
- if (entry->Type == 0)
- *(int*)entry->Value = entry->DefaultInt;
- else
- {
- strncpy((char*)entry->Value, entry->DefaultStr, entry->StrLength);
- ((char*)entry->Value)[entry->StrLength] = '\0';
- }
-
- entry++;
+ char name[100] = {0};
+ snprintf(name, 99, kUniqueConfigFile, inst+1);
+ f = Platform::OpenLocalFile(name, "r");
}
+ else
+ f = Platform::OpenLocalFile(kConfigFile, "r");
- FILE* f = Platform::OpenLocalFile(kConfigFile, "r");
if (!f) return;
char linebuf[1024];
@@ -334,44 +359,75 @@ void Load()
entryname[31] = '\0';
if (ret < 2) continue;
- ConfigEntry* entry = &ConfigFile[0];
- for (;;)
+ for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
{
- if (!entry->Value) break;
-
if (!strncmp(entry->Name, entryname, 32))
{
- if (entry->Type == 0)
- *(int*)entry->Value = strtol(entryval, NULL, 10);
- else
- strncpy((char*)entry->Value, entryval, entry->StrLength);
+ if ((inst > 0) && (!entry->InstanceUnique))
+ break;
+
+ switch (entry->Type)
+ {
+ case 0: *(int*)entry->Value = strtol(entryval, NULL, 10); break;
+ case 1: *(bool*)entry->Value = strtol(entryval, NULL, 10) ? true:false; break;
+ case 2: *(std::string*)entry->Value = entryval; break;
+ }
break;
}
-
- entry++;
}
}
fclose(f);
}
+void Load()
+{
+
+ for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
+ {
+ switch (entry->Type)
+ {
+ case 0: *(int*)entry->Value = std::get<int>(entry->Default); break;
+ case 1: *(bool*)entry->Value = std::get<bool>(entry->Default); break;
+ case 2: *(std::string*)entry->Value = std::get<std::string>(entry->Default); break;
+ }
+ }
+
+ LoadFile(0);
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ LoadFile(inst);
+}
+
void Save()
{
- FILE* f = Platform::OpenLocalFile(kConfigFile, "w");
- if (!f) return;
+ int inst = Platform::InstanceID();
- ConfigEntry* entry = &ConfigFile[0];
- for (;;)
+ FILE* f;
+ if (inst > 0)
{
- if (!entry->Value) break;
+ char name[100] = {0};
+ snprintf(name, 99, kUniqueConfigFile, inst+1);
+ f = Platform::OpenLocalFile(name, "w");
+ }
+ else
+ f = Platform::OpenLocalFile(kConfigFile, "w");
- if (entry->Type == 0)
- fprintf(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value);
- else
- fprintf(f, "%s=%s\r\n", entry->Name, (char*)entry->Value);
+ if (!f) return;
- entry++;
+ for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
+ {
+ if ((inst > 0) && (!entry->InstanceUnique))
+ continue;
+
+ switch (entry->Type)
+ {
+ case 0: fprintf(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value); break;
+ case 1: fprintf(f, "%s=%d\r\n", entry->Name, *(bool*)entry->Value ? 1:0); break;
+ case 2: fprintf(f, "%s=%s\r\n", entry->Name, (*(std::string*)entry->Value).c_str()); break;
+ }
}
fclose(f);
diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h
index ad9b4c6..6ccae5f 100644
--- a/src/frontend/qt_sdl/Config.h
+++ b/src/frontend/qt_sdl/Config.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,6 +19,9 @@
#ifndef PLATFORMCONFIG_H
#define PLATFORMCONFIG_H
+#include <variant>
+#include <string>
+
enum
{
HK_Lid = 0,
@@ -35,17 +38,35 @@ enum
HK_MAX
};
+enum
+{
+ screenSizing_Even,
+ screenSizing_EmphTop,
+ screenSizing_EmphBot,
+ screenSizing_Auto,
+ screenSizing_TopOnly,
+ screenSizing_BotOnly,
+ screenSizing_MAX,
+};
+
namespace Config
{
struct ConfigEntry
{
char Name[32];
- int Type;
- void* Value;
- int DefaultInt;
- const char* DefaultStr;
- int StrLength; // should be set to actual array length minus one
+ int Type; // 0=int 1=bool 2=string
+ void* Value; // pointer to the value variable
+ std::variant<int, bool, std::string> Default;
+ bool InstanceUnique; // whether the setting can exist individually for each instance in multiplayer
+};
+
+struct CameraConfig
+{
+ int InputType; // 0=blank 1=image 2=camera
+ std::string ImagePath;
+ std::string CamDeviceName;
+ bool XFlip;
};
@@ -59,99 +80,110 @@ extern int JoystickID;
extern int WindowWidth;
extern int WindowHeight;
-extern int WindowMaximized;
+extern bool WindowMaximized;
extern int ScreenRotation;
extern int ScreenGap;
extern int ScreenLayout;
-extern int ScreenSwap;
+extern bool ScreenSwap;
extern int ScreenSizing;
extern int ScreenAspectTop;
extern int ScreenAspectBot;
-extern int IntegerScaling;
-extern int ScreenFilter;
+extern bool IntegerScaling;
+extern bool ScreenFilter;
-extern int ScreenUseGL;
-extern int ScreenVSync;
+extern bool ScreenUseGL;
+extern bool ScreenVSync;
extern int ScreenVSyncInterval;
extern int _3DRenderer;
-extern int Threaded3D;
+extern bool Threaded3D;
extern int GL_ScaleFactor;
-extern int GL_BetterPolygons;
+extern bool GL_BetterPolygons;
-extern int LimitFPS;
-extern int AudioSync;
-extern int ShowOSD;
+extern bool LimitFPS;
+extern bool AudioSync;
+extern bool ShowOSD;
extern int ConsoleType;
-extern int DirectBoot;
+extern bool DirectBoot;
#ifdef JIT_ENABLED
-extern int JIT_Enable;
+extern bool JIT_Enable;
extern int JIT_MaxBlockSize;
-extern int JIT_BranchOptimisations;
-extern int JIT_LiteralOptimisations;
-extern int JIT_FastMemory;
+extern bool JIT_BranchOptimisations;
+extern bool JIT_LiteralOptimisations;
+extern bool JIT_FastMemory;
#endif
-extern int ExternalBIOSEnable;
+extern bool ExternalBIOSEnable;
-extern char BIOS9Path[1024];
-extern char BIOS7Path[1024];
-extern char FirmwarePath[1024];
+extern std::string BIOS9Path;
+extern std::string BIOS7Path;
+extern std::string FirmwarePath;
-extern char DSiBIOS9Path[1024];
-extern char DSiBIOS7Path[1024];
-extern char DSiFirmwarePath[1024];
-extern char DSiNANDPath[1024];
+extern std::string DSiBIOS9Path;
+extern std::string DSiBIOS7Path;
+extern std::string DSiFirmwarePath;
+extern std::string DSiNANDPath;
-extern int DLDIEnable;
-extern char DLDISDPath[1024];
+extern bool DLDIEnable;
+extern std::string DLDISDPath;
extern int DLDISize;
-extern int DLDIReadOnly;
-extern int DLDIFolderSync;
-extern char DLDIFolderPath[1024];
+extern bool DLDIReadOnly;
+extern bool DLDIFolderSync;
+extern std::string DLDIFolderPath;
-extern int DSiSDEnable;
-extern char DSiSDPath[1024];
+extern bool DSiSDEnable;
+extern std::string DSiSDPath;
extern int DSiSDSize;
-extern int DSiSDReadOnly;
-extern int DSiSDFolderSync;
-extern char DSiSDFolderPath[1024];
+extern bool DSiSDReadOnly;
+extern bool DSiSDFolderSync;
+extern std::string DSiSDFolderPath;
-extern int FirmwareOverrideSettings;
-extern char FirmwareUsername[64];
+extern bool FirmwareOverrideSettings;
+extern std::string FirmwareUsername;
extern int FirmwareLanguage;
extern int FirmwareBirthdayMonth;
extern int FirmwareBirthdayDay;
extern int FirmwareFavouriteColour;
-extern char FirmwareMessage[1024];
-extern char FirmwareMAC[18];
-extern int RandomizeMAC;
+extern std::string FirmwareMessage;
+extern std::string FirmwareMAC;
+
+extern int MPAudioMode;
+extern int MPRecvTimeout;
-extern int SocketBindAnyAddr;
-extern char LANDevice[128];
-extern int DirectLAN;
+extern std::string LANDevice;
+extern bool DirectLAN;
-extern int SavestateRelocSRAM;
+extern bool SavestateRelocSRAM;
extern int AudioInterp;
extern int AudioBitrate;
extern int AudioVolume;
extern int MicInputType;
-extern char MicWavPath[1024];
+extern std::string MicWavPath;
-extern char LastROMFolder[1024];
+extern std::string LastROMFolder;
-extern char RecentROMList[10][1024];
+extern std::string RecentROMList[10];
-extern int EnableCheats;
+extern std::string SaveFilePath;
+extern std::string SavestatePath;
+extern std::string CheatFilePath;
-extern int MouseHide;
+extern bool EnableCheats;
+
+extern bool MouseHide;
extern int MouseHideSeconds;
-extern int PauseLostFocus;
+extern bool PauseLostFocus;
+
+extern bool DSBatteryLevelOkay;
+extern int DSiBatteryLevel;
+extern bool DSiBatteryCharging;
+
+extern CameraConfig Camera[2];
void Load();
diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
index fd2ca85..bd40568 100644
--- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -42,27 +42,27 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
- ui->chkExternalBIOS->setChecked(Config::ExternalBIOSEnable != 0);
- ui->txtBIOS9Path->setText(Config::BIOS9Path);
- ui->txtBIOS7Path->setText(Config::BIOS7Path);
- ui->txtFirmwarePath->setText(Config::FirmwarePath);
+ ui->chkExternalBIOS->setChecked(Config::ExternalBIOSEnable);
+ ui->txtBIOS9Path->setText(QString::fromStdString(Config::BIOS9Path));
+ ui->txtBIOS7Path->setText(QString::fromStdString(Config::BIOS7Path));
+ ui->txtFirmwarePath->setText(QString::fromStdString(Config::FirmwarePath));
- ui->txtDSiBIOS9Path->setText(Config::DSiBIOS9Path);
- ui->txtDSiBIOS7Path->setText(Config::DSiBIOS7Path);
- ui->txtDSiFirmwarePath->setText(Config::DSiFirmwarePath);
- ui->txtDSiNANDPath->setText(Config::DSiNANDPath);
+ ui->txtDSiBIOS9Path->setText(QString::fromStdString(Config::DSiBIOS9Path));
+ ui->txtDSiBIOS7Path->setText(QString::fromStdString(Config::DSiBIOS7Path));
+ ui->txtDSiFirmwarePath->setText(QString::fromStdString(Config::DSiFirmwarePath));
+ ui->txtDSiNANDPath->setText(QString::fromStdString(Config::DSiNANDPath));
ui->cbxConsoleType->addItem("DS");
ui->cbxConsoleType->addItem("DSi (experimental)");
ui->cbxConsoleType->setCurrentIndex(Config::ConsoleType);
- ui->chkDirectBoot->setChecked(Config::DirectBoot != 0);
+ ui->chkDirectBoot->setChecked(Config::DirectBoot);
#ifdef JIT_ENABLED
- ui->chkEnableJIT->setChecked(Config::JIT_Enable != 0);
- ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations != 0);
- ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations != 0);
- ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory != 0);
+ ui->chkEnableJIT->setChecked(Config::JIT_Enable);
+ ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations);
+ ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations);
+ ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory);
#ifdef __APPLE__
ui->chkJITFastMemory->setDisabled(true);
#endif
@@ -101,20 +101,20 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->cbxDSiSDSize->addItem(sizelbl);
}
- ui->cbDLDIEnable->setChecked(Config::DLDIEnable != 0);
- ui->txtDLDISDPath->setText(Config::DLDISDPath);
+ ui->cbDLDIEnable->setChecked(Config::DLDIEnable);
+ ui->txtDLDISDPath->setText(QString::fromStdString(Config::DLDISDPath));
ui->cbxDLDISize->setCurrentIndex(Config::DLDISize);
- ui->cbDLDIReadOnly->setChecked(Config::DLDIReadOnly != 0);
- ui->cbDLDIFolder->setChecked(Config::DLDIFolderSync != 0);
- ui->txtDLDIFolder->setText(Config::DLDIFolderPath);
+ ui->cbDLDIReadOnly->setChecked(Config::DLDIReadOnly);
+ ui->cbDLDIFolder->setChecked(Config::DLDIFolderSync);
+ ui->txtDLDIFolder->setText(QString::fromStdString(Config::DLDIFolderPath));
on_cbDLDIEnable_toggled();
- ui->cbDSiSDEnable->setChecked(Config::DSiSDEnable != 0);
- ui->txtDSiSDPath->setText(Config::DSiSDPath);
+ ui->cbDSiSDEnable->setChecked(Config::DSiSDEnable);
+ ui->txtDSiSDPath->setText(QString::fromStdString(Config::DSiSDPath));
ui->cbxDSiSDSize->setCurrentIndex(Config::DSiSDSize);
- ui->cbDSiSDReadOnly->setChecked(Config::DSiSDReadOnly != 0);
- ui->cbDSiSDFolder->setChecked(Config::DSiSDFolderSync != 0);
- ui->txtDSiSDFolder->setText(Config::DSiSDFolderPath);
+ ui->cbDSiSDReadOnly->setChecked(Config::DSiSDReadOnly);
+ ui->cbDSiSDFolder->setChecked(Config::DSiSDFolderSync);
+ ui->txtDSiSDFolder->setText(QString::fromStdString(Config::DSiSDFolderPath));
on_cbDSiSDEnable_toggled();
}
@@ -140,8 +140,7 @@ void EmuSettingsDialog::verifyFirmware()
// looked at has 0x180 bytes from the header repeated at 0x3FC80, but
// bytes 0x0C-0x14 are different.
- char filename[1024];
- strncpy(filename, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); filename[1023] = '\0';
+ std::string filename = ui->txtFirmwarePath->text().toStdString();
FILE* f = Platform::OpenLocalFile(filename, "rb");
if (!f) return;
u8 chk1[0x180], chk2[0x180];
@@ -175,24 +174,24 @@ void EmuSettingsDialog::done(int r)
verifyFirmware();
int consoleType = ui->cbxConsoleType->currentIndex();
- int directBoot = ui->chkDirectBoot->isChecked() ? 1:0;
+ bool directBoot = ui->chkDirectBoot->isChecked();
- int jitEnable = ui->chkEnableJIT->isChecked() ? 1:0;
+ bool jitEnable = ui->chkEnableJIT->isChecked();
int jitMaxBlockSize = ui->spnJITMaximumBlockSize->value();
- int jitBranchOptimisations = ui->chkJITBranchOptimisations->isChecked() ? 1:0;
- int jitLiteralOptimisations = ui->chkJITLiteralOptimisations->isChecked() ? 1:0;
- int jitFastMemory = ui->chkJITFastMemory->isChecked() ? 1:0;
+ bool jitBranchOptimisations = ui->chkJITBranchOptimisations->isChecked();
+ bool jitLiteralOptimisations = ui->chkJITLiteralOptimisations->isChecked();
+ bool jitFastMemory = ui->chkJITFastMemory->isChecked();
- int externalBiosEnable = ui->chkExternalBIOS->isChecked() ? 1:0;
+ bool externalBiosEnable = ui->chkExternalBIOS->isChecked();
std::string bios9Path = ui->txtBIOS9Path->text().toStdString();
std::string bios7Path = ui->txtBIOS7Path->text().toStdString();
std::string firmwarePath = ui->txtFirmwarePath->text().toStdString();
- int dldiEnable = ui->cbDLDIEnable->isChecked() ? 1:0;
+ bool dldiEnable = ui->cbDLDIEnable->isChecked();
std::string dldiSDPath = ui->txtDLDISDPath->text().toStdString();
int dldiSize = ui->cbxDLDISize->currentIndex();
- int dldiReadOnly = ui->cbDLDIReadOnly->isChecked() ? 1:0;
- int dldiFolderSync = ui->cbDLDIFolder->isChecked() ? 1:0;
+ bool dldiReadOnly = ui->cbDLDIReadOnly->isChecked();
+ bool dldiFolderSync = ui->cbDLDIFolder->isChecked();
std::string dldiFolderPath = ui->txtDLDIFolder->text().toStdString();
std::string dsiBios9Path = ui->txtDSiBIOS9Path->text().toStdString();
@@ -200,11 +199,11 @@ void EmuSettingsDialog::done(int r)
std::string dsiFirmwarePath = ui->txtDSiFirmwarePath->text().toStdString();
std::string dsiNANDPath = ui->txtDSiNANDPath->text().toStdString();
- int dsiSDEnable = ui->cbDSiSDEnable->isChecked() ? 1:0;
+ bool dsiSDEnable = ui->cbDSiSDEnable->isChecked();
std::string dsiSDPath = ui->txtDSiSDPath->text().toStdString();
int dsiSDSize = ui->cbxDSiSDSize->currentIndex();
- int dsiSDReadOnly = ui->cbDSiSDReadOnly->isChecked() ? 1:0;
- int dsiSDFolderSync = ui->cbDSiSDFolder->isChecked() ? 1:0;
+ bool dsiSDReadOnly = ui->cbDSiSDReadOnly->isChecked();
+ bool dsiSDFolderSync = ui->cbDSiSDFolder->isChecked();
std::string dsiSDFolderPath = ui->txtDSiSDFolder->text().toStdString();
if (consoleType != Config::ConsoleType
@@ -217,25 +216,25 @@ void EmuSettingsDialog::done(int r)
|| jitFastMemory != Config::JIT_FastMemory
#endif
|| externalBiosEnable != Config::ExternalBIOSEnable
- || strcmp(Config::BIOS9Path, bios9Path.c_str()) != 0
- || strcmp(Config::BIOS7Path, bios7Path.c_str()) != 0
- || strcmp(Config::FirmwarePath, firmwarePath.c_str()) != 0
+ || bios9Path != Config::BIOS9Path
+ || bios7Path != Config::BIOS7Path
+ || firmwarePath != Config::FirmwarePath
|| dldiEnable != Config::DLDIEnable
- || strcmp(Config::DLDISDPath, dldiSDPath.c_str()) != 0
+ || dldiSDPath != Config::DLDISDPath
|| dldiSize != Config::DLDISize
|| dldiReadOnly != Config::DLDIReadOnly
|| dldiFolderSync != Config::DLDIFolderSync
- || strcmp(Config::DLDIFolderPath, dldiFolderPath.c_str()) != 0
- || strcmp(Config::DSiBIOS9Path, dsiBios9Path.c_str()) != 0
- || strcmp(Config::DSiBIOS7Path, dsiBios7Path.c_str()) != 0
- || strcmp(Config::DSiFirmwarePath, dsiFirmwarePath.c_str()) != 0
- || strcmp(Config::DSiNANDPath, dsiNANDPath.c_str()) != 0
+ || dldiFolderPath != Config::DLDIFolderPath
+ || dsiBios9Path != Config::DSiBIOS9Path
+ || dsiBios7Path != Config::DSiBIOS7Path
+ || dsiFirmwarePath != Config::DSiFirmwarePath
+ || dsiNANDPath != Config::DSiNANDPath
|| dsiSDEnable != Config::DSiSDEnable
- || strcmp(Config::DSiSDPath, dsiSDPath.c_str()) != 0
+ || dsiSDPath != Config::DSiSDPath
|| dsiSDSize != Config::DSiSDSize
|| dsiSDReadOnly != Config::DSiSDReadOnly
|| dsiSDFolderSync != Config::DSiSDFolderSync
- || strcmp(Config::DSiSDFolderPath, dsiSDFolderPath.c_str()) != 0)
+ || dsiSDFolderPath != Config::DSiSDFolderPath)
{
if (RunningSomething
&& QMessageBox::warning(this, "Reset necessary to apply changes",
@@ -244,28 +243,28 @@ void EmuSettingsDialog::done(int r)
return;
Config::ExternalBIOSEnable = externalBiosEnable;
- strncpy(Config::BIOS9Path, bios9Path.c_str(), 1023); Config::BIOS9Path[1023] = '\0';
- strncpy(Config::BIOS7Path, bios7Path.c_str(), 1023); Config::BIOS7Path[1023] = '\0';
- strncpy(Config::FirmwarePath, firmwarePath.c_str(), 1023); Config::FirmwarePath[1023] = '\0';
+ Config::BIOS9Path = bios9Path;
+ Config::BIOS7Path = bios7Path;
+ Config::FirmwarePath = firmwarePath;
Config::DLDIEnable = dldiEnable;
- strncpy(Config::DLDISDPath, dldiSDPath.c_str(), 1023); Config::DLDISDPath[1023] = '\0';
+ Config::DLDISDPath = dldiSDPath;
Config::DLDISize = dldiSize;
Config::DLDIReadOnly = dldiReadOnly;
Config::DLDIFolderSync = dldiFolderSync;
- strncpy(Config::DLDIFolderPath, dldiFolderPath.c_str(), 1023); Config::DLDIFolderPath[1023] = '\0';
+ Config::DLDIFolderPath = dldiFolderPath;
- strncpy(Config::DSiBIOS9Path, dsiBios9Path.c_str(), 1023); Config::DSiBIOS9Path[1023] = '\0';
- strncpy(Config::DSiBIOS7Path, dsiBios7Path.c_str(), 1023); Config::DSiBIOS7Path[1023] = '\0';
- strncpy(Config::DSiFirmwarePath, dsiFirmwarePath.c_str(), 1023); Config::DSiFirmwarePath[1023] = '\0';
- strncpy(Config::DSiNANDPath, dsiNANDPath.c_str(), 1023); Config::DSiNANDPath[1023] = '\0';
+ Config::DSiBIOS9Path = dsiBios9Path;
+ Config::DSiBIOS7Path = dsiBios7Path;
+ Config::DSiFirmwarePath = dsiFirmwarePath;
+ Config::DSiNANDPath = dsiNANDPath;
Config::DSiSDEnable = dsiSDEnable;
- strncpy(Config::DSiSDPath, dsiSDPath.c_str(), 1023); Config::DSiSDPath[1023] = '\0';
+ Config::DSiSDPath = dsiSDPath;
Config::DSiSDSize = dsiSDSize;
Config::DSiSDReadOnly = dsiSDReadOnly;
Config::DSiSDFolderSync = dsiSDFolderSync;
- strncpy(Config::DSiSDFolderPath, dsiSDFolderPath.c_str(), 1023); Config::DSiSDFolderPath[1023] = '\0';
+ Config::DSiSDFolderPath = dsiSDFolderPath;
#ifdef JIT_ENABLED
Config::JIT_Enable = jitEnable;
diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h
index 60e2160..6a79626 100644
--- a/src/frontend/qt_sdl/EmuSettingsDialog.h
+++ b/src/frontend/qt_sdl/EmuSettingsDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp
index 0b2cad6..ffca567 100644
--- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2020 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -18,6 +18,7 @@
#include <QMessageBox>
+#include "Platform.h"
#include "Config.h"
#include "FirmwareSettingsDialog.h"
@@ -35,7 +36,7 @@ FirmwareSettingsDialog::FirmwareSettingsDialog(QWidget* parent) : QDialog(parent
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
- ui->usernameEdit->setText(Config::FirmwareUsername);
+ ui->usernameEdit->setText(QString::fromStdString(Config::FirmwareUsername));
ui->languageBox->addItems(languages);
ui->languageBox->setCurrentIndex(Config::FirmwareLanguage);
@@ -59,13 +60,19 @@ FirmwareSettingsDialog::FirmwareSettingsDialog(QWidget* parent) : QDialog(parent
}
ui->colorsEdit->setCurrentIndex(Config::FirmwareFavouriteColour);
- ui->messageEdit->setText(Config::FirmwareMessage);
+ ui->messageEdit->setText(QString::fromStdString(Config::FirmwareMessage));
ui->overrideFirmwareBox->setChecked(Config::FirmwareOverrideSettings);
- ui->txtMAC->setText(Config::FirmwareMAC);
- ui->cbRandomizeMAC->setChecked(Config::RandomizeMAC != 0);
- on_cbRandomizeMAC_toggled();
+ ui->txtMAC->setText(QString::fromStdString(Config::FirmwareMAC));
+
+ on_overrideFirmwareBox_toggled();
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Configuring settings for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
}
FirmwareSettingsDialog::~FirmwareSettingsDialog()
@@ -123,7 +130,7 @@ void FirmwareSettingsDialog::done(int r)
return;
}
- int newOverride = ui->overrideFirmwareBox->isChecked();
+ bool newOverride = ui->overrideFirmwareBox->isChecked();
std::string newName = ui->usernameEdit->text().toStdString();
int newLanguage = ui->languageBox->currentIndex();
@@ -133,17 +140,15 @@ void FirmwareSettingsDialog::done(int r)
std::string newMessage = ui->messageEdit->text().toStdString();
std::string newMAC = ui->txtMAC->text().toStdString();
- int newRandomizeMAC = ui->cbRandomizeMAC->isChecked() ? 1:0;
if ( newOverride != Config::FirmwareOverrideSettings
- || strcmp(newName.c_str(), Config::FirmwareUsername) != 0
+ || newName != Config::FirmwareUsername
|| newLanguage != Config::FirmwareLanguage
|| newFavColor != Config::FirmwareFavouriteColour
|| newBirthdayDay != Config::FirmwareBirthdayDay
|| newBirthdayMonth != Config::FirmwareBirthdayMonth
- || strcmp(newMessage.c_str(), Config::FirmwareMessage) != 0
- || strcmp(newMAC.c_str(), Config::FirmwareMAC) != 0
- || newRandomizeMAC != Config::RandomizeMAC)
+ || newMessage != Config::FirmwareMessage
+ || newMAC != Config::FirmwareMAC)
{
if (RunningSomething
&& QMessageBox::warning(this, "Reset necessary to apply changes",
@@ -153,15 +158,14 @@ void FirmwareSettingsDialog::done(int r)
Config::FirmwareOverrideSettings = newOverride;
- strncpy(Config::FirmwareUsername, newName.c_str(), 63); Config::FirmwareUsername[63] = '\0';
+ Config::FirmwareUsername = newName;
Config::FirmwareLanguage = newLanguage;
Config::FirmwareFavouriteColour = newFavColor;
Config::FirmwareBirthdayDay = newBirthdayDay;
Config::FirmwareBirthdayMonth = newBirthdayMonth;
- strncpy(Config::FirmwareMessage, newMessage.c_str(), 1023); Config::FirmwareMessage[1023] = '\0';
+ Config::FirmwareMessage = newMessage;
- strncpy(Config::FirmwareMAC, newMAC.c_str(), 17); Config::FirmwareMAC[17] = '\0';
- Config::RandomizeMAC = newRandomizeMAC;
+ Config::FirmwareMAC = newMAC;
Config::Save();
@@ -202,8 +206,9 @@ void FirmwareSettingsDialog::on_cbxBirthdayMonth_currentIndexChanged(int idx)
}
}
-void FirmwareSettingsDialog::on_cbRandomizeMAC_toggled()
+void FirmwareSettingsDialog::on_overrideFirmwareBox_toggled()
{
- bool disable = ui->cbRandomizeMAC->isChecked();
- ui->txtMAC->setDisabled(disable);
+ bool disable = !ui->overrideFirmwareBox->isChecked();
+ ui->grpUserSettings->setDisabled(disable);
+ ui->grpWifiSettings->setDisabled(disable);
}
diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.h b/src/frontend/qt_sdl/FirmwareSettingsDialog.h
index 1ae409f..b3695e2 100644
--- a/src/frontend/qt_sdl/FirmwareSettingsDialog.h
+++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2020 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -109,7 +109,7 @@ public:
}
currentDlg = new FirmwareSettingsDialog(parent);
- currentDlg->show();
+ currentDlg->open();
return currentDlg;
}
static void closeDlg()
@@ -123,7 +123,7 @@ private slots:
void done(int r);
void on_cbxBirthdayMonth_currentIndexChanged(int idx);
- void on_cbRandomizeMAC_toggled();
+ void on_overrideFirmwareBox_toggled();
private:
bool verifyMAC();
diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.ui b/src/frontend/qt_sdl/FirmwareSettingsDialog.ui
index a97689c..3714629 100644
--- a/src/frontend/qt_sdl/FirmwareSettingsDialog.ui
+++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>511</width>
- <height>342</height>
+ <height>357</height>
</rect>
</property>
<property name="sizePolicy">
@@ -24,6 +24,13 @@
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
+ <widget class="QLabel" name="lblInstanceNum">
+ <property name="text">
+ <string>Configuring settings for instance X</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QGroupBox" name="grpGeneral">
<property name="title">
<string>General</string>
@@ -144,9 +151,9 @@
</widget>
</item>
<item row="1" column="1">
- <widget class="QCheckBox" name="cbRandomizeMAC">
+ <widget class="QLabel" name="label_6">
<property name="text">
- <string>Randomize</string>
+ <string>(leave empty to use default MAC)</string>
</property>
</widget>
</item>
diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp
index cca11e7..c1ef87c 100644
--- a/src/frontend/qt_sdl/Input.cpp
+++ b/src/frontend/qt_sdl/Input.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/Input.h b/src/frontend/qt_sdl/Input.h
index d349f19..0d2292d 100644
--- a/src/frontend/qt_sdl/Input.h
+++ b/src/frontend/qt_sdl/Input.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp
index 2a50539..92a0186 100644
--- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp
+++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -24,6 +24,7 @@
#include <SDL2/SDL.h>
#include "types.h"
+#include "Platform.h"
#include "Config.h"
#include "MapButton.h"
@@ -123,6 +124,12 @@ InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new
}
setupKeypadPage();
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Configuring mappings for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
}
InputConfigDialog::~InputConfigDialog()
diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h
index 069dfcb..f1ea059 100644
--- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h
+++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui
index 6f4bb5d..0db61b1 100644
--- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui
+++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>710</width>
- <height>709</height>
+ <width>770</width>
+ <height>678</height>
</rect>
</property>
<property name="windowTitle">
@@ -20,7 +20,7 @@
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
- <item row="7" column="1">
+ <item row="8" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -30,49 +30,7 @@
</property>
</widget>
</item>
- <item row="6" column="0" colspan="2">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QLabel" name="label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Joystick:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="cbxJoystick">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="0" colspan="2">
+ <item row="1" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
@@ -82,13 +40,128 @@
<string>DS keypad</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
- <item row="1" column="0">
+ <item row="0" column="0">
<widget class="QStackedWidget" name="stackMapping">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="keyPage">
<layout class="QGridLayout" name="gridLayout_2">
+ <item row="4" column="1" colspan="3">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyMapSwitch">
+ <property name="text">
+ <string>Switch to Joystick mappings</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="3">
+ <widget class="QWidget" name="widget_4" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <item>
+ <widget class="QGroupBox" name="grp_R">
+ <property name="title">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;R&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnKeyR">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>R</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_8">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
<item row="1" column="1">
<widget class="QWidget" name="widget_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_14">
@@ -108,12 +181,12 @@
<item>
<widget class="QGroupBox" name="grp_L">
<property name="title">
- <string>L</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>3</number>
</property>
@@ -130,10 +203,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;L&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyL">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -157,32 +240,6 @@
</layout>
</widget>
</item>
- <item row="1" column="2">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="resources/ds.qrc">:/ds/ds_back.svg</pixmap>
- </property>
- <property name="scaledContents">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="resources/ds.qrc">:/ds/ds_open.svg</pixmap>
- </property>
- <property name="scaledContents">
- <bool>false</bool>
- </property>
- </widget>
- </item>
<item row="2" column="3">
<widget class="QGroupBox" name="grp_ABXY">
<property name="sizePolicy">
@@ -250,7 +307,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_X">
<property name="title">
- <string>X</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -272,10 +329,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;X&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyX">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -320,7 +387,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_Y">
<property name="title">
- <string>Y</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -342,10 +409,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Y&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyY">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -369,7 +446,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_A">
<property name="title">
- <string>A</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -391,10 +468,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;A&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyA">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -451,7 +538,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_B">
<property name="title">
- <string>B</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -473,10 +560,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;B&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyB">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -516,6 +613,131 @@
</layout>
</widget>
</item>
+ <item row="3" column="2">
+ <layout class="QHBoxLayout" name="layout_SelectStart">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="grp_Select">
+ <property name="title">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Select&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnKeySelect">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Select</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="grp_Start">
+ <property name="title">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_19">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Start&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnKeyStart">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Start</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
<item row="2" column="1">
<widget class="QGroupBox" name="grp_ControlPad">
<property name="sizePolicy">
@@ -583,7 +805,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_Up">
<property name="title">
- <string>Up</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -605,10 +827,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Up&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyUp">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -653,7 +885,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_Left">
<property name="title">
- <string>Left</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -675,10 +907,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Left&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyLeft">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -702,7 +944,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_Right">
<property name="title">
- <string>Right</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -724,10 +966,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Right&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyRight">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -784,7 +1036,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_Down">
<property name="title">
- <string>Down</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -806,10 +1058,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Down&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnKeyDown">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -849,6 +1111,32 @@
</layout>
</widget>
</item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/ds.qrc">:/ds/ds_open.svg</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/ds.qrc">:/ds/ds_back.svg</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
<item row="0" column="2">
<widget class="QLabel" name="label_7">
<property name="font">
@@ -864,10 +1152,14 @@
</property>
</widget>
</item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="joyPage">
+ <layout class="QGridLayout" name="gridLayout_3">
<item row="4" column="1" colspan="3">
- <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
- <spacer name="horizontalSpacer">
+ <spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -880,14 +1172,14 @@
</spacer>
</item>
<item>
- <widget class="QPushButton" name="btnJoyMapSwitch">
+ <widget class="QPushButton" name="btnKeyMapSwitch">
<property name="text">
- <string>Switch to Joystick mappings</string>
+ <string>Switch to Keyboard mappings</string>
</property>
</widget>
</item>
<item>
- <spacer name="horizontalSpacer_2">
+ <spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -901,183 +1193,6 @@
</item>
</layout>
</item>
- <item row="3" column="2">
- <layout class="QHBoxLayout" name="layout_SelectStart">
- <property name="spacing">
- <number>3</number>
- </property>
- <item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_Select">
- <property name="title">
- <string>Select</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2">
- <property name="spacing">
- <number>3</number>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>3</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>3</number>
- </property>
- <item>
- <widget class="QPushButton" name="btnKeySelect">
- <property name="minimumSize">
- <size>
- <width>72</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>68</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">min-width: 68px;</string>
- </property>
- <property name="text">
- <string>Select</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_Start">
- <property name="title">
- <string>Start</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2">
- <property name="spacing">
- <number>3</number>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>3</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>3</number>
- </property>
- <item>
- <widget class="QPushButton" name="btnKeyStart">
- <property name="minimumSize">
- <size>
- <width>72</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>68</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">min-width: 68px;</string>
- </property>
- <property name="text">
- <string>Start</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="3">
- <widget class="QWidget" name="widget_4" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout_15">
- <item>
- <widget class="QGroupBox" name="grp_R">
- <property name="title">
- <string>R</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_4">
- <property name="spacing">
- <number>3</number>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>3</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>3</number>
- </property>
- <item>
- <widget class="QPushButton" name="btnKeyR">
- <property name="minimumSize">
- <size>
- <width>72</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>68</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">min-width: 68px;</string>
- </property>
- <property name="text">
- <string>R</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_8">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="joyPage">
- <layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="1">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
@@ -1097,12 +1212,12 @@
<item>
<widget class="QGroupBox" name="grp_L_2">
<property name="title">
- <string>L</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>3</number>
</property>
@@ -1119,10 +1234,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_20">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;L&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnJoyL">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1146,8 +1271,34 @@
</layout>
</widget>
</item>
- <item row="2" column="3">
- <widget class="QGroupBox" name="grp_ABXY_2">
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/ds.qrc">:/ds/ds_back.svg</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/ds.qrc">:/ds/ds_open.svg</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QGroupBox" name="grp_ControlPad_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -1163,7 +1314,7 @@
<property name="checkable">
<bool>false</bool>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
@@ -1180,8 +1331,8 @@
<number>3</number>
</property>
<item>
- <widget class="QWidget" name="widget_X_2" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <widget class="QWidget" name="widget_Up_2" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_25">
<property name="spacing">
<number>0</number>
</property>
@@ -1198,7 +1349,7 @@
<number>0</number>
</property>
<item>
- <spacer name="spacer_9">
+ <spacer name="spacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1211,14 +1362,14 @@
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_X_2">
+ <widget class="QGroupBox" name="grp_Up_2">
<property name="title">
- <string>X</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout_2">
+ <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
@@ -1235,10 +1386,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyX">
+ <widget class="QLabel" name="label_23">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Up&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyUp">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1252,7 +1413,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>X</string>
+ <string>Up</string>
</property>
</widget>
</item>
@@ -1260,7 +1421,7 @@
</widget>
</item>
<item>
- <spacer name="spacer_10">
+ <spacer name="spacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1276,19 +1437,19 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="layout_YA_2">
+ <layout class="QHBoxLayout" name="layout_LeftRight_2">
<property name="spacing">
<number>3</number>
</property>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_Y_2">
+ <widget class="QGroupBox" name="grp_Left_2">
<property name="title">
- <string>Y</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout_2">
+ <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_4">
<property name="spacing">
<number>3</number>
</property>
@@ -1305,10 +1466,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyY">
+ <widget class="QLabel" name="label_22">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Left&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyLeft">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1322,7 +1493,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>Y</string>
+ <string>Left</string>
</property>
</widget>
</item>
@@ -1330,14 +1501,14 @@
</widget>
</item>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_A_2">
+ <widget class="QGroupBox" name="grp_Right_2">
<property name="title">
- <string>A</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout_2">
+ <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_4">
<property name="spacing">
<number>3</number>
</property>
@@ -1354,10 +1525,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyA">
+ <widget class="QLabel" name="label_30">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Right&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyRight">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1371,7 +1552,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>A</string>
+ <string>Right</string>
</property>
</widget>
</item>
@@ -1381,8 +1562,8 @@
</layout>
</item>
<item>
- <widget class="QWidget" name="widget_B_2" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <widget class="QWidget" name="widget_Down_2" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_26">
<property name="spacing">
<number>0</number>
</property>
@@ -1399,7 +1580,7 @@
<number>0</number>
</property>
<item>
- <spacer name="spacer_11">
+ <spacer name="spacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1412,14 +1593,14 @@
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_B_2">
+ <widget class="QGroupBox" name="grp_Down_2">
<property name="title">
- <string>B</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout_2">
+ <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
@@ -1436,10 +1617,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyB">
+ <widget class="QLabel" name="label_31">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Down&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyDown">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1453,7 +1644,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>B</string>
+ <string>Down</string>
</property>
</widget>
</item>
@@ -1461,7 +1652,7 @@
</widget>
</item>
<item>
- <spacer name="spacer_12">
+ <spacer name="spacer_16">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1479,8 +1670,8 @@
</layout>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QGroupBox" name="grp_ControlPad_2">
+ <item row="2" column="3">
+ <widget class="QGroupBox" name="grp_ABXY_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -1496,7 +1687,7 @@
<property name="checkable">
<bool>false</bool>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
@@ -1513,8 +1704,8 @@
<number>3</number>
</property>
<item>
- <widget class="QWidget" name="widget_Up_2" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout_25">
+ <widget class="QWidget" name="widget_X_2" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>0</number>
</property>
@@ -1531,7 +1722,7 @@
<number>0</number>
</property>
<item>
- <spacer name="spacer_13">
+ <spacer name="spacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1544,14 +1735,14 @@
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_Up_2">
+ <widget class="QGroupBox" name="grp_X_2">
<property name="title">
- <string>Up</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout_2">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
@@ -1568,10 +1759,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyUp">
+ <widget class="QLabel" name="label_24">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;X&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyX">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1585,7 +1786,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>Up</string>
+ <string>X</string>
</property>
</widget>
</item>
@@ -1593,7 +1794,7 @@
</widget>
</item>
<item>
- <spacer name="spacer_14">
+ <spacer name="spacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1609,19 +1810,19 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="layout_LeftRight_2">
+ <layout class="QHBoxLayout" name="layout_YA_2">
<property name="spacing">
<number>3</number>
</property>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_Left_2">
+ <widget class="QGroupBox" name="grp_Y_2">
<property name="title">
- <string>Left</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_4">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
@@ -1638,10 +1839,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyLeft">
+ <widget class="QLabel" name="label_25">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Y&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyY">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1655,7 +1866,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>Left</string>
+ <string>Y</string>
</property>
</widget>
</item>
@@ -1663,14 +1874,14 @@
</widget>
</item>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_Right_2">
+ <widget class="QGroupBox" name="grp_A_2">
<property name="title">
- <string>Right</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_4">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
@@ -1687,10 +1898,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyRight">
+ <widget class="QLabel" name="label_26">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;A&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyA">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1704,7 +1925,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>Right</string>
+ <string>A</string>
</property>
</widget>
</item>
@@ -1714,8 +1935,8 @@
</layout>
</item>
<item>
- <widget class="QWidget" name="widget_Down_2" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout_26">
+ <widget class="QWidget" name="widget_B_2" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
<property name="spacing">
<number>0</number>
</property>
@@ -1732,7 +1953,7 @@
<number>0</number>
</property>
<item>
- <spacer name="spacer_15">
+ <spacer name="spacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1745,14 +1966,14 @@
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="grp_Down_2">
+ <widget class="QGroupBox" name="grp_B_2">
<property name="title">
- <string>Down</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout_2">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
@@ -1769,10 +1990,20 @@
<number>3</number>
</property>
<item>
- <widget class="QPushButton" name="btnJoyDown">
+ <widget class="QLabel" name="label_27">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;B&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnJoyB">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1786,7 +2017,7 @@
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
- <string>Down</string>
+ <string>B</string>
</property>
</widget>
</item>
@@ -1794,7 +2025,7 @@
</widget>
</item>
<item>
- <spacer name="spacer_16">
+ <spacer name="spacer_12">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -1812,84 +2043,6 @@
</layout>
</widget>
</item>
- <item row="2" column="2">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="resources/ds.qrc">:/ds/ds_open.svg</pixmap>
- </property>
- <property name="scaledContents">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="label_6">
- <property name="font">
- <font>
- <pointsize>15</pointsize>
- </font>
- </property>
- <property name="text">
- <string>Joystick mappings</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="resources/ds.qrc">:/ds/ds_back.svg</pixmap>
- </property>
- <property name="scaledContents">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="4" column="1" colspan="3">
- <layout class="QHBoxLayout" name="horizontalLayout_12">
- <item>
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="btnKeyMapSwitch">
- <property name="text">
- <string>Switch to Keyboard mappings</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
<item row="3" column="2">
<layout class="QHBoxLayout" name="layout_SelectStart_2">
<property name="spacing">
@@ -1898,7 +2051,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_Select_2">
<property name="title">
- <string>Select</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -1920,10 +2073,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_28">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Select&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnJoySelect">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -1947,7 +2110,7 @@
<item alignment="Qt::AlignHCenter">
<widget class="QGroupBox" name="grp_Start_2">
<property name="title">
- <string>Start</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -1969,10 +2132,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_29">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Start&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnJoyStart">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -2001,12 +2174,12 @@
<item>
<widget class="QGroupBox" name="grp_R_2">
<property name="title">
- <string>R</string>
+ <string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <layout class="QVBoxLayout" name="verticalLayout_8">
<property name="spacing">
<number>3</number>
</property>
@@ -2023,10 +2196,20 @@
<number>3</number>
</property>
<item>
+ <widget class="QLabel" name="label_21">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;R&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnJoyR">
<property name="minimumSize">
<size>
- <width>72</width>
+ <width>70</width>
<height>0</height>
</size>
</property>
@@ -2063,6 +2246,21 @@
</layout>
</widget>
</item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_6">
+ <property name="font">
+ <font>
+ <pointsize>15</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Joystick mappings</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</widget>
@@ -2081,6 +2279,55 @@
</widget>
</widget>
</item>
+ <item row="7" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Joystick:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="cbxJoystick">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblInstanceNum">
+ <property name="text">
+ <string>Configuring mappings for instance X</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<resources>
diff --git a/src/frontend/qt_sdl/InputConfig/MapButton.h b/src/frontend/qt_sdl/InputConfig/MapButton.h
index a1b1a16..afefed7 100644
--- a/src/frontend/qt_sdl/InputConfig/MapButton.h
+++ b/src/frontend/qt_sdl/InputConfig/MapButton.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -163,6 +163,7 @@ public:
setCheckable(true);
setText(mappingText());
+ setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick);
@@ -245,19 +246,21 @@ protected:
Sint16 axisval = SDL_JoystickGetAxis(joy, i);
int diff = abs(axisval - axesRest[i]);
- if (axesRest[i] < -16384 && axisval >= 0)
- {
- *mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
- click();
- return;
- }
- else if (diff > 16384)
+ if (diff >= 16384)
{
- int axistype;
- if (axisval > 0) axistype = 0;
- else axistype = 1;
+ if (axesRest[i] < -16384) // Trigger
+ {
+ *mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
+ }
+ else // Analog stick
+ {
+ int axistype;
+ if (axisval > 0) axistype = 0;
+ else axistype = 1;
+
+ *mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
+ }
- *mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
click();
return;
}
@@ -352,4 +355,4 @@ private:
int axesRest[16];
};
-#endif // MAPBUTTON_H \ No newline at end of file
+#endif // MAPBUTTON_H
diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp
index 788e596..7c5eae6 100644
--- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, WaluigiWare64
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.h b/src/frontend/qt_sdl/InterfaceSettingsDialog.h
index ae21e3f..114aa04 100644
--- a/src/frontend/qt_sdl/InterfaceSettingsDialog.h
+++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, WaluigiWare64
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp
index ed3eee9..86c218f 100644
--- a/src/frontend/qt_sdl/LAN_PCap.cpp
+++ b/src/frontend/qt_sdl/LAN_PCap.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -114,6 +114,12 @@ bool TryLoadPCap(void* lib)
bool Init(bool open_adapter)
{
+ PCapAdapter = NULL;
+ PacketLen = 0;
+ RXNum = 0;
+
+ NumAdapters = 0;
+
// TODO: how to deal with cases where an adapter is unplugged or changes config??
if (!PCapLib)
{
@@ -142,12 +148,6 @@ bool Init(bool open_adapter)
}
}
- PCapAdapter = NULL;
- PacketLen = 0;
- RXNum = 0;
-
- NumAdapters = 0;
-
char errbuf[PCAP_ERRBUF_SIZE];
int ret;
@@ -318,7 +318,7 @@ bool Init(bool open_adapter)
PCapAdapterData = &Adapters[0];
for (int i = 0; i < NumAdapters; i++)
{
- if (!strncmp(Adapters[i].DeviceName, Config::LANDevice, 128))
+ if (!strncmp(Adapters[i].DeviceName, Config::LANDevice.c_str(), 128))
PCapAdapterData = &Adapters[i];
}
diff --git a/src/frontend/qt_sdl/LAN_PCap.h b/src/frontend/qt_sdl/LAN_PCap.h
index 8e9ad9f..610c0ae 100644
--- a/src/frontend/qt_sdl/LAN_PCap.h
+++ b/src/frontend/qt_sdl/LAN_PCap.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/LAN_Socket.cpp b/src/frontend/qt_sdl/LAN_Socket.cpp
index 83ddd99..6c00b58 100644
--- a/src/frontend/qt_sdl/LAN_Socket.cpp
+++ b/src/frontend/qt_sdl/LAN_Socket.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/LAN_Socket.h b/src/frontend/qt_sdl/LAN_Socket.h
index c6afc08..2073d1b 100644
--- a/src/frontend/qt_sdl/LAN_Socket.h
+++ b/src/frontend/qt_sdl/LAN_Socket.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp
new file mode 100644
index 0000000..fb7ef7a
--- /dev/null
+++ b/src/frontend/qt_sdl/LocalMP.cpp
@@ -0,0 +1,634 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 <stdlib.h>
+#include <string.h>
+
+#ifdef __WIN32__
+ #include <windows.h>
+#else
+ #include <fcntl.h>
+ #include <semaphore.h>
+ #include <time.h>
+ #ifdef __APPLE__
+ #include "sem_timedwait.h"
+ #endif
+#endif
+
+#include <string>
+#include <QSharedMemory>
+
+#include "Config.h"
+#include "LocalMP.h"
+
+
+namespace LocalMP
+{
+
+u32 MPUniqueID;
+u8 PacketBuffer[2048];
+
+struct MPQueueHeader
+{
+ u16 NumInstances;
+ u16 InstanceBitmask; // bitmask of all instances present
+ u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive packets
+ u32 PacketWriteOffset;
+ u32 ReplyWriteOffset;
+ u16 MPHostInstanceID; // instance ID from which the last CMD frame was sent
+ u16 MPReplyBitmask; // bitmask of which clients replied in time
+};
+
+struct MPPacketHeader
+{
+ u32 Magic;
+ u32 SenderID;
+ u32 Type; // 0=regular 1=CMD 2=reply 3=ack
+ u32 Length;
+ u64 Timestamp;
+};
+
+struct MPSync
+{
+ u32 Magic;
+ u32 SenderID;
+ u16 ClientMask;
+ u16 Type;
+ u64 Timestamp;
+};
+
+QSharedMemory* MPQueue;
+int InstanceID;
+u32 PacketReadOffset;
+u32 ReplyReadOffset;
+
+const u32 kQueueSize = 0x20000;
+const u32 kMaxFrameSize = 0x800;
+const u32 kPacketStart = sizeof(MPQueueHeader);
+const u32 kReplyStart = kQueueSize / 2;
+const u32 kPacketEnd = kReplyStart;
+const u32 kReplyEnd = kQueueSize;
+
+int RecvTimeout;
+
+int LastHostID;
+
+
+// we need to come up with our own abstraction layer for named semaphores
+// because QSystemSemaphore doesn't support waiting with a timeout
+// and, as such, is unsuitable to our needs
+
+#ifdef __WIN32__
+
+bool SemInited[32];
+HANDLE SemPool[32];
+
+void SemPoolInit()
+{
+ for (int i = 0; i < 32; i++)
+ {
+ SemPool[i] = INVALID_HANDLE_VALUE;
+ SemInited[i] = false;
+ }
+}
+
+void SemDeinit(int num);
+
+void SemPoolDeinit()
+{
+ for (int i = 0; i < 32; i++)
+ SemDeinit(i);
+}
+
+bool SemInit(int num)
+{
+ if (SemInited[num])
+ return true;
+
+ char semname[64];
+ sprintf(semname, "Local\\melonNIFI_Sem%02d", num);
+
+ HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname);
+ SemPool[num] = sem;
+ SemInited[num] = true;
+ return sem != INVALID_HANDLE_VALUE;
+}
+
+void SemDeinit(int num)
+{
+ if (SemPool[num] != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(SemPool[num]);
+ SemPool[num] = INVALID_HANDLE_VALUE;
+ }
+
+ SemInited[num] = false;
+}
+
+bool SemPost(int num)
+{
+ SemInit(num);
+ return ReleaseSemaphore(SemPool[num], 1, nullptr) != 0;
+}
+
+bool SemWait(int num, int timeout)
+{
+ return WaitForSingleObject(SemPool[num], timeout) == WAIT_OBJECT_0;
+}
+
+void SemReset(int num)
+{
+ while (WaitForSingleObject(SemPool[num], 0) == WAIT_OBJECT_0);
+}
+
+#else
+
+bool SemInited[32];
+sem_t* SemPool[32];
+
+void SemPoolInit()
+{
+ for (int i = 0; i < 32; i++)
+ {
+ SemPool[i] = SEM_FAILED;
+ SemInited[i] = false;
+ }
+}
+
+void SemDeinit(int num);
+
+void SemPoolDeinit()
+{
+ for (int i = 0; i < 32; i++)
+ SemDeinit(i);
+}
+
+bool SemInit(int num)
+{
+ if (SemInited[num])
+ return true;
+
+ char semname[64];
+ sprintf(semname, "/melonNIFI_Sem%02d", num);
+
+ sem_t* sem = sem_open(semname, O_CREAT, 0644, 0);
+ SemPool[num] = sem;
+ SemInited[num] = true;
+ return sem != SEM_FAILED;
+}
+
+void SemDeinit(int num)
+{
+ if (SemPool[num] != SEM_FAILED)
+ {
+ sem_close(SemPool[num]);
+ SemPool[num] = SEM_FAILED;
+ }
+
+ SemInited[num] = false;
+}
+
+bool SemPost(int num)
+{
+ SemInit(num);
+ return sem_post(SemPool[num]) == 0;
+}
+
+bool SemWait(int num, int timeout)
+{
+ if (!timeout)
+ return sem_trywait(SemPool[num]) == 0;
+
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec += timeout * 1000000;
+ long sec = ts.tv_nsec / 1000000000;
+ ts.tv_nsec -= sec * 1000000000;
+ ts.tv_sec += sec;
+
+ return sem_timedwait(SemPool[num], &ts) == 0;
+}
+
+void SemReset(int num)
+{
+ while (sem_trywait(SemPool[num]) == 0);
+}
+
+#endif
+
+
+bool Init()
+{
+ MPQueue = new QSharedMemory("melonNIFI");
+
+ if (!MPQueue->attach())
+ {
+ printf("MP sharedmem doesn't exist. creating\n");
+ if (!MPQueue->create(kQueueSize))
+ {
+ printf("MP sharedmem create failed :(\n");
+ return false;
+ }
+
+ MPQueue->lock();
+ memset(MPQueue->data(), 0, MPQueue->size());
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ header->PacketWriteOffset = kPacketStart;
+ header->ReplyWriteOffset = kReplyStart;
+ MPQueue->unlock();
+ }
+
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+
+ u16 mask = header->InstanceBitmask;
+ for (int i = 0; i < 16; i++)
+ {
+ if (!(mask & (1<<i)))
+ {
+ InstanceID = i;
+ header->InstanceBitmask |= (1<<i);
+ //header->ConnectedBitmask |= (1 << i);
+ break;
+ }
+ }
+ header->NumInstances++;
+
+ PacketReadOffset = header->PacketWriteOffset;
+ ReplyReadOffset = header->ReplyWriteOffset;
+
+ MPQueue->unlock();
+
+ // prepare semaphores
+ // semaphores 0-15: regular frames; semaphore I is posted when instance I needs to process a new frame
+ // semaphores 16-31: MP replies; semaphore I is posted when instance I needs to process a new MP reply
+
+ SemPoolInit();
+ SemInit(InstanceID);
+ SemInit(16+InstanceID);
+
+ LastHostID = -1;
+
+ printf("MP comm init OK, instance ID %d\n", InstanceID);
+
+ RecvTimeout = 25;
+
+ return true;
+}
+
+void DeInit()
+{
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ header->ConnectedBitmask &= ~(1 << InstanceID);
+ header->InstanceBitmask &= ~(1 << InstanceID);
+ header->NumInstances--;
+ MPQueue->unlock();
+
+ SemPoolDeinit();
+
+ MPQueue->detach();
+ delete MPQueue;
+}
+
+void SetRecvTimeout(int timeout)
+{
+ RecvTimeout = timeout;
+}
+
+void Begin()
+{
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ PacketReadOffset = header->PacketWriteOffset;
+ ReplyReadOffset = header->ReplyWriteOffset;
+ SemReset(InstanceID);
+ SemReset(16+InstanceID);
+ header->ConnectedBitmask |= (1 << InstanceID);
+ MPQueue->unlock();
+}
+
+void End()
+{
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ //SemReset(InstanceID);
+ //SemReset(16+InstanceID);
+ header->ConnectedBitmask &= ~(1 << InstanceID);
+ MPQueue->unlock();
+}
+
+void FIFORead(int fifo, void* buf, int len)
+{
+ u8* data = (u8*)MPQueue->data();
+
+ u32 offset, start, end;
+ if (fifo == 0)
+ {
+ offset = PacketReadOffset;
+ start = kPacketStart;
+ end = kPacketEnd;
+ }
+ else
+ {
+ offset = ReplyReadOffset;
+ start = kReplyStart;
+ end = kReplyEnd;
+ }
+
+ if ((offset + len) >= end)
+ {
+ u32 part1 = end - offset;
+ memcpy(buf, &data[offset], part1);
+ memcpy(&((u8*)buf)[part1], &data[start], len - part1);
+ offset = start + len - part1;
+ }
+ else
+ {
+ memcpy(buf, &data[offset], len);
+ offset += len;
+ }
+
+ if (fifo == 0) PacketReadOffset = offset;
+ else ReplyReadOffset = offset;
+}
+
+void FIFOWrite(int fifo, void* buf, int len)
+{
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ u32 offset, start, end;
+ if (fifo == 0)
+ {
+ offset = header->PacketWriteOffset;
+ start = kPacketStart;
+ end = kPacketEnd;
+ }
+ else
+ {
+ offset = header->ReplyWriteOffset;
+ start = kReplyStart;
+ end = kReplyEnd;
+ }
+
+ if ((offset + len) >= end)
+ {
+ u32 part1 = end - offset;
+ memcpy(&data[offset], buf, part1);
+ memcpy(&data[start], &((u8*)buf)[part1], len - part1);
+ offset = start + len - part1;
+ }
+ else
+ {
+ memcpy(&data[offset], buf, len);
+ offset += len;
+ }
+
+ if (fifo == 0) header->PacketWriteOffset = offset;
+ else header->ReplyWriteOffset = offset;
+}
+
+int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
+{
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ u16 mask = header->ConnectedBitmask;
+
+ // TODO: check if the FIFO is full!
+
+ MPPacketHeader pktheader;
+ pktheader.Magic = 0x4946494E;
+ pktheader.SenderID = InstanceID;
+ pktheader.Type = type;
+ pktheader.Length = len;
+ pktheader.Timestamp = timestamp;
+
+ type &= 0xFFFF;
+ int nfifo = (type == 2) ? 1 : 0;
+ FIFOWrite(nfifo, &pktheader, sizeof(pktheader));
+ if (len)
+ FIFOWrite(nfifo, packet, len);
+
+ if (type == 1)
+ {
+ // NOTE: this is not guarded against, say, multiple multiplay games happening on the same machine
+ // we would need to pass the packet's SenderID through the wifi module for that
+ header->MPHostInstanceID = InstanceID;
+ header->MPReplyBitmask = 0;
+ ReplyReadOffset = header->ReplyWriteOffset;
+ SemReset(16 + InstanceID);
+ }
+ else if (type == 2)
+ {
+ header->MPReplyBitmask |= (1 << InstanceID);
+ }
+
+ MPQueue->unlock();
+
+ if (type == 2)
+ {
+ SemPost(16 + header->MPHostInstanceID);
+ }
+ else
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ if (mask & (1<<i))
+ SemPost(i);
+ }
+ }
+
+ return len;
+}
+
+int RecvPacketGeneric(u8* packet, bool block, u64* timestamp)
+{
+ for (;;)
+ {
+ if (!SemWait(InstanceID, block ? RecvTimeout : 0))
+ {
+ return 0;
+ }
+
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ MPPacketHeader pktheader;
+ FIFORead(0, &pktheader, sizeof(pktheader));
+
+ if (pktheader.Magic != 0x4946494E)
+ {
+ printf("PACKET FIFO OVERFLOW\n");
+ PacketReadOffset = header->PacketWriteOffset;
+ SemReset(InstanceID);
+ MPQueue->unlock();
+ return 0;
+ }
+
+ if (pktheader.SenderID == InstanceID)
+ {
+ // skip this packet
+ PacketReadOffset += pktheader.Length;
+ if (PacketReadOffset >= kPacketEnd)
+ PacketReadOffset += kPacketStart - kPacketEnd;
+
+ MPQueue->unlock();
+ continue;
+ }
+
+ if (pktheader.Length)
+ {
+ FIFORead(0, packet, pktheader.Length);
+
+ if (pktheader.Type == 1)
+ LastHostID = pktheader.SenderID;
+ }
+
+ if (timestamp) *timestamp = pktheader.Timestamp;
+ MPQueue->unlock();
+ return pktheader.Length;
+ }
+}
+
+int SendPacket(u8* packet, int len, u64 timestamp)
+{
+ return SendPacketGeneric(0, packet, len, timestamp);
+}
+
+int RecvPacket(u8* packet, u64* timestamp)
+{
+ return RecvPacketGeneric(packet, false, timestamp);
+}
+
+
+int SendCmd(u8* packet, int len, u64 timestamp)
+{
+ return SendPacketGeneric(1, packet, len, timestamp);
+}
+
+int SendReply(u8* packet, int len, u64 timestamp, u16 aid)
+{
+ return SendPacketGeneric(2 | (aid<<16), packet, len, timestamp);
+}
+
+int SendAck(u8* packet, int len, u64 timestamp)
+{
+ return SendPacketGeneric(3, packet, len, timestamp);
+}
+
+int RecvHostPacket(u8* packet, u64* timestamp)
+{
+ if (LastHostID != -1)
+ {
+ // check if the host is still connected
+
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+ u16 curinstmask = header->ConnectedBitmask;
+ MPQueue->unlock();
+
+ if (!(curinstmask & (1 << LastHostID)))
+ return -1;
+ }
+
+ return RecvPacketGeneric(packet, true, timestamp);
+}
+
+u16 RecvReplies(u8* packets, u64 timestamp, u16 aidmask)
+{
+ u16 ret = 0;
+ u16 myinstmask = (1 << InstanceID);
+ u16 curinstmask;
+
+ {
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+ curinstmask = header->ConnectedBitmask;
+ MPQueue->unlock();
+ }
+
+ // if all clients have left: return early
+ if ((myinstmask & curinstmask) == curinstmask)
+ return 0;
+
+ for (;;)
+ {
+ if (!SemWait(16+InstanceID, RecvTimeout))
+ {
+ // no more replies available
+ return ret;
+ }
+
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ MPPacketHeader pktheader;
+ FIFORead(1, &pktheader, sizeof(pktheader));
+
+ if (pktheader.Magic != 0x4946494E)
+ {
+ printf("REPLY FIFO OVERFLOW\n");
+ ReplyReadOffset = header->ReplyWriteOffset;
+ SemReset(16+InstanceID);
+ MPQueue->unlock();
+ return 0;
+ }
+
+ if ((pktheader.SenderID == InstanceID) || // packet we sent out (shouldn't happen, but hey)
+ (pktheader.Timestamp < (timestamp - 32))) // stale packet
+ {
+ // skip this packet
+ ReplyReadOffset += pktheader.Length;
+ if (ReplyReadOffset >= kReplyEnd)
+ ReplyReadOffset += kReplyStart - kReplyEnd;
+
+ MPQueue->unlock();
+ continue;
+ }
+
+ if (pktheader.Length)
+ {
+ u32 aid = (pktheader.Type >> 16);
+ FIFORead(1, &packets[(aid-1)*1024], pktheader.Length);
+ ret |= (1 << aid);
+ }
+
+ myinstmask |= (1 << pktheader.SenderID);
+ if (((myinstmask & curinstmask) == curinstmask) ||
+ ((ret & aidmask) == aidmask))
+ {
+ // all the clients have sent their reply
+
+ MPQueue->unlock();
+ return ret;
+ }
+
+ MPQueue->unlock();
+ }
+}
+
+}
+
diff --git a/src/NDSCart_SRAMManager.h b/src/frontend/qt_sdl/LocalMP.h
index 89b65ce..51dfcb9 100644
--- a/src/NDSCart_SRAMManager.h
+++ b/src/frontend/qt_sdl/LocalMP.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -16,24 +16,30 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
-#ifndef NDSCART_SRAMMANAGER_H
-#define NDSCART_SRAMMANAGER_H
+#ifndef LOCALMP_H
+#define LOCALMP_H
#include "types.h"
-namespace NDSCart_SRAMManager
+namespace LocalMP
{
- extern u32 SecondaryBufferLength;
- bool Init();
- void DeInit();
+bool Init();
+void DeInit();
- void Setup(const char* path, u8* buffer, u32 length);
- void RequestFlush();
+void SetRecvTimeout(int timeout);
+
+void Begin();
+void End();
+
+int SendPacket(u8* data, int len, u64 timestamp);
+int RecvPacket(u8* data, u64* timestamp);
+int SendCmd(u8* data, int len, u64 timestamp);
+int SendReply(u8* data, int len, u64 timestamp, u16 aid);
+int SendAck(u8* data, int len, u64 timestamp);
+int RecvHostPacket(u8* data, u64* timestamp);
+u16 RecvReplies(u8* data, u64 timestamp, u16 aidmask);
- bool NeedsFlush();
- void FlushSecondaryBuffer(u8* dst = NULL, s32 dstLength = 0);
- void UpdateBuffer(u8* src, s32 srcLength);
}
-#endif // NDSCART_SRAMMANAGER_H \ No newline at end of file
+#endif // LOCALMP_H
diff --git a/src/frontend/qt_sdl/MPSettingsDialog.cpp b/src/frontend/qt_sdl/MPSettingsDialog.cpp
new file mode 100644
index 0000000..e311422
--- /dev/null
+++ b/src/frontend/qt_sdl/MPSettingsDialog.cpp
@@ -0,0 +1,73 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 <QMessageBox>
+
+#include "types.h"
+#include "Platform.h"
+#include "Config.h"
+
+#include "LAN_Socket.h"
+#include "LAN_PCap.h"
+#include "Wifi.h"
+
+#include "MPSettingsDialog.h"
+#include "ui_MPSettingsDialog.h"
+
+
+MPSettingsDialog* MPSettingsDialog::currentDlg = nullptr;
+
+extern bool RunningSomething;
+
+
+MPSettingsDialog::MPSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MPSettingsDialog)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ grpAudioMode = new QButtonGroup(this);
+ grpAudioMode->addButton(ui->rbAudioAll, 0);
+ grpAudioMode->addButton(ui->rbAudioOneOnly, 1);
+ grpAudioMode->addButton(ui->rbAudioActiveOnly, 2);
+ grpAudioMode->button(Config::MPAudioMode)->setChecked(true);
+
+ ui->sbReceiveTimeout->setValue(Config::MPRecvTimeout);
+}
+
+MPSettingsDialog::~MPSettingsDialog()
+{
+ delete ui;
+}
+
+void MPSettingsDialog::done(int r)
+{
+ if (r == QDialog::Accepted)
+ {
+ Config::MPAudioMode = grpAudioMode->checkedId();
+ Config::MPRecvTimeout = ui->sbReceiveTimeout->value();
+
+ Config::Save();
+ }
+
+ QDialog::done(r);
+
+ closeDlg();
+}
+
+//
diff --git a/src/frontend/qt_sdl/MPSettingsDialog.h b/src/frontend/qt_sdl/MPSettingsDialog.h
new file mode 100644
index 0000000..fe917e8
--- /dev/null
+++ b/src/frontend/qt_sdl/MPSettingsDialog.h
@@ -0,0 +1,65 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 MPSETTINGSDIALOG_H
+#define MPSETTINGSDIALOG_H
+
+#include <QDialog>
+#include <QButtonGroup>
+
+namespace Ui { class MPSettingsDialog; }
+class MPSettingsDialog;
+
+class MPSettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit MPSettingsDialog(QWidget* parent);
+ ~MPSettingsDialog();
+
+ static MPSettingsDialog* currentDlg;
+ static MPSettingsDialog* openDlg(QWidget* parent)
+ {
+ if (currentDlg)
+ {
+ currentDlg->activateWindow();
+ return currentDlg;
+ }
+
+ currentDlg = new MPSettingsDialog(parent);
+ currentDlg->open();
+ return currentDlg;
+ }
+ static void closeDlg()
+ {
+ currentDlg = nullptr;
+ }
+
+private slots:
+ void done(int r);
+
+ //
+
+private:
+ Ui::MPSettingsDialog* ui;
+
+ QButtonGroup* grpAudioMode;
+};
+
+#endif // MPSETTINGSDIALOG_H
diff --git a/src/frontend/qt_sdl/MPSettingsDialog.ui b/src/frontend/qt_sdl/MPSettingsDialog.ui
new file mode 100644
index 0000000..bce0fc9
--- /dev/null
+++ b/src/frontend/qt_sdl/MPSettingsDialog.ui
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MPSettingsDialog</class>
+ <widget class="QDialog" name="MPSettingsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>466</width>
+ <height>202</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Multiplayer settings - melonDS</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Audio output</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="0">
+ <widget class="QRadioButton" name="rbAudioOneOnly">
+ <property name="text">
+ <string>Instance 1 only</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="rbAudioAll">
+ <property name="text">
+ <string>All instances</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="rbAudioActiveOnly">
+ <property name="text">
+ <string>Active instance only</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Network</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="sbReceiveTimeout">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>1000</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Data reception timeout: </string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>milliseconds</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>MPSettingsDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>MPSettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp
index d9f75fd..6f060a9 100644
--- a/src/frontend/qt_sdl/OSD.cpp
+++ b/src/frontend/qt_sdl/OSD.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -146,7 +146,7 @@ void LayoutText(const char* text, u32* width, u32* height, int* breaks)
u32 w = 0;
u32 h = 14;
u32 totalw = 0;
- u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
+ u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2);
int lastbreak = -1;
int numbrk = 0;
u16* ptr;
@@ -236,7 +236,7 @@ void RenderText(u32 color, const char* text, Item* item)
memset(item->Bitmap, 0, w*h*sizeof(u32));
u32 x = 0, y = 1;
- u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
+ u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2);
int curline = 0;
u16* ptr;
diff --git a/src/frontend/qt_sdl/OSD.h b/src/frontend/qt_sdl/OSD.h
index 8fbfc7b..d624fc6 100644
--- a/src/frontend/qt_sdl/OSD.h
+++ b/src/frontend/qt_sdl/OSD.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/OSD_shaders.h b/src/frontend/qt_sdl/OSD_shaders.h
index 3ff51a9..e224fd1 100644
--- a/src/frontend/qt_sdl/OSD_shaders.h
+++ b/src/frontend/qt_sdl/OSD_shaders.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp
new file mode 100644
index 0000000..286032e
--- /dev/null
+++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp
@@ -0,0 +1,126 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 <QFileDialog>
+#include <QMessageBox>
+
+#include "types.h"
+#include "Config.h"
+#include "Platform.h"
+
+#include "PathSettingsDialog.h"
+#include "ui_PathSettingsDialog.h"
+
+
+PathSettingsDialog* PathSettingsDialog::currentDlg = nullptr;
+
+extern std::string EmuDirectory;
+extern bool RunningSomething;
+
+bool PathSettingsDialog::needsReset = false;
+
+
+PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PathSettingsDialog)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ ui->txtSaveFilePath->setText(QString::fromStdString(Config::SaveFilePath));
+ ui->txtSavestatePath->setText(QString::fromStdString(Config::SavestatePath));
+ ui->txtCheatFilePath->setText(QString::fromStdString(Config::CheatFilePath));
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Configuring paths for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
+}
+
+PathSettingsDialog::~PathSettingsDialog()
+{
+ delete ui;
+}
+
+void PathSettingsDialog::done(int r)
+{
+ needsReset = false;
+
+ if (r == QDialog::Accepted)
+ {
+ std::string saveFilePath = ui->txtSaveFilePath->text().toStdString();
+ std::string savestatePath = ui->txtSavestatePath->text().toStdString();
+ std::string cheatFilePath = ui->txtCheatFilePath->text().toStdString();
+
+ if ( saveFilePath != Config::SaveFilePath
+ || savestatePath != Config::SavestatePath
+ || cheatFilePath != Config::CheatFilePath)
+ {
+ if (RunningSomething
+ && QMessageBox::warning(this, "Reset necessary to apply changes",
+ "The emulation will be reset for the changes to take place.",
+ QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok)
+ return;
+
+ Config::SaveFilePath = saveFilePath;
+ Config::SavestatePath = savestatePath;
+ Config::CheatFilePath = cheatFilePath;
+
+ Config::Save();
+
+ needsReset = true;
+ }
+ }
+
+ QDialog::done(r);
+
+ closeDlg();
+}
+
+void PathSettingsDialog::on_btnSaveFileBrowse_clicked()
+{
+ QString dir = QFileDialog::getExistingDirectory(this,
+ "Select save files path...",
+ QString::fromStdString(EmuDirectory));
+
+ if (dir.isEmpty()) return;
+
+ ui->txtSaveFilePath->setText(dir);
+}
+
+void PathSettingsDialog::on_btnSavestateBrowse_clicked()
+{
+ QString dir = QFileDialog::getExistingDirectory(this,
+ "Select savestates path...",
+ QString::fromStdString(EmuDirectory));
+
+ if (dir.isEmpty()) return;
+
+ ui->txtSavestatePath->setText(dir);
+}
+
+void PathSettingsDialog::on_btnCheatFileBrowse_clicked()
+{
+ QString dir = QFileDialog::getExistingDirectory(this,
+ "Select cheat files path...",
+ QString::fromStdString(EmuDirectory));
+
+ if (dir.isEmpty()) return;
+
+ ui->txtCheatFilePath->setText(dir);
+}
diff --git a/src/frontend/qt_sdl/PathSettingsDialog.h b/src/frontend/qt_sdl/PathSettingsDialog.h
new file mode 100644
index 0000000..ef4fd2d
--- /dev/null
+++ b/src/frontend/qt_sdl/PathSettingsDialog.h
@@ -0,0 +1,67 @@
+
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 PATHSETTINGSDIALOG_H
+#define PATHSETTINGSDIALOG_H
+
+#include <QDialog>
+
+namespace Ui { class PathSettingsDialog; }
+class PathSettingsDialog;
+
+class PathSettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PathSettingsDialog(QWidget* parent);
+ ~PathSettingsDialog();
+
+ static PathSettingsDialog* currentDlg;
+ static PathSettingsDialog* openDlg(QWidget* parent)
+ {
+ if (currentDlg)
+ {
+ currentDlg->activateWindow();
+ return currentDlg;
+ }
+
+ currentDlg = new PathSettingsDialog(parent);
+ currentDlg->open();
+ return currentDlg;
+ }
+ static void closeDlg()
+ {
+ currentDlg = nullptr;
+ }
+
+ static bool needsReset;
+
+private slots:
+ void done(int r);
+
+ void on_btnSaveFileBrowse_clicked();
+ void on_btnSavestateBrowse_clicked();
+ void on_btnCheatFileBrowse_clicked();
+
+private:
+ Ui::PathSettingsDialog* ui;
+};
+
+#endif // PATHSETTINGSDIALOG_H
diff --git a/src/frontend/qt_sdl/PathSettingsDialog.ui b/src/frontend/qt_sdl/PathSettingsDialog.ui
new file mode 100644
index 0000000..295b1c4
--- /dev/null
+++ b/src/frontend/qt_sdl/PathSettingsDialog.ui
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PathSettingsDialog</class>
+ <widget class="QDialog" name="PathSettingsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>439</width>
+ <height>185</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Path settings - melonDS</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Cheat files path:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QPushButton" name="btnCheatFileBrowse">
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Savestates path:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" colspan="3">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="txtSavestatePath">
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="btnSaveFileBrowse">
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="txtCheatFilePath">
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" colspan="3">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QPushButton" name="btnSavestateBrowse">
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Save files path:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="3">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Leave a path blank to use the current ROM's path.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="txtSaveFilePath">
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="3">
+ <widget class="QLabel" name="lblInstanceNum">
+ <property name="text">
+ <string>Configuring paths for instance X</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>txtSaveFilePath</tabstop>
+ <tabstop>btnSaveFileBrowse</tabstop>
+ <tabstop>txtSavestatePath</tabstop>
+ <tabstop>btnSavestateBrowse</tabstop>
+ <tabstop>txtCheatFilePath</tabstop>
+ <tabstop>btnCheatFileBrowse</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>PathSettingsDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>PathSettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp
index 812c953..f9eaf42 100644
--- a/src/frontend/qt_sdl/Platform.cpp
+++ b/src/frontend/qt_sdl/Platform.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -20,28 +20,7 @@
#include <stdlib.h>
#include <string.h>
-#ifdef __WIN32__
- #define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
- #include <winsock2.h>
- #include <windows.h>
- //#include <knownfolders.h> // FUCK THAT SHIT
- #include <shlobj.h>
- #include <ws2tcpip.h>
- #include <io.h>
- #define dup _dup
- #define socket_t SOCKET
- #define sockaddr_t SOCKADDR
-#else
- #include <unistd.h>
- #include <netinet/in.h>
- #include <sys/select.h>
- #include <sys/socket.h>
-
- #define socket_t int
- #define sockaddr_t struct sockaddr
- #define closesocket close
-#endif
-
+#include <string>
#include <QStandardPaths>
#include <QString>
#include <QDir>
@@ -49,31 +28,83 @@
#include <QSemaphore>
#include <QMutex>
#include <QOpenGLContext>
+#include <QSharedMemory>
#include "Platform.h"
#include "Config.h"
+#include "ROMManager.h"
+#include "CameraManager.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
-#include <string>
-
-#ifndef INVALID_SOCKET
- #define INVALID_SOCKET (socket_t)-1
-#endif
+#include "LocalMP.h"
std::string EmuDirectory;
+extern CameraManager* camManager[2];
+
void emuStop();
namespace Platform
{
-socket_t MPSocket;
-sockaddr_t MPSendAddr;
-u8 PacketBuffer[2048];
+QSharedMemory* IPCBuffer = nullptr;
+int IPCInstanceID;
+
+void IPCInit()
+{
+ IPCInstanceID = 0;
+
+ IPCBuffer = new QSharedMemory("melonIPC");
+
+ if (!IPCBuffer->attach())
+ {
+ printf("IPC sharedmem doesn't exist. creating\n");
+ if (!IPCBuffer->create(1024))
+ {
+ printf("IPC sharedmem create failed :(\n");
+ delete IPCBuffer;
+ IPCBuffer = nullptr;
+ return;
+ }
+
+ IPCBuffer->lock();
+ memset(IPCBuffer->data(), 0, IPCBuffer->size());
+ IPCBuffer->unlock();
+ }
+
+ IPCBuffer->lock();
+ u8* data = (u8*)IPCBuffer->data();
+ u16 mask = *(u16*)&data[0];
+ for (int i = 0; i < 16; i++)
+ {
+ if (!(mask & (1<<i)))
+ {
+ IPCInstanceID = i;
+ *(u16*)&data[0] |= (1<<i);
+ break;
+ }
+ }
+ IPCBuffer->unlock();
+
+ printf("IPC: instance ID %d\n", IPCInstanceID);
+}
+
+void IPCDeInit()
+{
+ if (IPCBuffer)
+ {
+ IPCBuffer->lock();
+ u8* data = (u8*)IPCBuffer->data();
+ *(u16*)&data[0] &= ~(1<<IPCInstanceID);
+ IPCBuffer->unlock();
-#define NIFI_VER 1
+ IPCBuffer->detach();
+ delete IPCBuffer;
+ }
+ IPCBuffer = nullptr;
+}
void Init(int argc, char** argv)
@@ -109,10 +140,13 @@ void Init(int argc, char** argv)
confdir = config.absolutePath() + "/melonDS/";
EmuDirectory = confdir.toStdString();
#endif
+
+ IPCInit();
}
void DeInit()
{
+ IPCDeInit();
}
@@ -122,6 +156,22 @@ void StopEmu()
}
+int InstanceID()
+{
+ return IPCInstanceID;
+}
+
+std::string InstanceFileSuffix()
+{
+ int inst = IPCInstanceID;
+ if (inst == 0) return "";
+
+ char suffix[16] = {0};
+ snprintf(suffix, 15, ".%d", inst+1);
+ return suffix;
+}
+
+
int GetConfigInt(ConfigEntry entry)
{
const int imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
@@ -168,7 +218,6 @@ bool GetConfigBool(ConfigEntry entry)
case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0;
case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0;
- case Firm_RandomizeMAC: return Config::RandomizeMAC != 0;
case Firm_OverrideSettings: return Config::FirmwareOverrideSettings != 0;
}
@@ -207,7 +256,7 @@ bool GetConfigArray(ConfigEntry entry, void* data)
{
case Firm_MAC:
{
- char* mac_in = Config::FirmwareMAC;
+ std::string& mac_in = Config::FirmwareMAC;
u8* mac_out = (u8*)data;
int o = 0;
@@ -371,140 +420,80 @@ bool Mutex_TryLock(Mutex* mutex)
return ((QMutex*) mutex)->try_lock();
}
-
-bool MP_Init()
+void Sleep(u64 usecs)
{
- int opt_true = 1;
- int res;
+ QThread::usleep(usecs);
+}
-#ifdef __WIN32__
- WSADATA wsadata;
- if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
- {
- return false;
- }
-#endif // __WIN32__
- MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
- if (MPSocket < 0)
- {
- return false;
- }
-
- res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt_true, sizeof(int));
- if (res < 0)
- {
- closesocket(MPSocket);
- MPSocket = INVALID_SOCKET;
- return false;
- }
+void WriteNDSSave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen)
+{
+ if (ROMManager::NDSSave)
+ ROMManager::NDSSave->RequestFlush(savedata, savelen, writeoffset, writelen);
+}
- sockaddr_t saddr;
- saddr.sa_family = AF_INET;
- *(u32*)&saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
- *(u16*)&saddr.sa_data[0] = htons(7064);
- res = bind(MPSocket, &saddr, sizeof(sockaddr_t));
- if (res < 0)
- {
- closesocket(MPSocket);
- MPSocket = INVALID_SOCKET;
- return false;
- }
+void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen)
+{
+ if (ROMManager::GBASave)
+ ROMManager::GBASave->RequestFlush(savedata, savelen, writeoffset, writelen);
+}
- res = setsockopt(MPSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&opt_true, sizeof(int));
- if (res < 0)
- {
- closesocket(MPSocket);
- MPSocket = INVALID_SOCKET;
- return false;
- }
- MPSendAddr.sa_family = AF_INET;
- *(u32*)&MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
- *(u16*)&MPSendAddr.sa_data[0] = htons(7064);
- return true;
+bool MP_Init()
+{
+ return LocalMP::Init();
}
void MP_DeInit()
{
- if (MPSocket >= 0)
- closesocket(MPSocket);
-
-#ifdef __WIN32__
- WSACleanup();
-#endif // __WIN32__
+ return LocalMP::DeInit();
}
-int MP_SendPacket(u8* data, int len)
+void MP_Begin()
{
- if (MPSocket < 0)
- return 0;
-
- if (len > 2048-8)
- {
- printf("MP_SendPacket: error: packet too long (%d)\n", len);
- return 0;
- }
-
- *(u32*)&PacketBuffer[0] = htonl(0x4946494E); // NIFI
- PacketBuffer[4] = NIFI_VER;
- PacketBuffer[5] = 0;
- *(u16*)&PacketBuffer[6] = htons(len);
- memcpy(&PacketBuffer[8], data, len);
-
- int slen = sendto(MPSocket, (const char*)PacketBuffer, len+8, 0, &MPSendAddr, sizeof(sockaddr_t));
- if (slen < 8) return 0;
- return slen - 8;
+ return LocalMP::Begin();
}
-int MP_RecvPacket(u8* data, bool block)
+void MP_End()
{
- if (MPSocket < 0)
- return 0;
-
- fd_set fd;
- struct timeval tv;
-
- FD_ZERO(&fd);
- FD_SET(MPSocket, &fd);
- tv.tv_sec = 0;
- tv.tv_usec = block ? 5000 : 0;
-
- if (!select(MPSocket+1, &fd, 0, 0, &tv))
- {
- return 0;
- }
+ return LocalMP::End();
+}
- sockaddr_t fromAddr;
- socklen_t fromLen = sizeof(sockaddr_t);
- int rlen = recvfrom(MPSocket, (char*)PacketBuffer, 2048, 0, &fromAddr, &fromLen);
- if (rlen < 8+24)
- {
- return 0;
- }
- rlen -= 8;
+int MP_SendPacket(u8* data, int len, u64 timestamp)
+{
+ return LocalMP::SendPacket(data, len, timestamp);
+}
- if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E)
- {
- return 0;
- }
+int MP_RecvPacket(u8* data, u64* timestamp)
+{
+ return LocalMP::RecvPacket(data, timestamp);
+}
- if (PacketBuffer[4] != NIFI_VER)
- {
- return 0;
- }
+int MP_SendCmd(u8* data, int len, u64 timestamp)
+{
+ return LocalMP::SendCmd(data, len, timestamp);
+}
- if (ntohs(*(u16*)&PacketBuffer[6]) != rlen)
- {
- return 0;
- }
+int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid)
+{
+ return LocalMP::SendReply(data, len, timestamp, aid);
+}
- memcpy(data, &PacketBuffer[8], rlen);
- return rlen;
+int MP_SendAck(u8* data, int len, u64 timestamp)
+{
+ return LocalMP::SendAck(data, len, timestamp);
}
+int MP_RecvHostPacket(u8* data, u64* timestamp)
+{
+ return LocalMP::RecvHostPacket(data, timestamp);
+}
+u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask)
+{
+ return LocalMP::RecvReplies(data, timestamp, aidmask);
+}
bool LAN_Init()
{
@@ -549,9 +538,20 @@ int LAN_RecvPacket(u8* data)
return LAN_Socket::RecvPacket(data);
}
-void Sleep(u64 usecs)
+
+void Camera_Start(int num)
{
- QThread::usleep(usecs);
+ return camManager[num]->start();
+}
+
+void Camera_Stop(int num)
+{
+ return camManager[num]->stop();
+}
+
+void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv)
+{
+ return camManager[num]->captureFrame(frame, width, height, yuv);
}
}
diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp
new file mode 100644
index 0000000..89f74e5
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp
@@ -0,0 +1,154 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 "PowerManagementDialog.h"
+#include "ui_PowerManagementDialog.h"
+
+#include "SPI.h"
+#include "DSi_I2C.h"
+#include "NDS.h"
+#include "Config.h"
+#include "Platform.h"
+
+#include "types.h"
+
+#include <QtDebug>
+
+PowerManagementDialog* PowerManagementDialog::currentDlg = nullptr;
+
+PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PowerManagementDialog)
+{
+ inited = false;
+
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ if (NDS::ConsoleType == 1)
+ {
+ ui->grpDSBattery->setEnabled(false);
+
+ oldDSiBatteryLevel = DSi_BPTWL::GetBatteryLevel();
+ oldDSiBatteryCharging = DSi_BPTWL::GetBatteryCharging();
+ }
+ else
+ {
+ ui->grpDSiBattery->setEnabled(false);
+
+ oldDSBatteryLevel = SPI_Powerman::GetBatteryLevelOkay();
+ }
+
+ updateDSBatteryLevelControls();
+
+ ui->cbDSiBatteryCharging->setChecked(DSi_BPTWL::GetBatteryCharging());
+ int dsiBatterySliderPos;
+ switch (DSi_BPTWL::GetBatteryLevel())
+ {
+ case DSi_BPTWL::batteryLevel_AlmostEmpty: dsiBatterySliderPos = 0; break;
+ case DSi_BPTWL::batteryLevel_Low: dsiBatterySliderPos = 1; break;
+ case DSi_BPTWL::batteryLevel_Half: dsiBatterySliderPos = 2; break;
+ case DSi_BPTWL::batteryLevel_ThreeQuarters: dsiBatterySliderPos = 3; break;
+ case DSi_BPTWL::batteryLevel_Full: dsiBatterySliderPos = 4; break;
+ }
+ ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos);
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Setting battery levels for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
+
+ inited = true;
+}
+
+PowerManagementDialog::~PowerManagementDialog()
+{
+ delete ui;
+}
+
+void PowerManagementDialog::done(int r)
+{
+ if (r == QDialog::Accepted)
+ {
+ if (NDS::ConsoleType == 1)
+ {
+ Config::DSiBatteryLevel = DSi_BPTWL::GetBatteryLevel();
+ Config::DSiBatteryCharging = DSi_BPTWL::GetBatteryCharging();
+ }
+ else
+ {
+ Config::DSBatteryLevelOkay = SPI_Powerman::GetBatteryLevelOkay();
+ }
+ }
+ else
+ {
+ if (NDS::ConsoleType == 1)
+ {
+ DSi_BPTWL::SetBatteryLevel(oldDSiBatteryLevel);
+ DSi_BPTWL::SetBatteryCharging(oldDSiBatteryCharging);
+ }
+ else
+ {
+ SPI_Powerman::SetBatteryLevelOkay(oldDSBatteryLevel);
+ }
+ }
+
+ QDialog::done(r);
+
+ closeDlg();
+}
+
+void PowerManagementDialog::on_rbDSBatteryLow_clicked()
+{
+ SPI_Powerman::SetBatteryLevelOkay(false);
+}
+
+void PowerManagementDialog::on_rbDSBatteryOkay_clicked()
+{
+ SPI_Powerman::SetBatteryLevelOkay(true);
+}
+
+void PowerManagementDialog::updateDSBatteryLevelControls()
+{
+ if (SPI_Powerman::GetBatteryLevelOkay())
+ ui->rbDSBatteryOkay->setChecked(true);
+ else
+ ui->rbDSBatteryLow->setChecked(true);
+}
+
+void PowerManagementDialog::on_cbDSiBatteryCharging_toggled()
+{
+ DSi_BPTWL::SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked());
+}
+
+void PowerManagementDialog::on_sliderDSiBatteryLevel_valueChanged(int value)
+{
+ if (!inited) return;
+
+ u8 newBatteryLevel;
+ switch (value)
+ {
+ case 0: newBatteryLevel = DSi_BPTWL::batteryLevel_AlmostEmpty; break;
+ case 1: newBatteryLevel = DSi_BPTWL::batteryLevel_Low; break;
+ case 2: newBatteryLevel = DSi_BPTWL::batteryLevel_Half; break;
+ case 3: newBatteryLevel = DSi_BPTWL::batteryLevel_ThreeQuarters; break;
+ case 4: newBatteryLevel = DSi_BPTWL::batteryLevel_Full; break;
+ }
+ DSi_BPTWL::SetBatteryLevel(newBatteryLevel);
+ updateDSBatteryLevelControls();
+}
+
diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h
new file mode 100644
index 0000000..f335a5e
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h
@@ -0,0 +1,77 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 POWERMANAGEMENTDIALOG_H
+#define POWERMANAGEMENTDIALOG_H
+
+#include <QDialog>
+#include <QAbstractButton>
+
+#include "types.h"
+
+namespace Ui { class PowerManagementDialog; }
+class PowerManagementDialog;
+
+class PowerManagementDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PowerManagementDialog(QWidget* parent);
+ ~PowerManagementDialog();
+
+ static PowerManagementDialog* currentDlg;
+ static PowerManagementDialog* openDlg(QWidget* parent)
+ {
+ if (currentDlg)
+ {
+ currentDlg->activateWindow();
+ return currentDlg;
+ }
+
+ currentDlg = new PowerManagementDialog(parent);
+ currentDlg->open();
+ return currentDlg;
+ }
+ static void closeDlg()
+ {
+ currentDlg = nullptr;
+ }
+
+private slots:
+ void done(int r);
+
+ void on_rbDSBatteryLow_clicked();
+ void on_rbDSBatteryOkay_clicked();
+
+ void on_cbDSiBatteryCharging_toggled();
+ void on_sliderDSiBatteryLevel_valueChanged(int value);
+
+private:
+ Ui::PowerManagementDialog* ui;
+
+ bool inited;
+ bool oldDSBatteryLevel;
+ u8 oldDSiBatteryLevel;
+ bool oldDSiBatteryCharging;
+
+ void updateDSBatteryLevelControls();
+};
+
+#endif // POWERMANAGEMENTDIALOG_H
+
diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui
new file mode 100644
index 0000000..77af225
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PowerManagementDialog</class>
+ <widget class="QDialog" name="PowerManagementDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>562</width>
+ <height>288</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Power management - melonDS</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item row="4" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QGroupBox" name="grpDSiBattery">
+ <property name="title">
+ <string>DSi Battery</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Almost Empty</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0" colspan="5">
+ <widget class="QCheckBox" name="cbDSiBatteryCharging">
+ <property name="text">
+ <string>Charging</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="8">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Full</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_1">
+ <property name="text">
+ <string>Battery Level</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/battery.qrc">:/dsibattery/dsi_batteryalmostempty.svg</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="4">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/battery.qrc">:/dsibattery/dsi_batterylow.svg</pixmap>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="5">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/battery.qrc">:/dsibattery/dsi_battery2.svg</pixmap>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="6">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/battery.qrc">:/dsibattery/dsi_battery3.svg</pixmap>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="7">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources/battery.qrc">:/dsibattery/dsi_batteryfull.svg</pixmap>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3" colspan="5">
+ <widget class="QSlider" name="sliderDSiBatteryLevel">
+ <property name="maximum">
+ <number>4</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="3">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="grpDSBattery">
+ <property name="title">
+ <string>DS Battery</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="1">
+ <widget class="QRadioButton" name="rbDSBatteryLow">
+ <property name="text">
+ <string>Low</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" rowspan="2">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Battery Level</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QRadioButton" name="rbDSBatteryOkay">
+ <property name="text">
+ <string>Okay</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblInstanceNum">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Configuring settings for instance X</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="resources/battery.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>PowerManagementDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>PowerManagementDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/frontend/qt_sdl/PowerManagement/resources/battery.qrc b/src/frontend/qt_sdl/PowerManagement/resources/battery.qrc
new file mode 100644
index 0000000..7f9c95b
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/resources/battery.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="dsibattery">
+ <file>dsi_batteryalmostempty.svg</file>
+ <file>dsi_batterylow.svg</file>
+ <file>dsi_battery2.svg</file>
+ <file>dsi_battery3.svg</file>
+ <file>dsi_batteryfull.svg</file>
+ </qresource>
+</RCC>
diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg
new file mode 100644
index 0000000..e9c4b75
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="12.236976mm"
+ height="6.2838535mm"
+ viewBox="0 0 12.236976 6.283854"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="battery2.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ff8100"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="1"
+ inkscape:pagecheckerboard="false"
+ inkscape:document-units="mm"
+ showgrid="true"
+ height="200mm"
+ showborder="true"
+ inkscape:showpageshadow="false"
+ borderlayer="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="10.24"
+ inkscape:cx="2.7832031"
+ inkscape:cy="2.0507813"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid11"
+ spacingx="0.26458333"
+ spacingy="0.26458333"
+ originx="-91.777345"
+ originy="-47.128916" />
+ </sodipodi:namedview>
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-91.777344,-47.12891)">
+ <rect
+ style="fill:#000000;fill-rule:evenodd;stroke-width:0.0211535"
+ id="rect153"
+ width="1.3229166"
+ height="2.9765623"
+ x="91.777344"
+ y="48.782555" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect177"
+ width="10.914062"
+ height="0.66145831"
+ x="93.100258"
+ y="47.12891" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect179"
+ width="0.66145831"
+ height="4.9609375"
+ x="93.100258"
+ y="47.790367" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect181"
+ width="10.914062"
+ height="0.66145873"
+ x="93.100258"
+ y="52.751305" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect183"
+ width="0.66145831"
+ height="4.9609375"
+ x="103.35286"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0162585"
+ id="rect318"
+ width="9.5911455"
+ height="0.49609375"
+ x="93.761719"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect408"
+ width="0.49609372"
+ height="3.96875"
+ x="93.761719"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.015706"
+ id="rect410"
+ width="9.5911455"
+ height="0.49609351"
+ x="93.761719"
+ y="52.255211" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect412"
+ width="0.4960939"
+ height="3.9687498"
+ x="102.85677"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0224686"
+ id="rect414"
+ width="0.66145849"
+ height="3.9687498"
+ x="95.911453"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-3"
+ width="0.66145885"
+ height="3.9687498"
+ x="98.226562"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-6"
+ width="0.66145819"
+ height="3.9687498"
+ x="100.54166"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1736"
+ width="1.6536455"
+ height="3.9687498"
+ x="94.257812"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1738"
+ width="1.6536458"
+ height="3.96875"
+ x="96.572914"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1740"
+ width="1.6536458"
+ height="3.96875"
+ x="98.888016"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1742"
+ width="1.6536458"
+ height="3.96875"
+ x="101.20312"
+ y="48.286461" />
+ </g>
+</svg>
diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg
new file mode 100644
index 0000000..d464ef3
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="12.236976mm"
+ height="6.2838535mm"
+ viewBox="0 0 12.236976 6.283854"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="battery3.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ff8100"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="1"
+ inkscape:pagecheckerboard="false"
+ inkscape:document-units="mm"
+ showgrid="true"
+ height="200mm"
+ showborder="true"
+ inkscape:showpageshadow="false"
+ borderlayer="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="10.24"
+ inkscape:cx="2.7832031"
+ inkscape:cy="2.0507813"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid11"
+ spacingx="0.26458333"
+ spacingy="0.26458333"
+ originx="-91.777345"
+ originy="-47.128916" />
+ </sodipodi:namedview>
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-91.777344,-47.12891)">
+ <rect
+ style="fill:#000000;fill-rule:evenodd;stroke-width:0.0211535"
+ id="rect153"
+ width="1.3229166"
+ height="2.9765623"
+ x="91.777344"
+ y="48.782555" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect177"
+ width="10.914062"
+ height="0.66145831"
+ x="93.100258"
+ y="47.12891" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect179"
+ width="0.66145831"
+ height="4.9609375"
+ x="93.100258"
+ y="47.790367" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect181"
+ width="10.914062"
+ height="0.66145873"
+ x="93.100258"
+ y="52.751305" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect183"
+ width="0.66145831"
+ height="4.9609375"
+ x="103.35286"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0162585"
+ id="rect318"
+ width="9.5911455"
+ height="0.49609375"
+ x="93.761719"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect408"
+ width="0.49609372"
+ height="3.96875"
+ x="93.761719"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.015706"
+ id="rect410"
+ width="9.5911455"
+ height="0.49609351"
+ x="93.761719"
+ y="52.255211" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect412"
+ width="0.4960939"
+ height="3.9687498"
+ x="102.85677"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0224686"
+ id="rect414"
+ width="0.66145849"
+ height="3.9687498"
+ x="95.911453"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-3"
+ width="0.66145885"
+ height="3.9687498"
+ x="98.226562"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-6"
+ width="0.66145819"
+ height="3.9687498"
+ x="100.54166"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1736"
+ width="1.6536455"
+ height="3.9687498"
+ x="94.257812"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1738"
+ width="1.6536458"
+ height="3.96875"
+ x="96.572914"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1740"
+ width="1.6536458"
+ height="3.96875"
+ x="98.888016"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1742"
+ width="1.6536458"
+ height="3.96875"
+ x="101.20312"
+ y="48.286461" />
+ </g>
+</svg>
diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg
new file mode 100644
index 0000000..4f598fa
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="12.236976mm"
+ height="6.2838535mm"
+ viewBox="0 0 12.236976 6.283854"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="dsi_batteryalmostempty.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ff8100"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="1"
+ inkscape:pagecheckerboard="false"
+ inkscape:document-units="mm"
+ showgrid="true"
+ height="200mm"
+ showborder="true"
+ inkscape:showpageshadow="false"
+ borderlayer="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="14.481547"
+ inkscape:cx="35.389866"
+ inkscape:cy="16.503762"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid11"
+ spacingx="0.26458333"
+ spacingy="0.26458333"
+ originx="-91.77735"
+ originy="-47.128928" />
+ </sodipodi:namedview>
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-91.777344,-47.12891)">
+ <rect
+ style="fill:#ff2a2a;fill-rule:evenodd;stroke-width:0.0211535"
+ id="rect153"
+ width="1.3229166"
+ height="2.9765623"
+ x="91.777344"
+ y="48.782555" />
+ <rect
+ style="fill:#ff2a2a;stroke-width:0.0162915"
+ id="rect177"
+ width="10.914062"
+ height="0.66145831"
+ x="93.100258"
+ y="47.12891" />
+ <rect
+ style="fill:#ff2a2a;stroke-width:0.0165364"
+ id="rect179"
+ width="0.66145831"
+ height="4.9609375"
+ x="93.100258"
+ y="47.790367" />
+ <rect
+ style="fill:#ff2a2a;stroke-width:0.0162915"
+ id="rect181"
+ width="10.914062"
+ height="0.66145873"
+ x="93.100258"
+ y="52.751305" />
+ <rect
+ style="fill:#ff2a2a;stroke-width:0.0165364"
+ id="rect183"
+ width="0.66145831"
+ height="4.9609375"
+ x="103.35286"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0162585"
+ id="rect318"
+ width="9.5911455"
+ height="0.49609375"
+ x="93.761719"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect408"
+ width="0.49609372"
+ height="3.96875"
+ x="93.761719"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.015706"
+ id="rect410"
+ width="9.5911455"
+ height="0.49609351"
+ x="93.761719"
+ y="52.255211" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect412"
+ width="0.4960939"
+ height="3.9687498"
+ x="102.85677"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0224686"
+ id="rect414"
+ width="0.66145849"
+ height="3.9687498"
+ x="95.911453"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-3"
+ width="0.66145885"
+ height="3.9687498"
+ x="98.226562"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-6"
+ width="0.66145819"
+ height="3.9687498"
+ x="100.54166"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1736"
+ width="1.6536455"
+ height="3.9687498"
+ x="94.257812"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1738"
+ width="1.6536458"
+ height="3.96875"
+ x="96.572914"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1740"
+ width="1.6536458"
+ height="3.96875"
+ x="98.888016"
+ y="48.286461" />
+ <rect
+ style="fill:#ff2a2a;stroke-width:0.0165364"
+ id="rect1742"
+ width="1.6536458"
+ height="3.96875"
+ x="101.20312"
+ y="48.286461" />
+ </g>
+</svg>
diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg
new file mode 100644
index 0000000..dbf8499
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="12.236976mm"
+ height="6.2838535mm"
+ viewBox="0 0 12.236976 6.283854"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="batteryfull.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ff8100"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="1"
+ inkscape:pagecheckerboard="false"
+ inkscape:document-units="mm"
+ showgrid="true"
+ height="200mm"
+ showborder="true"
+ inkscape:showpageshadow="false"
+ borderlayer="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="10.24"
+ inkscape:cx="2.7832031"
+ inkscape:cy="2.0507813"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid11"
+ spacingx="0.26458333"
+ spacingy="0.26458333"
+ originx="-91.777345"
+ originy="-47.128916" />
+ </sodipodi:namedview>
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-91.777344,-47.12891)">
+ <rect
+ style="fill:#000000;fill-rule:evenodd;stroke-width:0.0211535"
+ id="rect153"
+ width="1.3229166"
+ height="2.9765623"
+ x="91.777344"
+ y="48.782555" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect177"
+ width="10.914062"
+ height="0.66145831"
+ x="93.100258"
+ y="47.12891" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect179"
+ width="0.66145831"
+ height="4.9609375"
+ x="93.100258"
+ y="47.790367" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect181"
+ width="10.914062"
+ height="0.66145873"
+ x="93.100258"
+ y="52.751305" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect183"
+ width="0.66145831"
+ height="4.9609375"
+ x="103.35286"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0162585"
+ id="rect318"
+ width="9.5911455"
+ height="0.49609375"
+ x="93.761719"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect408"
+ width="0.49609372"
+ height="3.96875"
+ x="93.761719"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.015706"
+ id="rect410"
+ width="9.5911455"
+ height="0.49609351"
+ x="93.761719"
+ y="52.255211" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect412"
+ width="0.4960939"
+ height="3.9687498"
+ x="102.85677"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0224686"
+ id="rect414"
+ width="0.66145849"
+ height="3.9687498"
+ x="95.911453"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-3"
+ width="0.66145885"
+ height="3.9687498"
+ x="98.226562"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-6"
+ width="0.66145819"
+ height="3.9687498"
+ x="100.54166"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1736"
+ width="1.6536455"
+ height="3.9687498"
+ x="94.257812"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1738"
+ width="1.6536458"
+ height="3.96875"
+ x="96.572914"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1740"
+ width="1.6536458"
+ height="3.96875"
+ x="98.888016"
+ y="48.286461" />
+ <rect
+ style="fill:#00ccff;stroke-width:0.0165364"
+ id="rect1742"
+ width="1.6536458"
+ height="3.96875"
+ x="101.20312"
+ y="48.286461" />
+ </g>
+</svg>
diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg
new file mode 100644
index 0000000..d337675
--- /dev/null
+++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="12.236976mm"
+ height="6.2838535mm"
+ viewBox="0 0 12.236976 6.283854"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="batterylow.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ff8100"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="1"
+ inkscape:pagecheckerboard="false"
+ inkscape:document-units="mm"
+ showgrid="true"
+ height="200mm"
+ showborder="true"
+ inkscape:showpageshadow="false"
+ borderlayer="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="0.45254834"
+ inkscape:cx="325.93203"
+ inkscape:cy="232.01941"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid11"
+ spacingx="0.26458333"
+ spacingy="0.26458333"
+ originx="-91.777345"
+ originy="-47.128916" />
+ </sodipodi:namedview>
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-91.777344,-47.12891)">
+ <rect
+ style="fill:#000000;fill-rule:evenodd;stroke-width:0.0211535"
+ id="rect153"
+ width="1.3229166"
+ height="2.9765623"
+ x="91.777344"
+ y="48.782555" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect177"
+ width="10.914062"
+ height="0.66145831"
+ x="93.100258"
+ y="47.12891" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect179"
+ width="0.66145831"
+ height="4.9609375"
+ x="93.100258"
+ y="47.790367" />
+ <rect
+ style="fill:#000000;stroke-width:0.0162915"
+ id="rect181"
+ width="10.914062"
+ height="0.66145873"
+ x="93.100258"
+ y="52.751305" />
+ <rect
+ style="fill:#000000;stroke-width:0.0165364"
+ id="rect183"
+ width="0.66145831"
+ height="4.9609375"
+ x="103.35286"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0162585"
+ id="rect318"
+ width="9.5911455"
+ height="0.49609375"
+ x="93.761719"
+ y="47.790367" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect408"
+ width="0.49609372"
+ height="3.96875"
+ x="93.761719"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.015706"
+ id="rect410"
+ width="9.5911455"
+ height="0.49609351"
+ x="93.761719"
+ y="52.255211" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect412"
+ width="0.4960939"
+ height="3.9687498"
+ x="102.85677"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0224686"
+ id="rect414"
+ width="0.66145849"
+ height="3.9687498"
+ x="95.911453"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-3"
+ width="0.66145885"
+ height="3.9687498"
+ x="98.226562"
+ y="48.286461" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.0158876"
+ id="rect414-6"
+ width="0.66145819"
+ height="3.9687498"
+ x="100.54166"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1736"
+ width="1.6536455"
+ height="3.9687498"
+ x="94.257812"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1738"
+ width="1.6536458"
+ height="3.96875"
+ x="96.572914"
+ y="48.286461" />
+ <rect
+ style="fill:#999999;stroke-width:0.0165364"
+ id="rect1740"
+ width="1.6536458"
+ height="3.96875"
+ x="98.888016"
+ y="48.286461" />
+ <rect
+ style="fill:#ff2a2a;stroke-width:0.0165364"
+ id="rect1742"
+ width="1.6536458"
+ height="3.96875"
+ x="101.20312"
+ y="48.286461" />
+ </g>
+</svg>
diff --git a/src/frontend/qt_sdl/QPathInput.h b/src/frontend/qt_sdl/QPathInput.h
index 1cb1f7c..beb618c 100644
--- a/src/frontend/qt_sdl/QPathInput.h
+++ b/src/frontend/qt_sdl/QPathInput.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/RAMInfoDialog.cpp b/src/frontend/qt_sdl/RAMInfoDialog.cpp
new file mode 100644
index 0000000..b13ff02
--- /dev/null
+++ b/src/frontend/qt_sdl/RAMInfoDialog.cpp
@@ -0,0 +1,302 @@
+/*
+ Copyright 2016-2021 Arisotura
+
+ 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 "RAMInfoDialog.h"
+#include "ui_RAMInfoDialog.h"
+
+#include "main.h"
+
+extern EmuThread* emuThread;
+
+s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType)
+{
+ switch (byteType)
+ {
+ case ramInfo_OneByte:
+ return *(s8*)(NDS::MainRAM + (addr&NDS::MainRAMMask));
+ case ramInfo_TwoBytes:
+ return *(s16*)(NDS::MainRAM + (addr&NDS::MainRAMMask));
+ case ramInfo_FourBytes:
+ return *(s32*)(NDS::MainRAM + (addr&NDS::MainRAMMask));
+ default:
+ return 0;
+ }
+}
+
+RAMInfoDialog* RAMInfoDialog::currentDlg = nullptr;
+
+RAMInfoDialog::RAMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::RAMInfoDialog)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ qRegisterMetaType<QVector<int>>("QVector<int>");
+ qRegisterMetaType<u32>("u32");
+ qRegisterMetaType<s32>("s32");
+ qRegisterMetaType<s16>("s16");
+ qRegisterMetaType<s8>("s8");
+
+ SearchThread = new RAMSearchThread(this);
+ connect(SearchThread, &RAMSearchThread::SetProgressbarValue, this, &RAMInfoDialog::SetProgressbarValue);
+ connect(SearchThread, &RAMSearchThread::finished, this, &RAMInfoDialog::OnSearchFinished);
+ // First search (Show everything in main ram)
+ SearchThread->Start(ramInfoSTh_SearchAll);
+
+ TableUpdater = new QTimer(this);
+ TableUpdater->setInterval(100);
+ connect(TableUpdater, &QTimer::timeout, this, &RAMInfoDialog::ShowRowsInTable);
+ TableUpdater->start();
+}
+
+RAMInfoDialog::~RAMInfoDialog()
+{
+ delete SearchThread;
+ if (TableUpdater->isActive())
+ TableUpdater->stop();
+ delete TableUpdater;
+ delete ui;
+}
+
+void RAMInfoDialog::OnSearchFinished()
+{
+ SearchThread->wait();
+ ui->btnSearch->setEnabled(true);
+ ui->ramTable->clearContents();
+ ui->ramTable->setRowCount(SearchThread->GetResults()->size());
+ ui->ramTable->verticalScrollBar()->setSliderPosition(0);
+ ui->txtFound->setText(QString("Found: %1").arg(SearchThread->GetResults()->size()));
+}
+
+void RAMInfoDialog::ShowRowsInTable()
+{
+ const u32& scrollValue = ui->ramTable->verticalScrollBar()->sliderPosition();
+ std::vector<ramInfo_RowData>* RowDataVector = SearchThread->GetResults();
+
+ for (u32 row = scrollValue; row < std::min<u32>(scrollValue+25, RowDataVector->size()); row++)
+ {
+ ramInfo_RowData& rowData = RowDataVector->at(row);
+ rowData.Update(SearchThread->GetSearchByteType());
+
+ if (ui->ramTable->item(row, ramInfo_Address) == nullptr)
+ {
+ // A new row
+ QTableWidgetItem* addressItem = new QTableWidgetItem(QString("%1").arg(rowData.Address, 8, 16));
+ QTableWidgetItem* valueItem = new QTableWidgetItem(QString("%1").arg(rowData.Value));
+ QTableWidgetItem* previousItem = new QTableWidgetItem(QString("%1").arg(rowData.Previous));
+
+ addressItem->setFlags(addressItem->flags() & ~Qt::ItemIsEditable);
+ valueItem->setFlags(valueItem->flags() | Qt::ItemIsEditable);
+ previousItem->setFlags(previousItem->flags() & ~Qt::ItemIsEditable);
+
+ ui->ramTable->setItem(row, ramInfo_Address, addressItem);
+ ui->ramTable->setItem(row, ramInfo_Value, valueItem);
+ ui->ramTable->setItem(row, ramInfo_Previous, previousItem);
+ }
+ else
+ {
+ // A row that exists
+ ui->ramTable->item(row, ramInfo_Address)->setText(QString("%1").arg(rowData.Address, 8, 16));
+ ui->ramTable->item(row, ramInfo_Value)->setText(QString("%1").arg(rowData.Value));
+ ui->ramTable->item(row, ramInfo_Previous)->setText(QString("%1").arg(rowData.Previous));
+ if (rowData.Value != rowData.Previous)
+ ui->ramTable->item(row, ramInfo_Previous)->setForeground(Qt::red);
+ }
+ }
+}
+
+void RAMInfoDialog::ClearTableContents()
+{
+ ui->ramTable->clearContents();
+ ui->ramTable->setRowCount(0);
+}
+
+void RAMInfoDialog::SetProgressbarValue(const u32& value)
+{
+ ui->progressBar->setValue(value);
+}
+
+void RAMInfoDialog::done(int r)
+{
+ QDialog::done(r);
+ closeDlg();
+}
+
+void RAMInfoDialog::on_btnSearch_clicked()
+{
+ ui->btnSearch->setEnabled(false);
+ ui->radiobtn1byte->setEnabled(false);
+ ui->radiobtn2bytes->setEnabled(false);
+ ui->radiobtn4bytes->setEnabled(false);
+
+ if (ui->txtSearch->text().isEmpty())
+ SearchThread->Start(ramInfoSTh_SearchAll);
+ else
+ SearchThread->Start(ui->txtSearch->text().toInt());
+
+ if (!TableUpdater->isActive())
+ TableUpdater->start();
+}
+
+void RAMInfoDialog::on_btnClear_clicked()
+{
+ SearchThread->Stop();
+ TableUpdater->stop();
+
+ ui->radiobtn1byte->setEnabled(true);
+ ui->radiobtn2bytes->setEnabled(true);
+ ui->radiobtn4bytes->setEnabled(true);
+
+ OnSearchFinished();
+}
+
+void RAMInfoDialog::on_radiobtn1byte_clicked()
+{
+ SearchThread->SetSearchByteType(ramInfo_OneByte);
+}
+
+void RAMInfoDialog::on_radiobtn2bytes_clicked()
+{
+ SearchThread->SetSearchByteType(ramInfo_TwoBytes);
+}
+
+void RAMInfoDialog::on_radiobtn4bytes_clicked()
+{
+ SearchThread->SetSearchByteType(ramInfo_FourBytes);
+}
+
+void RAMInfoDialog::on_ramTable_itemChanged(QTableWidgetItem *item)
+{
+ ramInfo_RowData& rowData = SearchThread->GetResults()->at(item->row());
+ s32 itemValue = item->text().toInt();
+
+ if (rowData.Value != itemValue)
+ rowData.SetValue(itemValue);
+}
+
+/**
+ * RAMSearchThread
+ */
+
+RAMSearchThread::RAMSearchThread(RAMInfoDialog* dialog) : Dialog(dialog)
+{
+ RowDataVector = new std::vector<ramInfo_RowData>();
+}
+
+RAMSearchThread::~RAMSearchThread()
+{
+ Stop();
+ if (RowDataVector)
+ {
+ delete RowDataVector;
+ RowDataVector = nullptr;
+ }
+}
+
+void RAMSearchThread::Start(const s32& searchValue, const ramInfoSTh_SearchMode& searchMode)
+{
+ SearchValue = searchValue;
+ SearchMode = searchMode;
+ start();
+}
+
+void RAMSearchThread::Start(const ramInfoSTh_SearchMode& searchMode)
+{
+ SearchMode = searchMode;
+ start();
+}
+
+void RAMSearchThread::Stop()
+{
+ SearchRunning = false;
+ RowDataVector->clear();
+ quit();
+ wait();
+}
+
+void RAMSearchThread::run()
+{
+ SearchRunning = true;
+ u32 progress = 0;
+
+ // Pause game running
+ emuThread->emuPause();
+
+ // For following search modes below, RowDataVector must be filled.
+ if (SearchMode == ramInfoSTh_SearchAll || RowDataVector->size() == 0)
+ {
+ // First search mode
+ for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+NDS::MainRAMMaxSize; addr += SearchByteType)
+ {
+ const s32& value = GetMainRAMValue(addr, SearchByteType);
+
+ RowDataVector->push_back({ addr, value, value });
+
+ // A solution to prevent to call too many slot.
+ u32 newProgress = (int)((addr-0x02000000) / (NDS::MainRAMMaxSize-1.0f) * 100);
+ if (progress < newProgress)
+ {
+ progress = newProgress;
+ emit SetProgressbarValue(progress);
+ }
+ }
+ }
+
+ if (SearchMode == ramInfoSTh_Default)
+ {
+ // Next search mode
+ std::vector<ramInfo_RowData>* newRowDataVector = new std::vector<ramInfo_RowData>();
+ for (u32 row = 0; SearchRunning && row < RowDataVector->size(); row++)
+ {
+ const u32& addr = RowDataVector->at(row).Address;
+ const s32& value = GetMainRAMValue(addr, SearchByteType);
+
+ if (SearchValue == value)
+ newRowDataVector->push_back({ addr, value, value });
+
+ // A solution to prevent to call too many slot.
+ u32 newProgress = (int)(row / (RowDataVector->size()-1.0f) * 100);
+ if (progress < newProgress)
+ {
+ progress = newProgress;
+ emit SetProgressbarValue(progress);
+ }
+ }
+ delete RowDataVector;
+ RowDataVector = newRowDataVector;
+ }
+
+ // Unpause game running
+ emuThread->emuUnpause();
+
+ SearchRunning = false;
+}
+
+void RAMSearchThread::SetSearchByteType(const ramInfo_ByteType& bytetype)
+{
+ SearchByteType = bytetype;
+}
+
+ramInfo_ByteType RAMSearchThread::GetSearchByteType() const
+{
+ return SearchByteType;
+}
+
+std::vector<ramInfo_RowData>* RAMSearchThread::GetResults()
+{
+ return RowDataVector;
+}
diff --git a/src/frontend/qt_sdl/RAMInfoDialog.h b/src/frontend/qt_sdl/RAMInfoDialog.h
new file mode 100644
index 0000000..f44ae93
--- /dev/null
+++ b/src/frontend/qt_sdl/RAMInfoDialog.h
@@ -0,0 +1,161 @@
+/*
+ Copyright 2016-2021 Arisotura
+
+ 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 RAMINFODIALOG_H
+#define RAMINFODIALOG_H
+
+#include <QDialog>
+#include <QTableWidget>
+#include <QScrollBar>
+#include <QThread>
+#include <QTimer>
+
+#include "types.h"
+#include "NDS.h"
+
+namespace Ui { class RAMInfoDialog; }
+class RAMInfoDialog;
+class RAMSearchThread;
+class RAMUpdateThread;
+
+enum ramInfo_ByteType
+{
+ ramInfo_OneByte = 1,
+ ramInfo_TwoBytes = 2,
+ ramInfo_FourBytes = 4
+};
+
+enum ramInfoSTh_SearchMode
+{
+ ramInfoSTh_Default,
+ ramInfoSTh_SearchAll
+};
+
+enum
+{
+ ramInfo_Address,
+ ramInfo_Value,
+ ramInfo_Previous
+};
+
+s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType);
+
+struct ramInfo_RowData
+{
+ u32 Address;
+ s32 Value;
+ s32 Previous;
+
+ void Update(const ramInfo_ByteType& byteType)
+ {
+ Value = GetMainRAMValue(Address, byteType);
+ }
+
+ void SetValue(const s32& value)
+ {
+ NDS::MainRAM[Address&NDS::MainRAMMask] = (u32)value;
+ Value = value;
+ }
+};
+
+class RAMInfoDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit RAMInfoDialog(QWidget* parent);
+ ~RAMInfoDialog();
+
+ static RAMInfoDialog* currentDlg;
+ static RAMInfoDialog* openDlg(QWidget* parent)
+ {
+ if (currentDlg)
+ {
+ currentDlg->activateWindow();
+ return currentDlg;
+ }
+
+ currentDlg = new RAMInfoDialog(parent);
+ currentDlg->show();
+ return currentDlg;
+ }
+ static void closeDlg()
+ {
+ currentDlg = nullptr;
+ }
+
+ s32 SearchValue = 0;
+
+ void ClearTableContents();
+
+private slots:
+ void done(int r);
+
+ void on_btnSearch_clicked();
+ void on_btnClear_clicked();
+ void on_radiobtn1byte_clicked();
+ void on_radiobtn2bytes_clicked();
+ void on_radiobtn4bytes_clicked();
+ void on_ramTable_itemChanged(QTableWidgetItem *item);
+
+ void OnSearchFinished();
+ void ShowRowsInTable();
+ void SetProgressbarValue(const u32& value);
+
+private:
+ Ui::RAMInfoDialog* ui;
+
+ RAMSearchThread* SearchThread;
+ QTimer* TableUpdater;
+};
+
+class RAMSearchThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ explicit RAMSearchThread(RAMInfoDialog* dialog);
+ ~RAMSearchThread() override;
+
+ void Start(const s32& searchValue, const ramInfoSTh_SearchMode& searchMode = ramInfoSTh_Default);
+ void Start(const ramInfoSTh_SearchMode& searchMode);
+
+ void SetSearchByteType(const ramInfo_ByteType& bytetype);
+ ramInfo_ByteType GetSearchByteType() const;
+ std::vector<ramInfo_RowData>* GetResults();
+
+ void Stop();
+
+private:
+ void run();
+
+ RAMInfoDialog* Dialog;
+ bool SearchRunning = false;
+
+ ramInfoSTh_SearchMode SearchMode;
+ s32 SearchValue;
+ ramInfo_ByteType SearchByteType = ramInfo_OneByte;
+ std::vector<ramInfo_RowData>* RowDataVector = nullptr;
+
+ void ClearTableContents();
+
+signals:
+ void SetProgressbarValue(const u32& value);
+};
+
+#endif // RAMINFODIALOG_H
diff --git a/src/frontend/qt_sdl/RAMInfoDialog.ui b/src/frontend/qt_sdl/RAMInfoDialog.ui
new file mode 100644
index 0000000..46beaa5
--- /dev/null
+++ b/src/frontend/qt_sdl/RAMInfoDialog.ui
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RAMInfoDialog</class>
+ <widget class="QDialog" name="RAMInfoDialog">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>550</width>
+ <height>411</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>550</width>
+ <height>411</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>550</width>
+ <height>411</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>RAM info - melonDS</string>
+ </property>
+ <property name="modal">
+ <bool>false</bool>
+ </property>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="geometry">
+ <rect>
+ <x>340</x>
+ <y>10</y>
+ <width>201</width>
+ <height>111</height>
+ </rect>
+ </property>
+ <property name="title">
+ <string>Search</string>
+ </property>
+ <widget class="QPushButton" name="btnSearch">
+ <property name="geometry">
+ <rect>
+ <x>130</x>
+ <y>20</y>
+ <width>61</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Search</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" name="txtSearch">
+ <property name="geometry">
+ <rect>
+ <x>50</x>
+ <y>20</y>
+ <width>71</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="maxLength">
+ <number>5</number>
+ </property>
+ </widget>
+ <widget class="QPushButton" name="btnClear">
+ <property name="geometry">
+ <rect>
+ <x>120</x>
+ <y>80</y>
+ <width>71</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="labelValue">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>20</y>
+ <width>41</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Value:</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" name="radiobtn1byte">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>50</y>
+ <width>90</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>1byte</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" name="radiobtn2bytes">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>70</y>
+ <width>90</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>2bytes</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" name="radiobtn4bytes">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>90</y>
+ <width>90</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>4bytes</string>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>380</y>
+ <width>321</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="textVisible">
+ <bool>true</bool>
+ </property>
+ <property name="format">
+ <string>%p%</string>
+ </property>
+ </widget>
+ <widget class="QTableWidget" name="ramTable">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>30</y>
+ <width>321</width>
+ <height>341</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>321</width>
+ <height>341</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>321</width>
+ <height>341</height>
+ </size>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked</set>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderMinimumSectionSize">
+ <number>16</number>
+ </attribute>
+ <attribute name="verticalHeaderDefaultSectionSize">
+ <number>16</number>
+ </attribute>
+ <column>
+ <property name="text">
+ <string>Address</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Value</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Previous</string>
+ </property>
+ </column>
+ </widget>
+ <widget class="QLabel" name="txtFound">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>101</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Found:</string>
+ </property>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp
index 9166efe..e82ec4b 100644
--- a/src/frontend/qt_sdl/ROMInfoDialog.cpp
+++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, WaluigiWare64
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -45,14 +45,14 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI
u32 iconData[32 * 32];
- Frontend::ROMIcon(NDSCart::Banner.Icon, NDSCart::Banner.Palette, iconData);
+ ROMManager::ROMIcon(NDSCart::Banner.Icon, NDSCart::Banner.Palette, iconData);
iconImage = QImage(reinterpret_cast<unsigned char*>(iconData), 32, 32, QImage::Format_ARGB32).copy();
ui->iconImage->setPixmap(QPixmap::fromImage(iconImage));
if (NDSCart::Banner.Version == 0x103)
{
u32 animatedIconData[32 * 32 * 64] = {0};
- Frontend::AnimatedROMIcon(NDSCart::Banner.DSiIcon, NDSCart::Banner.DSiPalette, NDSCart::Banner.DSiSequence, animatedIconData, animatedSequence);
+ ROMManager::AnimatedROMIcon(NDSCart::Banner.DSiIcon, NDSCart::Banner.DSiPalette, NDSCart::Banner.DSiSequence, animatedIconData, animatedSequence);
for (int i = 0; i < 64; i++)
{
@@ -75,12 +75,12 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI
ui->iconTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
- ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle, 128));
- ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle, 128));
- ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle, 128));
- ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle, 128));
- ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle, 128));
- ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle, 128));
+ ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle));
+ ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
+ ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle));
+ ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle));
+ ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle));
+ ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle));
if (NDSCart::Banner.Version > 1)
ui->chineseTitle->setText(QString::fromUtf16(NDSCart::Banner.ChineseTitle));
@@ -130,7 +130,7 @@ void ROMInfoDialog::on_saveIconButton_clicked()
{
QString filename = QFileDialog::getSaveFileName(this,
"Save Icon",
- Config::LastROMFolder,
+ QString::fromStdString(Config::LastROMFolder),
"PNG Images (*.png)");
if (filename.isEmpty())
return;
diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h
index 5193554..fd036a0 100644
--- a/src/frontend/qt_sdl/ROMInfoDialog.h
+++ b/src/frontend/qt_sdl/ROMInfoDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura, WaluigiWare64
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -25,7 +25,7 @@
#include <QImage>
#include "types.h"
-#include "FrontendUtil.h"
+#include "ROMManager.h"
namespace Ui { class ROMInfoDialog; }
class ROMInfoDialog;
@@ -58,7 +58,7 @@ public:
private slots:
void done(int r);
-
+
void on_saveIconButton_clicked();
void iconSetFrame(int frame);
diff --git a/src/frontend/qt_sdl/ROMInfoDialog.ui b/src/frontend/qt_sdl/ROMInfoDialog.ui
index 0c65cab..1c9d844 100644
--- a/src/frontend/qt_sdl/ROMInfoDialog.ui
+++ b/src/frontend/qt_sdl/ROMInfoDialog.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>427</width>
- <height>434</height>
+ <width>559</width>
+ <height>532</height>
</rect>
</property>
<property name="sizePolicy">
@@ -22,12 +22,6 @@
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QGroupBox" name="titles">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="title">
<string>Titles</string>
</property>
@@ -350,12 +344,6 @@
</item>
<item row="3" column="1">
<widget class="QGroupBox" name="filesystem">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="title">
<string>Filesystem</string>
</property>
@@ -441,12 +429,6 @@
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="generalInfo">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="title">
<string>General info</string>
</property>
@@ -668,7 +650,7 @@
</layout>
</widget>
</item>
- <item row="0" column="3">
+ <item row="0" column="2">
<widget class="QGroupBox" name="dsiIconBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@@ -742,43 +724,11 @@
</layout>
</widget>
</item>
- <item row="0" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>55</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="4">
+ <item row="0" column="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="2">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
</spacer>
</item>
<item row="1" column="1">
@@ -788,6 +738,13 @@
</property>
</widget>
</item>
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp
new file mode 100644
index 0000000..716a454
--- /dev/null
+++ b/src/frontend/qt_sdl/ROMManager.cpp
@@ -0,0 +1,875 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 <string.h>
+
+#include <string>
+#include <utility>
+
+#ifdef ARCHIVE_SUPPORT_ENABLED
+#include "ArchiveUtil.h"
+#endif
+#include "ROMManager.h"
+#include "Config.h"
+#include "Platform.h"
+
+#include "NDS.h"
+#include "DSi.h"
+#include "SPI.h"
+#include "DSi_I2C.h"
+
+
+namespace ROMManager
+{
+
+int CartType = -1;
+std::string BaseROMDir = "";
+std::string BaseROMName = "";
+std::string BaseAssetName = "";
+
+int GBACartType = -1;
+std::string BaseGBAROMDir = "";
+std::string BaseGBAROMName = "";
+std::string BaseGBAAssetName = "";
+
+SaveManager* NDSSave = nullptr;
+SaveManager* GBASave = nullptr;
+
+bool SavestateLoaded = false;
+std::string PreviousSaveFile = "";
+
+ARCodeFile* CheatFile = nullptr;
+bool CheatsOn = false;
+
+
+int LastSep(std::string path)
+{
+ int i = path.length() - 1;
+ while (i >= 0)
+ {
+ if (path[i] == '/' || path[i] == '\\')
+ return i;
+
+ i--;
+ }
+
+ return -1;
+}
+
+std::string GetAssetPath(bool gba, std::string configpath, std::string ext, std::string file="")
+{
+ if (configpath.empty())
+ configpath = gba ? BaseGBAROMDir : BaseROMDir;
+
+ if (file.empty())
+ {
+ file = gba ? BaseGBAAssetName : BaseAssetName;
+ if (file.empty())
+ file = "firmware";
+ }
+
+ for (;;)
+ {
+ int i = configpath.length() - 1;
+ if (i < 0) break;
+ if (configpath[i] == '/' || configpath[i] == '\\')
+ configpath = configpath.substr(0, i);
+ else
+ break;
+ }
+
+ if (!configpath.empty())
+ configpath += "/";
+
+ return configpath + file + ext;
+}
+
+
+QString VerifyDSBIOS()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
+ if (!f) return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x1000)
+ {
+ fclose(f);
+ return "DS ARM9 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
+ if (!f) return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x4000)
+ {
+ fclose(f);
+ return "DS ARM7 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSiBIOS()
+{
+ FILE* f;
+ long len;
+
+ // TODO: check the first 32 bytes
+
+ f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb");
+ if (!f) return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x10000)
+ {
+ fclose(f);
+ return "DSi ARM9 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
+ if (!f) return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x10000)
+ {
+ fclose(f);
+ return "DSi ARM7 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSFirmware()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::FirmwarePath, "rb");
+ if (!f) return "DS firmware was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len == 0x20000)
+ {
+ // 128KB firmware, not bootable
+ fclose(f);
+ // TODO report it somehow? detect in core?
+ return "";
+ }
+ else if (len != 0x40000 && len != 0x80000)
+ {
+ fclose(f);
+ return "DS firmware is not a valid firmware dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSiFirmware()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb");
+ if (!f) return "DSi firmware was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x20000)
+ {
+ // not 128KB
+ // TODO: check whether those work
+ fclose(f);
+ return "DSi firmware is not a valid firmware dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSiNAND()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b");
+ if (!f) return "DSi NAND was not found or could not be accessed. Check your emu settings.";
+
+ // TODO: some basic checks
+ // check that it has the nocash footer, and all
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifySetup()
+{
+ QString res;
+
+ if (Config::ExternalBIOSEnable)
+ {
+ res = VerifyDSBIOS();
+ if (!res.isEmpty()) return res;
+ }
+
+ if (Config::ConsoleType == 1)
+ {
+ res = VerifyDSiBIOS();
+ if (!res.isEmpty()) return res;
+
+ if (Config::ExternalBIOSEnable)
+ {
+ res = VerifyDSiFirmware();
+ if (!res.isEmpty()) return res;
+ }
+
+ res = VerifyDSiNAND();
+ if (!res.isEmpty()) return res;
+ }
+ else
+ {
+ if (Config::ExternalBIOSEnable)
+ {
+ res = VerifyDSFirmware();
+ if (!res.isEmpty()) return res;
+ }
+ }
+
+ return "";
+}
+
+
+std::string GetSavestateName(int slot)
+{
+ std::string ext = ".ml";
+ ext += (char)('0'+slot);
+ return GetAssetPath(false, Config::SavestatePath, ext);
+}
+
+bool SavestateExists(int slot)
+{
+ std::string ssfile = GetSavestateName(slot);
+ return Platform::FileExists(ssfile);
+}
+
+bool LoadState(std::string filename)
+{
+ // backup
+ Savestate* backup = new Savestate("timewarp.mln", true);
+ NDS::DoSavestate(backup);
+ delete backup;
+
+ bool failed = false;
+
+ Savestate* state = new Savestate(filename, false);
+ if (state->Error)
+ {
+ delete state;
+
+ // current state might be crapoed, so restore from sane backup
+ state = new Savestate("timewarp.mln", false);
+ failed = true;
+ }
+
+ bool res = NDS::DoSavestate(state);
+ delete state;
+
+ if (!res)
+ {
+ failed = true;
+ state = new Savestate("timewarp.mln", false);
+ NDS::DoSavestate(state);
+ delete state;
+ }
+
+ if (failed) return false;
+
+ if (Config::SavestateRelocSRAM && NDSSave)
+ {
+ PreviousSaveFile = NDSSave->GetPath();
+
+ std::string savefile = filename.substr(LastSep(filename)+1);
+ savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
+ savefile += Platform::InstanceFileSuffix();
+ NDSSave->SetPath(savefile, true);
+ }
+
+ SavestateLoaded = true;
+
+ return true;
+}
+
+bool SaveState(std::string filename)
+{
+ Savestate* state = new Savestate(filename, true);
+ if (state->Error)
+ {
+ delete state;
+ return false;
+ }
+
+ NDS::DoSavestate(state);
+ delete state;
+
+ if (Config::SavestateRelocSRAM && NDSSave)
+ {
+ std::string savefile = filename.substr(LastSep(filename)+1);
+ savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
+ savefile += Platform::InstanceFileSuffix();
+ NDSSave->SetPath(savefile, false);
+ }
+
+ return true;
+}
+
+void UndoStateLoad()
+{
+ if (!SavestateLoaded) return;
+
+ // 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 (NDSSave && (!PreviousSaveFile.empty()))
+ {
+ NDSSave->SetPath(PreviousSaveFile, true);
+ }
+}
+
+
+void UnloadCheats()
+{
+ if (CheatFile)
+ {
+ delete CheatFile;
+ CheatFile = nullptr;
+ }
+}
+
+void LoadCheats()
+{
+ UnloadCheats();
+
+ std::string filename = GetAssetPath(false, Config::CheatFilePath, ".mch");
+
+ // TODO: check for error (malformed cheat file, ...)
+ CheatFile = new ARCodeFile(filename);
+
+ AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
+}
+
+void EnableCheats(bool enable)
+{
+ CheatsOn = enable;
+ if (CheatFile)
+ AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
+}
+
+ARCodeFile* GetCheatFile()
+{
+ return CheatFile;
+}
+
+
+void SetBatteryLevels()
+{
+ if (NDS::ConsoleType == 1)
+ {
+ DSi_BPTWL::SetBatteryLevel(Config::DSiBatteryLevel);
+ DSi_BPTWL::SetBatteryCharging(Config::DSiBatteryCharging);
+ }
+ else
+ {
+ SPI_Powerman::SetBatteryLevelOkay(Config::DSBatteryLevelOkay);
+ }
+}
+
+void Reset()
+{
+ NDS::SetConsoleType(Config::ConsoleType);
+ if (Config::ConsoleType == 1) EjectGBACart();
+ NDS::Reset();
+ SetBatteryLevels();
+
+ if ((CartType != -1) && NDSSave)
+ {
+ std::string oldsave = NDSSave->GetPath();
+ std::string newsave = GetAssetPath(false, Config::SaveFilePath, ".sav");
+ newsave += Platform::InstanceFileSuffix();
+ if (oldsave != newsave)
+ NDSSave->SetPath(newsave, false);
+ }
+
+ if ((GBACartType != -1) && GBASave)
+ {
+ std::string oldsave = GBASave->GetPath();
+ std::string newsave = GetAssetPath(true, Config::SaveFilePath, ".sav");
+ newsave += Platform::InstanceFileSuffix();
+ if (oldsave != newsave)
+ GBASave->SetPath(newsave, false);
+ }
+
+ if (!BaseROMName.empty())
+ {
+ if (Config::DirectBoot || NDS::NeedsDirectBoot())
+ {
+ NDS::SetupDirectBoot(BaseROMName);
+ }
+ }
+}
+
+
+bool LoadBIOS()
+{
+ NDS::SetConsoleType(Config::ConsoleType);
+
+ if (NDS::NeedsDirectBoot())
+ return false;
+
+ /*if (NDSSave) delete NDSSave;
+ NDSSave = nullptr;
+
+ CartType = -1;
+ BaseROMDir = "";
+ BaseROMName = "";
+ BaseAssetName = "";*/
+
+ NDS::Reset();
+ SetBatteryLevels();
+ return true;
+}
+
+
+bool LoadROM(QStringList filepath, bool reset)
+{
+ if (filepath.empty()) return false;
+
+ u8* filedata;
+ u32 filelen;
+
+ std::string basepath;
+ std::string romname;
+
+ int num = filepath.count();
+ if (num == 1)
+ {
+ // regular file
+
+ std::string filename = filepath.at(0).toStdString();
+ FILE* f = Platform::OpenFile(filename, "rb", true);
+ if (!f) return false;
+
+ fseek(f, 0, SEEK_END);
+ long len = ftell(f);
+ if (len > 0x40000000)
+ {
+ fclose(f);
+ return false;
+ }
+
+ fseek(f, 0, SEEK_SET);
+ filedata = new u8[len];
+ size_t nread = fread(filedata, (size_t)len, 1, f);
+ if (nread != 1)
+ {
+ fclose(f);
+ delete[] filedata;
+ return false;
+ }
+
+ fclose(f);
+ filelen = (u32)len;
+
+ int pos = LastSep(filename);
+ basepath = filename.substr(0, pos);
+ romname = filename.substr(pos+1);
+ }
+#ifdef ARCHIVE_SUPPORT_ENABLED
+ else if (num == 2)
+ {
+ // file inside archive
+
+ u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
+ if (lenread < 0) return false;
+ if (!filedata) return false;
+ if (lenread != filelen)
+ {
+ delete[] filedata;
+ return false;
+ }
+
+ std::string std_archivepath = filepath.at(0).toStdString();
+ basepath = std_archivepath.substr(0, LastSep(std_archivepath));
+
+ std::string std_romname = filepath.at(1).toStdString();
+ romname = std_romname.substr(LastSep(std_romname)+1);
+ }
+#endif
+ else
+ return false;
+
+ if (NDSSave) delete NDSSave;
+ NDSSave = nullptr;
+
+ BaseROMDir = basepath;
+ BaseROMName = romname;
+ BaseAssetName = romname.substr(0, romname.rfind('.'));
+
+ if (reset)
+ {
+ NDS::SetConsoleType(Config::ConsoleType);
+ NDS::EjectCart();
+ NDS::Reset();
+ SetBatteryLevels();
+ }
+
+ u32 savelen = 0;
+ u8* savedata = nullptr;
+
+ std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
+ std::string origsav = savname;
+ savname += Platform::InstanceFileSuffix();
+
+ FILE* sav = Platform::OpenFile(savname, "rb", true);
+ if (!sav) sav = Platform::OpenFile(origsav, "rb", true);
+ if (sav)
+ {
+ fseek(sav, 0, SEEK_END);
+ savelen = (u32)ftell(sav);
+
+ fseek(sav, 0, SEEK_SET);
+ savedata = new u8[savelen];
+ fread(savedata, savelen, 1, sav);
+ fclose(sav);
+ }
+
+ bool res = NDS::LoadCart(filedata, filelen, savedata, savelen);
+ if (res && reset)
+ {
+ if (Config::DirectBoot || NDS::NeedsDirectBoot())
+ {
+ NDS::SetupDirectBoot(romname);
+ }
+ }
+
+ if (res)
+ {
+ CartType = 0;
+ NDSSave = new SaveManager(savname);
+
+ LoadCheats();
+ }
+
+ if (savedata) delete[] savedata;
+ delete[] filedata;
+ return res;
+}
+
+void EjectCart()
+{
+ if (NDSSave) delete NDSSave;
+ NDSSave = nullptr;
+
+ UnloadCheats();
+
+ NDS::EjectCart();
+
+ CartType = -1;
+ BaseROMDir = "";
+ BaseROMName = "";
+ BaseAssetName = "";
+}
+
+bool CartInserted()
+{
+ return CartType != -1;
+}
+
+QString CartLabel()
+{
+ if (CartType == -1)
+ return "(none)";
+
+ QString ret = QString::fromStdString(BaseROMName);
+
+ int maxlen = 32;
+ if (ret.length() > maxlen)
+ ret = ret.left(maxlen-6) + "..." + ret.right(3);
+
+ return ret;
+}
+
+
+bool LoadGBAROM(QStringList filepath)
+{
+ if (Config::ConsoleType == 1) return false;
+ if (filepath.empty()) return false;
+
+ u8* filedata;
+ u32 filelen;
+
+ std::string basepath;
+ std::string romname;
+
+ int num = filepath.count();
+ if (num == 1)
+ {
+ // regular file
+
+ std::string filename = filepath.at(0).toStdString();
+ FILE* f = Platform::OpenFile(filename, "rb", true);
+ if (!f) return false;
+
+ fseek(f, 0, SEEK_END);
+ long len = ftell(f);
+ if (len > 0x40000000)
+ {
+ fclose(f);
+ return false;
+ }
+
+ fseek(f, 0, SEEK_SET);
+ filedata = new u8[len];
+ size_t nread = fread(filedata, (size_t)len, 1, f);
+ if (nread != 1)
+ {
+ fclose(f);
+ delete[] filedata;
+ return false;
+ }
+
+ fclose(f);
+ filelen = (u32)len;
+
+ int pos = LastSep(filename);
+ basepath = filename.substr(0, pos);
+ romname = filename.substr(pos+1);
+ }
+#ifdef ARCHIVE_SUPPORT_ENABLED
+ else if (num == 2)
+ {
+ // file inside archive
+
+ u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
+ if (lenread < 0) return false;
+ if (!filedata) return false;
+ if (lenread != filelen)
+ {
+ delete[] filedata;
+ return false;
+ }
+
+ std::string std_archivepath = filepath.at(0).toStdString();
+ basepath = std_archivepath.substr(0, LastSep(std_archivepath));
+
+ std::string std_romname = filepath.at(1).toStdString();
+ romname = std_romname.substr(LastSep(std_romname)+1);
+ }
+#endif
+ else
+ return false;
+
+ if (GBASave) delete GBASave;
+ GBASave = nullptr;
+
+ BaseGBAROMDir = basepath;
+ BaseGBAROMName = romname;
+ BaseGBAAssetName = romname.substr(0, romname.rfind('.'));
+
+ u32 savelen = 0;
+ u8* savedata = nullptr;
+
+ std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
+ std::string origsav = savname;
+ savname += Platform::InstanceFileSuffix();
+
+ FILE* sav = Platform::OpenFile(savname, "rb", true);
+ if (!sav) sav = Platform::OpenFile(origsav, "rb", true);
+ if (sav)
+ {
+ fseek(sav, 0, SEEK_END);
+ savelen = (u32)ftell(sav);
+
+ fseek(sav, 0, SEEK_SET);
+ savedata = new u8[savelen];
+ fread(savedata, savelen, 1, sav);
+ fclose(sav);
+ }
+
+ bool res = NDS::LoadGBACart(filedata, filelen, savedata, savelen);
+
+ if (res)
+ {
+ GBACartType = 0;
+ GBASave = new SaveManager(savname);
+ }
+
+ if (savedata) delete[] savedata;
+ delete[] filedata;
+ return res;
+}
+
+void LoadGBAAddon(int type)
+{
+ if (Config::ConsoleType == 1) return;
+
+ if (GBASave) delete GBASave;
+ GBASave = nullptr;
+
+ NDS::LoadGBAAddon(type);
+
+ GBACartType = type;
+ BaseGBAROMDir = "";
+ BaseGBAROMName = "";
+ BaseGBAAssetName = "";
+}
+
+void EjectGBACart()
+{
+ if (GBASave) delete GBASave;
+ GBASave = nullptr;
+
+ NDS::EjectGBACart();
+
+ GBACartType = -1;
+ BaseGBAROMDir = "";
+ BaseGBAROMName = "";
+ BaseGBAAssetName = "";
+}
+
+bool GBACartInserted()
+{
+ return GBACartType != -1;
+}
+
+QString GBACartLabel()
+{
+ if (Config::ConsoleType == 1) return "none (DSi)";
+
+ switch (GBACartType)
+ {
+ case 0:
+ {
+ QString ret = QString::fromStdString(BaseGBAROMName);
+
+ int maxlen = 32;
+ if (ret.length() > maxlen)
+ ret = ret.left(maxlen-6) + "..." + ret.right(3);
+
+ return ret;
+ }
+
+ case NDS::GBAAddon_RAMExpansion:
+ return "Memory expansion";
+ }
+
+ return "(none)";
+}
+
+
+void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef)
+{
+ int index = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ for (int k = 0; k < 8; k++)
+ {
+ for (int l = 0; l < 8; l++)
+ {
+ u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F;
+ u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31;
+ u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31;
+ u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31;
+ u8 a = pal_index ? 255: 0;
+ u32* row = &iconRef[256 * i + 32 * k + 8 * j];
+ row[l] = (a << 24) | (r << 16) | (g << 8) | b;
+ index++;
+ }
+ }
+ }
+ }
+}
+
+#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15)
+#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14)
+#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11)
+#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8)
+#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0)
+
+void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef)
+{
+ for (int i = 0; i < 64; i++)
+ {
+ if (!sequence[i])
+ break;
+ u32* frame = &animatedTexRef[32 * 32 * i];
+ ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame);
+
+ if (SEQ_FLIPH(sequence[i]))
+ {
+ for (int x = 0; x < 32; x++)
+ {
+ for (int y = 0; y < 32/2; y++)
+ {
+ std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]);
+ }
+ }
+ }
+ if (SEQ_FLIPV(sequence[i]))
+ {
+ for (int x = 0; x < 32/2; x++)
+ {
+ for (int y = 0; y < 32; y++)
+ {
+ std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]);
+ }
+ }
+ }
+
+ for (int j = 0; j < SEQ_DUR(sequence[i]); j++)
+ animatedSequenceRef.push_back(i);
+ }
+}
+
+}
diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h
new file mode 100644
index 0000000..9e82a5b
--- /dev/null
+++ b/src/frontend/qt_sdl/ROMManager.h
@@ -0,0 +1,66 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 ROMMANAGER_H
+#define ROMMANAGER_H
+
+#include "types.h"
+#include "SaveManager.h"
+#include "AREngine.h"
+
+#include <string>
+#include <vector>
+
+namespace ROMManager
+{
+
+extern SaveManager* NDSSave;
+extern SaveManager* GBASave;
+
+QString VerifySetup();
+void Reset();
+bool LoadBIOS();
+
+bool LoadROM(QStringList filepath, bool reset);
+void EjectCart();
+bool CartInserted();
+QString CartLabel();
+
+bool LoadGBAROM(QStringList filepath);
+void LoadGBAAddon(int type);
+void EjectGBACart();
+bool GBACartInserted();
+QString GBACartLabel();
+
+std::string GetSavestateName(int slot);
+bool SavestateExists(int slot);
+bool LoadState(std::string filename);
+bool SaveState(std::string filename);
+void UndoStateLoad();
+
+void EnableCheats(bool enable);
+ARCodeFile* GetCheatFile();
+
+void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef);
+void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16],
+ u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64],
+ std::vector<int> &animatedSequenceRef);
+
+}
+
+#endif // ROMMANAGER_H
diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp
new file mode 100644
index 0000000..d9e2138
--- /dev/null
+++ b/src/frontend/qt_sdl/SaveManager.cpp
@@ -0,0 +1,194 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 <string.h>
+
+#include "SaveManager.h"
+#include "Platform.h"
+
+
+SaveManager::SaveManager(std::string path) : QThread()
+{
+ SecondaryBuffer = nullptr;
+ SecondaryBufferLength = 0;
+ SecondaryBufferLock = new QMutex();
+
+ Running = false;
+
+ Path = path;
+
+ Buffer = nullptr;
+ Length = 0;
+ FlushRequested = false;
+
+ FlushVersion = 0;
+ PreviousFlushVersion = 0;
+ TimeAtLastFlushRequest = 0;
+
+ if (!path.empty())
+ {
+ Running = true;
+ start();
+ }
+}
+
+SaveManager::~SaveManager()
+{
+ if (Running)
+ {
+ Running = false;
+ wait();
+ FlushSecondaryBuffer();
+ }
+
+ if (SecondaryBuffer) delete[] SecondaryBuffer;
+
+ delete SecondaryBufferLock;
+
+ if (Buffer) delete[] Buffer;
+}
+
+std::string SaveManager::GetPath()
+{
+ return Path;
+}
+
+void SaveManager::SetPath(std::string path, bool reload)
+{
+ Path = path;
+
+ if (reload)
+ {
+ FILE* f = Platform::OpenFile(Path, "rb", true);
+ if (f)
+ {
+ fread(Buffer, 1, Length, f);
+ fclose(f);
+ }
+ }
+ else
+ FlushRequested = true;
+}
+
+void SaveManager::RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen)
+{
+ if (Length != savelen)
+ {
+ if (Buffer) delete[] Buffer;
+
+ Length = savelen;
+ Buffer = new u8[Length];
+
+ memcpy(Buffer, savedata, Length);
+ }
+ else
+ {
+ if ((writeoffset+writelen) > savelen)
+ {
+ u32 len = savelen - writeoffset;
+ memcpy(&Buffer[writeoffset], &savedata[writeoffset], len);
+ len = writelen - len;
+ if (len > savelen) len = savelen;
+ memcpy(&Buffer[0], &savedata[0], len);
+ }
+ else
+ {
+ memcpy(&Buffer[writeoffset], &savedata[writeoffset], writelen);
+ }
+ }
+
+ FlushRequested = true;
+}
+
+void SaveManager::CheckFlush()
+{
+ if (!FlushRequested) return;
+
+ SecondaryBufferLock->lock();
+
+ printf("SaveManager: Flush requested\n");
+
+ if (SecondaryBufferLength != Length)
+ {
+ if (SecondaryBuffer) delete[] SecondaryBuffer;
+
+ SecondaryBufferLength = Length;
+ SecondaryBuffer = new u8[SecondaryBufferLength];
+ }
+
+ memcpy(SecondaryBuffer, Buffer, Length);
+
+ FlushRequested = false;
+ FlushVersion++;
+ TimeAtLastFlushRequest = time(nullptr);
+
+ SecondaryBufferLock->unlock();
+}
+
+void SaveManager::run()
+{
+ for (;;)
+ {
+ QThread::msleep(100);
+
+ if (!Running) return;
+
+ // We debounce for two seconds after last flush request to ensure that writing has finished.
+ if (TimeAtLastFlushRequest == 0 || difftime(time(nullptr), TimeAtLastFlushRequest) < 2)
+ {
+ continue;
+ }
+
+ FlushSecondaryBuffer();
+ }
+}
+
+void SaveManager::FlushSecondaryBuffer(u8* dst, u32 dstLength)
+{
+ if (!SecondaryBuffer) return;
+
+ // When flushing to a file, there's no point in re-writing the exact same data.
+ if (!dst && !NeedsFlush()) return;
+ // When flushing to memory, we don't know if dst already has any data so we only check that we CAN flush.
+ if (dst && dstLength < SecondaryBufferLength) return;
+
+ SecondaryBufferLock->lock();
+ if (dst)
+ {
+ memcpy(dst, SecondaryBuffer, SecondaryBufferLength);
+ }
+ else
+ {
+ FILE* f = Platform::OpenFile(Path, "wb");
+ if (f)
+ {
+ printf("SaveManager: Written\n");
+ fwrite(SecondaryBuffer, SecondaryBufferLength, 1, f);
+ fclose(f);
+ }
+ }
+ PreviousFlushVersion = FlushVersion;
+ TimeAtLastFlushRequest = 0;
+ SecondaryBufferLock->unlock();
+}
+
+bool SaveManager::NeedsFlush()
+{
+ return FlushVersion != PreviousFlushVersion;
+}
diff --git a/src/frontend/qt_sdl/SaveManager.h b/src/frontend/qt_sdl/SaveManager.h
new file mode 100644
index 0000000..0d38f4b
--- /dev/null
+++ b/src/frontend/qt_sdl/SaveManager.h
@@ -0,0 +1,70 @@
+/*
+ Copyright 2016-2022 melonDS team
+
+ 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 SAVEMANAGER_H
+#define SAVEMANAGER_H
+
+#include <string>
+#include <unistd.h>
+#include <time.h>
+#include <atomic>
+#include <QThread>
+#include <QMutex>
+
+#include "types.h"
+
+class SaveManager : public QThread
+{
+ Q_OBJECT
+ void run() override;
+
+public:
+ SaveManager(std::string path);
+ ~SaveManager();
+
+ std::string GetPath();
+ void SetPath(std::string path, bool reload);
+
+ void RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen);
+ void CheckFlush();
+
+ bool NeedsFlush();
+ void FlushSecondaryBuffer(u8* dst = nullptr, u32 dstLength = 0);
+
+private:
+ std::string Path;
+
+ std::atomic_bool Running;
+
+ u8* Buffer;
+ u32 Length;
+ bool FlushRequested;
+
+ QMutex* SecondaryBufferLock;
+ u8* SecondaryBuffer;
+ u32 SecondaryBufferLength;
+
+ time_t TimeAtLastFlushRequest;
+
+ // We keep versions in case the user closes the application before
+ // a flush cycle is finished.
+ u32 PreviousFlushVersion;
+ u32 FlushVersion;
+};
+
+#endif // SAVEMANAGER_H
diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp
index 0a5e65d..8087ee6 100644
--- a/src/frontend/qt_sdl/TitleManagerDialog.cpp
+++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -23,7 +23,7 @@
#include "types.h"
#include "Platform.h"
#include "Config.h"
-#include "FrontendUtil.h"
+#include "ROMManager.h"
#include "DSi_NAND.h"
#include "TitleManagerDialog.h"
@@ -31,7 +31,7 @@
#include "ui_TitleImportDialog.h"
-FILE* TitleManagerDialog::curNAND = nullptr;
+bool TitleManagerDialog::NANDInited = false;
TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr;
extern std::string EmuDirectory;
@@ -111,7 +111,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid)
DSi_NAND::GetTitleInfo(category, titleid, version, &header, &banner);
u32 icondata[32*32];
- Frontend::ROMIcon(banner.Icon, banner.Palette, icondata);
+ ROMManager::ROMIcon(banner.Icon, banner.Palette, icondata);
QImage iconimg((const uchar*)icondata, 32, 32, QImage::Format_ARGB32);
QIcon icon(QPixmap::fromImage(iconimg.copy()));
@@ -136,6 +136,8 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid)
bool TitleManagerDialog::openNAND()
{
+ NANDInited = false;
+
FILE* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
if (!bios7i)
return false;
@@ -145,28 +147,21 @@ bool TitleManagerDialog::openNAND()
fread(es_keyY, 16, 1, bios7i);
fclose(bios7i);
- curNAND = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b");
- if (!curNAND)
- return false;
-
- if (!DSi_NAND::Init(curNAND, es_keyY))
+ if (!DSi_NAND::Init(es_keyY))
{
- fclose(curNAND);
- curNAND = nullptr;
return false;
}
+ NANDInited = true;
return true;
}
void TitleManagerDialog::closeNAND()
{
- if (curNAND)
+ if (NANDInited)
{
DSi_NAND::DeInit();
-
- fclose(curNAND);
- curNAND = nullptr;
+ NANDInited = false;
}
}
diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h
index 682362a..cba7047 100644
--- a/src/frontend/qt_sdl/TitleManagerDialog.h
+++ b/src/frontend/qt_sdl/TitleManagerDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -45,7 +45,7 @@ public:
explicit TitleManagerDialog(QWidget* parent);
~TitleManagerDialog();
- static FILE* curNAND;
+ static bool NANDInited;
static bool openNAND();
static void closeNAND();
diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp
index 0c3f13e..87a796d 100644
--- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h
index 4503012..527cc93 100644
--- a/src/frontend/qt_sdl/VideoSettingsDialog.h
+++ b/src/frontend/qt_sdl/VideoSettingsDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp
index d438179..9bf265e 100644
--- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -50,12 +50,12 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
- LAN_Socket::Init();
haspcap = LAN_PCap::Init(false);
ui->rbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)");
- ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr != 0);
+ ui->lblAdapterMAC->setText("(none)");
+ ui->lblAdapterIP->setText("(none)");
int sel = 0;
for (int i = 0; i < LAN_PCap::NumAdapters; i++)
@@ -64,13 +64,14 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
ui->cbxDirectAdapter->addItem(QString(adapter->FriendlyName));
- if (!strncmp(adapter->DeviceName, Config::LANDevice, 128))
+ if (!strncmp(adapter->DeviceName, Config::LANDevice.c_str(), 128))
sel = i;
}
ui->cbxDirectAdapter->setCurrentIndex(sel);
- ui->rbDirectMode->setChecked(Config::DirectLAN != 0);
- ui->rbIndirectMode->setChecked(Config::DirectLAN == 0);
+ // errrr???
+ ui->rbDirectMode->setChecked(Config::DirectLAN);
+ ui->rbIndirectMode->setChecked(!Config::DirectLAN);
if (!haspcap) ui->rbDirectMode->setEnabled(false);
updateAdapterControls();
@@ -87,19 +88,17 @@ void WifiSettingsDialog::done(int r)
if (r == QDialog::Accepted)
{
- Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked() ? 1:0;
- Config::DirectLAN = ui->rbDirectMode->isChecked() ? 1:0;
+ Config::DirectLAN = ui->rbDirectMode->isChecked();
int sel = ui->cbxDirectAdapter->currentIndex();
if (sel < 0 || sel >= LAN_PCap::NumAdapters) sel = 0;
if (LAN_PCap::NumAdapters < 1)
{
- Config::LANDevice[0] = '\0';
+ Config::LANDevice = "";
}
else
{
- strncpy(Config::LANDevice, LAN_PCap::Adapters[sel].DeviceName, 127);
- Config::LANDevice[127] = '\0';
+ Config::LANDevice = LAN_PCap::Adapters[sel].DeviceName;
}
Config::Save();
diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.h b/src/frontend/qt_sdl/WifiSettingsDialog.h
index a7cf538..da94924 100644
--- a/src/frontend/qt_sdl/WifiSettingsDialog.h
+++ b/src/frontend/qt_sdl/WifiSettingsDialog.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.ui b/src/frontend/qt_sdl/WifiSettingsDialog.ui
index 0897059..444e1d5 100644
--- a/src/frontend/qt_sdl/WifiSettingsDialog.ui
+++ b/src/frontend/qt_sdl/WifiSettingsDialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>572</width>
- <height>273</height>
+ <height>217</height>
</rect>
</property>
<property name="sizePolicy">
@@ -26,16 +26,26 @@
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
- <string>Local</string>
+ <string>Network mode</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
- <widget class="QCheckBox" name="cbBindAnyAddr">
+ <widget class="QRadioButton" name="rbIndirectMode">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indirect mode uses libslirp. It requires no extra setup and is easy to use.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Indirect mode (uses libslirp, recommended)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QRadioButton" name="rbDirectMode">
<property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enabling this allows (theoretically) playing local multiplayer games over a local network. It may or may not help make for a better connection in general.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>Bind socket to any address</string>
+ <string>Direct mode [TEXT PLACEHOLDER]</string>
</property>
</widget>
</item>
@@ -43,91 +53,62 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="groupBox_2">
+ <widget class="QGroupBox" name="groupBox_3">
<property name="title">
- <string>Online</string>
+ <string>Direct mode settings</string>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="3" column="0" rowspan="3" colspan="2">
- <widget class="QGroupBox" name="groupBox_3">
- <property name="title">
- <string>Direct mode settings</string>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Network adapter:</string>
</property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Network adapter:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="cbxDirectAdapter">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>300</width>
- <height>0</height>
- </size>
- </property>
- <property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects the network adapter through which to route network traffic under direct mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>MAC address:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="lblAdapterMAC">
- <property name="text">
- <string>[PLACEHOLDER]</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>IP address:</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLabel" name="lblAdapterIP">
- <property name="text">
- <string>[PLACEHOLDER]</string>
- </property>
- </widget>
- </item>
- </layout>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QRadioButton" name="rbIndirectMode">
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cbxDirectAdapter">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indirect mode uses libslirp. It requires no extra setup and is easy to use.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects the network adapter through which to route network traffic under direct mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
<property name="text">
- <string>Indirect mode (uses libslirp, recommended)</string>
+ <string>MAC address:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="lblAdapterMAC">
+ <property name="text">
+ <string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
<item row="2" column="0">
- <widget class="QRadioButton" name="rbDirectMode">
- <property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>IP address:</string>
</property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="lblAdapterIP">
<property name="text">
- <string>Direct mode [TEXT PLACEHOLDER]</string>
+ <string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
diff --git a/src/frontend/qt_sdl/font.h b/src/frontend/qt_sdl/font.h
index 59720ab..01e3bd2 100644
--- a/src/frontend/qt_sdl/font.h
+++ b/src/frontend/qt_sdl/font.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp
index 31bb3c0..88704b6 100644
--- a/src/frontend/qt_sdl/main.cpp
+++ b/src/frontend/qt_sdl/main.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -25,6 +25,7 @@
#include <string>
#include <algorithm>
+#include <QProcess>
#include <QApplication>
#include <QMessageBox>
#include <QMenuBar>
@@ -54,12 +55,17 @@
#include "EmuSettingsDialog.h"
#include "InputConfig/InputConfigDialog.h"
#include "VideoSettingsDialog.h"
+#include "CameraSettingsDialog.h"
#include "AudioSettingsDialog.h"
#include "FirmwareSettingsDialog.h"
+#include "PathSettingsDialog.h"
+#include "MPSettingsDialog.h"
#include "WifiSettingsDialog.h"
#include "InterfaceSettingsDialog.h"
#include "ROMInfoDialog.h"
+#include "RAMInfoDialog.h"
#include "TitleManagerDialog.h"
+#include "PowerManagement/PowerManagementDialog.h"
#include "types.h"
#include "version.h"
@@ -74,13 +80,16 @@
#include "SPU.h"
#include "Wifi.h"
#include "Platform.h"
+#include "LocalMP.h"
#include "Config.h"
#include "Savestate.h"
#include "main_shaders.h"
+#include "ROMManager.h"
#include "ArchiveUtil.h"
+#include "CameraManager.h"
// TODO: uniform variable spelling
@@ -97,6 +106,7 @@ bool videoSettingsDirty;
SDL_AudioDeviceID audioDevice;
int audioFreq;
+bool audioMuted;
SDL_cond* audioSync;
SDL_mutex* audioSyncLock;
@@ -107,9 +117,22 @@ u32 micExtBufferWritePos;
u32 micWavLength;
s16* micWavBuffer;
+CameraManager* camManager[2];
+bool camStarted[2];
+
+const struct { int id; float ratio; const char* label; } aspectRatios[] =
+{
+ { 0, 1, "4:3 (native)" },
+ { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"},
+ { 1, (16.f / 9) / (4.f / 3), "16:9" },
+ { 2, (21.f / 9) / (4.f / 3), "21:9" },
+ { 3, 0, "window" }
+};
+
void micCallback(void* data, Uint8* stream, int len);
+
void audioCallback(void* data, Uint8* stream, int len)
{
len /= (sizeof(s16) * 2);
@@ -125,7 +148,7 @@ void audioCallback(void* data, Uint8* stream, int len)
SDL_CondSignal(audioSync);
SDL_UnlockMutex(audioSyncLock);
- if (num_in < 1)
+ if ((num_in < 1) || audioMuted)
{
memset(stream, 0, len*sizeof(s16)*2);
return;
@@ -145,6 +168,23 @@ void audioCallback(void* data, Uint8* stream, int len)
Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len, Config::AudioVolume);
}
+void audioMute()
+{
+ int inst = Platform::InstanceID();
+ audioMuted = false;
+
+ switch (Config::MPAudioMode)
+ {
+ case 1: // only instance 1
+ if (inst > 0) audioMuted = true;
+ break;
+
+ case 2: // only currently focused instance
+ if (!mainWindow->isActiveWindow()) audioMuted = true;
+ break;
+ }
+}
+
void micOpen()
{
@@ -180,7 +220,7 @@ void micClose()
micDevice = 0;
}
-void micLoadWav(const char* name)
+void micLoadWav(std::string name)
{
SDL_AudioSpec format;
memset(&format, 0, sizeof(SDL_AudioSpec));
@@ -191,7 +231,7 @@ void micLoadWav(const char* name)
u8* buf;
u32 len;
- if (!SDL_LoadWAV(name, &format, &buf, &len))
+ if (!SDL_LoadWAV(name.c_str(), &format, &buf, &len))
return;
const u64 dstfreq = 44100;
@@ -317,7 +357,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
EmuPause = 0;
RunningSomething = false;
- connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(repaint()));
+ connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint()));
connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString)));
connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart()));
connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop()));
@@ -325,7 +365,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger()));
connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger()));
connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger()));
- connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged()));
+ connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged()));
connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled()));
connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger()));
@@ -413,6 +453,8 @@ void EmuThread::run()
double frameLimitError = 0.0;
double lastMeasureTime = lastTime;
+ u32 winUpdateCount = 0, winUpdateFreq = 1;
+
char melontitle[100];
while (EmuRunning != 0)
@@ -499,7 +541,7 @@ void EmuThread::run()
micProcess();
// auto screen layout
- if (Config::ScreenSizing == 3)
+ if (Config::ScreenSizing == screenSizing_Auto)
{
mainScreenPos[2] = mainScreenPos[1];
mainScreenPos[1] = mainScreenPos[0];
@@ -511,14 +553,14 @@ void EmuThread::run()
{
// constant flickering, likely displaying 3D on both screens
// TODO: when both screens are used for 2D only...???
- guess = 0;
+ guess = screenSizing_Even;
}
else
{
if (mainScreenPos[0] == 1)
- guess = 1;
+ guess = screenSizing_EmphTop;
else
- guess = 2;
+ guess = screenSizing_EmphBot;
}
if (guess != autoScreenSizing)
@@ -541,6 +583,12 @@ void EmuThread::run()
// emulate
u32 nlines = NDS::RunFrame();
+ if (ROMManager::NDSSave)
+ ROMManager::NDSSave->CheckFlush();
+
+ if (ROMManager::GBASave)
+ ROMManager::GBASave->CheckFlush();
+
FrontBufferLock.lock();
FrontBuffer = GPU::FrontBuffer;
#ifdef OGLRENDERER_ENABLED
@@ -563,11 +611,16 @@ void EmuThread::run()
if (EmuRunning == 0) break;
- emit windowUpdate();
+ winUpdateCount++;
+ if (winUpdateCount >= winUpdateFreq)
+ {
+ emit windowUpdate();
+ winUpdateCount = 0;
+ }
bool fastforward = Input::HotkeyDown(HK_FastForward);
- if (Config::AudioSync && (!fastforward) && audioDevice)
+ if (Config::AudioSync && !fastforward && audioDevice)
{
SDL_LockMutex(audioSyncLock);
while (SPU::GetOutputSize() > 1024)
@@ -616,7 +669,15 @@ void EmuThread::run()
float fpstarget = 1.0/frametimeStep;
- sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
+ winUpdateFreq = fps / (u32)round(fpstarget);
+ if (winUpdateFreq < 1)
+ winUpdateFreq = 1;
+
+ int inst = Platform::InstanceID();
+ if (inst == 0)
+ sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
+ else
+ sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1);
changeWindowTitle(melontitle);
}
}
@@ -631,7 +692,11 @@ void EmuThread::run()
EmuStatus = EmuRunning;
- sprintf(melontitle, "melonDS " MELONDS_VERSION);
+ int inst = Platform::InstanceID();
+ if (inst == 0)
+ sprintf(melontitle, "melonDS " MELONDS_VERSION);
+ else
+ sprintf(melontitle, "melonDS (%d)", inst+1);
changeWindowTitle(melontitle);
SDL_Delay(75);
@@ -719,19 +784,39 @@ bool EmuThread::emuIsActive()
return (RunningSomething == 1);
}
+ScreenHandler::ScreenHandler(QWidget* widget)
+{
+ widget->setMouseTracking(true);
+ widget->setAttribute(Qt::WA_AcceptTouchEvents);
+ QTimer* mouseTimer = setupMouseTimer();
+ widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);});
+}
+
+ScreenHandler::~ScreenHandler()
+{
+ mouseTimer->stop();
+}
void ScreenHandler::screenSetupLayout(int w, int h)
{
int sizing = Config::ScreenSizing;
if (sizing == 3) sizing = autoScreenSizing;
- float aspectRatios[] =
+ float aspectTop, aspectBot;
+
+ for (auto ratio : aspectRatios)
{
- 1.f,
- (16.f/9)/(4.f/3),
- (21.f/9)/(4.f/3),
- ((float)w/h)/(4.f/3)
- };
+ if (ratio.id == Config::ScreenAspectTop)
+ aspectTop = ratio.ratio;
+ if (ratio.id == Config::ScreenAspectBot)
+ aspectBot = ratio.ratio;
+ }
+
+ if (aspectTop == 0)
+ aspectTop = (float) w / h;
+
+ if (aspectBot == 0)
+ aspectBot = (float) w / h;
Frontend::SetupScreenLayout(w, h,
Config::ScreenLayout,
@@ -740,8 +825,8 @@ void ScreenHandler::screenSetupLayout(int w, int h)
Config::ScreenGap,
Config::IntegerScaling != 0,
Config::ScreenSwap != 0,
- aspectRatios[Config::ScreenAspectTop],
- aspectRatios[Config::ScreenAspectBot]);
+ aspectTop,
+ aspectBot);
numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind);
}
@@ -754,6 +839,11 @@ QSize ScreenHandler::screenGetMinSize(int factor = 1)
int w = 256 * factor;
int h = 192 * factor;
+ if (Config::ScreenSizing == 4 || Config::ScreenSizing == 5)
+ {
+ return QSize(w, h);
+ }
+
if (Config::ScreenLayout == 0) // natural
{
if (isHori)
@@ -889,7 +979,7 @@ void ScreenHandler::screenHandleTouch(QTouchEvent* event)
void ScreenHandler::showCursor()
{
- mainWindow->panel->setCursor(Qt::ArrowCursor);
+ mainWindow->panelWidget->setCursor(Qt::ArrowCursor);
mouseTimer->start();
}
@@ -903,7 +993,7 @@ QTimer* ScreenHandler::setupMouseTimer()
return mouseTimer;
}
-ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent)
+ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this)
{
screen[0] = QImage(256, 192, QImage::Format_RGB32);
screen[1] = QImage(256, 192, QImage::Format_RGB32);
@@ -911,17 +1001,12 @@ ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent)
screenTrans[0].reset();
screenTrans[1].reset();
- touching = false;
-
- setAttribute(Qt::WA_AcceptTouchEvents);
-
OSD::Init(nullptr);
}
ScreenPanelNative::~ScreenPanelNative()
{
OSD::DeInit(nullptr);
- mouseTimer->stop();
}
void ScreenPanelNative::setupScreenLayout()
@@ -1020,17 +1105,11 @@ void ScreenPanelNative::onScreenLayoutChanged()
}
-ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent)
-{
- touching = false;
-
- setAttribute(Qt::WA_AcceptTouchEvents);
-}
+ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent), ScreenHandler(this)
+{}
ScreenPanelGL::~ScreenPanelGL()
{
- mouseTimer->stop();
-
makeCurrent();
OSD::DeInit(this);
@@ -1281,11 +1360,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
oldW = Config::WindowWidth;
oldH = Config::WindowHeight;
- oldMax = Config::WindowMaximized!=0;
+ oldMax = Config::WindowMaximized;
setWindowTitle("melonDS " MELONDS_VERSION);
setAttribute(Qt::WA_DeleteOnClose);
setAcceptDrops(true);
+ setFocusPolicy(Qt::ClickFocus);
+
+ int inst = Platform::InstanceID();
QMenuBar* menubar = new QMenuBar();
{
@@ -1295,16 +1377,16 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile);
actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open));
- actOpenROMArchive = menu->addAction("Open ROM inside archive...");
+ /*actOpenROMArchive = menu->addAction("Open ROM inside archive...");
connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive);
- actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));
+ actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/
recentMenu = menu->addMenu("Open recent");
for (int i = 0; i < 10; ++i)
{
- char* item = Config::RecentROMList[i];
- if (strlen(item) > 0)
- recentFileList.push_back(item);
+ std::string item = Config::RecentROMList[i];
+ if (!item.empty())
+ recentFileList.push_back(QString::fromStdString(item));
}
updateRecentFilesMenu();
@@ -1314,6 +1396,41 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
menu->addSeparator();
+ actCurrentCart = menu->addAction("DS slot: " + ROMManager::CartLabel());
+ actCurrentCart->setEnabled(false);
+
+ actInsertCart = menu->addAction("Insert cart...");
+ connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart);
+
+ actEjectCart = menu->addAction("Eject cart");
+ connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart);
+
+ menu->addSeparator();
+
+ actCurrentGBACart = menu->addAction("GBA slot: " + ROMManager::GBACartLabel());
+ actCurrentGBACart->setEnabled(false);
+
+ actInsertGBACart = menu->addAction("Insert ROM cart...");
+ connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart);
+
+ {
+ QMenu* submenu = menu->addMenu("Insert add-on cart");
+
+ actInsertGBAAddon[0] = submenu->addAction("Memory expansion");
+ actInsertGBAAddon[0]->setData(QVariant(NDS::GBAAddon_RAMExpansion));
+ connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon);
+ }
+
+ actEjectGBACart = menu->addAction("Eject cart");
+ connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart);
+
+ menu->addSeparator();
+
+ actImportSavefile = menu->addAction("Import savefile");
+ connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile);
+
+ menu->addSeparator();
+
{
QMenu* submenu = menu->addMenu("Save state");
@@ -1351,9 +1468,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12));
connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad);
- actImportSavefile = menu->addAction("Import savefile");
- connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile);
-
menu->addSeparator();
actQuit = menu->addAction("Quit");
@@ -1377,20 +1491,39 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
menu->addSeparator();
+ actPowerManagement = menu->addAction("Power management");
+ connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement);
+
+ menu->addSeparator();
+
actEnableCheats = menu->addAction("Enable cheats");
actEnableCheats->setCheckable(true);
connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats);
- actSetupCheats = menu->addAction("Setup cheat codes");
- actSetupCheats->setMenuRole(QAction::NoRole);
- connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats);
+ //if (inst == 0)
+ {
+ actSetupCheats = menu->addAction("Setup cheat codes");
+ actSetupCheats->setMenuRole(QAction::NoRole);
+ connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats);
+
+ menu->addSeparator();
+ actROMInfo = menu->addAction("ROM info");
+ connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo);
- menu->addSeparator();
- actROMInfo = menu->addAction("ROM info");
- connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo);
+ actRAMInfo = menu->addAction("RAM search");
+ connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo);
+
+ actTitleManager = menu->addAction("Manage DSi titles");
+ connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager);
+ }
- actTitleManager = menu->addAction("Manage DSi titles");
- connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager);
+ {
+ menu->addSeparator();
+ QMenu* submenu = menu->addMenu("Multiplayer");
+
+ actMPNewInstance = submenu->addAction("Launch new instance");
+ connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance);
+ }
}
{
QMenu* menu = menubar->addMenu("Config");
@@ -1399,7 +1532,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings);
#ifdef __APPLE__
- QAction* actPreferences = menu->addAction("Preferences...");
+ actPreferences = menu->addAction("Preferences...");
connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings);
actPreferences->setMenuRole(QAction::PreferencesRole);
#endif
@@ -1410,17 +1543,26 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actVideoSettings = menu->addAction("Video settings");
connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings);
+ actCameraSettings = menu->addAction("Camera settings");
+ connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings);
+
actAudioSettings = menu->addAction("Audio settings");
connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings);
+ actMPSettings = menu->addAction("Multiplayer settings");
+ connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings);
+
actWifiSettings = menu->addAction("Wifi settings");
connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings);
+ actFirmwareSettings = menu->addAction("Firmware settings");
+ connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings);
+
actInterfaceSettings = menu->addAction("Interface settings");
connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings);
- actFirmwareSettings = menu->addAction("Firmware settings");
- connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings);
+ actPathSettings = menu->addAction("Path settings");
+ connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings);
{
QMenu* submenu = menu->addMenu("Savestate settings");
@@ -1503,7 +1645,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"};
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < screenSizing_MAX; i++)
{
actScreenSizing[i] = submenu->addAction(QString(screensizing[i]));
actScreenSizing[i]->setActionGroup(grpScreenSizing);
@@ -1522,34 +1664,34 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
QMenu* submenu = menu->addMenu("Aspect ratio");
grpScreenAspectTop = new QActionGroup(submenu);
+ grpScreenAspectBot = new QActionGroup(submenu);
+ actScreenAspectTop = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])];
+ actScreenAspectBot = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])];
- const char* aspectRatiosTop[] = {"Top 4:3 (native)", "Top 16:9", "Top 21:9", "Top window"};
-
- for (int i = 0; i < 4; i++)
+ for (int i = 0; i < 2; i++)
{
- actScreenAspectTop[i] = submenu->addAction(QString(aspectRatiosTop[i]));
- actScreenAspectTop[i]->setActionGroup(grpScreenAspectTop);
- actScreenAspectTop[i]->setData(QVariant(i));
- actScreenAspectTop[i]->setCheckable(true);
- }
-
- connect(grpScreenAspectTop, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectTop);
+ QActionGroup* group = grpScreenAspectTop;
+ QAction** actions = actScreenAspectTop;
- submenu->addSeparator();
-
- grpScreenAspectBot = new QActionGroup(submenu);
+ if (i == 1)
+ {
+ group = grpScreenAspectBot;
+ submenu->addSeparator();
+ actions = actScreenAspectBot;
+ }
- const char* aspectRatiosBot[] = {"Bottom 4:3 (native)", "Bottom 16:9", "Bottom 21:9", "Bottom window"};
+ for (int j = 0; j < sizeof(aspectRatios) / sizeof(aspectRatios[0]); j++)
+ {
+ auto ratio = aspectRatios[j];
+ QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label);
+ actions[j] = submenu->addAction(label);
+ actions[j]->setActionGroup(group);
+ actions[j]->setData(QVariant(ratio.id));
+ actions[j]->setCheckable(true);
+ }
- for (int i = 0; i < 4; i++)
- {
- actScreenAspectBot[i] = submenu->addAction(QString(aspectRatiosBot[i]));
- actScreenAspectBot[i]->setActionGroup(grpScreenAspectBot);
- actScreenAspectBot[i]->setData(QVariant(i));
- actScreenAspectBot[i]->setCheckable(true);
+ connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect);
}
-
- connect(grpScreenAspectBot, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectBot);
}
actScreenFiltering = menu->addAction("Screen filtering");
@@ -1574,6 +1716,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
resize(Config::WindowWidth, Config::WindowHeight);
+ if (Config::FirmwareUsername == "Arisotura")
+ actMPNewInstance->setText("Fart");
+
#ifdef Q_OS_MAC
QPoint screenCenter = screen()->availableGeometry().center();
QRect frameGeo = frameGeometry();
@@ -1588,6 +1733,16 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
createScreenPanel();
+ actEjectCart->setEnabled(false);
+ actEjectGBACart->setEnabled(false);
+
+ if (Config::ConsoleType == 1)
+ {
+ actInsertGBACart->setEnabled(false);
+ for (int i = 0; i < 1; i++)
+ actInsertGBAAddon[i]->setEnabled(false);
+ }
+
for (int i = 0; i < 9; i++)
{
actSaveState[i]->setEnabled(false);
@@ -1601,14 +1756,17 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actStop->setEnabled(false);
actFrameStep->setEnabled(false);
+ actPowerManagement->setEnabled(false);
+
actSetupCheats->setEnabled(false);
- actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0);
+ actTitleManager->setEnabled(!Config::DSiNANDPath.empty());
- actEnableCheats->setChecked(Config::EnableCheats != 0);
+ actEnableCheats->setChecked(Config::EnableCheats);
actROMInfo->setEnabled(false);
+ actRAMInfo->setEnabled(false);
- actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0);
+ actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM);
actScreenRotation[Config::ScreenRotation]->setChecked(true);
@@ -1623,18 +1781,36 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actScreenLayout[Config::ScreenLayout]->setChecked(true);
actScreenSizing[Config::ScreenSizing]->setChecked(true);
- actIntegerScaling->setChecked(Config::IntegerScaling != 0);
+ actIntegerScaling->setChecked(Config::IntegerScaling);
- actScreenSwap->setChecked(Config::ScreenSwap != 0);
+ actScreenSwap->setChecked(Config::ScreenSwap);
- actScreenAspectTop[Config::ScreenAspectTop]->setChecked(true);
- actScreenAspectBot[Config::ScreenAspectBot]->setChecked(true);
+ for (int i = 0; i < sizeof(aspectRatios) / sizeof(aspectRatios[0]); i++)
+ {
+ if (Config::ScreenAspectTop == aspectRatios[i].id)
+ actScreenAspectTop[i]->setChecked(true);
+ if (Config::ScreenAspectBot == aspectRatios[i].id)
+ actScreenAspectBot[i]->setChecked(true);
+ }
- actScreenFiltering->setChecked(Config::ScreenFilter != 0);
- actShowOSD->setChecked(Config::ShowOSD != 0);
+ actScreenFiltering->setChecked(Config::ScreenFilter);
+ actShowOSD->setChecked(Config::ShowOSD);
- actLimitFramerate->setChecked(Config::LimitFPS != 0);
- actAudioSync->setChecked(Config::AudioSync != 0);
+ actLimitFramerate->setChecked(Config::LimitFPS);
+ actAudioSync->setChecked(Config::AudioSync);
+
+ if (inst > 0)
+ {
+ actEmuSettings->setEnabled(false);
+ actVideoSettings->setEnabled(false);
+ actMPSettings->setEnabled(false);
+ actWifiSettings->setEnabled(false);
+ actInterfaceSettings->setEnabled(false);
+
+#ifdef __APPLE__
+ actPreferences->setEnabled(false);
+#endif // __APPLE__
+ }
}
MainWindow::~MainWindow()
@@ -1645,17 +1821,13 @@ void MainWindow::createScreenPanel()
{
hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
- QTimer* mouseTimer;
-
if (hasOGL)
{
- panelGL = new ScreenPanelGL(this);
+ ScreenPanelGL* panelGL = new ScreenPanelGL(this);
panelGL->show();
panel = panelGL;
- panelGL->setMouseTracking(true);
- mouseTimer = panelGL->setupMouseTimer();
- connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelGL->setCursor(Qt::BlankCursor);});
+ panelWidget = panelGL;
if (!panelGL->isValid())
hasOGL = false;
@@ -1672,17 +1844,14 @@ void MainWindow::createScreenPanel()
if (!hasOGL)
{
- panelNative = new ScreenPanelNative(this);
+ ScreenPanelNative* panelNative = new ScreenPanelNative(this);
panel = panelNative;
- panel->show();
-
- panelNative->setMouseTracking(true);
- mouseTimer = panelNative->setupMouseTimer();
- connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelNative->setCursor(Qt::BlankCursor);});
+ panelWidget = panelNative;
+ panelWidget->show();
}
- setCentralWidget(panel);
+ setCentralWidget(panelWidget);
- connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged()));
+ connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged()));
emit screenLayoutChange();
}
@@ -1690,7 +1859,7 @@ QOpenGLContext* MainWindow::getOGLContext()
{
if (!hasOGL) return nullptr;
- QOpenGLWidget* glpanel = (QOpenGLWidget*)panel;
+ QOpenGLWidget* glpanel = dynamic_cast<QOpenGLWidget*>(panel);
return glpanel->context();
}
@@ -1755,9 +1924,9 @@ void MainWindow::dragEnterEvent(QDragEnterEvent* event)
QStringList acceptedExts{".nds", ".srl", ".dsi", ".gba", ".rar",
".zip", ".7z", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"};
- for(const QString &ext : acceptedExts)
+ for (const QString &ext : acceptedExts)
{
- if(filename.endsWith(ext, Qt::CaseInsensitive))
+ if (filename.endsWith(ext, Qt::CaseInsensitive))
event->acceptProposedAction();
}
}
@@ -1769,69 +1938,79 @@ void MainWindow::dropEvent(QDropEvent* event)
QList<QUrl> urls = event->mimeData()->urls();
if (urls.count() > 1) return; // not handling more than one file at once
- emuThread->emuPause();
-
QString filename = urls.at(0).toLocalFile();
- QString ext = filename.right(3).toLower();
+ QStringList arcexts{".zip", ".7z", ".rar", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"};
- recentFileList.removeAll(filename);
- recentFileList.prepend(filename);
- updateRecentFilesMenu();
-
- char _filename[1024];
- strncpy(_filename, filename.toStdString().c_str(), 1023); _filename[1023] = '\0';
+ emuThread->emuPause();
- int slot; int res;
- if (ext == "gba")
- {
- slot = 1;
- res = Frontend::LoadROM(_filename, Frontend::ROMSlot_GBA);
- }
- else if(ext == "nds" || ext == "srl" || ext == "dsi")
+ if (!verifySetup())
{
- slot = 0;
- res = Frontend::LoadROM(_filename, Frontend::ROMSlot_NDS);
+ emuThread->emuUnpause();
+ return;
}
- else
+
+ for (const QString &ext : arcexts)
{
- QByteArray romBuffer;
- QString romFileName = pickAndExtractFileFromArchive(_filename, &romBuffer);
- if(romFileName.isEmpty())
- {
- res = Frontend::Load_ROMLoadError;
- }
- else
+ if (filename.endsWith(ext, Qt::CaseInsensitive))
{
- slot = (romFileName.endsWith(".gba", Qt::CaseInsensitive) ? 1 : 0);
- QString sramFileName = QFileInfo(_filename).absolutePath() + QDir::separator() + QFileInfo(romFileName).completeBaseName() + ".sav";
-
- if(slot == 0)
- strncpy(Frontend::NDSROMExtension, QFileInfo(romFileName).suffix().toStdString().c_str(), 4);
+ QString arcfile = pickFileFromArchive(filename);
+ if (arcfile.isEmpty())
+ {
+ emuThread->emuUnpause();
+ return;
+ }
- res = Frontend::LoadROM((const u8*)romBuffer.constData(), romBuffer.size(),
- _filename, romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(),
- slot);
+ filename += "|" + arcfile;
}
}
- if (res != Frontend::Load_OK)
- {
- QMessageBox::critical(this,
- "melonDS",
- loadErrorStr(res));
- emuThread->emuUnpause();
- }
- else if (slot == 1)
+ QStringList file = filename.split('|');
+
+ if (filename.endsWith(".gba", Qt::CaseInsensitive))
{
- // checkme
+ if (!ROMManager::LoadGBAROM(file))
+ {
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+ emuThread->emuUnpause();
+ return;
+ }
+
emuThread->emuUnpause();
+
+ updateCartInserted(true);
}
else
{
+ if (!ROMManager::LoadROM(file, true))
+ {
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+ emuThread->emuUnpause();
+ return;
+ }
+
+ recentFileList.removeAll(filename);
+ recentFileList.prepend(filename);
+ updateRecentFilesMenu();
+
+ NDS::Start();
emuThread->emuRun();
+
+ updateCartInserted(false);
}
}
+void MainWindow::focusInEvent(QFocusEvent* event)
+{
+ audioMute();
+}
+
+void MainWindow::focusOutEvent(QFocusEvent* event)
+{
+ audioMute();
+}
+
void MainWindow::onAppStateChanged(Qt::ApplicationState state)
{
if (state == Qt::ApplicationInactive)
@@ -1846,145 +2025,179 @@ void MainWindow::onAppStateChanged(Qt::ApplicationState state)
}
}
-QString MainWindow::loadErrorStr(int error)
+bool MainWindow::verifySetup()
{
- switch (error)
+ QString res = ROMManager::VerifySetup();
+ if (!res.isEmpty())
{
- case Frontend::Load_BIOS9Missing:
- return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
- case Frontend::Load_BIOS9Bad:
- return "DS ARM9 BIOS is not a valid BIOS dump.";
+ QMessageBox::critical(this, "melonDS", res);
+ return false;
+ }
- case Frontend::Load_BIOS7Missing:
- return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
- case Frontend::Load_BIOS7Bad:
- return "DS ARM7 BIOS is not a valid BIOS dump.";
+ return true;
+}
- case Frontend::Load_FirmwareMissing:
- return "DS firmware was not found or could not be accessed. Check your emu settings.";
- case Frontend::Load_FirmwareBad:
- return "DS firmware is not a valid firmware dump.";
- case Frontend::Load_FirmwareNotBootable:
- return "DS firmware is not bootable.";
+bool MainWindow::preloadROMs(QString filename, QString gbafilename)
+{
+ if (!verifySetup())
+ {
+ return false;
+ }
- case Frontend::Load_DSiBIOS9Missing:
- return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
- case Frontend::Load_DSiBIOS9Bad:
- return "DSi ARM9 BIOS is not a valid BIOS dump.";
+ bool gbaloaded = false;
+ if (!gbafilename.isEmpty())
+ {
+ QStringList gbafile = gbafilename.split('|');
+ if (!ROMManager::LoadGBAROM(gbafile))
+ {
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM.");
+ return false;
+ }
- case Frontend::Load_DSiBIOS7Missing:
- return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
- case Frontend::Load_DSiBIOS7Bad:
- return "DSi ARM7 BIOS is not a valid BIOS dump.";
+ gbaloaded = true;
+ }
- case Frontend::Load_DSiNANDMissing:
- return "DSi NAND was not found or could not be accessed. Check your emu settings.";
- case Frontend::Load_DSiNANDBad:
- return "DSi NAND is not a valid NAND dump.";
+ QStringList file = filename.split('|');
+ if (!ROMManager::LoadROM(file, true))
+ {
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+ return false;
+ }
- case Frontend::Load_ROMLoadError:
- return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application.";
+ recentFileList.removeAll(filename);
+ recentFileList.prepend(filename);
+ updateRecentFilesMenu();
+
+ NDS::Start();
+ emuThread->emuRun();
- default: return "Unknown error during launch; smack Arisotura.";
+ updateCartInserted(false);
+
+ if (gbaloaded)
+ {
+ updateCartInserted(true);
}
+
+ return true;
}
-void MainWindow::loadROM(QByteArray *romData, QString archiveFileName, QString romFileName)
+QString MainWindow::pickFileFromArchive(QString archiveFileName)
{
- recentFileList.removeAll(archiveFileName);
- recentFileList.prepend(archiveFileName);
- updateRecentFilesMenu();
+ QVector<QString> archiveROMList = Archive::ListArchive(archiveFileName);
- // Strip entire archive name and get folder path
- strncpy(Config::LastROMFolder, QFileInfo(archiveFileName).absolutePath().toStdString().c_str(), 1024);
+ QString romFileName = ""; // file name inside archive
- QString sramFileName = QFileInfo(archiveFileName).absolutePath() + QDir::separator() + QFileInfo(romFileName).completeBaseName() + ".sav";
+ if (archiveROMList.size() > 2)
+ {
+ archiveROMList.removeFirst();
- int slot; int res;
- if (romFileName.endsWith("gba"))
+ bool ok;
+ QString toLoad = QInputDialog::getItem(this, "melonDS",
+ "This archive contains multiple files. Select which ROM you want to load.", archiveROMList.toList(), 0, false, &ok);
+ if (!ok) // User clicked on cancel
+ return QString();
+
+ romFileName = toLoad;
+ }
+ else if (archiveROMList.size() == 2)
{
- slot = 1;
- res = Frontend::LoadROM((const u8*)romData->constData(), romData->size(),
- archiveFileName.toStdString().c_str(),
- romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(),
- Frontend::ROMSlot_GBA);
+ romFileName = archiveROMList.at(1);
}
- else
+ else if ((archiveROMList.size() == 1) && (archiveROMList[0] == QString("OK")))
{
- strncpy(Frontend::NDSROMExtension, QFileInfo(romFileName).suffix().toStdString().c_str(), 4);
- slot = 0;
- res = Frontend::LoadROM((const u8*)romData->constData(), romData->size(),
- archiveFileName.toStdString().c_str(),
- romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(),
- Frontend::ROMSlot_NDS);
+ QMessageBox::warning(this, "melonDS", "This archive is empty.");
}
-
- if (res != Frontend::Load_OK)
+ else
{
- QMessageBox::critical(this,
- "melonDS",
- loadErrorStr(res));
- emuThread->emuUnpause();
+ QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions.");
}
- else if (slot == 1)
+
+ return romFileName;
+}
+
+QStringList MainWindow::pickROM(bool gba)
+{
+ QString console;
+ QStringList romexts;
+ QStringList arcexts{"*.zip", "*.7z", "*.rar", "*.tar", "*.tar.gz", "*.tar.xz", "*.tar.bz2"};
+ QStringList ret;
+
+ if (gba)
{
- // checkme
- emuThread->emuUnpause();
+ console = "GBA";
+ romexts.append("*.gba");
}
else
{
- emuThread->emuRun();
+ console = "DS";
+ romexts.append({"*.nds", "*.dsi", "*.ids", "*.srl"});
}
-}
-void MainWindow::loadROM(QString filename)
-{
- recentFileList.removeAll(filename);
- recentFileList.prepend(filename);
- updateRecentFilesMenu();
+ QString filter = romexts.join(' ') + " " + arcexts.join(' ');
+ filter = console + " ROMs (" + filter + ");;Any file (*.*)";
- // TODO: validate the input file!!
- // * check that it is a proper ROM
- // * ensure the binary offsets are sane
- // * etc
+ QString filename = QFileDialog::getOpenFileName(this,
+ "Open "+console+" ROM",
+ QString::fromStdString(Config::LastROMFolder),
+ filter);
+ if (filename.isEmpty())
+ return ret;
- // this shit is stupid
- char file[1024];
- strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0';
+ int pos = filename.length() - 1;
+ while (filename[pos] != '/' && filename[pos] != '\\' && pos > 0) pos--;
+ QString path_dir = filename.left(pos);
+ QString path_file = filename.mid(pos+1);
- int pos = strlen(file)-1;
- while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--;
- strncpy(Config::LastROMFolder, file, pos);
- Config::LastROMFolder[pos] = '\0';
- char* ext = &file[strlen(file)-3];
+ Config::LastROMFolder = path_dir.toStdString();
- int slot; int res;
- if (!strcasecmp(ext, "gba"))
+ bool isarc = false;
+ for (const auto& ext : arcexts)
{
- slot = 1;
- res = Frontend::LoadROM(file, Frontend::ROMSlot_GBA);
+ int l = ext.length() - 1;
+ if (path_file.right(l).toLower() == ext.right(l))
+ {
+ isarc = true;
+ break;
+ }
}
- else
+
+ if (isarc)
{
- slot = 0;
- res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS);
- }
+ path_file = pickFileFromArchive(filename);
+ if (path_file.isEmpty())
+ return ret;
- if (res != Frontend::Load_OK)
+ ret.append(filename);
+ ret.append(path_file);
+ }
+ else
{
- QMessageBox::critical(this,
- "melonDS",
- loadErrorStr(res));
- emuThread->emuUnpause();
+ ret.append(filename);
}
- else if (slot == 1)
+
+ return ret;
+}
+
+void MainWindow::updateCartInserted(bool gba)
+{
+ bool inserted;
+ if (gba)
{
- // checkme
- emuThread->emuUnpause();
+ inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0);
+ actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel());
+ actEjectGBACart->setEnabled(inserted);
}
else
{
- emuThread->emuRun();
+ inserted = ROMManager::CartInserted();
+ actCurrentCart->setText("DS slot: " + ROMManager::CartLabel());
+ actEjectCart->setEnabled(inserted);
+ actImportSavefile->setEnabled(inserted);
+ actSetupCheats->setEnabled(inserted);
+ actROMInfo->setEnabled(inserted);
+ actRAMInfo->setEnabled(inserted);
}
}
@@ -1992,99 +2205,43 @@ void MainWindow::onOpenFile()
{
emuThread->emuPause();
- QString filename = QFileDialog::getOpenFileName(this,
- "Open ROM",
- Config::LastROMFolder,
- "DS ROMs (*.nds *.dsi *.srl);;GBA ROMs (*.gba *.zip);;Any file (*.*)");
- if (filename.isEmpty())
+ if (!verifySetup())
{
emuThread->emuUnpause();
return;
}
- loadROM(filename);
-}
-
-void MainWindow::onOpenFileArchive()
-{
- emuThread->emuPause();
-
- QString archiveFileName = QFileDialog::getOpenFileName(this,
- "Open ROM Archive",
- Config::LastROMFolder,
- "Archived ROMs (*.zip *.7z *.rar *.tar *.tar.gz *.tar.xz *.tar.bz2);;Any file (*.*)");
- if (archiveFileName.isEmpty())
+ QStringList file = pickROM(false);
+ if (file.isEmpty())
{
emuThread->emuUnpause();
return;
}
- QByteArray romBuffer;
- QString romFileName = pickAndExtractFileFromArchive(archiveFileName, &romBuffer);
- if(!romFileName.isEmpty())
+ if (!ROMManager::LoadROM(file, true))
{
- loadROM(&romBuffer, archiveFileName, romFileName);
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+ emuThread->emuUnpause();
+ return;
}
-}
-QString MainWindow::pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer)
-{
- printf("Finding list of ROMs...\n");
- QVector<QString> archiveROMList = Archive::ListArchive(archiveFileName.toUtf8().constData());
-
-
- QString romFileName; // file name inside archive
-
- if (archiveROMList.size() > 2)
- {
- archiveROMList.removeFirst();
-
- bool ok;
- QString toLoad = QInputDialog::getItem(this, "melonDS",
- "The archive was found to have multiple files. Select which ROM you want to load.", archiveROMList.toList(), 0, false, &ok);
- if(!ok) // User clicked on cancel
- return QString();
+ QString filename = file.join('|');
+ recentFileList.removeAll(filename);
+ recentFileList.prepend(filename);
+ updateRecentFilesMenu();
- printf("Extracting '%s'\n", toLoad.toUtf8().constData());
- QVector<QString> extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), toLoad.toUtf8().constData(), romBuffer);
- if (extractResult[0] != QString("Err"))
- {
- romFileName = extractResult[0];
- }
- else
- {
- QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]);
- }
- }
- else if (archiveROMList.size() == 2)
- {
- printf("Extracting the only ROM in archive\n");
- QVector<QString> extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), archiveROMList.at(1).toUtf8().constData(), romBuffer);
- if (extractResult[0] != QString("Err"))
- {
- romFileName = extractResult[0];
- }
- else
- {
- QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]);
- }
- }
- else if ((archiveROMList.size() == 1) && (archiveROMList[0] == QString("OK")))
- {
- QMessageBox::warning(this, "melonDS", "The archive is intact, but there are no files inside.");
- }
- else
- {
- QMessageBox::critical(this, "melonDS", "The archive could not be read. It may be corrupt or you don't have the permissions.");
- }
+ NDS::Start();
+ emuThread->emuRun();
- return romFileName;
+ updateCartInserted(false);
}
void MainWindow::onClearRecentFiles()
{
recentFileList.clear();
- memset(Config::RecentROMList, 0, 10 * 1024);
+ for (int i = 0; i < 10; i++)
+ Config::RecentROMList[i] = "";
updateRecentFilesMenu();
}
@@ -2092,8 +2249,10 @@ void MainWindow::updateRecentFilesMenu()
{
recentMenu->clear();
- for(int i = 0; i < recentFileList.size(); ++i)
+ for (int i = 0; i < recentFileList.size(); ++i)
{
+ if (i >= 10) break;
+
QString item_full = recentFileList.at(i);
QString item_display = item_full;
int itemlen = item_full.length();
@@ -2120,16 +2279,18 @@ void MainWindow::updateRecentFilesMenu()
actRecentFile_i->setData(item_full);
connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile);
- if(i < 10)
- strncpy(Config::RecentROMList[i], recentFileList.at(i).toStdString().c_str(), 1024);
+ Config::RecentROMList[i] = recentFileList.at(i).toStdString();
}
+ while (recentFileList.size() > 10)
+ recentFileList.removeLast();
+
recentMenu->addSeparator();
QAction *actClearRecentList = recentMenu->addAction("Clear");
connect(actClearRecentList, &QAction::triggered, this, &MainWindow::onClearRecentFiles);
- if(recentFileList.empty())
+ if (recentFileList.empty())
actClearRecentList->setEnabled(false);
Config::Save();
@@ -2138,48 +2299,139 @@ void MainWindow::updateRecentFilesMenu()
void MainWindow::onClickRecentFile()
{
QAction *act = (QAction *)sender();
- QString fileName = act->data().toString();
+ QString filename = act->data().toString();
+ QStringList file = filename.split('|');
+
+ emuThread->emuPause();
- if (fileName.endsWith(".gba", Qt::CaseInsensitive) ||
- fileName.endsWith(".nds", Qt::CaseInsensitive) ||
- fileName.endsWith(".srl", Qt::CaseInsensitive) ||
- fileName.endsWith(".dsi", Qt::CaseInsensitive))
+ if (!verifySetup())
{
- emuThread->emuPause();
- loadROM(fileName);
+ emuThread->emuUnpause();
+ return;
}
- else
+
+ if (!ROMManager::LoadROM(file, true))
{
- // Archives
- QString archiveFileName = fileName;
- QByteArray romBuffer;
- QString romFileName = MainWindow::pickAndExtractFileFromArchive(archiveFileName, &romBuffer);
- if(!romFileName.isEmpty())
- {
- emuThread->emuPause();
- loadROM(&romBuffer, archiveFileName, romFileName);
- }
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+ emuThread->emuUnpause();
+ return;
}
+
+ recentFileList.removeAll(filename);
+ recentFileList.prepend(filename);
+ updateRecentFilesMenu();
+
+ NDS::Start();
+ emuThread->emuRun();
+
+ updateCartInserted(false);
}
void MainWindow::onBootFirmware()
{
- // TODO: check the whole GBA cart shito
+ emuThread->emuPause();
+ if (!verifySetup())
+ {
+ emuThread->emuUnpause();
+ return;
+ }
+
+ if (!ROMManager::LoadBIOS())
+ {
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "This firmware is not bootable.");
+ emuThread->emuUnpause();
+ return;
+ }
+
+ NDS::Start();
+ emuThread->emuRun();
+}
+
+void MainWindow::onInsertCart()
+{
emuThread->emuPause();
- int res = Frontend::LoadBIOS();
- if (res != Frontend::Load_OK)
+ QStringList file = pickROM(false);
+ if (file.isEmpty())
{
- QMessageBox::critical(this,
- "melonDS",
- loadErrorStr(res));
emuThread->emuUnpause();
+ return;
}
- else
+
+ if (!ROMManager::LoadROM(file, false))
{
- emuThread->emuRun();
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+ emuThread->emuUnpause();
+ return;
}
+
+ emuThread->emuUnpause();
+
+ updateCartInserted(false);
+}
+
+void MainWindow::onEjectCart()
+{
+ emuThread->emuPause();
+
+ ROMManager::EjectCart();
+
+ emuThread->emuUnpause();
+
+ updateCartInserted(false);
+}
+
+void MainWindow::onInsertGBACart()
+{
+ emuThread->emuPause();
+
+ QStringList file = pickROM(true);
+ if (file.isEmpty())
+ {
+ emuThread->emuUnpause();
+ return;
+ }
+
+ if (!ROMManager::LoadGBAROM(file))
+ {
+ // TODO: better error reporting?
+ QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+ emuThread->emuUnpause();
+ return;
+ }
+
+ emuThread->emuUnpause();
+
+ updateCartInserted(true);
+}
+
+void MainWindow::onInsertGBAAddon()
+{
+ QAction* act = (QAction*)sender();
+ int type = act->data().toInt();
+
+ emuThread->emuPause();
+
+ ROMManager::LoadGBAAddon(type);
+
+ emuThread->emuUnpause();
+
+ updateCartInserted(true);
+}
+
+void MainWindow::onEjectGBACart()
+{
+ emuThread->emuPause();
+
+ ROMManager::EjectGBACart();
+
+ emuThread->emuUnpause();
+
+ updateCartInserted(true);
}
void MainWindow::onSaveState()
@@ -2188,17 +2440,17 @@ void MainWindow::onSaveState()
emuThread->emuPause();
- char filename[1024];
+ std::string filename;
if (slot > 0)
{
- Frontend::GetSavestateName(slot, filename, 1024);
+ filename = ROMManager::GetSavestateName(slot);
}
else
{
// TODO: specific 'last directory' for savestate files?
QString qfilename = QFileDialog::getSaveFileName(this,
"Save state",
- Config::LastROMFolder,
+ QString::fromStdString(Config::LastROMFolder),
"melonDS savestates (*.mln);;Any file (*.*)");
if (qfilename.isEmpty())
{
@@ -2206,10 +2458,10 @@ void MainWindow::onSaveState()
return;
}
- strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0';
+ filename = qfilename.toStdString();
}
- if (Frontend::SaveState(filename))
+ if (ROMManager::SaveState(filename))
{
char msg[64];
if (slot > 0) sprintf(msg, "State saved to slot %d", slot);
@@ -2232,17 +2484,17 @@ void MainWindow::onLoadState()
emuThread->emuPause();
- char filename[1024];
+ std::string filename;
if (slot > 0)
{
- Frontend::GetSavestateName(slot, filename, 1024);
+ filename = ROMManager::GetSavestateName(slot);
}
else
{
// TODO: specific 'last directory' for savestate files?
QString qfilename = QFileDialog::getOpenFileName(this,
"Load state",
- Config::LastROMFolder,
+ QString::fromStdString(Config::LastROMFolder),
"melonDS savestates (*.ml*);;Any file (*.*)");
if (qfilename.isEmpty())
{
@@ -2250,7 +2502,7 @@ void MainWindow::onLoadState()
return;
}
- strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0';
+ filename = qfilename.toStdString();
}
if (!Platform::FileExists(filename))
@@ -2264,7 +2516,7 @@ void MainWindow::onLoadState()
return;
}
- if (Frontend::LoadState(filename))
+ if (ROMManager::LoadState(filename))
{
char msg[64];
if (slot > 0) sprintf(msg, "State loaded from slot %d", slot);
@@ -2284,7 +2536,7 @@ void MainWindow::onLoadState()
void MainWindow::onUndoStateLoad()
{
emuThread->emuPause();
- Frontend::UndoStateLoad();
+ ROMManager::UndoStateLoad();
emuThread->emuUnpause();
OSD::AddMessage(0, "State load undone");
@@ -2292,36 +2544,52 @@ void MainWindow::onUndoStateLoad()
void MainWindow::onImportSavefile()
{
- if (!RunningSomething) return;
-
emuThread->emuPause();
QString path = QFileDialog::getOpenFileName(this,
"Select savefile",
- Config::LastROMFolder,
+ QString::fromStdString(Config::LastROMFolder),
"Savefiles (*.sav *.bin *.dsv);;Any file (*.*)");
- if (!path.isEmpty())
+ if (path.isEmpty())
+ {
+ emuThread->emuUnpause();
+ return;
+ }
+
+ FILE* f = Platform::OpenFile(path.toStdString(), "rb", true);
+ if (!f)
+ {
+ QMessageBox::critical(this, "melonDS", "Could not open the given savefile.");
+ emuThread->emuUnpause();
+ return;
+ }
+
+ if (RunningSomething)
{
if (QMessageBox::warning(this,
- "Emulation will be reset and data overwritten",
+ "melonDS",
"The emulation will be reset and the current savefile overwritten.",
- QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok)
+ QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok)
{
- int res = Frontend::Reset();
- if (res != Frontend::Load_OK)
- {
- QMessageBox::critical(this, "melonDS", "Reset failed\n" + loadErrorStr(res));
- }
- else
- {
- int diff = Frontend::ImportSRAM(path.toStdString().c_str());
- if (diff > 0)
- OSD::AddMessage(0, "Trimmed savefile");
- else if (diff < 0)
- OSD::AddMessage(0, "Savefile shorter than SRAM");
- }
+ emuThread->emuUnpause();
+ return;
}
+
+ ROMManager::Reset();
}
+
+ u32 len;
+ fseek(f, 0, SEEK_END);
+ len = (u32)ftell(f);
+
+ u8* data = new u8[len];
+ fseek(f, 0, SEEK_SET);
+ fread(data, len, 1, f);
+
+ NDS::LoadSave(data, len);
+ delete[] data;
+
+ fclose(f);
emuThread->emuUnpause();
}
@@ -2360,19 +2628,10 @@ void MainWindow::onReset()
actUndoStateLoad->setEnabled(false);
- int res = Frontend::Reset();
- if (res != Frontend::Load_OK)
- {
- QMessageBox::critical(this,
- "melonDS",
- loadErrorStr(res));
- emuThread->emuUnpause();
- }
- else
- {
- OSD::AddMessage(0, "Reset");
- emuThread->emuRun();
- }
+ ROMManager::Reset();
+
+ OSD::AddMessage(0, "Reset");
+ emuThread->emuRun();
}
void MainWindow::onStop()
@@ -2393,7 +2652,7 @@ void MainWindow::onFrameStep()
void MainWindow::onEnableCheats(bool checked)
{
Config::EnableCheats = checked?1:0;
- Frontend::EnableCheats(Config::EnableCheats != 0);
+ ROMManager::EnableCheats(Config::EnableCheats != 0);
}
void MainWindow::onSetupCheats()
@@ -2414,11 +2673,33 @@ void MainWindow::onROMInfo()
ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this);
}
+void MainWindow::onRAMInfo()
+{
+ RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this);
+}
+
void MainWindow::onOpenTitleManager()
{
TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this);
}
+void MainWindow::onMPNewInstance()
+{
+ //QProcess::startDetached(QApplication::applicationFilePath());
+ QProcess newinst;
+ newinst.setProgram(QApplication::applicationFilePath());
+ newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1));
+
+#ifdef __WIN32__
+ newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args)
+ {
+ args->flags |= CREATE_NEW_CONSOLE;
+ });
+#endif
+
+ newinst.startDetached();
+}
+
void MainWindow::onOpenEmuSettings()
{
emuThread->emuPause();
@@ -2431,11 +2712,33 @@ void MainWindow::onEmuSettingsDialogFinished(int res)
{
emuThread->emuUnpause();
+ if (Config::ConsoleType == 1)
+ {
+ actInsertGBACart->setEnabled(false);
+ for (int i = 0; i < 1; i++)
+ actInsertGBAAddon[i]->setEnabled(false);
+ actEjectGBACart->setEnabled(false);
+ }
+ else
+ {
+ actInsertGBACart->setEnabled(true);
+ for (int i = 0; i < 1; i++)
+ actInsertGBAAddon[i]->setEnabled(true);
+ actEjectGBACart->setEnabled(ROMManager::GBACartInserted());
+ }
+
if (EmuSettingsDialog::needsReset)
onReset();
+ actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel());
+
if (!RunningSomething)
- actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0);
+ actTitleManager->setEnabled(!Config::DSiNANDPath.empty());
+}
+
+void MainWindow::onOpenPowerManagement()
+{
+ PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this);
}
void MainWindow::onOpenInputConfig()
@@ -2457,6 +2760,27 @@ void MainWindow::onOpenVideoSettings()
connect(dlg, &VideoSettingsDialog::updateVideoSettings, this, &MainWindow::onUpdateVideoSettings);
}
+void MainWindow::onOpenCameraSettings()
+{
+ emuThread->emuPause();
+
+ camStarted[0] = camManager[0]->isStarted();
+ camStarted[1] = camManager[1]->isStarted();
+ if (camStarted[0]) camManager[0]->stop();
+ if (camStarted[1]) camManager[1]->stop();
+
+ CameraSettingsDialog* dlg = CameraSettingsDialog::openDlg(this);
+ connect(dlg, &CameraSettingsDialog::finished, this, &MainWindow::onCameraSettingsFinished);
+}
+
+void MainWindow::onCameraSettingsFinished(int res)
+{
+ if (camStarted[0]) camManager[0]->start();
+ if (camStarted[1]) camManager[1]->start();
+
+ emuThread->emuUnpause();
+}
+
void MainWindow::onOpenAudioSettings()
{
AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this);
@@ -2480,6 +2804,22 @@ void MainWindow::onFirmwareSettingsFinished(int res)
emuThread->emuUnpause();
}
+void MainWindow::onOpenPathSettings()
+{
+ emuThread->emuPause();
+
+ PathSettingsDialog* dlg = PathSettingsDialog::openDlg(this);
+ connect(dlg, &PathSettingsDialog::finished, this, &MainWindow::onPathSettingsFinished);
+}
+
+void MainWindow::onPathSettingsFinished(int res)
+{
+ if (PathSettingsDialog::needsReset)
+ onReset();
+
+ emuThread->emuUnpause();
+}
+
void MainWindow::onUpdateAudioSettings()
{
SPU::SetInterpolation(Config::AudioInterp);
@@ -2515,6 +2855,22 @@ void MainWindow::onAudioSettingsFinished(int res)
micOpen();
}
+void MainWindow::onOpenMPSettings()
+{
+ emuThread->emuPause();
+
+ MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this);
+ connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished);
+}
+
+void MainWindow::onMPSettingsFinished(int res)
+{
+ audioMute();
+ LocalMP::SetRecvTimeout(Config::MPRecvTimeout);
+
+ emuThread->emuUnpause();
+}
+
void MainWindow::onOpenWifiSettings()
{
emuThread->emuPause();
@@ -2525,12 +2881,6 @@ void MainWindow::onOpenWifiSettings()
void MainWindow::onWifiSettingsFinished(int res)
{
- if (Wifi::MPInited)
- {
- Platform::MP_DeInit();
- Platform::MP_Init();
- }
-
Platform::LAN_DeInit();
Platform::LAN_Init();
@@ -2550,10 +2900,7 @@ void MainWindow::onOpenInterfaceSettings()
void MainWindow::onUpdateMouseTimer()
{
- if (hasOGL)
- panelGL->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
- else
- panelNative->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
+ panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
}
void MainWindow::onInterfaceSettingsFinished(int res)
@@ -2569,8 +2916,8 @@ void MainWindow::onChangeSavestateSRAMReloc(bool checked)
void MainWindow::onChangeScreenSize()
{
int factor = ((QAction*)sender())->data().toInt();
- QSize diff = size() - panel->size();
- resize(dynamic_cast<ScreenHandler*>(panel)->screenGetMinSize(factor) + diff);
+ QSize diff = size() - panelWidget->size();
+ resize(panel->screenGetMinSize(factor) + diff);
}
void MainWindow::onChangeScreenRotation(QAction* act)
@@ -2601,6 +2948,22 @@ void MainWindow::onChangeScreenSwap(bool checked)
{
Config::ScreenSwap = checked?1:0;
+ // Swap between top and bottom screen when displaying one screen.
+ if (Config::ScreenSizing == screenSizing_TopOnly)
+ {
+ // Bottom Screen.
+ Config::ScreenSizing = screenSizing_BotOnly;
+ actScreenSizing[screenSizing_TopOnly]->setChecked(false);
+ actScreenSizing[Config::ScreenSizing]->setChecked(true);
+ }
+ else if (Config::ScreenSizing == screenSizing_BotOnly)
+ {
+ // Top Screen.
+ Config::ScreenSizing = screenSizing_TopOnly;
+ actScreenSizing[screenSizing_BotOnly]->setChecked(false);
+ actScreenSizing[Config::ScreenSizing]->setChecked(true);
+ }
+
emit screenLayoutChange();
}
@@ -2612,18 +2975,19 @@ void MainWindow::onChangeScreenSizing(QAction* act)
emit screenLayoutChange();
}
-void MainWindow::onChangeScreenAspectTop(QAction* act)
+void MainWindow::onChangeScreenAspect(QAction* act)
{
int aspect = act->data().toInt();
- Config::ScreenAspectTop = aspect;
-
- emit screenLayoutChange();
-}
+ QActionGroup* group = act->actionGroup();
-void MainWindow::onChangeScreenAspectBot(QAction* act)
-{
- int aspect = act->data().toInt();
- Config::ScreenAspectBot = aspect;
+ if (group == grpScreenAspectTop)
+ {
+ Config::ScreenAspectTop = aspect;
+ }
+ else
+ {
+ Config::ScreenAspectBot = aspect;
+ }
emit screenLayoutChange();
}
@@ -2678,39 +3042,24 @@ void MainWindow::onFullscreenToggled()
void MainWindow::onEmuStart()
{
- // TODO: make savestates work in DSi mode!!
- if (Config::ConsoleType == 1)
+ for (int i = 1; i < 9; i++)
{
- for (int i = 0; i < 9; i++)
- {
- actSaveState[i]->setEnabled(false);
- actLoadState[i]->setEnabled(false);
- }
- actUndoStateLoad->setEnabled(false);
- }
- else
- {
- for (int i = 1; i < 9; i++)
- {
- actSaveState[i]->setEnabled(true);
- actLoadState[i]->setEnabled(Frontend::SavestateExists(i));
- }
- actSaveState[0]->setEnabled(true);
- actLoadState[0]->setEnabled(true);
- actUndoStateLoad->setEnabled(false);
+ actSaveState[i]->setEnabled(true);
+ actLoadState[i]->setEnabled(ROMManager::SavestateExists(i));
}
+ actSaveState[0]->setEnabled(true);
+ actLoadState[0]->setEnabled(true);
+ actUndoStateLoad->setEnabled(false);
actPause->setEnabled(true);
actPause->setChecked(false);
actReset->setEnabled(true);
actStop->setEnabled(true);
actFrameStep->setEnabled(true);
- actImportSavefile->setEnabled(true);
- actSetupCheats->setEnabled(true);
- actTitleManager->setEnabled(false);
+ actPowerManagement->setEnabled(true);
- actROMInfo->setEnabled(true);
+ actTitleManager->setEnabled(false);
}
void MainWindow::onEmuStop()
@@ -2723,17 +3072,15 @@ void MainWindow::onEmuStop()
actLoadState[i]->setEnabled(false);
}
actUndoStateLoad->setEnabled(false);
- actImportSavefile->setEnabled(false);
actPause->setEnabled(false);
actReset->setEnabled(false);
actStop->setEnabled(false);
actFrameStep->setEnabled(false);
- actSetupCheats->setEnabled(false);
- actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0);
+ actPowerManagement->setEnabled(false);
- actROMInfo->setEnabled(false);
+ actTitleManager->setEnabled(!Config::DSiNANDPath.empty());
}
void MainWindow::onUpdateVideoSettings(bool glchange)
@@ -2743,16 +3090,10 @@ void MainWindow::onUpdateVideoSettings(bool glchange)
emuThread->emuPause();
if (hasOGL)
- {
emuThread->deinitOpenGL();
- delete panelGL;
- }
- else
- {
- delete panelNative;
- }
+ delete panel;
createScreenPanel();
- connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint()));
+ connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint()));
if (hasOGL) emuThread->initOpenGL();
}
@@ -2767,9 +3108,6 @@ void emuStop()
{
RunningSomething = false;
- Frontend::UnloadROM(Frontend::ROMSlot_NDS);
- Frontend::UnloadROM(Frontend::ROMSlot_GBA);
-
emit emuThread->windowEmuStop();
OSD::AddMessage(0xFFC040, "Shutdown");
@@ -2788,7 +3126,8 @@ bool MelonApplication::event(QEvent *event)
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent*>(event);
emuThread->emuPause();
- mainWindow->loadROM(openEvent->file());
+ if (!mainWindow->preloadROMs(openEvent->file(), ""))
+ emuThread->emuUnpause();
}
return QApplication::event(event);
@@ -2796,7 +3135,7 @@ bool MelonApplication::event(QEvent *event)
int main(int argc, char** argv)
{
- srand(time(NULL));
+ srand(time(nullptr));
printf("melonDS " MELONDS_VERSION "\n");
printf(MELONDS_URL "\n");
@@ -2816,9 +3155,13 @@ int main(int argc, char** argv)
{
printf("SDL couldn't init joystick\n");
}
- if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
+ if (SDL_Init(SDL_INIT_AUDIO) < 0)
{
- QMessageBox::critical(NULL, "melonDS", "SDL shat itself :(");
+ const char* err = SDL_GetError();
+ QString errorStr = "Failed to initialize SDL. This could indicate an issue with your audio driver.\n\nThe error was: ";
+ errorStr += err;
+
+ QMessageBox::critical(NULL, "melonDS", errorStr);
return 1;
}
@@ -2843,7 +3186,7 @@ int main(int argc, char** argv)
SANITIZE(Config::ScreenRotation, 0, 3);
SANITIZE(Config::ScreenGap, 0, 500);
SANITIZE(Config::ScreenLayout, 0, 3);
- SANITIZE(Config::ScreenSizing, 0, 5);
+ SANITIZE(Config::ScreenSizing, 0, (int)screenSizing_MAX);
SANITIZE(Config::ScreenAspectTop, 0, 4);
SANITIZE(Config::ScreenAspectBot, 0, 4);
#undef SANITIZE
@@ -2856,6 +3199,7 @@ int main(int argc, char** argv)
format.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(format);
+ audioMuted = false;
audioSync = SDL_CreateCond();
audioSyncLock = SDL_CreateMutex();
@@ -2881,13 +3225,18 @@ int main(int argc, char** argv)
micDevice = 0;
-
memset(micExtBuffer, 0, sizeof(micExtBuffer));
micExtBufferWritePos = 0;
micWavBuffer = nullptr;
- Frontend::Init_ROM();
- Frontend::EnableCheats(Config::EnableCheats != 0);
+ camStarted[0] = false;
+ camStarted[1] = false;
+ camManager[0] = new CameraManager(0, 640, 480, true);
+ camManager[1] = new CameraManager(1, 640, 480, true);
+ camManager[0]->setXFlip(Config::Camera[0].XFlip);
+ camManager[1]->setXFlip(Config::Camera[1].XFlip);
+
+ ROMManager::EnableCheats(Config::EnableCheats != 0);
Frontend::Init_Audio(audioFreq);
@@ -2910,33 +3259,17 @@ int main(int argc, char** argv)
emuThread->start();
emuThread->emuPause();
+ audioMute();
+
QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged);
if (argc > 1)
{
- char* file = argv[1];
- char* ext = &file[strlen(file)-3];
-
- if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl") || !strcasecmp(ext, "dsi"))
- {
- int res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS);
+ QString file = argv[1];
+ QString gbafile = "";
+ if (argc > 2) gbafile = argv[2];
- if (res == Frontend::Load_OK)
- {
- if (argc > 2)
- {
- file = argv[2];
- ext = &file[strlen(file)-3];
-
- if (!strcasecmp(ext, "gba"))
- {
- Frontend::LoadROM(file, Frontend::ROMSlot_GBA);
- }
- }
-
- emuThread->emuRun();
- }
- }
+ mainWindow->preloadROMs(file, gbafile);
}
int ret = melon.exec();
@@ -2947,8 +3280,6 @@ int main(int argc, char** argv)
Input::CloseJoystick();
- Frontend::DeInit_ROM();
-
if (audioDevice) SDL_CloseAudioDevice(audioDevice);
micClose();
@@ -2957,6 +3288,9 @@ int main(int argc, char** argv)
if (micWavBuffer) delete[] micWavBuffer;
+ delete camManager[0];
+ delete camManager[1];
+
Config::Save();
SDL_Quit();
@@ -2972,7 +3306,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
{
int argc = 0;
wchar_t** argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
- char* nullarg = "";
+ char nullarg[] = {'\0'};
char** argv = new char*[argc];
for (int i = 0; i < argc; i++)
@@ -2987,7 +3321,8 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
if (argv_w) LocalFree(argv_w);
- /*if (AttachConsole(ATTACH_PARENT_PROCESS))
+ //if (AttachConsole(ATTACH_PARENT_PROCESS))
+ /*if (AllocConsole())
{
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h
index 0b5e917..1977b7f 100644
--- a/src/frontend/qt_sdl/main.h
+++ b/src/frontend/qt_sdl/main.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -101,7 +101,8 @@ class ScreenHandler
Q_GADGET
public:
- virtual ~ScreenHandler() {}
+ ScreenHandler(QWidget* widget);
+ virtual ~ScreenHandler();
QTimer* setupMouseTimer();
void updateMouseTimer();
QTimer* mouseTimer;
@@ -121,7 +122,7 @@ protected:
int screenKind[Frontend::MaxScreenTransforms];
int numScreens;
- bool touching;
+ bool touching = false;
void showCursor();
};
@@ -133,7 +134,7 @@ class ScreenPanelNative : public QWidget, public ScreenHandler
public:
explicit ScreenPanelNative(QWidget* parent);
- ~ScreenPanelNative();
+ virtual ~ScreenPanelNative();
protected:
void paintEvent(QPaintEvent* event) override;
@@ -163,7 +164,7 @@ class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler, protected QOpe
public:
explicit ScreenPanelGL(QWidget* parent);
- ~ScreenPanelGL();
+ virtual ~ScreenPanelGL();
protected:
void initializeGL() override;
@@ -211,8 +212,7 @@ public:
bool hasOGL;
QOpenGLContext* getOGLContext();
- void loadROM(QString filename);
- void loadROM(QByteArray *romData, QString archiveFileName, QString romFileName);
+ bool preloadROMs(QString filename, QString gbafilename);
void onAppStateChanged(Qt::ApplicationState state);
@@ -226,15 +226,22 @@ protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
+ void focusInEvent(QFocusEvent* event) override;
+ void focusOutEvent(QFocusEvent* event) override;
+
signals:
void screenLayoutChange();
private slots:
void onOpenFile();
- void onOpenFileArchive();
void onClickRecentFile();
void onClearRecentFiles();
void onBootFirmware();
+ void onInsertCart();
+ void onEjectCart();
+ void onInsertGBACart();
+ void onInsertGBAAddon();
+ void onEjectGBACart();
void onSaveState();
void onLoadState();
void onUndoStateLoad();
@@ -249,20 +256,29 @@ private slots:
void onSetupCheats();
void onCheatsDialogFinished(int res);
void onROMInfo();
+ void onRAMInfo();
void onOpenTitleManager();
+ void onMPNewInstance();
void onOpenEmuSettings();
void onEmuSettingsDialogFinished(int res);
+ void onOpenPowerManagement();
void onOpenInputConfig();
void onInputConfigFinished(int res);
void onOpenVideoSettings();
+ void onOpenCameraSettings();
+ void onCameraSettingsFinished(int res);
void onOpenAudioSettings();
- void onOpenFirmwareSettings();
void onUpdateAudioSettings();
void onAudioSettingsFinished(int res);
+ void onOpenMPSettings();
+ void onMPSettingsFinished(int res);
void onOpenWifiSettings();
void onWifiSettingsFinished(int res);
+ void onOpenFirmwareSettings();
void onFirmwareSettingsFinished(int res);
+ void onOpenPathSettings();
+ void onPathSettingsFinished(int res);
void onOpenInterfaceSettings();
void onInterfaceSettingsFinished(int res);
void onUpdateMouseTimer();
@@ -273,8 +289,7 @@ private slots:
void onChangeScreenLayout(QAction* act);
void onChangeScreenSwap(bool checked);
void onChangeScreenSizing(QAction* act);
- void onChangeScreenAspectTop(QAction* act);
- void onChangeScreenAspectBot(QAction* act);
+ void onChangeScreenAspect(QAction* act);
void onChangeIntegerScaling(bool checked);
void onChangeScreenFiltering(bool checked);
void onChangeShowOSD(bool checked);
@@ -291,33 +306,41 @@ private slots:
void onFullscreenToggled();
private:
+ QStringList currentROM;
+ QStringList currentGBAROM;
QList<QString> recentFileList;
QMenu *recentMenu;
void updateRecentFilesMenu();
- QString pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer);
+ bool verifySetup();
+ QString pickFileFromArchive(QString archiveFileName);
+ QStringList pickROM(bool gba);
+ void updateCartInserted(bool gba);
void createScreenPanel();
- QString loadErrorStr(int error);
-
bool pausedManually = false;
int oldW, oldH;
bool oldMax;
public:
- QWidget* panel;
- ScreenPanelGL* panelGL;
- ScreenPanelNative* panelNative;
+ ScreenHandler* panel;
+ QWidget* panelWidget;
QAction* actOpenROM;
- QAction* actOpenROMArchive;
QAction* actBootFirmware;
+ QAction* actCurrentCart;
+ QAction* actInsertCart;
+ QAction* actEjectCart;
+ QAction* actCurrentGBACart;
+ QAction* actInsertGBACart;
+ QAction* actInsertGBAAddon[1];
+ QAction* actEjectGBACart;
+ QAction* actImportSavefile;
QAction* actSaveState[9];
QAction* actLoadState[9];
QAction* actUndoStateLoad;
- QAction* actImportSavefile;
QAction* actQuit;
QAction* actPause;
@@ -327,14 +350,23 @@ public:
QAction* actEnableCheats;
QAction* actSetupCheats;
QAction* actROMInfo;
+ QAction* actRAMInfo;
QAction* actTitleManager;
+ QAction* actMPNewInstance;
QAction* actEmuSettings;
+#ifdef __APPLE__
+ QAction* actPreferences;
+#endif
+ QAction* actPowerManagement;
QAction* actInputConfig;
QAction* actVideoSettings;
+ QAction* actCameraSettings;
QAction* actAudioSettings;
+ QAction* actMPSettings;
QAction* actWifiSettings;
QAction* actFirmwareSettings;
+ QAction* actPathSettings;
QAction* actInterfaceSettings;
QAction* actSavestateSRAMReloc;
QAction* actScreenSize[4];
@@ -349,9 +381,9 @@ public:
QAction* actScreenSizing[6];
QAction* actIntegerScaling;
QActionGroup* grpScreenAspectTop;
- QAction* actScreenAspectTop[4];
+ QAction** actScreenAspectTop;
QActionGroup* grpScreenAspectBot;
- QAction* actScreenAspectBot[4];
+ QAction** actScreenAspectBot;
QAction* actScreenFiltering;
QAction* actShowOSD;
QAction* actLimitFramerate;
diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h
index 0aa99ad..ca835c0 100644
--- a/src/frontend/qt_sdl/main_shaders.h
+++ b/src/frontend/qt_sdl/main_shaders.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/frontend/qt_sdl/sem_timedwait.cpp b/src/frontend/qt_sdl/sem_timedwait.cpp
new file mode 100644
index 0000000..38b3c16
--- /dev/null
+++ b/src/frontend/qt_sdl/sem_timedwait.cpp
@@ -0,0 +1,488 @@
+/*
+ * s e m _ t i m e d w a i t
+ *
+ * Function:
+ * Implements a version of sem_timedwait().
+ *
+ * Description:
+ * Not all systems implement sem_timedwait(), which is a version of
+ * sem_wait() with a timeout. Mac OS X is one example, at least up to
+ * and including version 10.6 (Leopard). If such a function is needed,
+ * this code provides a reasonable implementation, which I think is
+ * compatible with the standard version, although possibly less
+ * efficient. It works by creating a thread that interrupts a normal
+ * sem_wait() call after the specified timeout.
+ *
+ * Call:
+ *
+ * The Linux man pages say:
+ *
+ * #include <semaphore.h>
+ *
+ * int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
+ *
+ * sem_timedwait() is the same as sem_wait(), except that abs_timeout
+ * specifies a limit on the amount of time that the call should block if
+ * the decrement cannot be immediately performed. The abs_timeout argument
+ * points to a structure that specifies an absolute timeout in seconds and
+ * nanoseconds since the Epoch (00:00:00, 1 January 1970). This structure
+ * is defined as follows:
+ *
+ * struct timespec {
+ * time_t tv_sec; Seconds
+ * long tv_nsec; Nanoseconds [0 .. 999999999]
+ * };
+ *
+ * If the timeout has already expired by the time of the call, and the
+ * semaphore could not be locked immediately, then sem_timedwait() fails
+ * with a timeout error (errno set to ETIMEDOUT).
+ * If the operation can be performed immediately, then sem_timedwait()
+ * never fails with a timeout error, regardless of the value of abs_timeout.
+ * Furthermore, the validity of abs_timeout is not checked in this case.
+ *
+ * Limitations:
+ *
+ * The mechanism used involves sending a SIGUSR2 signal to the thread
+ * calling sem_timedwait(). The handler for this signal is set to a null
+ * routine which does nothing, and with any flags for the signal
+ * (eg SA_RESTART) cleared. Note that this effective disabling of the
+ * SIGUSR2 signal is a side-effect of using this routine, and means it
+ * may not be a completely transparent plug-in replacement for a
+ * 'normal' sig_timedwait() call. Since OS X does not declare the
+ * sem_timedwait() call in its standard include files, the relevant
+ * declaration (shown above in the man pages extract) will probably have
+ * to be added to any code that uses this.
+ *
+ * Compiling:
+ * This compiles and runs cleanly on OS X (10.6) with gcc with the
+ * -Wall -ansi -pedantic flags. On Linux, using -ansi causes a sweep of
+ * compiler complaints about the timespec structure, but it compiles
+ * and works fine with just -Wall -pedantic. (Since Linux provides
+ * sem_timedwait() anyway, this really isn't needed on Linux.) However,
+ * since Linux provides sem_timedwait anyway, the sem_timedwait()
+ * code in this file is only compiled on OS X, and is a null on other
+ * systems.
+ *
+ * Testing:
+ * This file contains a test program that exercises the sem_timedwait
+ * code. It is compiled if the pre-processor variable TEST is defined.
+ * For more details, see the comments for the test routine at the end
+ * of the file.
+ *
+ * Author: Keith Shortridge, AAO.
+ *
+ * History:
+ * 8th Sep 2009. Original version. KS.
+ * 24th Sep 2009. Added test that the calling thread still exists before
+ * trying to set the timed-out flag. KS.
+ * 2nd Oct 2009. No longer restores the original SIGUSR2 signal handler.
+ * See comments in the body of the code for more details.
+ * Prototypes for now discontinued internal routines removed.
+ * 12th Aug 2010. Added the cleanup handler, so that this code no longer
+ * leaks resources if the calling thread is cancelled. KS.
+ * 21st Sep 2011. Added copyright notice below. Modified header comments
+ * to describe the use of SIGUSR2 more accurately in the
+ * light of the 2/10/09 change above. Now undefs DEBUG
+ * before defining it, to avoid any possible clash. KS.
+ * 14th Feb 2012. Tidied out a number of TABs that had got into the
+ * code. KS.
+ * 6th May 2013. Copyright notice modified to one based on the MIT licence,
+ * which is more permissive than the previous notice. KS.
+ *
+ * Copyright (c) Australian Astronomical Observatory (AAO), (2013).
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifdef __APPLE__
+
+#include <semaphore.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <setjmp.h>
+
+#include "sem_timedwait.h"
+
+/* Some useful definitions - TRUE, FALSE, and DEBUG */
+
+#undef TRUE
+#define TRUE 1
+#undef FALSE
+#define FALSE 0
+#undef DEBUG
+#define DEBUG printf
+
+/* A structure of type timeoutDetails is passed to the thread used to
+ * implement the timeout.
+ */
+
+typedef struct {
+ struct timespec delay; /* Specifies the delay, relative to now */
+ pthread_t callingThread; /* The thread doing the sem_wait call */
+ volatile short *timedOutShort; /* Address of a flag set to indicate that
+ * the timeout was triggered. */
+} timeoutDetails;
+
+/* A structure of type cleanupDetails is passed to the thread cleanup
+ * routine which is called at the end of the routine or if the thread calling
+ * it is cancelled.
+ */
+
+typedef struct {
+ pthread_t *threadIdAddr; /* Address of the variable that holds
+ * the Id of the timeout thread. */
+ struct sigaction *sigHandlerAddr; /* Address of the old signal action
+ * handler. */
+ volatile short *timedOutShort; /* Address of a flag set to indicate that
+ * the timeout was triggered. */
+} cleanupDetails;
+
+/* Forward declarations of internal routines */
+
+static void* timeoutThreadMain (void* passedPtr);
+static int triggerSignal (int Signal, pthread_t Thread);
+static void ignoreSignal (int Signal);
+static void timeoutThreadCleanup (void* passedPtr);
+
+/* -------------------------------------------------------------------------- */
+/*
+ * s e m _ t i m e d w a i t
+ *
+ * This is the main code for the sem_timedwait() implementation.
+ */
+
+int sem_timedwait (
+ sem_t *sem,
+ const struct timespec *abs_timeout)
+{
+ int result = 0; /* Code returned by this routine 0 or -1 */
+
+ /* "Under no circumstances shall the function fail if the semaphore
+ * can be locked immediately". So we try to get it quickly to see if we
+ * can avoid all the timeout overheads.
+ */
+
+ if (sem_trywait(sem) == 0) {
+
+ /* Yes, got it immediately. */
+
+ result = 0;
+
+ } else {
+
+ /* No, we've got to do it with a sem_wait() call and a thread to run
+ * the timeout. First, work out the time from now to the specified
+ * timeout, which we will pass to the timeout thread in a way that can
+ * be used to pass to nanosleep(). So we need this in seconds and
+ * nanoseconds. Along the way, we check for an invalid passed time,
+ * and for one that's already expired.
+ */
+
+ if ((abs_timeout->tv_nsec < 0) || (abs_timeout->tv_nsec > 1000000000)) {
+
+ /* Passed time is invalid */
+
+ result = -1;
+ errno = EINVAL;
+
+ } else {
+
+ struct timeval currentTime; /* Time now */
+ long secsToWait,nsecsToWait; /* Seconds and nsec to delay */
+ gettimeofday (&currentTime,NULL);
+ secsToWait = abs_timeout->tv_sec - currentTime.tv_sec;
+ nsecsToWait = (abs_timeout->tv_nsec - (currentTime.tv_usec * 1000));
+ while (nsecsToWait < 0) {
+ nsecsToWait += 1000000000;
+ secsToWait--;
+ }
+ if ((secsToWait < 0) || ((secsToWait == 0) && (nsecsToWait < 0))) {
+
+ /* Time has passed. Report an immediate timeout. */
+
+ result = -1;
+ errno = ETIMEDOUT;
+
+ } else {
+
+ /* We're going to have to do a sem_wait() with a timeout thread.
+ * The thread will wait the specified time, then will issue a
+ * SIGUSR2 signal that will interrupt the sem_wait() call.
+ * We pass the thread the id of the current thread, the delay,
+ * and the address of a flag to set on a timeout, so we can
+ * distinguish an interrupt caused by the timeout thread from
+ * one caused by some other signal.
+ */
+
+ volatile short timedOut; /* Flag to set on timeout */
+ timeoutDetails details; /* All the stuff the thread must know */
+ struct sigaction oldSignalAction; /* Current signal setting */
+ pthread_t timeoutThread; /* Id of timeout thread */
+ cleanupDetails cleaningDetails; /* What the cleanup routine needs */
+ int oldCancelState; /* Previous cancellation state */
+ int ignoreCancelState; /* Used in call, but ignored */
+ int createStatus; /* Status of pthread_create() call */
+
+ /* If the current thread is cancelled (and CML does do this)
+ * we don't want to leave our timer thread running - if we've
+ * started the thread we want to make sure we join it in order
+ * to release its resources. So we set a cleanup handler to
+ * do this. We pass it the address of the structure that will
+ * hold all it needs to know. While we set all this up,
+ * we prevent ourselves being cancelled, so all this data is
+ * coherent.
+ */
+
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,&oldCancelState);
+ timeoutThread = (pthread_t) 0;
+ cleaningDetails.timedOutShort = &timedOut;
+ cleaningDetails.threadIdAddr = &timeoutThread;
+ cleaningDetails.sigHandlerAddr = &oldSignalAction;
+ pthread_cleanup_push (timeoutThreadCleanup,&cleaningDetails);
+
+ /* Set up the details for the thread. Clear the timeout flag,
+ * record the current SIGUSR2 action settings so we can restore
+ * them later.
+ */
+
+ details.delay.tv_sec = secsToWait;
+ details.delay.tv_nsec = nsecsToWait;
+ details.callingThread = pthread_self();
+ details.timedOutShort = &timedOut;
+ timedOut = FALSE;
+ sigaction (SIGUSR2,NULL,&oldSignalAction);
+
+ /* Start up the timeout thread. Once we've done that, we can
+ * restore the previous cancellation state.
+ */
+
+ createStatus = pthread_create(&timeoutThread,NULL,
+ timeoutThreadMain, (void*)&details);
+ pthread_setcancelstate (oldCancelState,&ignoreCancelState);
+
+ if (createStatus < 0) {
+
+ /* Failed to create thread. errno will already be set properly */
+
+ result = -1;
+
+ } else {
+
+ /* Thread created OK. This is where we wait for the semaphore.
+ */
+
+ if (sem_wait(sem) == 0) {
+
+ /* Got the semaphore OK. We return zero, and all's well. */
+
+ result = 0;
+
+ } else {
+
+ /* If we got a -1 error from sem_wait(), it may be because
+ * it was interrupted by a timeout, or failed for some
+ * other reason. We check for the expected timeout
+ * condition, which is an 'interrupted' status and the
+ * timeout flag set by the timeout thread. We report that as
+ * a timeout error. Anything else is some other error and
+ * errno is already set properly.
+ */
+
+ result = -1;
+ if (errno == EINTR) {
+ if (timedOut) errno = ETIMEDOUT;
+ }
+ }
+
+ }
+
+ /* The cleanup routine - timeoutThreadCleanup() - packages up
+ * any tidying up that is needed, including joining with the
+ * timer thread. This will be called if the current thread is
+ * cancelled, but we need it to happen anyway, so we set the
+ * execute flag true here as we remove it from the list of
+ * cleanup routines to be called. So normally, this line amounts
+ * to calling timeoutThreadCleanup().
+ */
+
+ pthread_cleanup_pop (TRUE);
+ }
+ }
+ }
+ return (result);
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * t i m e o u t T h r e a d C l e a n u p
+ *
+ * This internal routine tidies up at the end of a sem_timedwait() call.
+ * It is set as a cleanup routine for the current thread (not the timer
+ * thread) so it is executed even if the thread is cancelled. This is
+ * important, as we need to tidy up the timeout thread. If we took the
+ * semaphore (in other words, if we didn't timeout) then the timer thread
+ * will still be running, sitting in its nanosleep() call, and we need
+ * to cancel it. If the timer thread did signal a timeout then it will
+ * now be closing down. In either case, we need to join it (using a call
+ * to pthread_join()) or its resources will never be released.
+ * The single argument is a pointer to a cleanupDetails structure that has
+ * all the routine needs to know.
+ */
+
+static void timeoutThreadCleanup (void* passedPtr)
+{
+ /* Get what we need from the structure we've been passed. */
+
+ cleanupDetails *detailsPtr = (cleanupDetails*) passedPtr;
+ short timedOut = *(detailsPtr->timedOutShort);
+ pthread_t timeoutThread = *(detailsPtr->threadIdAddr);
+
+ /* If we created the thread, stop it - doesn't matter if it's no longer
+ * running, pthread_cancel can handle that. We make sure we wait for it
+ * to complete, because it is this pthread_join() call that releases any
+ * memory the thread may have allocated. Note that cancelling a thread is
+ * generally not a good idea, because of the difficulty of cleaning up
+ * after it, but this is a very simple thread that does nothing but call
+ * nanosleep(), and that we can cancel quite happily.
+ */
+
+ if (!timedOut) pthread_cancel(timeoutThread);
+ pthread_join(timeoutThread,NULL);
+
+ /* The code originally restored the old action handler, which generally
+ * was the default handler that caused the task to exit. Just occasionally,
+ * there seem to be cases where the signal is still queued and ready to
+ * trigger even though the thread that presumably sent it off just before
+ * it was cancelled has finished. I had thought that once we'd joined
+ * that thread, we could be sure of not seeing the signal, but that seems
+ * not to be the case, and so restoring a handler that will allow the task
+ * to crash is not a good idea, and so the line below has been commented
+ * out.
+ *
+ * sigaction (SIGUSR2,detailsPtr->sigHandlerAddr,NULL);
+ */
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * t i m e o u t T h r e a d M a i n
+ *
+ * This internal routine is the main code for the timeout thread.
+ * The single argument is a pointer to a timeoutDetails structure that has
+ * all the thread needs to know - thread to signal, delay time, and the
+ * address of a flag to set if it triggers a timeout.
+ */
+
+static void* timeoutThreadMain (void* passedPtr)
+{
+ void* Return = (void*) 0;
+
+ /* We grab all the data held in the calling thread right now. In some
+ * cases, we find that the calling thread has vanished and released
+ * its memory, including the details structure, by the time the timeout
+ * expires, and then we get an access violation when we try to set the
+ * 'timed out' flag.
+ */
+
+ timeoutDetails details = *((timeoutDetails*) passedPtr);
+ struct timespec requestedDelay = details.delay;
+
+ /* We do a nanosleep() for the specified delay, and then trigger a
+ * timeout. Note that we allow for the case where the nanosleep() is
+ * interrupted, and restart it for the remaining time. If the
+ * thread that is doing the sem_wait() call gets the semaphore, it
+ * will cancel this thread, which is fine as we aren't doing anything
+ * other than a sleep and a signal.
+ */
+
+ for (;;) {
+ struct timespec remainingDelay;
+ if (nanosleep (&requestedDelay,&remainingDelay) == 0) {
+ break;
+ } else if (errno == EINTR) {
+ requestedDelay = remainingDelay;
+ } else {
+ Return = (void*) errno;
+ break;
+ }
+ }
+
+ /* We've completed the delay without being cancelled, so we now trigger
+ * the timeout by sending a signal to the calling thread. And that's it,
+ * although we set the timeout flag first to indicate that it was us
+ * that interrupted the sem_wait() call. One precaution: before we
+ * try to set the timed-out flag, make sure the calling thread still
+ * exists - this may not be the case if things are closing down a bit
+ * messily. We check this quickly using a zero test signal.
+ */
+
+ if (pthread_kill(details.callingThread,0) == 0) {
+ *(details.timedOutShort) = TRUE;
+ if (triggerSignal (SIGUSR2,details.callingThread) < 0) {
+ Return = (void*) errno;
+ }
+ }
+
+ return Return;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * t r i g g e r S i g n a l
+ *
+ * This is a general purpose routine that sends a specified signal to
+ * a specified thread, setting up a signal handler that does nothing,
+ * and then giving the signal. The only effect will be to interrupt any
+ * operation that is currently blocking - in this case, we expect this to
+ * be a sem_wait() call.
+ */
+
+static int triggerSignal (int Signal, pthread_t Thread)
+{
+ int Result = 0;
+ struct sigaction SignalDetails;
+ SignalDetails.sa_handler = ignoreSignal;
+ SignalDetails.sa_flags = 0;
+ (void) sigemptyset(&SignalDetails.sa_mask);
+ if ((Result = sigaction(Signal,&SignalDetails,NULL)) == 0) {
+ Result = pthread_kill(Thread,Signal);
+ }
+ return Result;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * i g n o r e S i g n a l
+ *
+ * And this is the signal handler that does nothing. (It clears its argument,
+ * but this has no effect and prevents a compiler warning about an unused
+ * argument.)
+ */
+
+static void ignoreSignal (int Signal) {
+ Signal = 0;
+}
+
+#endif
diff --git a/src/frontend/qt_sdl/sem_timedwait.h b/src/frontend/qt_sdl/sem_timedwait.h
new file mode 100644
index 0000000..42ae201
--- /dev/null
+++ b/src/frontend/qt_sdl/sem_timedwait.h
@@ -0,0 +1,8 @@
+#ifndef __SEM_TIMEDWAIT_H
+#define __SEM_TIMEDWAIT_H
+
+#ifdef __APPLE__
+int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
+#endif
+
+#endif
diff --git a/src/melonDLDI.h b/src/melonDLDI.h
index a770459..261f798 100644
--- a/src/melonDLDI.h
+++ b/src/melonDLDI.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,9 +19,11 @@
#ifndef MELONDLDI_H
#define MELONDLDI_H
+#include "types.h"
+
const u8 melonDLDI[] =
{
- 0xED, 0xA5, 0x8D, 0xBF, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6D, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0xED, 0xA5, 0x8D, 0xBF, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6D, 0x00, 0x01, 0x09, 0x00, 0x00,
0x6D, 0x65, 0x6C, 0x6F, 0x6E, 0x44, 0x53, 0x20, 0x44, 0x4C, 0x44, 0x49, 0x20, 0x64, 0x72, 0x69,
0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/src/teakra/CMakeLists.txt b/src/teakra/CMakeLists.txt
index 046f8c4..e4ecb1a 100644
--- a/src/teakra/CMakeLists.txt
+++ b/src/teakra/CMakeLists.txt
@@ -58,6 +58,7 @@ else()
-pedantic
-pedantic-errors
-Wfatal-errors
+ -Wno-error=maybe-uninitialized
-Wno-missing-braces
-Wno-unused-parameter)
diff --git a/src/teakra/externals/catch/catch.hpp b/src/teakra/externals/catch/catch.hpp
index b1b2411..71c863a 100644
--- a/src/teakra/externals/catch/catch.hpp
+++ b/src/teakra/externals/catch/catch.hpp
@@ -1,9 +1,9 @@
/*
- * Catch v2.5.0
- * Generated: 2018-11-26 20:46:12.165372
+ * Catch v2.13.8
+ * Generated: 2022-01-03 21:20:09.589503
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
- * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
+ * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,8 +14,8 @@
#define CATCH_VERSION_MAJOR 2
-#define CATCH_VERSION_MINOR 5
-#define CATCH_VERSION_PATCH 0
+#define CATCH_VERSION_MINOR 13
+#define CATCH_VERSION_PATCH 8
#ifdef __clang__
# pragma clang system_header
@@ -36,10 +36,11 @@
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# endif
#elif defined __GNUC__
- // GCC likes to warn on REQUIREs, and we cannot suppress them
- // locally because g++'s support for _Pragma is lacking in older,
- // still supported, versions
-# pragma GCC diagnostic ignored "-Wparentheses"
+ // Because REQUIREs trigger GCC's -Wparentheses, and because still
+ // supported version of g++ have only buggy support for _Pragmas,
+ // Wparentheses have to be suppressed globally.
+# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
+
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-variable"
# pragma GCC diagnostic ignored "-Wpadded"
@@ -65,13 +66,16 @@
#if !defined(CATCH_CONFIG_IMPL_ONLY)
// start catch_platform.h
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
#ifdef __APPLE__
-# include <TargetConditionals.h>
-# if TARGET_OS_OSX == 1
-# define CATCH_PLATFORM_MAC
-# elif TARGET_OS_IPHONE == 1
-# define CATCH_PLATFORM_IPHONE
-# endif
+# include <TargetConditionals.h>
+# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
+ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
+# define CATCH_PLATFORM_MAC
+# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+# define CATCH_PLATFORM_IPHONE
+# endif
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX
@@ -131,30 +135,51 @@ namespace Catch {
#endif
-#if defined(CATCH_CPP17_OR_GREATER)
-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
+
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
+
#endif
-#ifdef __clang__
+#if defined(__clang__)
-# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- _Pragma( "clang diagnostic push" ) \
- _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
- _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
-# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
- _Pragma( "clang diagnostic pop" )
-
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
- _Pragma( "clang diagnostic push" ) \
- _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
-# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
- _Pragma( "clang diagnostic pop" )
-
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
- _Pragma( "clang diagnostic push" ) \
- _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
-# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
- _Pragma( "clang diagnostic pop" )
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
+
+// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
+// which results in calls to destructors being emitted for each temporary,
+// without a matching initialization. In practice, this can result in something
+// like `std::string::~string` being called on an uninitialized value.
+//
+// For example, this code will likely segfault under IBM XL:
+// ```
+// REQUIRE(std::string("12") + "34" == "1234")
+// ```
+//
+// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
+# if !defined(__ibmxl__) && !defined(__CUDACC__)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
+# endif
+
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
#endif // __clang__
@@ -179,6 +204,7 @@ namespace Catch {
// Android somehow still does not support std::to_string
#if defined(__ANDROID__)
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
#endif
////////////////////////////////////////////////////////////////////////////////
@@ -203,20 +229,16 @@ namespace Catch {
// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
- && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
-# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
# endif
#endif // __CYGWIN__
////////////////////////////////////////////////////////////////////////////////
// Visual C++
-#ifdef _MSC_VER
-
-# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
-# endif
+#if defined(_MSC_VER)
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
@@ -226,13 +248,25 @@ namespace Catch {
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
# endif
+# if !defined(__clang__) // Handle Clang masquerading for msvc
+
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
-# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
-# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-# endif
+# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+# endif // MSVC_TRADITIONAL
+
+// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
+# endif // __clang__
+
+#endif // _MSC_VER
+#if defined(_REENTRANT) || defined(_MSC_VER)
+// Enable async processing, as -pthread is specified or no additional linking is required
+# define CATCH_INTERNAL_CONFIG_USE_ASYNC
#endif // _MSC_VER
////////////////////////////////////////////////////////////////////////////////
@@ -265,30 +299,56 @@ namespace Catch {
#endif
////////////////////////////////////////////////////////////////////////////////
-// Check if string_view is available and usable
-// The check is split apart to work around v140 (VS2015) preprocessor issue...
-#if defined(__has_include)
-#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
-# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+
+// RTX is a special version of Windows that is real time.
+// This means that it is detected as Windows, but does not provide
+// the same set of capabilities as real Windows does.
+#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
+ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+ #define CATCH_INTERNAL_CONFIG_NO_ASYNC
+ #define CATCH_CONFIG_COLOUR_NONE
#endif
+
+#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
+#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
#endif
-////////////////////////////////////////////////////////////////////////////////
-// Check if variant is available and usable
+// Various stdlib support checks that require __has_include
#if defined(__has_include)
-# if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
-# if defined(__clang__) && (__clang_major__ < 8)
- // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
- // fix should be in clang 8, workaround in libstdc++ 8.2
-# include <ciso646>
-# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
-# define CATCH_CONFIG_NO_CPP17_VARIANT
-# else
-# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
-# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
-# endif // defined(__clang__) && (__clang_major__ < 8)
-# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
-#endif // __has_include
+ // Check if string_view is available and usable
+ #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+ #endif
+
+ // Check if optional is available and usable
+ # if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
+ # endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if byte is available and usable
+ # if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
+ # include <cstddef>
+ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
+ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE
+ # endif
+ # endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if variant is available and usable
+ # if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+ # if defined(__clang__) && (__clang_major__ < 8)
+ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+ // fix should be in clang 8, workaround in libstdc++ 8.2
+ # include <ciso646>
+ # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # define CATCH_CONFIG_NO_CPP17_VARIANT
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__clang__) && (__clang_major__ < 8)
+ # endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+#endif // defined(__has_include)
#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
# define CATCH_CONFIG_COUNTER
@@ -309,8 +369,8 @@ namespace Catch {
# define CATCH_CONFIG_CPP11_TO_STRING
#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
-# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
+# define CATCH_CONFIG_CPP17_OPTIONAL
#endif
#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
@@ -321,6 +381,10 @@ namespace Catch {
# define CATCH_CONFIG_CPP17_VARIANT
#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+# define CATCH_CONFIG_CPP17_BYTE
+#endif
+
#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
#endif
@@ -337,17 +401,53 @@ namespace Catch {
# define CATCH_CONFIG_POLYFILL_ISNAN
#endif
+#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
+# define CATCH_CONFIG_USE_ASYNC
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+# define CATCH_CONFIG_ANDROID_LOGWRITE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+# define CATCH_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Even if we do not think the compiler has that warning, we still have
+// to provide a macro that can be used by the code.
+#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
-# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
-# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
+#endif
+
+// The goal of this macro is to avoid evaluation of the arguments, but
+// still have the compiler warn on problems inside...
+#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
+#endif
+
+#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#elif defined(__clang__) && (__clang_major__ < 5)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
@@ -407,12 +507,12 @@ namespace Catch {
line( _line )
{}
- SourceLineInfo( SourceLineInfo const& other ) = default;
- SourceLineInfo( SourceLineInfo && ) = default;
- SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
- SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+ SourceLineInfo( SourceLineInfo const& other ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo( SourceLineInfo&& ) noexcept = default;
+ SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
- bool empty() const noexcept;
+ bool empty() const noexcept { return file[0] == '\0'; }
bool operator == ( SourceLineInfo const& other ) const noexcept;
bool operator < ( SourceLineInfo const& other ) const noexcept;
@@ -453,9 +553,10 @@ namespace Catch {
} // end namespace Catch
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
// end catch_tag_alias_autoregistrar.h
// start catch_test_registry.h
@@ -463,7 +564,6 @@ namespace Catch {
// start catch_interfaces_testcase.h
#include <vector>
-#include <memory>
namespace Catch {
@@ -474,8 +574,6 @@ namespace Catch {
virtual ~ITestInvoker();
};
- using ITestCasePtr = std::shared_ptr<ITestInvoker>;
-
class TestCase;
struct IConfig;
@@ -485,6 +583,7 @@ namespace Catch {
virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
};
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
@@ -497,55 +596,30 @@ namespace Catch {
#include <cstddef>
#include <string>
#include <iosfwd>
+#include <cassert>
namespace Catch {
- class StringData;
-
/// A non-owning string class (similar to the forthcoming std::string_view)
/// Note that, because a StringRef may be a substring of another string,
- /// it may not be null terminated. c_str() must return a null terminated
- /// string, however, and so the StringRef will internally take ownership
- /// (taking a copy), if necessary. In theory this ownership is not externally
- /// visible - but it does mean (substring) StringRefs should not be shared between
- /// threads.
+ /// it may not be null terminated.
class StringRef {
public:
using size_type = std::size_t;
+ using const_iterator = const char*;
private:
- friend struct StringRefTestAccess;
-
- char const* m_start;
- size_type m_size;
-
- char* m_data = nullptr;
-
- void takeOwnership();
-
static constexpr char const* const s_empty = "";
- public: // construction/ assignment
- StringRef() noexcept
- : StringRef( s_empty, 0 )
- {}
+ char const* m_start = s_empty;
+ size_type m_size = 0;
- StringRef( StringRef const& other ) noexcept
- : m_start( other.m_start ),
- m_size( other.m_size )
- {}
-
- StringRef( StringRef&& other ) noexcept
- : m_start( other.m_start ),
- m_size( other.m_size ),
- m_data( other.m_data )
- {
- other.m_data = nullptr;
- }
+ public: // construction
+ constexpr StringRef() noexcept = default;
StringRef( char const* rawChars ) noexcept;
- StringRef( char const* rawChars, size_type size ) noexcept
+ constexpr StringRef( char const* rawChars, size_type size ) noexcept
: m_start( rawChars ),
m_size( size )
{}
@@ -555,99 +629,64 @@ namespace Catch {
m_size( stdString.size() )
{}
- ~StringRef() noexcept {
- delete[] m_data;
- }
-
- auto operator = ( StringRef const &other ) noexcept -> StringRef& {
- delete[] m_data;
- m_data = nullptr;
- m_start = other.m_start;
- m_size = other.m_size;
- return *this;
+ explicit operator std::string() const {
+ return std::string(m_start, m_size);
}
- operator std::string() const;
-
- void swap( StringRef& other ) noexcept;
-
public: // operators
auto operator == ( StringRef const& other ) const noexcept -> bool;
- auto operator != ( StringRef const& other ) const noexcept -> bool;
+ auto operator != (StringRef const& other) const noexcept -> bool {
+ return !(*this == other);
+ }
- auto operator[] ( size_type index ) const noexcept -> char;
+ auto operator[] ( size_type index ) const noexcept -> char {
+ assert(index < m_size);
+ return m_start[index];
+ }
public: // named queries
- auto empty() const noexcept -> bool {
+ constexpr auto empty() const noexcept -> bool {
return m_size == 0;
}
- auto size() const noexcept -> size_type {
+ constexpr auto size() const noexcept -> size_type {
return m_size;
}
- auto numberOfCharacters() const noexcept -> size_type;
+ // Returns the current start pointer. If the StringRef is not
+ // null-terminated, throws std::domain_exception
auto c_str() const -> char const*;
public: // substrings and searches
- auto substr( size_type start, size_type size ) const noexcept -> StringRef;
+ // Returns a substring of [start, start + length).
+ // If start + length > size(), then the substring is [start, size()).
+ // If start > size(), then the substring is empty.
+ auto substr( size_type start, size_type length ) const noexcept -> StringRef;
- // Returns the current start pointer.
- // Note that the pointer can change when if the StringRef is a substring
- auto currentData() const noexcept -> char const*;
+ // Returns the current start pointer. May not be null-terminated.
+ auto data() const noexcept -> char const*;
- private: // ownership queries - may not be consistent between calls
- auto isOwned() const noexcept -> bool;
- auto isSubstring() const noexcept -> bool;
- };
+ constexpr auto isNullTerminated() const noexcept -> bool {
+ return m_start[m_size] == '\0';
+ }
- auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
- auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
- auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
+ public: // iterators
+ constexpr const_iterator begin() const { return m_start; }
+ constexpr const_iterator end() const { return m_start + m_size; }
+ };
auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
- inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
return StringRef( rawChars, size );
}
-
} // namespace Catch
-inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
return Catch::StringRef( rawChars, size );
}
// end catch_stringref.h
-// start catch_type_traits.hpp
-
-
-namespace Catch{
-
-#ifdef CATCH_CPP17_OR_GREATER
- template <typename...>
- inline constexpr auto is_unique = std::true_type{};
-
- template <typename T, typename... Rest>
- inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
- (!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
- >{};
-#else
-
-template <typename...>
-struct is_unique : std::true_type{};
-
-template <typename T0, typename T1, typename... Rest>
-struct is_unique<T0, T1, Rest...> : std::integral_constant
-<bool,
- !std::is_same<T0, T1>::value
- && is_unique<T0, Rest...>::value
- && is_unique<T1, Rest...>::value
->{};
-
-#endif
-}
-
-// end catch_type_traits.hpp
// start catch_preprocessor.hpp
@@ -699,21 +738,224 @@ struct is_unique<T0, T1, Rest...> : std::integral_constant
#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
+#else
+// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
+#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
+#endif
+
+#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
+#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__)
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
#else
-// MSVC is adding extra space and needs more calls to properly remove ()
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__)
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#endif
+
+#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
+ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
+
+#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
+#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
+#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
+#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
+#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
+#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
+#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
+#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
+#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
+#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
+
+#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define INTERNAL_CATCH_TYPE_GEN\
+ template<typename...> struct TypeList {};\
+ template<typename...Ts>\
+ constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
+ template<template<typename...> class...> struct TemplateTypeList{};\
+ template<template<typename...> class...Cs>\
+ constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
+ template<typename...>\
+ struct append;\
+ template<typename...>\
+ struct rewrap;\
+ template<template<typename...> class, typename...>\
+ struct create;\
+ template<template<typename...> class, typename>\
+ struct convert;\
+ \
+ template<typename T> \
+ struct append<T> { using type = T; };\
+ template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
+ struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
+ template< template<typename...> class L1, typename...E1, typename...Rest>\
+ struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
+ \
+ template< template<typename...> class Container, template<typename...> class List, typename...elems>\
+ struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
+ template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
+ struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
+ \
+ template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
+ struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
+ template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
+ struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
+ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
+ constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
+ \
+ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
+ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
+ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
+ template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
+ struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+
+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
+ template<typename Type>\
+ void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
+ template<typename Type>\
+ void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
+ template<typename TestType> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
+ template<typename TestType> \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_NTTP_0
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+#else
+#define INTERNAL_CATCH_NTTP_0(signature)
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
#endif
// end catch_preprocessor.hpp
+// start catch_meta.hpp
+
+
+#include <type_traits>
+
+namespace Catch {
+ template<typename T>
+ struct always_false : std::false_type {};
+
+ template <typename> struct true_given : std::true_type {};
+ struct is_callable_tester {
+ template <typename Fun, typename... Args>
+ true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
+ template <typename...>
+ std::false_type static test(...);
+ };
+
+ template <typename T>
+ struct is_callable;
+
+ template <typename Fun, typename... Args>
+ struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
+
+#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
+ // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
+ // replaced with std::invoke_result here.
+ template <typename Func, typename... U>
+ using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>;
+#else
+ // Keep ::type here because we still support C++11
+ template <typename Func, typename... U>
+ using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U...)>::type>::type>::type;
+#endif
+
+} // namespace Catch
+
+namespace mpl_{
+ struct na;
+}
+
+// end catch_meta.hpp
namespace Catch {
template<typename C>
@@ -758,38 +1000,70 @@ struct AutoReg : NonCopyable {
}; \
} \
void TestName::test()
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \
- template<typename TestType> \
- static void TestName()
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
namespace{ \
- template<typename TestType> \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
- void test(); \
- }; \
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
} \
- template<typename TestType> \
- void TestName::test()
+ } \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
#endif
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
static void TestName(); \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
static void TestName()
#define INTERNAL_CATCH_TESTCASE( ... ) \
- INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ \
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
@@ -797,88 +1071,273 @@ struct AutoReg : NonCopyable {
}; \
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
} \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
void TestName::test()
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
- INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- template<typename TestType> \
- static void TestFunc();\
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
template<typename...Types> \
struct TestName{\
- template<typename...Ts> \
- TestName(Ts...names){\
- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
+ TestName(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
using expander = int[];\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
+ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
}\
};\
- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+ return 0;\
+ }();\
}\
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
- template<typename TestType> \
- static void TestFunc()
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
-#if defined(CATCH_CPP17_OR_GREATER)
-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case");
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
#else
-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case");
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ )
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) )
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
#endif
- #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- TestName<CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)>(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\
- return 0;\
- }();
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> static void TestFuncName(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
+ template<typename... Types> \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
+ } \
+ }; \
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ } \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ static void TestFuncName()
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ \
- template<typename TestType> \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> static void TestFunc(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ template<typename... Types> \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
+ } \
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename convert<TestName, TmplList>::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ static void TestFunc()
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList )
+
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template<typename...Types> \
+ struct TestNameClass{\
+ TestNameClass(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+ (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+ return 0;\
+ }();\
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> \
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
void test();\
};\
- template<typename...Types> \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template<typename...Types>\
struct TestNameClass{\
- template<typename...Ts> \
- TestNameClass(Ts...names){\
- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
+ void reg_tests(){\
+ int index = 0;\
using expander = int[];\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
}\
};\
- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
+ }\
}\
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
void TestName<TestType>::test()
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ )
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) )
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
#endif
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+ void test();\
+ };\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ template<typename...Types>\
+ struct TestNameClass{\
+ void reg_tests(){\
+ int index = 0;\
+ using expander = int[];\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename convert<TestNameClass, TmplList>::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ void TestName<TestType>::test()
+
+#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList )
+
// end catch_test_registry.h
// start catch_capture.hpp
@@ -978,7 +1437,7 @@ namespace Catch {
auto makeStream( StringRef const &filename ) -> IStream const*;
- class ReusableStringStream {
+ class ReusableStringStream : NonCopyable {
std::size_t m_index;
std::ostream* m_oss;
public:
@@ -997,6 +1456,42 @@ namespace Catch {
}
// end catch_stream.h
+// start catch_interfaces_enum_values_registry.h
+
+#include <vector>
+
+namespace Catch {
+
+ namespace Detail {
+ struct EnumInfo {
+ StringRef m_name;
+ std::vector<std::pair<int, StringRef>> m_values;
+
+ ~EnumInfo();
+
+ StringRef lookup( int value ) const;
+ };
+ } // namespace Detail
+
+ struct IMutableEnumValuesRegistry {
+ virtual ~IMutableEnumValuesRegistry();
+
+ virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
+
+ template<typename E>
+ Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
+ static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
+ std::vector<int> intValues;
+ intValues.reserve( values.size() );
+ for( auto enumValue : values )
+ intValues.push_back( static_cast<int>( enumValue ) );
+ return registerEnum( enumName, allEnums, intValues );
+ }
+ };
+
+} // Catch
+
+// end catch_interfaces_enum_values_registry.h
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
#include <string_view>
@@ -1067,9 +1562,9 @@ namespace Catch {
template<typename T>
class IsStreamInsertable {
- template<typename SS, typename TT>
+ template<typename Stream, typename U>
static auto test(int)
- -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
+ -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
template<typename, typename>
static auto test(...)->std::false_type;
@@ -1231,6 +1726,12 @@ namespace Catch {
}
};
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+ template<>
+ struct StringMaker<std::byte> {
+ static std::string convert(std::byte value);
+ };
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
template<>
struct StringMaker<int> {
static std::string convert(int value);
@@ -1282,10 +1783,13 @@ namespace Catch {
template<>
struct StringMaker<float> {
static std::string convert(float value);
+ static int precision;
};
+
template<>
struct StringMaker<double> {
static std::string convert(double value);
+ static int precision;
};
template <typename T>
@@ -1321,8 +1825,8 @@ namespace Catch {
#endif
namespace Detail {
- template<typename InputIterator>
- std::string rangeToString(InputIterator first, InputIterator last) {
+ template<typename InputIterator, typename Sentinel = InputIterator>
+ std::string rangeToString(InputIterator first, Sentinel last) {
ReusableStringStream rss;
rss << "{ ";
if (first != last) {
@@ -1370,6 +1874,7 @@ namespace Catch {
# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
#endif
// Separate std::pair specialization
@@ -1391,6 +1896,24 @@ namespace Catch {
}
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#include <optional>
+namespace Catch {
+ template<typename T>
+ struct StringMaker<std::optional<T> > {
+ static std::string convert(const std::optional<T>& optional) {
+ ReusableStringStream rss;
+ if (optional.has_value()) {
+ rss << ::Catch::Detail::stringify(*optional);
+ } else {
+ rss << "{ }";
+ }
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+
// Separate std::tuple specialization
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
#include <tuple>
@@ -1461,20 +1984,27 @@ namespace Catch {
#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
namespace Catch {
- struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
-
- // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
+ // Import begin/ end from std here
using std::begin;
using std::end;
- not_this_one begin( ... );
- not_this_one end( ... );
+ namespace detail {
+ template <typename...>
+ struct void_type {
+ using type = void;
+ };
+
+ template <typename T, typename = void>
+ struct is_range_impl : std::false_type {
+ };
+
+ template <typename T>
+ struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type {
+ };
+ } // namespace detail
template <typename T>
- struct is_range {
- static const bool value =
- !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value &&
- !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
+ struct is_range : detail::is_range_impl<T> {
};
#if defined(_MANAGED) // Managed types are never ranges
@@ -1639,6 +2169,18 @@ struct ratio_string<std::milli> {
}
#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
+namespace Catch { \
+ template<> struct StringMaker<enumName> { \
+ static std::string convert( enumName value ) { \
+ static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
+ return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
+ } \
+ }; \
+}
+
+#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
+
#ifdef _MSC_VER
#pragma warning(pop)
#endif
@@ -1652,6 +2194,7 @@ struct ratio_string<std::milli> {
#pragma warning(disable:4018) // more "signed/unsigned mismatch"
#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
#pragma warning(disable:4180) // qualifier applied to function type has no meaning
+#pragma warning(disable:4800) // Forcing result to true or false
#endif
namespace Catch {
@@ -1695,6 +2238,62 @@ namespace Catch {
m_op( op ),
m_rhs( rhs )
{}
+
+ template<typename T>
+ auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
};
template<typename LhsT>
@@ -1707,7 +2306,7 @@ namespace Catch {
public:
explicit UnaryExpr( LhsT lhs )
- : ITransientExpression{ false, lhs ? true : false },
+ : ITransientExpression{ false, static_cast<bool>(lhs) },
m_lhs( lhs )
{}
};
@@ -1773,6 +2372,32 @@ namespace Catch {
auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs };
}
+ template <typename RhsT>
+ auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs | rhs), m_lhs, "|", rhs };
+ }
+ template <typename RhsT>
+ auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs & rhs), m_lhs, "&", rhs };
+ }
+ template <typename RhsT>
+ auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^", rhs };
+ }
+
+ template<typename RhsT>
+ auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<RhsT>::value,
+ "operator&& is not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename RhsT>
+ auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<RhsT>::value,
+ "operator|| is not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
return UnaryExpr<LhsT>{ m_lhs };
@@ -1807,6 +2432,7 @@ namespace Catch {
// start catch_interfaces_capture.h
#include <string>
+#include <chrono>
namespace Catch {
@@ -1815,15 +2441,20 @@ namespace Catch {
struct SectionInfo;
struct SectionEndInfo;
struct MessageInfo;
+ struct MessageBuilder;
struct Counts;
- struct BenchmarkInfo;
- struct BenchmarkStats;
struct AssertionReaction;
struct SourceLineInfo;
struct ITransientExpression;
struct IGeneratorTracker;
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ struct BenchmarkInfo;
+ template <typename Duration = std::chrono::duration<double, std::nano>>
+ struct BenchmarkStats;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
struct IResultCapture {
virtual ~IResultCapture();
@@ -1833,14 +2464,20 @@ namespace Catch {
virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
- virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+ virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ virtual void benchmarkPreparing( std::string const& name ) = 0;
virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
- virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0;
+ virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
+ virtual void benchmarkFailed( std::string const& error ) = 0;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
+ virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
+
virtual void handleFatalErrorCondition( StringRef message ) = 0;
virtual void handleExpr
@@ -2004,9 +2641,12 @@ namespace Catch {
class ScopedMessage {
public:
explicit ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage& duplicate ) = delete;
+ ScopedMessage( ScopedMessage&& old );
~ScopedMessage();
MessageInfo m_info;
+ bool m_moved;
};
class Capturer {
@@ -2062,15 +2702,16 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
do { \
+ CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
INTERNAL_CATCH_TRY { \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
- CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
- } while( (void)0, false && static_cast<bool>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
- // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+ } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
@@ -2151,6 +2792,10 @@ namespace Catch {
Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
+ Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
+
+///////////////////////////////////////////////////////////////////////////////
// Although this is matcher-based, it can be used with just a string
#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
do { \
@@ -2283,62 +2928,18 @@ namespace Catch {
} // end namespace Catch
#define INTERNAL_CATCH_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
- CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
- CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
// end catch_section.h
-// start catch_benchmark.h
-
-#include <cstdint>
-#include <string>
-
-namespace Catch {
-
- class BenchmarkLooper {
-
- std::string m_name;
- std::size_t m_count = 0;
- std::size_t m_iterationsToRun = 1;
- uint64_t m_resolution;
- Timer m_timer;
-
- static auto getResolution() -> uint64_t;
- public:
- // Keep most of this inline as it's on the code path that is being timed
- BenchmarkLooper( StringRef name )
- : m_name( name ),
- m_resolution( getResolution() )
- {
- reportStart();
- m_timer.start();
- }
-
- explicit operator bool() {
- if( m_count < m_iterationsToRun )
- return true;
- return needsMoreIterations();
- }
-
- void increment() {
- ++m_count;
- }
-
- void reportStart();
- auto needsMoreIterations() -> bool;
- };
-
-} // end namespace Catch
-
-#define BENCHMARK( name ) \
- for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() )
-
-// end catch_benchmark.h
// start catch_interfaces_exception.h
// start catch_interfaces_registry_hub.h
@@ -2355,6 +2956,8 @@ namespace Catch {
struct IReporterRegistry;
struct IReporterFactory;
struct ITagAliasRegistry;
+ struct IMutableEnumValuesRegistry;
+
class StartupExceptionRegistry;
using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
@@ -2365,7 +2968,6 @@ namespace Catch {
virtual IReporterRegistry const& getReporterRegistry() const = 0;
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
-
virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
@@ -2379,6 +2981,7 @@ namespace Catch {
virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
virtual void registerStartupException() noexcept = 0;
+ virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
};
IRegistryHub const& getRegistryHub();
@@ -2425,6 +3028,9 @@ namespace Catch {
{}
std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ return "";
+#else
try {
if( it == itEnd )
std::rethrow_exception(std::current_exception());
@@ -2434,6 +3040,7 @@ namespace Catch {
catch( T& ex ) {
return m_translateFunction( ex );
}
+#endif
}
protected:
@@ -2452,9 +3059,10 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
static std::string translatorName( signature ); \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
static std::string translatorName( signature )
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
@@ -2485,7 +3093,7 @@ namespace Detail {
Approx operator-() const;
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
- Approx operator()( T const& value ) {
+ Approx operator()( T const& value ) const {
Approx approx( static_cast<double>(value) );
approx.m_epsilon = m_epsilon;
approx.m_margin = m_margin;
@@ -2585,6 +3193,7 @@ struct StringMaker<Catch::Detail::Approx> {
#include <string>
#include <iosfwd>
+#include <vector>
namespace Catch {
@@ -2595,7 +3204,13 @@ namespace Catch {
bool contains( std::string const& s, std::string const& infix );
void toLowerInPlace( std::string& s );
std::string toLower( std::string const& s );
+ //! Returns a new string without whitespace at the start/end
std::string trim( std::string const& str );
+ //! Returns a substring of the original ref without whitespace. Beware lifetimes!
+ StringRef trim(StringRef ref);
+
+ // !!! Be aware, returns refs into original string - make sure original string outlives them
+ std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
struct pluralise {
@@ -2648,6 +3263,15 @@ namespace Matchers {
virtual bool match( ObjectT const& arg ) const = 0;
};
+#if defined(__OBJC__)
+ // Hack to fix Catch GH issue #1661. Could use id for generic Object support.
+ // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
+ template<>
+ struct MatcherMethod<NSString*> {
+ virtual bool match( NSString* arg ) const = 0;
+ };
+#endif
+
#ifdef __clang__
# pragma clang diagnostic pop
#endif
@@ -2685,9 +3309,10 @@ namespace Matchers {
return description;
}
- MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
- m_matchers.push_back( &other );
- return *this;
+ MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
+ auto copy(*this);
+ copy.m_matchers.push_back( &other );
+ return copy;
}
std::vector<MatcherBase<ArgT> const*> m_matchers;
@@ -2718,9 +3343,10 @@ namespace Matchers {
return description;
}
- MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
- m_matchers.push_back( &other );
- return *this;
+ MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
+ auto copy(*this);
+ copy.m_matchers.push_back( &other );
+ return copy;
}
std::vector<MatcherBase<ArgT> const*> m_matchers;
@@ -2764,10 +3390,34 @@ using Matchers::Impl::MatcherBase;
} // namespace Catch
// end catch_matchers.h
-// start catch_matchers_floating.h
+// start catch_matchers_exception.hpp
-#include <type_traits>
-#include <cmath>
+namespace Catch {
+namespace Matchers {
+namespace Exception {
+
+class ExceptionMessageMatcher : public MatcherBase<std::exception> {
+ std::string m_message;
+public:
+
+ ExceptionMessageMatcher(std::string const& message):
+ m_message(message)
+ {}
+
+ bool match(std::exception const& ex) const override;
+
+ std::string describe() const override;
+};
+
+} // namespace Exception
+
+Exception::ExceptionMessageMatcher Message(std::string const& message);
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_exception.hpp
+// start catch_matchers_floating.h
namespace Catch {
namespace Matchers {
@@ -2786,22 +3436,43 @@ namespace Matchers {
};
struct WithinUlpsMatcher : MatcherBase<double> {
- WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
+ WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
- int m_ulps;
+ uint64_t m_ulps;
FloatingPointKind m_type;
};
+ // Given IEEE-754 format for floats and doubles, we can assume
+ // that float -> double promotion is lossless. Given this, we can
+ // assume that if we do the standard relative comparison of
+ // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
+ // the same result if we do this for floats, as if we do this for
+ // doubles that were promoted from floats.
+ struct WithinRelMatcher : MatcherBase<double> {
+ WithinRelMatcher(double target, double epsilon);
+ bool match(double const& matchee) const override;
+ std::string describe() const override;
+ private:
+ double m_target;
+ double m_epsilon;
+ };
+
} // namespace Floating
// The following functions create the actual matcher objects.
// This allows the types to be inferred
- Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
- Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
+ Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
+ Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
Floating::WithinAbsMatcher WithinAbs(double target, double margin);
+ Floating::WithinRelMatcher WithinRel(double target, double eps);
+ // defaults epsilon to 100*numeric_limits<double>::epsilon()
+ Floating::WithinRelMatcher WithinRel(double target);
+ Floating::WithinRelMatcher WithinRel(float target, float eps);
+ // defaults epsilon to 100*numeric_limits<float>::epsilon()
+ Floating::WithinRelMatcher WithinRel(float target);
} // namespace Matchers
} // namespace Catch
@@ -2844,7 +3515,7 @@ public:
// The following functions create the actual matcher objects.
// The user has to explicitly specify type to the function, because
- // infering std::function<bool(T const&)> is hard (but possible) and
+ // inferring std::function<bool(T const&)> is hard (but possible) and
// requires a lot of TMP.
template<typename T>
Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
@@ -2932,34 +3603,12 @@ namespace Catch {
namespace Matchers {
namespace Vector {
- namespace Detail {
- template <typename InputIterator, typename T>
- size_t count(InputIterator first, InputIterator last, T const& item) {
- size_t cnt = 0;
- for (; first != last; ++first) {
- if (*first == item) {
- ++cnt;
- }
- }
- return cnt;
- }
- template <typename InputIterator, typename T>
- bool contains(InputIterator first, InputIterator last, T const& item) {
- for (; first != last; ++first) {
- if (*first == item) {
- return true;
- }
- }
- return false;
- }
- }
-
- template<typename T>
- struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
+ template<typename T, typename Alloc>
+ struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> {
ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
- bool match(std::vector<T> const &v) const override {
+ bool match(std::vector<T, Alloc> const &v) const override {
for (auto const& el : v) {
if (el == m_comparator) {
return true;
@@ -2975,12 +3624,12 @@ namespace Matchers {
T const& m_comparator;
};
- template<typename T>
- struct ContainsMatcher : MatcherBase<std::vector<T>> {
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
- ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+ ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
- bool match(std::vector<T> const &v) const override {
+ bool match(std::vector<T, AllocMatch> const &v) const override {
// !TBD: see note in EqualsMatcher
if (m_comparator.size() > v.size())
return false;
@@ -3002,18 +3651,18 @@ namespace Matchers {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
- std::vector<T> const& m_comparator;
+ std::vector<T, AllocComp> const& m_comparator;
};
- template<typename T>
- struct EqualsMatcher : MatcherBase<std::vector<T>> {
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
- EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+ EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
- bool match(std::vector<T> const &v) const override {
+ bool match(std::vector<T, AllocMatch> const &v) const override {
// !TBD: This currently works if all elements can be compared using !=
// - a more general approach would be via a compare template that defaults
- // to using !=. but could be specialised for, e.g. std::vector<T> etc
+ // to using !=. but could be specialised for, e.g. std::vector<T, Alloc> etc
// - then just call that directly
if (m_comparator.size() != v.size())
return false;
@@ -3025,47 +3674,60 @@ namespace Matchers {
std::string describe() const override {
return "Equals: " + ::Catch::Detail::stringify( m_comparator );
}
- std::vector<T> const& m_comparator;
+ std::vector<T, AllocComp> const& m_comparator;
};
- template<typename T>
- struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
- UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
- bool match(std::vector<T> const& vec) const override {
- // Note: This is a reimplementation of std::is_permutation,
- // because I don't want to include <algorithm> inside the common path
- if (m_target.size() != vec.size()) {
- return false;
- }
- auto lfirst = m_target.begin(), llast = m_target.end();
- auto rfirst = vec.begin(), rlast = vec.end();
- // Cut common prefix to optimize checking of permuted parts
- while (lfirst != llast && *lfirst == *rfirst) {
- ++lfirst; ++rfirst;
- }
- if (lfirst == llast) {
- return true;
- }
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> {
- for (auto mid = lfirst; mid != llast; ++mid) {
- // Skip already counted items
- if (Detail::contains(lfirst, mid, *mid)) {
- continue;
- }
- size_t num_vec = Detail::count(rfirst, rlast, *mid);
- if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
- return false;
- }
- }
+ ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {}
+ bool match(std::vector<T, AllocMatch> const &v) const override {
+ if (m_comparator.size() != v.size())
+ return false;
+ for (std::size_t i = 0; i < v.size(); ++i)
+ if (m_comparator[i] != approx(v[i]))
+ return false;
return true;
}
+ std::string describe() const override {
+ return "is approx: " + ::Catch::Detail::stringify( m_comparator );
+ }
+ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ ApproxMatcher& epsilon( T const& newEpsilon ) {
+ approx.epsilon(newEpsilon);
+ return *this;
+ }
+ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ ApproxMatcher& margin( T const& newMargin ) {
+ approx.margin(newMargin);
+ return *this;
+ }
+ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ ApproxMatcher& scale( T const& newScale ) {
+ approx.scale(newScale);
+ return *this;
+ }
+
+ std::vector<T, AllocComp> const& m_comparator;
+ mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom();
+ };
+
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct UnorderedEqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+ UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target) : m_target(target) {}
+ bool match(std::vector<T, AllocMatch> const& vec) const override {
+ if (m_target.size() != vec.size()) {
+ return false;
+ }
+ return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
+ }
std::string describe() const override {
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
}
private:
- std::vector<T> const& m_target;
+ std::vector<T, AllocComp> const& m_target;
};
} // namespace Vector
@@ -3073,24 +3735,29 @@ namespace Matchers {
// The following functions create the actual matcher objects.
// This allows the types to be inferred
- template<typename T>
- Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
- return Vector::ContainsMatcher<T>( comparator );
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
+ return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator );
}
- template<typename T>
- Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
- return Vector::ContainsElementMatcher<T>( comparator );
+ template<typename T, typename Alloc = std::allocator<T>>
+ Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
+ return Vector::ContainsElementMatcher<T, Alloc>( comparator );
}
- template<typename T>
- Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
- return Vector::EqualsMatcher<T>( comparator );
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
+ return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator );
}
- template<typename T>
- Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
- return Vector::UnorderedEqualsMatcher<T>(target);
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
+ return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
+ return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target );
}
} // namespace Matchers
@@ -3175,16 +3842,17 @@ namespace Catch {
namespace Catch {
namespace Generators {
- class GeneratorBase {
- protected:
- size_t m_size = 0;
-
+ class GeneratorUntypedBase {
public:
- GeneratorBase( size_t size ) : m_size( size ) {}
- virtual ~GeneratorBase();
- auto size() const -> size_t { return m_size; }
+ GeneratorUntypedBase() = default;
+ virtual ~GeneratorUntypedBase();
+ // Attempts to move the generator to the next element
+ //
+ // Returns true iff the move succeeded (and a valid element
+ // can be retrieved).
+ virtual bool next() = 0;
};
- using GeneratorBasePtr = std::unique_ptr<GeneratorBase>;
+ using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>;
} // namespace Generators
@@ -3193,7 +3861,6 @@ namespace Catch {
virtual auto hasGenerator() const -> bool = 0;
virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
- virtual auto getIndex() const -> std::size_t = 0;
};
} // namespace Catch
@@ -3201,7 +3868,7 @@ namespace Catch {
// end catch_interfaces_generatortracker.h
// start catch_enforce.h
-#include <stdexcept>
+#include <exception>
namespace Catch {
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
@@ -3214,18 +3881,30 @@ namespace Catch {
[[noreturn]]
void throw_exception(std::exception const& e);
#endif
+
+ [[noreturn]]
+ void throw_logic_error(std::string const& msg);
+ [[noreturn]]
+ void throw_domain_error(std::string const& msg);
+ [[noreturn]]
+ void throw_runtime_error(std::string const& msg);
+
} // namespace Catch;
-#define CATCH_PREPARE_EXCEPTION( type, msg ) \
- type( ( Catch::ReusableStringStream() << msg ).str() )
-#define CATCH_INTERNAL_ERROR( msg ) \
- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg))
-#define CATCH_ERROR( msg ) \
- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg ))
-#define CATCH_RUNTIME_ERROR( msg ) \
- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg ))
-#define CATCH_ENFORCE( condition, msg ) \
- do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
+#define CATCH_MAKE_MSG(...) \
+ (Catch::ReusableStringStream() << __VA_ARGS__).str()
+
+#define CATCH_INTERNAL_ERROR(...) \
+ Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
+
+#define CATCH_ERROR(...) \
+ Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_RUNTIME_ERROR(...) \
+ Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_ENFORCE( condition, ... ) \
+ do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
// end catch_enforce.h
#include <memory>
@@ -3233,8 +3912,21 @@ namespace Catch {
#include <cassert>
#include <utility>
+#include <exception>
namespace Catch {
+
+class GeneratorException : public std::exception {
+ const char* const m_msg = "";
+
+public:
+ GeneratorException(const char* msg):
+ m_msg(msg)
+ {}
+
+ const char* what() const noexcept override final;
+};
+
namespace Generators {
// !TBD move this into its own location?
@@ -3246,223 +3938,819 @@ namespace Generators {
}
template<typename T>
- struct IGenerator {
- virtual ~IGenerator() {}
- virtual auto get( size_t index ) const -> T = 0;
+ struct IGenerator : GeneratorUntypedBase {
+ virtual ~IGenerator() = default;
+
+ // Returns the current element of the generator
+ //
+ // \Precondition The generator is either freshly constructed,
+ // or the last call to `next()` returned true
+ virtual T const& get() const = 0;
+ using type = T;
};
template<typename T>
- class SingleValueGenerator : public IGenerator<T> {
+ class SingleValueGenerator final : public IGenerator<T> {
T m_value;
public:
- SingleValueGenerator( T const& value ) : m_value( value ) {}
+ SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
- auto get( size_t ) const -> T override {
+ T const& get() const override {
return m_value;
}
+ bool next() override {
+ return false;
+ }
};
template<typename T>
- class FixedValuesGenerator : public IGenerator<T> {
+ class FixedValuesGenerator final : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "FixedValuesGenerator does not support bools because of std::vector<bool>"
+ "specialization, use SingleValue Generator instead.");
std::vector<T> m_values;
-
+ size_t m_idx = 0;
public:
FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
- auto get( size_t index ) const -> T override {
- return m_values[index];
+ T const& get() const override {
+ return m_values[m_idx];
+ }
+ bool next() override {
+ ++m_idx;
+ return m_idx < m_values.size();
+ }
+ };
+
+ template <typename T>
+ class GeneratorWrapper final {
+ std::unique_ptr<IGenerator<T>> m_generator;
+ public:
+ GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
+ m_generator(std::move(generator))
+ {}
+ T const& get() const {
+ return m_generator->get();
+ }
+ bool next() {
+ return m_generator->next();
}
};
+ template <typename T>
+ GeneratorWrapper<T> value(T&& value) {
+ return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
+ }
+ template <typename T>
+ GeneratorWrapper<T> values(std::initializer_list<T> values) {
+ return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
+ }
+
template<typename T>
- class RangeGenerator : public IGenerator<T> {
- T const m_first;
- T const m_last;
+ class Generators : public IGenerator<T> {
+ std::vector<GeneratorWrapper<T>> m_generators;
+ size_t m_current = 0;
+
+ void populate(GeneratorWrapper<T>&& generator) {
+ m_generators.emplace_back(std::move(generator));
+ }
+ void populate(T&& val) {
+ m_generators.emplace_back(value(std::forward<T>(val)));
+ }
+ template<typename U>
+ void populate(U&& val) {
+ populate(T(std::forward<U>(val)));
+ }
+ template<typename U, typename... Gs>
+ void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
+ populate(std::forward<U>(valueOrGenerator));
+ populate(std::forward<Gs>(moreGenerators)...);
+ }
public:
- RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) {
- assert( m_last > m_first );
+ template <typename... Gs>
+ Generators(Gs &&... moreGenerators) {
+ m_generators.reserve(sizeof...(Gs));
+ populate(std::forward<Gs>(moreGenerators)...);
}
- auto get( size_t index ) const -> T override {
- // ToDo:: introduce a safe cast to catch potential overflows
- return static_cast<T>(m_first+index);
+ T const& get() const override {
+ return m_generators[m_current].get();
}
- };
- template<typename T>
- struct NullGenerator : IGenerator<T> {
- auto get( size_t ) const -> T override {
- CATCH_INTERNAL_ERROR("A Null Generator is always empty");
+ bool next() override {
+ if (m_current >= m_generators.size()) {
+ return false;
+ }
+ const bool current_status = m_generators[m_current].next();
+ if (!current_status) {
+ ++m_current;
+ }
+ return m_current < m_generators.size();
}
};
+ template<typename... Ts>
+ GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
+ return values<std::tuple<Ts...>>( tuples );
+ }
+
+ // Tag type to signal that a generator sequence should convert arguments to a specific type
+ template <typename T>
+ struct as {};
+
+ template<typename T, typename... Gs>
+ auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
+ return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
+ }
template<typename T>
- class Generator {
- std::unique_ptr<IGenerator<T>> m_generator;
- size_t m_size;
+ auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
+ return Generators<T>(std::move(generator));
+ }
+ template<typename T, typename... Gs>
+ auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
+ return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
+ }
+ template<typename T, typename U, typename... Gs>
+ auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
+ return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
+ }
- public:
- Generator( size_t size, std::unique_ptr<IGenerator<T>> generator )
- : m_generator( std::move( generator ) ),
- m_size( size )
- {}
+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+
+ template<typename L>
+ // Note: The type after -> is weird, because VS2015 cannot parse
+ // the expression used in the typedef inside, when it is in
+ // return type. Yeah.
+ auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
+ using UnderlyingType = typename decltype(generatorExpression())::type;
- auto size() const -> size_t { return m_size; }
- auto operator[]( size_t index ) const -> T {
- assert( index < m_size );
- return m_generator->get( index );
+ IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
+ if (!tracker.hasGenerator()) {
+ tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
}
- };
- std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize );
+ auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
+ return generator.get();
+ }
- template<typename T>
- class GeneratorRandomiser : public IGenerator<T> {
- Generator<T> m_baseGenerator;
+} // namespace Generators
+} // namespace Catch
+
+#define GENERATE( ... ) \
+ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_COPY( ... ) \
+ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_REF( ... ) \
+ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+
+// end catch_generators.hpp
+// start catch_generators_generic.hpp
- std::vector<size_t> m_indices;
+namespace Catch {
+namespace Generators {
+
+ template <typename T>
+ class TakeGenerator : public IGenerator<T> {
+ GeneratorWrapper<T> m_generator;
+ size_t m_returned = 0;
+ size_t m_target;
public:
- GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems )
- : m_baseGenerator( std::move( baseGenerator ) ),
- m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) )
- {}
+ TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
+ m_generator(std::move(generator)),
+ m_target(target)
+ {
+ assert(target != 0 && "Empty generators are not allowed");
+ }
+ T const& get() const override {
+ return m_generator.get();
+ }
+ bool next() override {
+ ++m_returned;
+ if (m_returned >= m_target) {
+ return false;
+ }
- auto get( size_t index ) const -> T override {
- return m_baseGenerator[m_indices[index]];
+ const auto success = m_generator.next();
+ // If the underlying generator does not contain enough values
+ // then we cut short as well
+ if (!success) {
+ m_returned = m_target;
+ }
+ return success;
}
};
- template<typename T>
- struct RequiresASpecialisationFor;
+ template <typename T>
+ GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
+ }
- template<typename T>
- auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); }
+ template <typename T, typename Predicate>
+ class FilterGenerator : public IGenerator<T> {
+ GeneratorWrapper<T> m_generator;
+ Predicate m_predicate;
+ public:
+ template <typename P = Predicate>
+ FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
+ m_generator(std::move(generator)),
+ m_predicate(std::forward<P>(pred))
+ {
+ if (!m_predicate(m_generator.get())) {
+ // It might happen that there are no values that pass the
+ // filter. In that case we throw an exception.
+ auto has_initial_value = nextImpl();
+ if (!has_initial_value) {
+ Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
+ }
+ }
+ }
- template<>
- auto all<int>() -> Generator<int>;
+ T const& get() const override {
+ return m_generator.get();
+ }
- template<typename T>
- auto range( T const& first, T const& last ) -> Generator<T> {
- return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) );
+ bool next() override {
+ return nextImpl();
+ }
+
+ private:
+ bool nextImpl() {
+ bool success = m_generator.next();
+ if (!success) {
+ return false;
+ }
+ while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
+ return success;
+ }
+ };
+
+ template <typename T, typename Predicate>
+ GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
}
- template<typename T>
- auto random( T const& first, T const& last ) -> Generator<T> {
- auto gen = range( first, last );
- auto size = gen.size();
+ template <typename T>
+ class RepeatGenerator : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "RepeatGenerator currently does not support bools"
+ "because of std::vector<bool> specialization");
+ GeneratorWrapper<T> m_generator;
+ mutable std::vector<T> m_returned;
+ size_t m_target_repeats;
+ size_t m_current_repeat = 0;
+ size_t m_repeat_index = 0;
+ public:
+ RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
+ m_generator(std::move(generator)),
+ m_target_repeats(repeats)
+ {
+ assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
+ }
- return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) );
+ T const& get() const override {
+ if (m_current_repeat == 0) {
+ m_returned.push_back(m_generator.get());
+ return m_returned.back();
+ }
+ return m_returned[m_repeat_index];
+ }
+
+ bool next() override {
+ // There are 2 basic cases:
+ // 1) We are still reading the generator
+ // 2) We are reading our own cache
+
+ // In the first case, we need to poke the underlying generator.
+ // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
+ if (m_current_repeat == 0) {
+ const auto success = m_generator.next();
+ if (!success) {
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
+ }
+
+ // In the second case, we need to move indices forward and check that we haven't run up against the end
+ ++m_repeat_index;
+ if (m_repeat_index == m_returned.size()) {
+ m_repeat_index = 0;
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
+ }
+ };
+
+ template <typename T>
+ GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
}
- template<typename T>
- auto random( size_t size ) -> Generator<T> {
- return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) );
+
+ template <typename T, typename U, typename Func>
+ class MapGenerator : public IGenerator<T> {
+ // TBD: provide static assert for mapping function, for friendly error message
+ GeneratorWrapper<U> m_generator;
+ Func m_function;
+ // To avoid returning dangling reference, we have to save the values
+ T m_cache;
+ public:
+ template <typename F2 = Func>
+ MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
+ m_generator(std::move(generator)),
+ m_function(std::forward<F2>(function)),
+ m_cache(m_function(m_generator.get()))
+ {}
+
+ T const& get() const override {
+ return m_cache;
+ }
+ bool next() override {
+ const auto success = m_generator.next();
+ if (success) {
+ m_cache = m_function(m_generator.get());
+ }
+ return success;
+ }
+ };
+
+ template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
+ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+ );
}
- template<typename T>
- auto values( std::initializer_list<T> values ) -> Generator<T> {
- return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) );
+ template <typename T, typename U, typename Func>
+ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+ );
}
- template<typename T>
- auto value( T const& val ) -> Generator<T> {
- return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) );
+
+ template <typename T>
+ class ChunkGenerator final : public IGenerator<std::vector<T>> {
+ std::vector<T> m_chunk;
+ size_t m_chunk_size;
+ GeneratorWrapper<T> m_generator;
+ bool m_used_up = false;
+ public:
+ ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
+ m_chunk_size(size), m_generator(std::move(generator))
+ {
+ m_chunk.reserve(m_chunk_size);
+ if (m_chunk_size != 0) {
+ m_chunk.push_back(m_generator.get());
+ for (size_t i = 1; i < m_chunk_size; ++i) {
+ if (!m_generator.next()) {
+ Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
+ }
+ m_chunk.push_back(m_generator.get());
+ }
+ }
+ }
+ std::vector<T> const& get() const override {
+ return m_chunk;
+ }
+ bool next() override {
+ m_chunk.clear();
+ for (size_t idx = 0; idx < m_chunk_size; ++idx) {
+ if (!m_generator.next()) {
+ return false;
+ }
+ m_chunk.push_back(m_generator.get());
+ }
+ return true;
+ }
+ };
+
+ template <typename T>
+ GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<std::vector<T>>(
+ pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
+ );
}
- template<typename T>
- auto as() -> Generator<T> {
- return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() );
+} // namespace Generators
+} // namespace Catch
+
+// end catch_generators_generic.hpp
+// start catch_generators_specific.hpp
+
+// start catch_context.h
+
+#include <memory>
+
+namespace Catch {
+
+ struct IResultCapture;
+ struct IRunner;
+ struct IConfig;
+ struct IMutableContext;
+
+ using IConfigPtr = std::shared_ptr<IConfig const>;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual IConfigPtr const& getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( IConfigPtr const& config ) = 0;
+
+ private:
+ static IMutableContext *currentContext;
+ friend IMutableContext& getCurrentMutableContext();
+ friend void cleanUpContext();
+ static void createContext();
+ };
+
+ inline IMutableContext& getCurrentMutableContext()
+ {
+ if( !IMutableContext::currentContext )
+ IMutableContext::createContext();
+ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+ return *IMutableContext::currentContext;
}
- template<typename... Ts>
- auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> {
- return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) );
+ inline IContext& getCurrentContext()
+ {
+ return getCurrentMutableContext();
}
- template<typename T>
- struct Generators : GeneratorBase {
- std::vector<Generator<T>> m_generators;
+ void cleanUpContext();
- using type = T;
+ class SimplePcg32;
+ SimplePcg32& rng();
+}
- Generators() : GeneratorBase( 0 ) {}
+// end catch_context.h
+// start catch_interfaces_config.h
+
+// start catch_option.hpp
+
+namespace Catch {
- void populate( T&& val ) {
- m_size += 1;
- m_generators.emplace_back( value( std::move( val ) ) );
+ // An optional type
+ template<typename T>
+ class Option {
+ public:
+ Option() : nullableValue( nullptr ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
+ {}
+
+ ~Option() {
+ reset();
}
- template<typename U>
- void populate( U&& val ) {
- populate( T( std::move( val ) ) );
+
+ Option& operator= ( Option const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Option& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
}
- void populate( Generator<T>&& generator ) {
- m_size += generator.size();
- m_generators.emplace_back( std::move( generator ) );
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = nullptr;
}
- template<typename U, typename... Gs>
- void populate( U&& valueOrGenerator, Gs... moreGenerators ) {
- populate( std::forward<U>( valueOrGenerator ) );
- populate( std::forward<Gs>( moreGenerators )... );
+ T& operator*() { return *nullableValue; }
+ T const& operator*() const { return *nullableValue; }
+ T* operator->() { return nullableValue; }
+ const T* operator->() const { return nullableValue; }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
}
- auto operator[]( size_t index ) const -> T {
- size_t sizes = 0;
- for( auto const& gen : m_generators ) {
- auto localIndex = index-sizes;
- sizes += gen.size();
- if( index < sizes )
- return gen[localIndex];
- }
- CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')');
+ bool some() const { return nullableValue != nullptr; }
+ bool none() const { return nullableValue == nullptr; }
+
+ bool operator !() const { return nullableValue == nullptr; }
+ explicit operator bool() const {
+ return some();
}
+
+ private:
+ T *nullableValue;
+ alignas(alignof(T)) char storage[sizeof(T)];
};
- template<typename T, typename... Gs>
- auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
- Generators<T> generators;
- generators.m_generators.reserve( 1+sizeof...(Gs) );
- generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... );
- return generators;
+} // end namespace Catch
+
+// end catch_option.hpp
+#include <chrono>
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+ enum class Verbosity {
+ Quiet = 0,
+ Normal,
+ High
+ };
+
+ struct WarnAbout { enum What {
+ Nothing = 0x00,
+ NoAssertions = 0x01,
+ NoTests = 0x02
+ }; };
+
+ struct ShowDurations { enum OrNot {
+ DefaultForReporter,
+ Always,
+ Never
+ }; };
+ struct RunTests { enum InWhatOrder {
+ InDeclarationOrder,
+ InLexicographicalOrder,
+ InRandomOrder
+ }; };
+ struct UseColour { enum YesOrNo {
+ Auto,
+ Yes,
+ No
+ }; };
+ struct WaitForKeypress { enum When {
+ Never,
+ BeforeStart = 1,
+ BeforeExit = 2,
+ BeforeStartAndExit = BeforeStart | BeforeExit
+ }; };
+
+ class TestSpec;
+
+ struct IConfig : NonCopyable {
+
+ virtual ~IConfig();
+
+ virtual bool allowThrows() const = 0;
+ virtual std::ostream& stream() const = 0;
+ virtual std::string name() const = 0;
+ virtual bool includeSuccessfulResults() const = 0;
+ virtual bool shouldDebugBreak() const = 0;
+ virtual bool warnAboutMissingAssertions() const = 0;
+ virtual bool warnAboutNoTests() const = 0;
+ virtual int abortAfter() const = 0;
+ virtual bool showInvisibles() const = 0;
+ virtual ShowDurations::OrNot showDurations() const = 0;
+ virtual double minDuration() const = 0;
+ virtual TestSpec const& testSpec() const = 0;
+ virtual bool hasTestFilters() const = 0;
+ virtual std::vector<std::string> const& getTestsOrTags() const = 0;
+ virtual RunTests::InWhatOrder runOrder() const = 0;
+ virtual unsigned int rngSeed() const = 0;
+ virtual UseColour::YesOrNo useColour() const = 0;
+ virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+ virtual Verbosity verbosity() const = 0;
+
+ virtual bool benchmarkNoAnalysis() const = 0;
+ virtual int benchmarkSamples() const = 0;
+ virtual double benchmarkConfidenceInterval() const = 0;
+ virtual unsigned int benchmarkResamples() const = 0;
+ virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
+ };
+
+ using IConfigPtr = std::shared_ptr<IConfig const>;
+}
+
+// end catch_interfaces_config.h
+// start catch_random_number_generator.h
+
+#include <cstdint>
+
+namespace Catch {
+
+ // This is a simple implementation of C++11 Uniform Random Number
+ // Generator. It does not provide all operators, because Catch2
+ // does not use it, but it should behave as expected inside stdlib's
+ // distributions.
+ // The implementation is based on the PCG family (http://pcg-random.org)
+ class SimplePcg32 {
+ using state_type = std::uint64_t;
+ public:
+ using result_type = std::uint32_t;
+ static constexpr result_type (min)() {
+ return 0;
+ }
+ static constexpr result_type (max)() {
+ return static_cast<result_type>(-1);
+ }
+
+ // Provide some default initial state for the default constructor
+ SimplePcg32():SimplePcg32(0xed743cc4U) {}
+
+ explicit SimplePcg32(result_type seed_);
+
+ void seed(result_type seed_);
+ void discard(uint64_t skip);
+
+ result_type operator()();
+
+ private:
+ friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
+ friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
+
+ // In theory we also need operator<< and operator>>
+ // In practice we do not use them, so we will skip them for now
+
+ std::uint64_t m_state;
+ // This part of the state determines which "stream" of the numbers
+ // is chosen -- we take it as a constant for Catch2, so we only
+ // need to deal with seeding the main state.
+ // Picked by reading 8 bytes from `/dev/random` :-)
+ static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL;
+ };
+
+} // end namespace Catch
+
+// end catch_random_number_generator.h
+#include <random>
+
+namespace Catch {
+namespace Generators {
+
+template <typename Float>
+class RandomFloatingGenerator final : public IGenerator<Float> {
+ Catch::SimplePcg32& m_rng;
+ std::uniform_real_distribution<Float> m_dist;
+ Float m_current_number;
+public:
+
+ RandomFloatingGenerator(Float a, Float b):
+ m_rng(rng()),
+ m_dist(a, b) {
+ static_cast<void>(next());
}
- template<typename T>
- auto makeGenerators( Generator<T>&& generator ) -> Generators<T> {
- Generators<T> generators;
- generators.populate( std::move( generator ) );
- return generators;
+
+ Float const& get() const override {
+ return m_current_number;
}
- template<typename T, typename... Gs>
- auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
- return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
+ bool next() override {
+ m_current_number = m_dist(m_rng);
+ return true;
}
- template<typename T, typename U, typename... Gs>
- auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> {
- return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
+};
+
+template <typename Integer>
+class RandomIntegerGenerator final : public IGenerator<Integer> {
+ Catch::SimplePcg32& m_rng;
+ std::uniform_int_distribution<Integer> m_dist;
+ Integer m_current_number;
+public:
+
+ RandomIntegerGenerator(Integer a, Integer b):
+ m_rng(rng()),
+ m_dist(a, b) {
+ static_cast<void>(next());
}
- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+ Integer const& get() const override {
+ return m_current_number;
+ }
+ bool next() override {
+ m_current_number = m_dist(m_rng);
+ return true;
+ }
+};
- template<typename L>
- // Note: The type after -> is weird, because VS2015 cannot parse
- // the expression used in the typedef inside, when it is in
- // return type. Yeah, ¯\_(ツ)_/¯
- auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) {
- using UnderlyingType = typename decltype(generatorExpression())::type;
+// TODO: Ideally this would be also constrained against the various char types,
+// but I don't expect users to run into that in practice.
+template <typename T>
+typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
+GeneratorWrapper<T>>::type
+random(T a, T b) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<RandomIntegerGenerator<T>>(a, b)
+ );
+}
+
+template <typename T>
+typename std::enable_if<std::is_floating_point<T>::value,
+GeneratorWrapper<T>>::type
+random(T a, T b) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<RandomFloatingGenerator<T>>(a, b)
+ );
+}
+
+template <typename T>
+class RangeGenerator final : public IGenerator<T> {
+ T m_current;
+ T m_end;
+ T m_step;
+ bool m_positive;
+
+public:
+ RangeGenerator(T const& start, T const& end, T const& step):
+ m_current(start),
+ m_end(end),
+ m_step(step),
+ m_positive(m_step > T(0))
+ {
+ assert(m_current != m_end && "Range start and end cannot be equal");
+ assert(m_step != T(0) && "Step size cannot be zero");
+ assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end");
+ }
+
+ RangeGenerator(T const& start, T const& end):
+ RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
+ {}
+
+ T const& get() const override {
+ return m_current;
+ }
+
+ bool next() override {
+ m_current += m_step;
+ return (m_positive) ? (m_current < m_end) : (m_current > m_end);
+ }
+};
+
+template <typename T>
+GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
+ static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
+ return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
+}
- IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
- if( !tracker.hasGenerator() )
- tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) );
+template <typename T>
+GeneratorWrapper<T> range(T const& start, T const& end) {
+ static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
+ return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
+}
- auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() );
- return generator[tracker.getIndex()];
+template <typename T>
+class IteratorGenerator final : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "IteratorGenerator currently does not support bools"
+ "because of std::vector<bool> specialization");
+
+ std::vector<T> m_elems;
+ size_t m_current = 0;
+public:
+ template <typename InputIterator, typename InputSentinel>
+ IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
+ if (m_elems.empty()) {
+ Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
+ }
+ }
+
+ T const& get() const override {
+ return m_elems[m_current];
}
+ bool next() override {
+ ++m_current;
+ return m_current != m_elems.size();
+ }
+};
+
+template <typename InputIterator,
+ typename InputSentinel,
+ typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
+GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
+ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
+}
+
+template <typename Container,
+ typename ResultType = typename Container::value_type>
+GeneratorWrapper<ResultType> from_range(Container const& cnt) {
+ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
+}
+
} // namespace Generators
} // namespace Catch
-#define GENERATE( ... ) \
- Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
-
-// end catch_generators.hpp
+// end catch_generators_specific.hpp
// These files are included here so the single_include script doesn't put them
// in the conditionally compiled sections
@@ -3662,7 +4950,7 @@ namespace Catch {
arcSafeRelease( m_substr );
}
- bool match( NSString* arg ) const override {
+ bool match( NSString* str ) const override {
return false;
}
@@ -3685,7 +4973,7 @@ namespace Catch {
struct Contains : StringHolder {
Contains( NSString* substr ) : StringHolder( substr ){}
- bool match( NSString* str ) const {
+ bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str rangeOfString:m_substr].location != NSNotFound;
}
@@ -3761,7 +5049,8 @@ return @ desc; \
// end catch_objc.hpp
#endif
-#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES
+// Benchmarking needs the externally-facing parts of reporters to work
+#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
// start catch_external_interfaces.h
// start catch_reporter_bases.hpp
@@ -3803,7 +5092,7 @@ namespace Catch
virtual bool matches( std::string const& str ) const;
private:
- std::string adjustCase( std::string const& str ) const;
+ std::string normaliseString( std::string const& str ) const;
CaseSensitive::Choice m_caseSensitivity;
WildcardPosition m_wildcard = NoWildcard;
std::string m_pattern;
@@ -3817,36 +5106,40 @@ namespace Catch
namespace Catch {
+ struct IConfig;
+
class TestSpec {
- struct Pattern {
+ class Pattern {
+ public:
+ explicit Pattern( std::string const& name );
virtual ~Pattern();
virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ std::string const& name() const;
+ private:
+ std::string const m_name;
};
using PatternPtr = std::shared_ptr<Pattern>;
class NamePattern : public Pattern {
public:
- NamePattern( std::string const& name );
- virtual ~NamePattern();
- virtual bool matches( TestCaseInfo const& testCase ) const override;
+ explicit NamePattern( std::string const& name, std::string const& filterString );
+ bool matches( TestCaseInfo const& testCase ) const override;
private:
WildcardPattern m_wildcardPattern;
};
class TagPattern : public Pattern {
public:
- TagPattern( std::string const& tag );
- virtual ~TagPattern();
- virtual bool matches( TestCaseInfo const& testCase ) const override;
+ explicit TagPattern( std::string const& tag, std::string const& filterString );
+ bool matches( TestCaseInfo const& testCase ) const override;
private:
std::string m_tag;
};
class ExcludedPattern : public Pattern {
public:
- ExcludedPattern( PatternPtr const& underlyingPattern );
- virtual ~ExcludedPattern();
- virtual bool matches( TestCaseInfo const& testCase ) const override;
+ explicit ExcludedPattern( PatternPtr const& underlyingPattern );
+ bool matches( TestCaseInfo const& testCase ) const override;
private:
PatternPtr m_underlyingPattern;
};
@@ -3855,15 +5148,25 @@ namespace Catch {
std::vector<PatternPtr> m_patterns;
bool matches( TestCaseInfo const& testCase ) const;
+ std::string name() const;
};
public:
+ struct FilterMatch {
+ std::string name;
+ std::vector<TestCase const*> tests;
+ };
+ using Matches = std::vector<FilterMatch>;
+ using vectorStrings = std::vector<std::string>;
+
bool hasFilters() const;
bool matches( TestCaseInfo const& testCase ) const;
+ Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
+ const vectorStrings & getInvalidArgs() const;
private:
std::vector<Filter> m_filters;
-
+ std::vector<std::string> m_invalidArgs;
friend class TestSpecParser;
};
}
@@ -3898,9 +5201,13 @@ namespace Catch {
class TestSpecParser {
enum Mode{ None, Name, QuotedName, Tag, EscapedName };
Mode m_mode = None;
+ Mode lastMode = None;
bool m_exclusion = false;
- std::size_t m_start = std::string::npos, m_pos = 0;
+ std::size_t m_pos = 0;
+ std::size_t m_realPatternPos = 0;
std::string m_arg;
+ std::string m_substring;
+ std::string m_patternName;
std::vector<std::size_t> m_escapeChars;
TestSpec::Filter m_currentFilter;
TestSpec m_testSpec;
@@ -3913,32 +5220,32 @@ namespace Catch {
TestSpec testSpec();
private:
- void visitChar( char c );
- void startNewMode( Mode mode, std::size_t start );
+ bool visitChar( char c );
+ void startNewMode( Mode mode );
+ bool processNoneChar( char c );
+ void processNameChar( char c );
+ bool processOtherChar( char c );
+ void endMode();
void escape();
- std::string subString() const;
+ bool isControlChar( char c ) const;
+ void saveLastMode();
+ void revertBackToLastMode();
+ void addFilter();
+ bool separate();
- template<typename T>
- void addPattern() {
- std::string token = subString();
- for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
- token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
- m_escapeChars.clear();
- if( startsWith( token, "exclude:" ) ) {
- m_exclusion = true;
- token = token.substr( 8 );
- }
- if( !token.empty() ) {
- TestSpec::PatternPtr pattern = std::make_shared<T>( token );
- if( m_exclusion )
- pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
- m_currentFilter.m_patterns.push_back( pattern );
- }
- m_exclusion = false;
- m_mode = None;
+ // Handles common preprocessing of the pattern for name/tag patterns
+ std::string preprocessPattern();
+ // Adds the current pattern as a test name
+ void addNamePattern();
+ // Adds the current pattern as a tag
+ void addTagPattern();
+
+ inline void addCharToPattern(char c) {
+ m_substring += c;
+ m_patternName += c;
+ m_realPatternPos++;
}
- void addFilter();
};
TestSpec parseTestSpec( std::string const& arg );
@@ -3949,79 +5256,6 @@ namespace Catch {
#endif
// end catch_test_spec_parser.h
-// start catch_interfaces_config.h
-
-#include <iosfwd>
-#include <string>
-#include <vector>
-#include <memory>
-
-namespace Catch {
-
- enum class Verbosity {
- Quiet = 0,
- Normal,
- High
- };
-
- struct WarnAbout { enum What {
- Nothing = 0x00,
- NoAssertions = 0x01,
- NoTests = 0x02
- }; };
-
- struct ShowDurations { enum OrNot {
- DefaultForReporter,
- Always,
- Never
- }; };
- struct RunTests { enum InWhatOrder {
- InDeclarationOrder,
- InLexicographicalOrder,
- InRandomOrder
- }; };
- struct UseColour { enum YesOrNo {
- Auto,
- Yes,
- No
- }; };
- struct WaitForKeypress { enum When {
- Never,
- BeforeStart = 1,
- BeforeExit = 2,
- BeforeStartAndExit = BeforeStart | BeforeExit
- }; };
-
- class TestSpec;
-
- struct IConfig : NonCopyable {
-
- virtual ~IConfig();
-
- virtual bool allowThrows() const = 0;
- virtual std::ostream& stream() const = 0;
- virtual std::string name() const = 0;
- virtual bool includeSuccessfulResults() const = 0;
- virtual bool shouldDebugBreak() const = 0;
- virtual bool warnAboutMissingAssertions() const = 0;
- virtual bool warnAboutNoTests() const = 0;
- virtual int abortAfter() const = 0;
- virtual bool showInvisibles() const = 0;
- virtual ShowDurations::OrNot showDurations() const = 0;
- virtual TestSpec const& testSpec() const = 0;
- virtual bool hasTestFilters() const = 0;
- virtual RunTests::InWhatOrder runOrder() const = 0;
- virtual unsigned int rngSeed() const = 0;
- virtual int benchmarkResolutionMultiple() const = 0;
- virtual UseColour::YesOrNo useColour() const = 0;
- virtual std::vector<std::string> const& getSectionsToRun() const = 0;
- virtual Verbosity verbosity() const = 0;
- };
-
- using IConfigPtr = std::shared_ptr<IConfig const>;
-}
-
-// end catch_interfaces_config.h
// Libstdc++ doesn't like incomplete classes for unique_ptr
#include <memory>
@@ -4052,11 +5286,17 @@ namespace Catch {
int abortAfter = -1;
unsigned int rngSeed = 0;
- int benchmarkResolutionMultiple = 100;
+
+ bool benchmarkNoAnalysis = false;
+ unsigned int benchmarkSamples = 100;
+ double benchmarkConfidenceInterval = 0.95;
+ unsigned int benchmarkResamples = 100000;
+ std::chrono::milliseconds::rep benchmarkWarmupTime = 100;
Verbosity verbosity = Verbosity::Normal;
WarnAbout::What warnings = WarnAbout::Nothing;
ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter;
+ double minDuration = -1;
RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder;
UseColour::YesOrNo useColour = UseColour::Auto;
WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
@@ -4091,10 +5331,10 @@ namespace Catch {
std::string getProcessName() const;
std::string const& getReporterName() const;
- std::vector<std::string> const& getTestsOrTags() const;
+ std::vector<std::string> const& getTestsOrTags() const override;
std::vector<std::string> const& getSectionsToRun() const override;
- virtual TestSpec const& testSpec() const override;
+ TestSpec const& testSpec() const override;
bool hasTestFilters() const override;
bool showHelp() const;
@@ -4107,14 +5347,19 @@ namespace Catch {
bool warnAboutMissingAssertions() const override;
bool warnAboutNoTests() const override;
ShowDurations::OrNot showDurations() const override;
+ double minDuration() const override;
RunTests::InWhatOrder runOrder() const override;
unsigned int rngSeed() const override;
- int benchmarkResolutionMultiple() const override;
UseColour::YesOrNo useColour() const override;
bool shouldDebugBreak() const override;
int abortAfter() const override;
bool showInvisibles() const override;
Verbosity verbosity() const override;
+ bool benchmarkNoAnalysis() const override;
+ int benchmarkSamples() const override;
+ double benchmarkConfidenceInterval() const override;
+ unsigned int benchmarkResamples() const override;
+ std::chrono::milliseconds benchmarkWarmupTime() const override;
private:
@@ -4175,76 +5420,61 @@ namespace Catch {
} // end namespace Catch
// end catch_assertionresult.h
-// start catch_option.hpp
-
-namespace Catch {
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_estimate.hpp
- // An optional type
- template<typename T>
- class Option {
- public:
- Option() : nullableValue( nullptr ) {}
- Option( T const& _value )
- : nullableValue( new( storage ) T( _value ) )
- {}
- Option( Option const& _other )
- : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
- {}
+ // Statistics estimates
- ~Option() {
- reset();
- }
- Option& operator= ( Option const& _other ) {
- if( &_other != this ) {
- reset();
- if( _other )
- nullableValue = new( storage ) T( *_other );
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct Estimate {
+ Duration point;
+ Duration lower_bound;
+ Duration upper_bound;
+ double confidence_interval;
+
+ template <typename Duration2>
+ operator Estimate<Duration2>() const {
+ return { point, lower_bound, upper_bound, confidence_interval };
}
- return *this;
- }
- Option& operator = ( T const& _value ) {
- reset();
- nullableValue = new( storage ) T( _value );
- return *this;
- }
-
- void reset() {
- if( nullableValue )
- nullableValue->~T();
- nullableValue = nullptr;
- }
+ };
+ } // namespace Benchmark
+} // namespace Catch
- T& operator*() { return *nullableValue; }
- T const& operator*() const { return *nullableValue; }
- T* operator->() { return nullableValue; }
- const T* operator->() const { return nullableValue; }
+// end catch_estimate.hpp
+// start catch_outlier_classification.hpp
- T valueOr( T const& defaultValue ) const {
- return nullableValue ? *nullableValue : defaultValue;
- }
+// Outlier information
- bool some() const { return nullableValue != nullptr; }
- bool none() const { return nullableValue == nullptr; }
-
- bool operator !() const { return nullableValue == nullptr; }
- explicit operator bool() const {
- return some();
- }
+namespace Catch {
+ namespace Benchmark {
+ struct OutlierClassification {
+ int samples_seen = 0;
+ int low_severe = 0; // more than 3 times IQR below Q1
+ int low_mild = 0; // 1.5 to 3 times IQR below Q1
+ int high_mild = 0; // 1.5 to 3 times IQR above Q3
+ int high_severe = 0; // more than 3 times IQR above Q3
+
+ int total() const {
+ return low_severe + low_mild + high_mild + high_severe;
+ }
+ };
+ } // namespace Benchmark
+} // namespace Catch
- private:
- T *nullableValue;
- alignas(alignof(T)) char storage[sizeof(T)];
- };
+// end catch_outlier_classification.hpp
-} // end namespace Catch
+#include <iterator>
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
-// end catch_option.hpp
#include <string>
#include <iosfwd>
#include <map>
#include <set>
#include <memory>
+#include <algorithm>
namespace Catch {
@@ -4301,8 +5531,8 @@ namespace Catch {
AssertionStats( AssertionStats const& ) = default;
AssertionStats( AssertionStats && ) = default;
- AssertionStats& operator = ( AssertionStats const& ) = default;
- AssertionStats& operator = ( AssertionStats && ) = default;
+ AssertionStats& operator = ( AssertionStats const& ) = delete;
+ AssertionStats& operator = ( AssertionStats && ) = delete;
virtual ~AssertionStats();
AssertionResult assertionResult;
@@ -4380,14 +5610,43 @@ namespace Catch {
bool aborting;
};
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
struct BenchmarkInfo {
std::string name;
+ double estimatedDuration;
+ int iterations;
+ int samples;
+ unsigned int resamples;
+ double clockResolution;
+ double clockCost;
};
+
+ template <class Duration>
struct BenchmarkStats {
BenchmarkInfo info;
- std::size_t iterations;
- uint64_t elapsedTimeInNanoseconds;
+
+ std::vector<Duration> samples;
+ Benchmark::Estimate<Duration> mean;
+ Benchmark::Estimate<Duration> standardDeviation;
+ Benchmark::OutlierClassification outliers;
+ double outlierVariance;
+
+ template <typename Duration2>
+ operator BenchmarkStats<Duration2>() const {
+ std::vector<Duration2> samples2;
+ samples2.reserve(samples.size());
+ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
+ return {
+ info,
+ std::move(samples2),
+ mean,
+ standardDeviation,
+ outliers,
+ outlierVariance,
+ };
+ }
};
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
struct IStreamingReporter {
virtual ~IStreamingReporter() = default;
@@ -4400,23 +5659,26 @@ namespace Catch {
virtual void noMatchingTestCases( std::string const& spec ) = 0;
+ virtual void reportInvalidArguments(std::string const&) {}
+
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
- // *** experimental ***
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ virtual void benchmarkPreparing( std::string const& ) {}
virtual void benchmarkStarting( BenchmarkInfo const& ) {}
+ virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
+ virtual void benchmarkFailed( std::string const& ) {}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
// The return value indicates if the messages buffer should be cleared:
virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
- // *** experimental ***
- virtual void benchmarkEnded( BenchmarkStats const& ) {}
-
virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
@@ -4465,6 +5727,11 @@ namespace Catch {
// Returns double formatted as %.3f (format expected on output)
std::string getFormattedDuration( double duration );
+ //! Should the reporter show
+ bool shouldShowDuration( IConfig const& config, double duration );
+
+ std::string serializeFilters( std::vector<std::string> const& container );
+
template<typename DerivedT>
struct StreamingReporterBase : IStreamingReporter {
@@ -4489,9 +5756,12 @@ namespace Catch {
void noMatchingTestCases(std::string const&) override {}
+ void reportInvalidArguments(std::string const&) override {}
+
void testRunStarting(TestRunInfo const& _testRunInfo) override {
currentTestRunInfo = _testRunInfo;
}
+
void testGroupStarting(GroupInfo const& _groupInfo) override {
currentGroupInfo = _groupInfo;
}
@@ -4782,11 +6052,11 @@ namespace Catch {
class ReporterFactory : public IReporterFactory {
- virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+ IStreamingReporterPtr create( ReporterConfig const& config ) const override {
return std::unique_ptr<T>( new T( config ) );
}
- virtual std::string getDescription() const override {
+ std::string getDescription() const override {
return T::getDescription();
}
};
@@ -4803,10 +6073,10 @@ namespace Catch {
class ListenerFactory : public IReporterFactory {
- virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+ IStreamingReporterPtr create( ReporterConfig const& config ) const override {
return std::unique_ptr<T>( new T( config ) );
}
- virtual std::string getDescription() const override {
+ std::string getDescription() const override {
return std::string();
}
};
@@ -4822,14 +6092,16 @@ namespace Catch {
#if !defined(CATCH_CONFIG_DISABLE)
#define CATCH_REGISTER_REPORTER( name, reporterType ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#define CATCH_REGISTER_LISTENER( listenerType ) \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#else // CATCH_CONFIG_DISABLE
#define CATCH_REGISTER_REPORTER(name, reporterType)
@@ -4851,8 +6123,6 @@ namespace Catch {
static std::string getDescription();
- ReporterPreferences getPreferences() const override;
-
void noMatchingTestCases(std::string const& spec) override;
void assertionStarting(AssertionInfo const&) override;
@@ -4891,6 +6161,8 @@ namespace Catch {
void noMatchingTestCases(std::string const& spec) override;
+ void reportInvalidArguments(std::string const&arg) override;
+
void assertionStarting(AssertionInfo const&) override;
bool assertionEnded(AssertionStats const& _assertionStats) override;
@@ -4898,13 +6170,17 @@ namespace Catch {
void sectionStarting(SectionInfo const& _sectionInfo) override;
void sectionEnded(SectionStats const& _sectionStats) override;
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing(std::string const& name) override;
void benchmarkStarting(BenchmarkInfo const& info) override;
- void benchmarkEnded(BenchmarkStats const& stats) override;
+ void benchmarkEnded(BenchmarkStats<> const& stats) override;
+ void benchmarkFailed(std::string const& error) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
void testCaseEnded(TestCaseStats const& _testCaseStats) override;
void testGroupEnded(TestGroupStats const& _testGroupStats) override;
void testRunEnded(TestRunStats const& _testRunStats) override;
-
+ void testRunStarting(TestRunInfo const& _testRunInfo) override;
private:
void lazyPrint();
@@ -4926,6 +6202,7 @@ namespace Catch {
void printTotalsDivider(Totals const& totals);
void printSummaryDivider();
+ void printTestFilters();
private:
bool m_headerPrinted = false;
@@ -4945,6 +6222,14 @@ namespace Catch {
#include <vector>
namespace Catch {
+ enum class XmlFormatting {
+ None = 0x00,
+ Indent = 0x01,
+ Newline = 0x02,
+ };
+
+ XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
+ XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
class XmlEncode {
public:
@@ -4966,14 +6251,14 @@ namespace Catch {
class ScopedElement {
public:
- ScopedElement( XmlWriter* writer );
+ ScopedElement( XmlWriter* writer, XmlFormatting fmt );
ScopedElement( ScopedElement&& other ) noexcept;
ScopedElement& operator=( ScopedElement&& other ) noexcept;
~ScopedElement();
- ScopedElement& writeText( std::string const& text, bool indent = true );
+ ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
template<typename T>
ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
@@ -4983,6 +6268,7 @@ namespace Catch {
private:
mutable XmlWriter* m_writer = nullptr;
+ XmlFormatting m_fmt;
};
XmlWriter( std::ostream& os = Catch::cout() );
@@ -4991,11 +6277,11 @@ namespace Catch {
XmlWriter( XmlWriter const& ) = delete;
XmlWriter& operator=( XmlWriter const& ) = delete;
- XmlWriter& startElement( std::string const& name );
+ XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
- ScopedElement scopedElement( std::string const& name );
+ ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
- XmlWriter& endElement();
+ XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
@@ -5008,9 +6294,9 @@ namespace Catch {
return writeAttribute( name, rss.str() );
}
- XmlWriter& writeText( std::string const& text, bool indent = true );
+ XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
- XmlWriter& writeComment( std::string const& text );
+ XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
void writeStylesheetRef( std::string const& url );
@@ -5020,6 +6306,8 @@ namespace Catch {
private:
+ void applyFormatting(XmlFormatting fmt);
+
void writeDeclaration();
void newlineIfNecessary();
@@ -5063,9 +6351,10 @@ namespace Catch {
void writeTestCase(TestCaseNode const& testCaseNode);
- void writeSection(std::string const& className,
- std::string const& rootName,
- SectionNode const& sectionNode);
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode,
+ bool testOkToFail );
void writeAssertions(SectionNode const& sectionNode);
void writeAssertion(AssertionStats const& stats);
@@ -5120,6 +6409,13 @@ namespace Catch {
void testRunEnded(TestRunStats const& testRunStats) override;
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing(std::string const& name) override;
+ void benchmarkStarting(BenchmarkInfo const&) override;
+ void benchmarkEnded(BenchmarkStats<> const&) override;
+ void benchmarkFailed(std::string const&) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
private:
Timer m_testCaseTimer;
XmlWriter m_xml;
@@ -5133,6 +6429,1032 @@ namespace Catch {
// end catch_external_interfaces.h
#endif
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_benchmarking_all.hpp
+
+// A proxy header that includes all of the benchmarking headers to allow
+// concise include of the benchmarking features. You should prefer the
+// individual includes in standard use.
+
+// start catch_benchmark.hpp
+
+ // Benchmark
+
+// start catch_chronometer.hpp
+
+// User-facing chronometer
+
+
+// start catch_clock.hpp
+
+// Clocks
+
+
+#include <chrono>
+#include <ratio>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Clock>
+ using ClockDuration = typename Clock::duration;
+ template <typename Clock>
+ using FloatDuration = std::chrono::duration<double, typename Clock::period>;
+
+ template <typename Clock>
+ using TimePoint = typename Clock::time_point;
+
+ using default_clock = std::chrono::steady_clock;
+
+ template <typename Clock>
+ struct now {
+ TimePoint<Clock> operator()() const {
+ return Clock::now();
+ }
+ };
+
+ using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_clock.hpp
+// start catch_optimizer.hpp
+
+ // Hinting the optimizer
+
+
+#if defined(_MSC_VER)
+# include <atomic> // atomic_thread_fence
+#endif
+
+namespace Catch {
+ namespace Benchmark {
+#if defined(__GNUC__) || defined(__clang__)
+ template <typename T>
+ inline void keep_memory(T* p) {
+ asm volatile("" : : "g"(p) : "memory");
+ }
+ inline void keep_memory() {
+ asm volatile("" : : : "memory");
+ }
+
+ namespace Detail {
+ inline void optimizer_barrier() { keep_memory(); }
+ } // namespace Detail
+#elif defined(_MSC_VER)
+
+#pragma optimize("", off)
+ template <typename T>
+ inline void keep_memory(T* p) {
+ // thanks @milleniumbug
+ *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
+ }
+ // TODO equivalent keep_memory()
+#pragma optimize("", on)
+
+ namespace Detail {
+ inline void optimizer_barrier() {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ }
+ } // namespace Detail
+
+#endif
+
+ template <typename T>
+ inline void deoptimize_value(T&& x) {
+ keep_memory(&x);
+ }
+
+ template <typename Fn, typename... Args>
+ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
+ deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
+ }
+
+ template <typename Fn, typename... Args>
+ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type {
+ std::forward<Fn>(fn) (std::forward<Args...>(args...));
+ }
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_optimizer.hpp
+// start catch_complete_invoke.hpp
+
+// Invoke with a special case for void
+
+
+#include <type_traits>
+#include <utility>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename T>
+ struct CompleteType { using type = T; };
+ template <>
+ struct CompleteType<void> { struct type {}; };
+
+ template <typename T>
+ using CompleteType_t = typename CompleteType<T>::type;
+
+ template <typename Result>
+ struct CompleteInvoker {
+ template <typename Fun, typename... Args>
+ static Result invoke(Fun&& fun, Args&&... args) {
+ return std::forward<Fun>(fun)(std::forward<Args>(args)...);
+ }
+ };
+ template <>
+ struct CompleteInvoker<void> {
+ template <typename Fun, typename... Args>
+ static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
+ std::forward<Fun>(fun)(std::forward<Args>(args)...);
+ return {};
+ }
+ };
+
+ // invoke and not return void :(
+ template <typename Fun, typename... Args>
+ CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
+ return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
+ }
+
+ const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
+ } // namespace Detail
+
+ template <typename Fun>
+ Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
+ CATCH_TRY{
+ return Detail::complete_invoke(std::forward<Fun>(fun));
+ } CATCH_CATCH_ALL{
+ getResultCapture().benchmarkFailed(translateActiveException());
+ CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg);
+ }
+ }
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_complete_invoke.hpp
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ struct ChronometerConcept {
+ virtual void start() = 0;
+ virtual void finish() = 0;
+ virtual ~ChronometerConcept() = default;
+ };
+ template <typename Clock>
+ struct ChronometerModel final : public ChronometerConcept {
+ void start() override { started = Clock::now(); }
+ void finish() override { finished = Clock::now(); }
+
+ ClockDuration<Clock> elapsed() const { return finished - started; }
+
+ TimePoint<Clock> started;
+ TimePoint<Clock> finished;
+ };
+ } // namespace Detail
+
+ struct Chronometer {
+ public:
+ template <typename Fun>
+ void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
+
+ int runs() const { return k; }
+
+ Chronometer(Detail::ChronometerConcept& meter, int k)
+ : impl(&meter)
+ , k(k) {}
+
+ private:
+ template <typename Fun>
+ void measure(Fun&& fun, std::false_type) {
+ measure([&fun](int) { return fun(); }, std::true_type());
+ }
+
+ template <typename Fun>
+ void measure(Fun&& fun, std::true_type) {
+ Detail::optimizer_barrier();
+ impl->start();
+ for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i);
+ impl->finish();
+ Detail::optimizer_barrier();
+ }
+
+ Detail::ChronometerConcept* impl;
+ int k;
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_chronometer.hpp
+// start catch_environment.hpp
+
+// Environment information
+
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct EnvironmentEstimate {
+ Duration mean;
+ OutlierClassification outliers;
+
+ template <typename Duration2>
+ operator EnvironmentEstimate<Duration2>() const {
+ return { mean, outliers };
+ }
+ };
+ template <typename Clock>
+ struct Environment {
+ using clock_type = Clock;
+ EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
+ EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_environment.hpp
+// start catch_execution_plan.hpp
+
+ // Execution plan
+
+
+// start catch_benchmark_function.hpp
+
+ // Dumb std::function implementation for consistent call overhead
+
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include <memory>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename T>
+ using Decay = typename std::decay<T>::type;
+ template <typename T, typename U>
+ struct is_related
+ : std::is_same<Decay<T>, Decay<U>> {};
+
+ /// We need to reinvent std::function because every piece of code that might add overhead
+ /// in a measurement context needs to have consistent performance characteristics so that we
+ /// can account for it in the measurement.
+ /// Implementations of std::function with optimizations that aren't always applicable, like
+ /// small buffer optimizations, are not uncommon.
+ /// This is effectively an implementation of std::function without any such optimizations;
+ /// it may be slow, but it is consistently slow.
+ struct BenchmarkFunction {
+ private:
+ struct callable {
+ virtual void call(Chronometer meter) const = 0;
+ virtual callable* clone() const = 0;
+ virtual ~callable() = default;
+ };
+ template <typename Fun>
+ struct model : public callable {
+ model(Fun&& fun) : fun(std::move(fun)) {}
+ model(Fun const& fun) : fun(fun) {}
+
+ model<Fun>* clone() const override { return new model<Fun>(*this); }
+
+ void call(Chronometer meter) const override {
+ call(meter, is_callable<Fun(Chronometer)>());
+ }
+ void call(Chronometer meter, std::true_type) const {
+ fun(meter);
+ }
+ void call(Chronometer meter, std::false_type) const {
+ meter.measure(fun);
+ }
+
+ Fun fun;
+ };
+
+ struct do_nothing { void operator()() const {} };
+
+ template <typename T>
+ BenchmarkFunction(model<T>* c) : f(c) {}
+
+ public:
+ BenchmarkFunction()
+ : f(new model<do_nothing>{ {} }) {}
+
+ template <typename Fun,
+ typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
+ BenchmarkFunction(Fun&& fun)
+ : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
+
+ BenchmarkFunction(BenchmarkFunction&& that)
+ : f(std::move(that.f)) {}
+
+ BenchmarkFunction(BenchmarkFunction const& that)
+ : f(that.f->clone()) {}
+
+ BenchmarkFunction& operator=(BenchmarkFunction&& that) {
+ f = std::move(that.f);
+ return *this;
+ }
+
+ BenchmarkFunction& operator=(BenchmarkFunction const& that) {
+ f.reset(that.f->clone());
+ return *this;
+ }
+
+ void operator()(Chronometer meter) const { f->call(meter); }
+
+ private:
+ std::unique_ptr<callable> f;
+ };
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_benchmark_function.hpp
+// start catch_repeat.hpp
+
+// repeat algorithm
+
+
+#include <type_traits>
+#include <utility>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Fun>
+ struct repeater {
+ void operator()(int k) const {
+ for (int i = 0; i < k; ++i) {
+ fun();
+ }
+ }
+ Fun fun;
+ };
+ template <typename Fun>
+ repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
+ return { std::forward<Fun>(fun) };
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_repeat.hpp
+// start catch_run_for_at_least.hpp
+
+// Run a function for a minimum amount of time
+
+
+// start catch_measure.hpp
+
+// Measure
+
+
+// start catch_timing.hpp
+
+// Timing
+
+
+#include <tuple>
+#include <type_traits>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration, typename Result>
+ struct Timing {
+ Duration elapsed;
+ Result result;
+ int iterations;
+ };
+ template <typename Clock, typename Func, typename... Args>
+ using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_timing.hpp
+#include <utility>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock, typename Fun, typename... Args>
+ TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
+ auto start = Clock::now();
+ auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...);
+ auto end = Clock::now();
+ auto delta = end - start;
+ return { delta, std::forward<decltype(r)>(r), 1 };
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_measure.hpp
+#include <utility>
+#include <type_traits>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock, typename Fun>
+ TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
+ return Detail::measure<Clock>(fun, iters);
+ }
+ template <typename Clock, typename Fun>
+ TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
+ Detail::ChronometerModel<Clock> meter;
+ auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
+
+ return { meter.elapsed(), std::move(result), iters };
+ }
+
+ template <typename Clock, typename Fun>
+ using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
+
+ struct optimized_away_error : std::exception {
+ const char* what() const noexcept override {
+ return "could not measure benchmark, maybe it was optimized away";
+ }
+ };
+
+ template <typename Clock, typename Fun>
+ TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) {
+ auto iters = seed;
+ while (iters < (1 << 30)) {
+ auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
+
+ if (Timing.elapsed >= how_long) {
+ return { Timing.elapsed, std::move(Timing.result), iters };
+ }
+ iters *= 2;
+ }
+ Catch::throw_exception(optimized_away_error{});
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_run_for_at_least.hpp
+#include <algorithm>
+#include <iterator>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct ExecutionPlan {
+ int iterations_per_sample;
+ Duration estimated_duration;
+ Detail::BenchmarkFunction benchmark;
+ Duration warmup_time;
+ int warmup_iterations;
+
+ template <typename Duration2>
+ operator ExecutionPlan<Duration2>() const {
+ return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
+ }
+
+ template <typename Clock>
+ std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+ // warmup a bit
+ Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
+
+ std::vector<FloatDuration<Clock>> times;
+ times.reserve(cfg.benchmarkSamples());
+ std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
+ Detail::ChronometerModel<Clock> model;
+ this->benchmark(Chronometer(model, iterations_per_sample));
+ auto sample_time = model.elapsed() - env.clock_cost.mean;
+ if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
+ return sample_time / iterations_per_sample;
+ });
+ return times;
+ }
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_execution_plan.hpp
+// start catch_estimate_clock.hpp
+
+ // Environment measurement
+
+
+// start catch_stats.hpp
+
+// Statistical analysis tools
+
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+#include <iterator>
+#include <numeric>
+#include <tuple>
+#include <cmath>
+#include <utility>
+#include <cstddef>
+#include <random>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ using sample = std::vector<double>;
+
+ double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
+
+ template <typename Iterator>
+ OutlierClassification classify_outliers(Iterator first, Iterator last) {
+ std::vector<double> copy(first, last);
+
+ auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
+ auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
+ auto iqr = q3 - q1;
+ auto los = q1 - (iqr * 3.);
+ auto lom = q1 - (iqr * 1.5);
+ auto him = q3 + (iqr * 1.5);
+ auto his = q3 + (iqr * 3.);
+
+ OutlierClassification o;
+ for (; first != last; ++first) {
+ auto&& t = *first;
+ if (t < los) ++o.low_severe;
+ else if (t < lom) ++o.low_mild;
+ else if (t > his) ++o.high_severe;
+ else if (t > him) ++o.high_mild;
+ ++o.samples_seen;
+ }
+ return o;
+ }
+
+ template <typename Iterator>
+ double mean(Iterator first, Iterator last) {
+ auto count = last - first;
+ double sum = std::accumulate(first, last, 0.);
+ return sum / count;
+ }
+
+ template <typename URng, typename Iterator, typename Estimator>
+ sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) {
+ auto n = last - first;
+ std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
+
+ sample out;
+ out.reserve(resamples);
+ std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
+ std::vector<double> resampled;
+ resampled.reserve(n);
+ std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
+ return estimator(resampled.begin(), resampled.end());
+ });
+ std::sort(out.begin(), out.end());
+ return out;
+ }
+
+ template <typename Estimator, typename Iterator>
+ sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
+ auto n = last - first;
+ auto second = std::next(first);
+ sample results;
+ results.reserve(n);
+
+ for (auto it = first; it != last; ++it) {
+ std::iter_swap(it, first);
+ results.push_back(estimator(second, last));
+ }
+
+ return results;
+ }
+
+ inline double normal_cdf(double x) {
+ return std::erfc(-x / std::sqrt(2.0)) / 2.0;
+ }
+
+ double erfc_inv(double x);
+
+ double normal_quantile(double p);
+
+ template <typename Iterator, typename Estimator>
+ Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
+ auto n_samples = last - first;
+
+ double point = estimator(first, last);
+ // Degenerate case with a single sample
+ if (n_samples == 1) return { point, point, point, confidence_level };
+
+ sample jack = jackknife(estimator, first, last);
+ double jack_mean = mean(jack.begin(), jack.end());
+ double sum_squares, sum_cubes;
+ std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
+ auto d = jack_mean - x;
+ auto d2 = d * d;
+ auto d3 = d2 * d;
+ return { sqcb.first + d2, sqcb.second + d3 };
+ });
+
+ double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
+ int n = static_cast<int>(resample.size());
+ double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n;
+ // degenerate case with uniform samples
+ if (prob_n == 0) return { point, point, point, confidence_level };
+
+ double bias = normal_quantile(prob_n);
+ double z1 = normal_quantile((1. - confidence_level) / 2.);
+
+ auto cumn = [n](double x) -> int {
+ return std::lround(normal_cdf(x) * n); };
+ auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
+ double b1 = bias + z1;
+ double b2 = bias - z1;
+ double a1 = a(b1);
+ double a2 = a(b2);
+ auto lo = (std::max)(cumn(a1), 0);
+ auto hi = (std::min)(cumn(a2), n - 1);
+
+ return { point, resample[lo], resample[hi], confidence_level };
+ }
+
+ double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
+
+ struct bootstrap_analysis {
+ Estimate<double> mean;
+ Estimate<double> standard_deviation;
+ double outlier_variance;
+ };
+
+ bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_stats.hpp
+#include <algorithm>
+#include <iterator>
+#include <tuple>
+#include <vector>
+#include <cmath>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock>
+ std::vector<double> resolution(int k) {
+ std::vector<TimePoint<Clock>> times;
+ times.reserve(k + 1);
+ std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
+
+ std::vector<double> deltas;
+ deltas.reserve(k);
+ std::transform(std::next(times.begin()), times.end(), times.begin(),
+ std::back_inserter(deltas),
+ [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
+
+ return deltas;
+ }
+
+ const auto warmup_iterations = 10000;
+ const auto warmup_time = std::chrono::milliseconds(100);
+ const auto minimum_ticks = 1000;
+ const auto warmup_seed = 10000;
+ const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
+ const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
+ const auto clock_cost_estimation_tick_limit = 100000;
+ const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
+ const auto clock_cost_estimation_iterations = 10000;
+
+ template <typename Clock>
+ int warmup() {
+ return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
+ .iterations;
+ }
+ template <typename Clock>
+ EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
+ auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
+ .result;
+ return {
+ FloatDuration<Clock>(mean(r.begin(), r.end())),
+ classify_outliers(r.begin(), r.end()),
+ };
+ }
+ template <typename Clock>
+ EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
+ auto time_limit = (std::min)(
+ resolution * clock_cost_estimation_tick_limit,
+ FloatDuration<Clock>(clock_cost_estimation_time_limit));
+ auto time_clock = [](int k) {
+ return Detail::measure<Clock>([k] {
+ for (int i = 0; i < k; ++i) {
+ volatile auto ignored = Clock::now();
+ (void)ignored;
+ }
+ }).elapsed;
+ };
+ time_clock(1);
+ int iters = clock_cost_estimation_iterations;
+ auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
+ std::vector<double> times;
+ int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
+ times.reserve(nsamples);
+ std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
+ return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
+ });
+ return {
+ FloatDuration<Clock>(mean(times.begin(), times.end())),
+ classify_outliers(times.begin(), times.end()),
+ };
+ }
+
+ template <typename Clock>
+ Environment<FloatDuration<Clock>> measure_environment() {
+ static Environment<FloatDuration<Clock>>* env = nullptr;
+ if (env) {
+ return *env;
+ }
+
+ auto iters = Detail::warmup<Clock>();
+ auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
+ auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
+
+ env = new Environment<FloatDuration<Clock>>{ resolution, cost };
+ return *env;
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_estimate_clock.hpp
+// start catch_analyse.hpp
+
+ // Run and analyse one benchmark
+
+
+// start catch_sample_analysis.hpp
+
+// Benchmark results
+
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <iterator>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct SampleAnalysis {
+ std::vector<Duration> samples;
+ Estimate<Duration> mean;
+ Estimate<Duration> standard_deviation;
+ OutlierClassification outliers;
+ double outlier_variance;
+
+ template <typename Duration2>
+ operator SampleAnalysis<Duration2>() const {
+ std::vector<Duration2> samples2;
+ samples2.reserve(samples.size());
+ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
+ return {
+ std::move(samples2),
+ mean,
+ standard_deviation,
+ outliers,
+ outlier_variance,
+ };
+ }
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_sample_analysis.hpp
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Duration, typename Iterator>
+ SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
+ if (!cfg.benchmarkNoAnalysis()) {
+ std::vector<double> samples;
+ samples.reserve(last - first);
+ std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
+
+ auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
+ auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
+
+ auto wrap_estimate = [](Estimate<double> e) {
+ return Estimate<Duration> {
+ Duration(e.point),
+ Duration(e.lower_bound),
+ Duration(e.upper_bound),
+ e.confidence_interval,
+ };
+ };
+ std::vector<Duration> samples2;
+ samples2.reserve(samples.size());
+ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
+ return {
+ std::move(samples2),
+ wrap_estimate(analysis.mean),
+ wrap_estimate(analysis.standard_deviation),
+ outliers,
+ analysis.outlier_variance,
+ };
+ } else {
+ std::vector<Duration> samples;
+ samples.reserve(last - first);
+
+ Duration mean = Duration(0);
+ int i = 0;
+ for (auto it = first; it < last; ++it, ++i) {
+ samples.push_back(Duration(*it));
+ mean += Duration(*it);
+ }
+ mean /= i;
+
+ return {
+ std::move(samples),
+ Estimate<Duration>{mean, mean, mean, 0.0},
+ Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
+ OutlierClassification{},
+ 0.0
+ };
+ }
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_analyse.hpp
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+#include <cmath>
+
+namespace Catch {
+ namespace Benchmark {
+ struct Benchmark {
+ Benchmark(std::string &&name)
+ : name(std::move(name)) {}
+
+ template <class FUN>
+ Benchmark(std::string &&name, FUN &&func)
+ : fun(std::move(func)), name(std::move(name)) {}
+
+ template <typename Clock>
+ ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+ auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
+ auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
+ auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
+ int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
+ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
+ }
+
+ template <typename Clock = default_clock>
+ void run() {
+ IConfigPtr cfg = getCurrentContext().getConfig();
+
+ auto env = Detail::measure_environment<Clock>();
+
+ getResultCapture().benchmarkPreparing(name);
+ CATCH_TRY{
+ auto plan = user_code([&] {
+ return prepare<Clock>(*cfg, env);
+ });
+
+ BenchmarkInfo info {
+ name,
+ plan.estimated_duration.count(),
+ plan.iterations_per_sample,
+ cfg->benchmarkSamples(),
+ cfg->benchmarkResamples(),
+ env.clock_resolution.mean.count(),
+ env.clock_cost.mean.count()
+ };
+
+ getResultCapture().benchmarkStarting(info);
+
+ auto samples = user_code([&] {
+ return plan.template run<Clock>(*cfg, env);
+ });
+
+ auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
+ BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
+ getResultCapture().benchmarkEnded(stats);
+
+ } CATCH_CATCH_ALL{
+ if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
+ std::rethrow_exception(std::current_exception());
+ }
+ }
+
+ // sets lambda to be used in fun *and* executes benchmark!
+ template <typename Fun,
+ typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0>
+ Benchmark & operator=(Fun func) {
+ fun = Detail::BenchmarkFunction(func);
+ run();
+ return *this;
+ }
+
+ explicit operator bool() {
+ return true;
+ }
+
+ private:
+ Detail::BenchmarkFunction fun;
+ std::string name;
+ };
+ }
+} // namespace Catch
+
+#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
+#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
+
+#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
+ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+ BenchmarkName = [&](int benchmarkIndex)
+
+#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
+ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+ BenchmarkName = [&]
+
+// end catch_benchmark.hpp
+// start catch_constructor.hpp
+
+// Constructor and destructor helpers
+
+
+#include <type_traits>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename T, bool Destruct>
+ struct ObjectStorage
+ {
+ using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
+
+ ObjectStorage() : data() {}
+
+ ObjectStorage(const ObjectStorage& other)
+ {
+ new(&data) T(other.stored_object());
+ }
+
+ ObjectStorage(ObjectStorage&& other)
+ {
+ new(&data) T(std::move(other.stored_object()));
+ }
+
+ ~ObjectStorage() { destruct_on_exit<T>(); }
+
+ template <typename... Args>
+ void construct(Args&&... args)
+ {
+ new (&data) T(std::forward<Args>(args)...);
+ }
+
+ template <bool AllowManualDestruction = !Destruct>
+ typename std::enable_if<AllowManualDestruction>::type destruct()
+ {
+ stored_object().~T();
+ }
+
+ private:
+ // If this is a constructor benchmark, destruct the underlying object
+ template <typename U>
+ void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
+ // Otherwise, don't
+ template <typename U>
+ void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
+
+ T& stored_object() {
+ return *static_cast<T*>(static_cast<void*>(&data));
+ }
+
+ T const& stored_object() const {
+ return *static_cast<T*>(static_cast<void*>(&data));
+ }
+
+ TStorage data;
+ };
+ }
+
+ template <typename T>
+ using storage_for = Detail::ObjectStorage<T, true>;
+
+ template <typename T>
+ using destructable_object = Detail::ObjectStorage<T, false>;
+ }
+}
+
+// end catch_constructor.hpp
+// end catch_benchmarking_all.hpp
+#endif
+
#endif // ! CATCH_CONFIG_IMPL_ONLY
#ifdef CATCH_IMPL
@@ -5158,23 +7480,37 @@ namespace TestCaseTracking {
SourceLineInfo location;
NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
+ friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
+ return lhs.name == rhs.name
+ && lhs.location == rhs.location;
+ }
};
- struct ITracker;
+ class ITracker;
using ITrackerPtr = std::shared_ptr<ITracker>;
- struct ITracker {
- virtual ~ITracker();
+ class ITracker {
+ NameAndLocation m_nameAndLocation;
+
+ public:
+ ITracker(NameAndLocation const& nameAndLoc) :
+ m_nameAndLocation(nameAndLoc)
+ {}
// static queries
- virtual NameAndLocation const& nameAndLocation() const = 0;
+ NameAndLocation const& nameAndLocation() const {
+ return m_nameAndLocation;
+ }
+
+ virtual ~ITracker();
// dynamic queries
virtual bool isComplete() const = 0; // Successfully completed or failed
virtual bool isSuccessfullyCompleted() const = 0;
virtual bool isOpen() const = 0; // Started but not complete
virtual bool hasChildren() const = 0;
+ virtual bool hasStarted() const = 0;
virtual ITracker& parent() = 0;
@@ -5189,7 +7525,7 @@ namespace TestCaseTracking {
// Debug/ checking
virtual bool isSectionTracker() const = 0;
- virtual bool isIndexTracker() const = 0;
+ virtual bool isGeneratorTracker() const = 0;
};
class TrackerContext {
@@ -5206,8 +7542,6 @@ namespace TestCaseTracking {
public:
- static TrackerContext& instance();
-
ITracker& startRun();
void endRun();
@@ -5231,7 +7565,6 @@ namespace TestCaseTracking {
};
using Children = std::vector<ITrackerPtr>;
- NameAndLocation m_nameAndLocation;
TrackerContext& m_ctx;
ITracker* m_parent;
Children m_children;
@@ -5240,11 +7573,13 @@ namespace TestCaseTracking {
public:
TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
- NameAndLocation const& nameAndLocation() const override;
bool isComplete() const override;
bool isSuccessfullyCompleted() const override;
bool isOpen() const override;
bool hasChildren() const override;
+ bool hasStarted() const override {
+ return m_runState != NotStarted;
+ }
void addChild( ITrackerPtr const& child ) override;
@@ -5254,7 +7589,7 @@ namespace TestCaseTracking {
void openChild() override;
bool isSectionTracker() const override;
- bool isIndexTracker() const override;
+ bool isGeneratorTracker() const override;
void open();
@@ -5269,33 +7604,24 @@ namespace TestCaseTracking {
class SectionTracker : public TrackerBase {
std::vector<std::string> m_filters;
+ std::string m_trimmed_name;
public:
SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isSectionTracker() const override;
+ bool isComplete() const override;
+
static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
void tryOpen();
void addInitialFilters( std::vector<std::string> const& filters );
void addNextFilters( std::vector<std::string> const& filters );
- };
-
- class IndexTracker : public TrackerBase {
- int m_size;
- int m_index = -1;
- public:
- IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size );
-
- bool isIndexTracker() const override;
- void close() override;
-
- static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size );
-
- int index() const;
-
- void moveNext();
+ //! Returns filters active in this tracker
+ std::vector<std::string> const& getFilters() const;
+ //! Returns whitespace-trimmed name of the tracked section
+ std::string const& trimmedName() const;
};
} // namespace TestCaseTracking
@@ -5303,7 +7629,6 @@ namespace TestCaseTracking {
using TestCaseTracking::ITracker;
using TestCaseTracking::TrackerContext;
using TestCaseTracking::SectionTracker;
-using TestCaseTracking::IndexTracker;
} // namespace Catch
@@ -5321,6 +7646,217 @@ namespace Catch {
}
// end catch_leak_detector.h
// Cpp files will be included in the single-header file here
+// start catch_stats.cpp
+
+// Statistical analysis tools
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+
+#include <cassert>
+#include <random>
+
+#if defined(CATCH_CONFIG_USE_ASYNC)
+#include <future>
+#endif
+
+namespace {
+ double erf_inv(double x) {
+ // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
+ double w, p;
+
+ w = -log((1.0 - x) * (1.0 + x));
+
+ if (w < 6.250000) {
+ w = w - 3.125000;
+ p = -3.6444120640178196996e-21;
+ p = -1.685059138182016589e-19 + p * w;
+ p = 1.2858480715256400167e-18 + p * w;
+ p = 1.115787767802518096e-17 + p * w;
+ p = -1.333171662854620906e-16 + p * w;
+ p = 2.0972767875968561637e-17 + p * w;
+ p = 6.6376381343583238325e-15 + p * w;
+ p = -4.0545662729752068639e-14 + p * w;
+ p = -8.1519341976054721522e-14 + p * w;
+ p = 2.6335093153082322977e-12 + p * w;
+ p = -1.2975133253453532498e-11 + p * w;
+ p = -5.4154120542946279317e-11 + p * w;
+ p = 1.051212273321532285e-09 + p * w;
+ p = -4.1126339803469836976e-09 + p * w;
+ p = -2.9070369957882005086e-08 + p * w;
+ p = 4.2347877827932403518e-07 + p * w;
+ p = -1.3654692000834678645e-06 + p * w;
+ p = -1.3882523362786468719e-05 + p * w;
+ p = 0.0001867342080340571352 + p * w;
+ p = -0.00074070253416626697512 + p * w;
+ p = -0.0060336708714301490533 + p * w;
+ p = 0.24015818242558961693 + p * w;
+ p = 1.6536545626831027356 + p * w;
+ } else if (w < 16.000000) {
+ w = sqrt(w) - 3.250000;
+ p = 2.2137376921775787049e-09;
+ p = 9.0756561938885390979e-08 + p * w;
+ p = -2.7517406297064545428e-07 + p * w;
+ p = 1.8239629214389227755e-08 + p * w;
+ p = 1.5027403968909827627e-06 + p * w;
+ p = -4.013867526981545969e-06 + p * w;
+ p = 2.9234449089955446044e-06 + p * w;
+ p = 1.2475304481671778723e-05 + p * w;
+ p = -4.7318229009055733981e-05 + p * w;
+ p = 6.8284851459573175448e-05 + p * w;
+ p = 2.4031110387097893999e-05 + p * w;
+ p = -0.0003550375203628474796 + p * w;
+ p = 0.00095328937973738049703 + p * w;
+ p = -0.0016882755560235047313 + p * w;
+ p = 0.0024914420961078508066 + p * w;
+ p = -0.0037512085075692412107 + p * w;
+ p = 0.005370914553590063617 + p * w;
+ p = 1.0052589676941592334 + p * w;
+ p = 3.0838856104922207635 + p * w;
+ } else {
+ w = sqrt(w) - 5.000000;
+ p = -2.7109920616438573243e-11;
+ p = -2.5556418169965252055e-10 + p * w;
+ p = 1.5076572693500548083e-09 + p * w;
+ p = -3.7894654401267369937e-09 + p * w;
+ p = 7.6157012080783393804e-09 + p * w;
+ p = -1.4960026627149240478e-08 + p * w;
+ p = 2.9147953450901080826e-08 + p * w;
+ p = -6.7711997758452339498e-08 + p * w;
+ p = 2.2900482228026654717e-07 + p * w;
+ p = -9.9298272942317002539e-07 + p * w;
+ p = 4.5260625972231537039e-06 + p * w;
+ p = -1.9681778105531670567e-05 + p * w;
+ p = 7.5995277030017761139e-05 + p * w;
+ p = -0.00021503011930044477347 + p * w;
+ p = -0.00013871931833623122026 + p * w;
+ p = 1.0103004648645343977 + p * w;
+ p = 4.8499064014085844221 + p * w;
+ }
+ return p * x;
+ }
+
+ double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ auto m = Catch::Benchmark::Detail::mean(first, last);
+ double variance = std::accumulate(first, last, 0., [m](double a, double b) {
+ double diff = b - m;
+ return a + diff * diff;
+ }) / (last - first);
+ return std::sqrt(variance);
+ }
+
+}
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+
+ double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ auto count = last - first;
+ double idx = (count - 1) * k / static_cast<double>(q);
+ int j = static_cast<int>(idx);
+ double g = idx - j;
+ std::nth_element(first, first + j, last);
+ auto xj = first[j];
+ if (g == 0) return xj;
+
+ auto xj1 = *std::min_element(first + (j + 1), last);
+ return xj + g * (xj1 - xj);
+ }
+
+ double erfc_inv(double x) {
+ return erf_inv(1.0 - x);
+ }
+
+ double normal_quantile(double p) {
+ static const double ROOT_TWO = std::sqrt(2.0);
+
+ double result = 0.0;
+ assert(p >= 0 && p <= 1);
+ if (p < 0 || p > 1) {
+ return result;
+ }
+
+ result = -erfc_inv(2.0 * p);
+ // result *= normal distribution standard deviation (1.0) * sqrt(2)
+ result *= /*sd * */ ROOT_TWO;
+ // result += normal disttribution mean (0)
+ return result;
+ }
+
+ double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
+ double sb = stddev.point;
+ double mn = mean.point / n;
+ double mg_min = mn / 2.;
+ double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
+ double sg2 = sg * sg;
+ double sb2 = sb * sb;
+
+ auto c_max = [n, mn, sb2, sg2](double x) -> double {
+ double k = mn - x;
+ double d = k * k;
+ double nd = n * d;
+ double k0 = -n * nd;
+ double k1 = sb2 - n * sg2 + nd;
+ double det = k1 * k1 - 4 * sg2 * k0;
+ return (int)(-2. * k0 / (k1 + std::sqrt(det)));
+ };
+
+ auto var_out = [n, sb2, sg2](double c) {
+ double nc = n - c;
+ return (nc / n) * (sb2 - nc * sg2);
+ };
+
+ return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
+ }
+
+ bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+ static std::random_device entropy;
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
+
+ auto mean = &Detail::mean<std::vector<double>::iterator>;
+ auto stddev = &standard_deviation;
+
+#if defined(CATCH_CONFIG_USE_ASYNC)
+ auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
+ auto seed = entropy();
+ return std::async(std::launch::async, [=] {
+ std::mt19937 rng(seed);
+ auto resampled = resample(rng, n_resamples, first, last, f);
+ return bootstrap(confidence_level, first, last, resampled, f);
+ });
+ };
+
+ auto mean_future = Estimate(mean);
+ auto stddev_future = Estimate(stddev);
+
+ auto mean_estimate = mean_future.get();
+ auto stddev_estimate = stddev_future.get();
+#else
+ auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
+ auto seed = entropy();
+ std::mt19937 rng(seed);
+ auto resampled = resample(rng, n_resamples, first, last, f);
+ return bootstrap(confidence_level, first, last, resampled, f);
+ };
+
+ auto mean_estimate = Estimate(mean);
+ auto stddev_estimate = Estimate(stddev);
+#endif // CATCH_USE_ASYNC
+
+ double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
+
+ return { mean_estimate, stddev_estimate, outlier_variance };
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+// end catch_stats.cpp
// start catch_approx.cpp
#include <cmath>
@@ -5365,21 +7901,22 @@ namespace Detail {
bool Approx::equalityComparisonImpl(const double other) const {
// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
// Thanks to Richard Harris for his help refining the scaled margin value
- return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
+ return marginComparison(m_value, other, m_margin)
+ || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
}
- void Approx::setMargin(double margin) {
- CATCH_ENFORCE(margin >= 0,
- "Invalid Approx::margin: " << margin << '.'
+ void Approx::setMargin(double newMargin) {
+ CATCH_ENFORCE(newMargin >= 0,
+ "Invalid Approx::margin: " << newMargin << '.'
<< " Approx::Margin has to be non-negative.");
- m_margin = margin;
+ m_margin = newMargin;
}
- void Approx::setEpsilon(double epsilon) {
- CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0,
- "Invalid Approx::epsilon: " << epsilon << '.'
+ void Approx::setEpsilon(double newEpsilon) {
+ CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
+ "Invalid Approx::epsilon: " << newEpsilon << '.'
<< " Approx::epsilon has to be in [0, 1]");
- m_epsilon = epsilon;
+ m_epsilon = newEpsilon;
}
} // end namespace Detail
@@ -5401,58 +7938,6 @@ std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx co
// end catch_approx.cpp
// start catch_assertionhandler.cpp
-// start catch_context.h
-
-#include <memory>
-
-namespace Catch {
-
- struct IResultCapture;
- struct IRunner;
- struct IConfig;
- struct IMutableContext;
-
- using IConfigPtr = std::shared_ptr<IConfig const>;
-
- struct IContext
- {
- virtual ~IContext();
-
- virtual IResultCapture* getResultCapture() = 0;
- virtual IRunner* getRunner() = 0;
- virtual IConfigPtr const& getConfig() const = 0;
- };
-
- struct IMutableContext : IContext
- {
- virtual ~IMutableContext();
- virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
- virtual void setRunner( IRunner* runner ) = 0;
- virtual void setConfig( IConfigPtr const& config ) = 0;
-
- private:
- static IMutableContext *currentContext;
- friend IMutableContext& getCurrentMutableContext();
- friend void cleanUpContext();
- static void createContext();
- };
-
- inline IMutableContext& getCurrentMutableContext()
- {
- if( !IMutableContext::currentContext )
- IMutableContext::createContext();
- return *IMutableContext::currentContext;
- }
-
- inline IContext& getCurrentContext()
- {
- return getCurrentMutableContext();
- }
-
- void cleanUpContext();
-}
-
-// end catch_context.h
// start catch_debugger.h
namespace Catch {
@@ -5461,7 +7946,24 @@ namespace Catch {
#ifdef CATCH_PLATFORM_MAC
- #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
+ #if defined(__i386__) || defined(__x86_64__)
+ #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
+ #elif defined(__aarch64__)
+ #define CATCH_TRAP() __asm__(".inst 0xd4200000")
+ #endif
+
+#elif defined(CATCH_PLATFORM_IPHONE)
+
+ // use inline assembler
+ #if defined(__i386__) || defined(__x86_64__)
+ #define CATCH_TRAP() __asm__("int $3")
+ #elif defined(__aarch64__)
+ #define CATCH_TRAP() __asm__(".inst 0xd4200000")
+ #elif defined(__arm__) && !defined(__thumb__)
+ #define CATCH_TRAP() __asm__(".inst 0xe7f001f0")
+ #elif defined(__arm__) && defined(__thumb__)
+ #define CATCH_TRAP() __asm__(".inst 0xde01")
+ #endif
#elif defined(CATCH_PLATFORM_LINUX)
// If we can use inline assembler, do it because this allows us to break
@@ -5481,13 +7983,12 @@ namespace Catch {
#define CATCH_TRAP() DebugBreak()
#endif
-#ifdef CATCH_TRAP
- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
-#else
- namespace Catch {
- inline void doNothing() {}
- }
- #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing()
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+ #ifdef CATCH_TRAP
+ #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
+ #else
+ #define CATCH_BREAK_INTO_DEBUGGER() []{}()
+ #endif
#endif
// end catch_debugger.h
@@ -5495,86 +7996,58 @@ namespace Catch {
// start catch_fatal_condition.h
-// start catch_windows_h_proxy.h
-
-
-#if defined(CATCH_PLATFORM_WINDOWS)
-
-#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
-# define CATCH_DEFINED_NOMINMAX
-# define NOMINMAX
-#endif
-#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
-# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-#endif
-
-#ifdef __AFXDLL
-#include <AfxWin.h>
-#else
-#include <windows.h>
-#endif
-
-#ifdef CATCH_DEFINED_NOMINMAX
-# undef NOMINMAX
-#endif
-#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
-# undef WIN32_LEAN_AND_MEAN
-#endif
-
-#endif // defined(CATCH_PLATFORM_WINDOWS)
-
-// end catch_windows_h_proxy.h
-#if defined( CATCH_CONFIG_WINDOWS_SEH )
+#include <cassert>
namespace Catch {
- struct FatalConditionHandler {
-
- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
+ // Wrapper for platform-specific fatal error (signals/SEH) handlers
+ //
+ // Tries to be cooperative with other handlers, and not step over
+ // other handlers. This means that unknown structured exceptions
+ // are passed on, previous signal handlers are called, and so on.
+ //
+ // Can only be instantiated once, and assumes that once a signal
+ // is caught, the binary will end up terminating. Thus, there
+ class FatalConditionHandler {
+ bool m_started = false;
+
+ // Install/disengage implementation for specific platform.
+ // Should be if-defed to work on current platform, can assume
+ // engage-disengage 1:1 pairing.
+ void engage_platform();
+ void disengage_platform();
+ public:
+ // Should also have platform-specific implementations as needed
FatalConditionHandler();
- static void reset();
~FatalConditionHandler();
- private:
- static bool isSet;
- static ULONG guaranteeSize;
- static PVOID exceptionHandlerHandle;
- };
-
-} // namespace Catch
-
-#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
-
-#include <signal.h>
-
-namespace Catch {
-
- struct FatalConditionHandler {
-
- static bool isSet;
- static struct sigaction oldSigActions[];
- static stack_t oldSigStack;
- static char altStackMem[];
-
- static void handleSignal( int sig );
+ void engage() {
+ assert(!m_started && "Handler cannot be installed twice.");
+ m_started = true;
+ engage_platform();
+ }
- FatalConditionHandler();
- ~FatalConditionHandler();
- static void reset();
+ void disengage() {
+ assert(m_started && "Handler cannot be uninstalled without being installed first");
+ m_started = false;
+ disengage_platform();
+ }
};
-} // namespace Catch
-
-#else
-
-namespace Catch {
- struct FatalConditionHandler {
- void reset();
+ //! Simple RAII guard for (dis)engaging the FatalConditionHandler
+ class FatalConditionHandlerGuard {
+ FatalConditionHandler* m_handler;
+ public:
+ FatalConditionHandlerGuard(FatalConditionHandler* handler):
+ m_handler(handler) {
+ m_handler->engage();
+ }
+ ~FatalConditionHandlerGuard() {
+ m_handler->disengage();
+ }
};
-}
-#endif
+} // end namespace Catch
// end catch_fatal_condition.h
#include <string>
@@ -5634,14 +8107,20 @@ namespace Catch {
void sectionEnded( SectionEndInfo const& endInfo ) override;
void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing( std::string const& name ) override;
void benchmarkStarting( BenchmarkInfo const& info ) override;
- void benchmarkEnded( BenchmarkStats const& stats ) override;
+ void benchmarkEnded( BenchmarkStats<> const& stats ) override;
+ void benchmarkFailed( std::string const& error ) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
+ void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
+
std::string getCurrentTestName() const override;
const AssertionResult* getLastResult() const override;
@@ -5682,22 +8161,26 @@ namespace Catch {
TestRunInfo m_runInfo;
IMutableContext& m_context;
TestCase const* m_activeTestCase = nullptr;
- ITracker* m_testCaseTracker;
+ ITracker* m_testCaseTracker = nullptr;
Option<AssertionResult> m_lastResult;
IConfigPtr m_config;
Totals m_totals;
IStreamingReporterPtr m_reporter;
std::vector<MessageInfo> m_messages;
+ std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
AssertionInfo m_lastAssertionInfo;
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;
+ FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults;
};
+ void seedRng(IConfig const& config);
+ unsigned int rngSeed();
} // end namespace Catch
// end catch_run_context.h
@@ -5844,7 +8327,7 @@ namespace Catch {
}
bool AssertionResult::hasExpression() const {
- return m_info.capturedExpression[0] != 0;
+ return !m_info.capturedExpression.empty();
}
bool AssertionResult::hasMessage() const {
@@ -5852,16 +8335,22 @@ namespace Catch {
}
std::string AssertionResult::getExpression() const {
- if( isFalseTest( m_info.resultDisposition ) )
- return "!(" + m_info.capturedExpression + ")";
- else
- return m_info.capturedExpression;
+ // Possibly overallocating by 3 characters should be basically free
+ std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
+ if (isFalseTest(m_info.resultDisposition)) {
+ expr += "!(";
+ }
+ expr += m_info.capturedExpression;
+ if (isFalseTest(m_info.resultDisposition)) {
+ expr += ')';
+ }
+ return expr;
}
std::string AssertionResult::getExpressionInMacro() const {
std::string expr;
- if( m_info.macroName[0] == 0 )
- expr = m_info.capturedExpression;
+ if( m_info.macroName.empty() )
+ expr = static_cast<std::string>(m_info.capturedExpression);
else {
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
expr += m_info.macroName;
@@ -5896,32 +8385,6 @@ namespace Catch {
} // end namespace Catch
// end catch_assertionresult.cpp
-// start catch_benchmark.cpp
-
-namespace Catch {
-
- auto BenchmarkLooper::getResolution() -> uint64_t {
- return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple();
- }
-
- void BenchmarkLooper::reportStart() {
- getResultCapture().benchmarkStarting( { m_name } );
- }
- auto BenchmarkLooper::needsMoreIterations() -> bool {
- auto elapsed = m_timer.getElapsedNanoseconds();
-
- // Exponentially increasing iterations until we're confident in our timer resolution
- if( elapsed < m_resolution ) {
- m_iterationsToRun *= 10;
- return true;
- }
-
- getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } );
- return false;
- }
-
-} // end namespace Catch
-// end catch_benchmark.cpp
// start catch_capture_matchers.cpp
namespace Catch {
@@ -6067,6 +8530,9 @@ public:
m_suffix = false;
auto width = m_column.m_width - indent();
m_end = m_pos;
+ if (line()[m_pos] == '\n') {
+ ++m_end;
+ }
while (m_end < line().size() && line()[m_end] != '\n')
++m_end;
@@ -6325,6 +8791,7 @@ inline auto Column::operator + (Column const& other) -> Columns {
// ----------- end of #include from clara_textflow.hpp -----------
// ........... back in clara.hpp
+#include <cctype>
#include <string>
#include <memory>
#include <set>
@@ -6617,7 +9084,7 @@ namespace detail {
}
inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
std::string srcLC = source;
- std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } );
+ std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower(c) ); } );
if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
target = true;
else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
@@ -7265,9 +9732,14 @@ namespace Catch {
if( !line.empty() && !startsWith( line, '#' ) ) {
if( !startsWith( line, '"' ) )
line = '"' + line + '"';
- config.testsOrTags.push_back( line + ',' );
+ config.testsOrTags.push_back( line );
+ config.testsOrTags.emplace_back( "," );
}
}
+ //Remove comma in the end
+ if(!config.testsOrTags.empty())
+ config.testsOrTags.erase( config.testsOrTags.end()-1 );
+
return ParserResult::ok( ParseResultType::Matched );
};
auto const setTestOrder = [&]( std::string const& order ) {
@@ -7302,14 +9774,16 @@ namespace Catch {
};
auto const setWaitForKeypress = [&]( std::string const& keypress ) {
auto keypressLc = toLower( keypress );
- if( keypressLc == "start" )
+ if (keypressLc == "never")
+ config.waitForKeypress = WaitForKeypress::Never;
+ else if( keypressLc == "start" )
config.waitForKeypress = WaitForKeypress::BeforeStart;
else if( keypressLc == "exit" )
config.waitForKeypress = WaitForKeypress::BeforeExit;
else if( keypressLc == "both" )
config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
else
- return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" );
+ return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
return ParserResult::ok( ParseResultType::Matched );
};
auto const setVerbosity = [&]( std::string const& verbosity ) {
@@ -7379,6 +9853,9 @@ namespace Catch {
| Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
["-d"]["--durations"]
( "show test durations" )
+ | Opt( config.minDuration, "seconds" )
+ ["-D"]["--min-duration"]
+ ( "show test durations for tests taking at least the given number of seconds" )
| Opt( loadTestNamesFromFile, "filename" )
["-f"]["--input-file"]
( "load test names to run from a file" )
@@ -7409,13 +9886,24 @@ namespace Catch {
| Opt( config.libIdentify )
["--libidentify"]
( "report name and version according to libidentify standard" )
- | Opt( setWaitForKeypress, "start|exit|both" )
+ | Opt( setWaitForKeypress, "never|start|exit|both" )
["--wait-for-keypress"]
( "waits for a keypress before exiting" )
- | Opt( config.benchmarkResolutionMultiple, "multiplier" )
- ["--benchmark-resolution-multiple"]
- ( "multiple of clock resolution to run benchmarks" )
-
+ | Opt( config.benchmarkSamples, "samples" )
+ ["--benchmark-samples"]
+ ( "number of samples to collect (default: 100)" )
+ | Opt( config.benchmarkResamples, "resamples" )
+ ["--benchmark-resamples"]
+ ( "number of resamples for the bootstrap (default: 100000)" )
+ | Opt( config.benchmarkConfidenceInterval, "confidence interval" )
+ ["--benchmark-confidence-interval"]
+ ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
+ | Opt( config.benchmarkNoAnalysis )
+ ["--benchmark-no-analysis"]
+ ( "perform only measurements; do not perform any analysis" )
+ | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
+ ["--benchmark-warmup-time"]
+ ( "amount of time in milliseconds spent on warming up each test (default: 100)" )
| Arg( config.testsOrTags, "test name|pattern|tags" )
( "which test or tests to use" );
@@ -7431,9 +9919,6 @@ namespace Catch {
namespace Catch {
- bool SourceLineInfo::empty() const noexcept {
- return file[0] == '\0';
- }
bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
}
@@ -7469,14 +9954,23 @@ namespace Catch {
: m_data( data ),
m_stream( openStream() )
{
- TestSpecParser parser(ITagAliasRegistry::get());
- if (data.testsOrTags.empty()) {
- parser.parse("~[.]"); // All not hidden tests
+ // We need to trim filter specs to avoid trouble with superfluous
+ // whitespace (esp. important for bdd macros, as those are manually
+ // aligned with whitespace).
+
+ for (auto& elem : m_data.testsOrTags) {
+ elem = trim(elem);
}
- else {
+ for (auto& elem : m_data.sectionsToRun) {
+ elem = trim(elem);
+ }
+
+ TestSpecParser parser(ITagAliasRegistry::get());
+ if (!m_data.testsOrTags.empty()) {
m_hasTestFilters = true;
- for( auto const& testOrTags : data.testsOrTags )
- parser.parse( testOrTags );
+ for (auto const& testOrTags : m_data.testsOrTags) {
+ parser.parse(testOrTags);
+ }
}
m_testSpec = parser.testSpec();
}
@@ -7509,15 +10003,21 @@ namespace Catch {
bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); }
bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); }
ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
+ double Config::minDuration() const { return m_data.minDuration; }
RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; }
unsigned int Config::rngSeed() const { return m_data.rngSeed; }
- int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; }
UseColour::YesOrNo Config::useColour() const { return m_data.useColour; }
bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
int Config::abortAfter() const { return m_data.abortAfter; }
bool Config::showInvisibles() const { return m_data.showInvisibles; }
Verbosity Config::verbosity() const { return m_data.verbosity; }
+ bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
+ int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
+ double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
+ unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
+ std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
+
IStream const* Config::openStream() {
return Catch::makeStream(m_data.outputFilename);
}
@@ -7546,6 +10046,36 @@ namespace Catch {
}
// end catch_errno_guard.h
+// start catch_windows_h_proxy.h
+
+
+#if defined(CATCH_PLATFORM_WINDOWS)
+
+#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+# define CATCH_DEFINED_NOMINMAX
+# define NOMINMAX
+#endif
+#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINED_NOMINMAX
+# undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+#endif // defined(CATCH_PLATFORM_WINDOWS)
+
+// end catch_windows_h_proxy.h
#include <sstream>
namespace Catch {
@@ -7557,7 +10087,7 @@ namespace Catch {
};
struct NoColourImpl : IColourImpl {
- void use( Colour::Code ) {}
+ void use( Colour::Code ) override {}
static IColourImpl* instance() {
static NoColourImpl s_instance;
@@ -7591,7 +10121,7 @@ namespace {
originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
}
- virtual void use( Colour::Code _colourCode ) override {
+ void use( Colour::Code _colourCode ) override {
switch( _colourCode ) {
case Colour::None: return setTextAttribute( originalForegroundAttributes );
case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
@@ -7654,7 +10184,7 @@ namespace {
// https://github.com/philsquared/Catch/pull/131
class PosixColourImpl : public IColourImpl {
public:
- virtual void use( Colour::Code _colourCode ) override {
+ void use( Colour::Code _colourCode ) override {
switch( _colourCode ) {
case Colour::None:
case Colour::White: return setColour( "[0m" );
@@ -7682,13 +10212,14 @@ namespace {
private:
void setColour( const char* _escapeCode ) {
- Catch::cout() << '\033' << _escapeCode;
+ getCurrentContext().getConfig()->stream()
+ << '\033' << _escapeCode;
}
};
bool useColourOnPlatform() {
return
-#ifdef CATCH_PLATFORM_MAC
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
!isDebuggerActive() &&
#endif
#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
@@ -7729,13 +10260,13 @@ namespace Catch {
namespace Catch {
Colour::Colour( Code _colourCode ) { use( _colourCode ); }
- Colour::Colour( Colour&& rhs ) noexcept {
- m_moved = rhs.m_moved;
- rhs.m_moved = true;
+ Colour::Colour( Colour&& other ) noexcept {
+ m_moved = other.m_moved;
+ other.m_moved = true;
}
- Colour& Colour::operator=( Colour&& rhs ) noexcept {
- m_moved = rhs.m_moved;
- rhs.m_moved = true;
+ Colour& Colour::operator=( Colour&& other ) noexcept {
+ m_moved = other.m_moved;
+ other.m_moved = true;
return *this;
}
@@ -7743,7 +10274,13 @@ namespace Catch {
void Colour::use( Code _colourCode ) {
static IColourImpl* impl = platformColourInstance();
- impl->use( _colourCode );
+ // Strictly speaking, this cannot possibly happen.
+ // However, under some conditions it does happen (see #1626),
+ // and this change is small enough that we can let practicality
+ // triumph over purity in this case.
+ if (impl != nullptr) {
+ impl->use( _colourCode );
+ }
}
std::ostream& operator << ( std::ostream& os, Colour const& ) {
@@ -7764,27 +10301,27 @@ namespace Catch {
class Context : public IMutableContext, NonCopyable {
public: // IContext
- virtual IResultCapture* getResultCapture() override {
+ IResultCapture* getResultCapture() override {
return m_resultCapture;
}
- virtual IRunner* getRunner() override {
+ IRunner* getRunner() override {
return m_runner;
}
- virtual IConfigPtr const& getConfig() const override {
+ IConfigPtr const& getConfig() const override {
return m_config;
}
- virtual ~Context() override;
+ ~Context() override;
public: // IMutableContext
- virtual void setResultCapture( IResultCapture* resultCapture ) override {
+ void setResultCapture( IResultCapture* resultCapture ) override {
m_resultCapture = resultCapture;
}
- virtual void setRunner( IRunner* runner ) override {
+ void setRunner( IRunner* runner ) override {
m_runner = runner;
}
- virtual void setConfig( IConfigPtr const& config ) override {
+ void setConfig( IConfigPtr const& config ) override {
m_config = config;
}
@@ -7810,6 +10347,12 @@ namespace Catch {
IContext::~IContext() = default;
IMutableContext::~IMutableContext() = default;
Context::~Context() = default;
+
+ SimplePcg32& rng() {
+ static SimplePcg32 s_rng;
+ return s_rng;
+ }
+
}
// end catch_context.cpp
// start catch_debug_console.cpp
@@ -7823,7 +10366,16 @@ namespace Catch {
}
// end catch_debug_console.h
-#ifdef CATCH_PLATFORM_WINDOWS
+#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+#include <android/log.h>
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
+ }
+ }
+
+#elif defined(CATCH_PLATFORM_WINDOWS)
namespace Catch {
void writeToDebugConsole( std::string const& text ) {
@@ -7844,25 +10396,28 @@ namespace Catch {
// end catch_debug_console.cpp
// start catch_debugger.cpp
-#ifdef CATCH_PLATFORM_MAC
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
-# include <assert.h>
-# include <stdbool.h>
+# include <cassert>
# include <sys/types.h>
# include <unistd.h>
-# include <sys/sysctl.h>
# include <cstddef>
# include <ostream>
-namespace Catch {
+#ifdef __apple_build_version__
+ // These headers will only compile with AppleClang (XCode)
+ // For other compilers (Clang, GCC, ... ) we need to exclude them
+# include <sys/sysctl.h>
+#endif
+ namespace Catch {
+ #ifdef __apple_build_version__
// The following function is taken directly from the following technical note:
- // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+ // https://developer.apple.com/library/archive/qa/qa1361/_index.html
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
bool isDebuggerActive(){
-
int mib[4];
struct kinfo_proc info;
std::size_t size;
@@ -7892,6 +10447,12 @@ namespace Catch {
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
}
+ #else
+ bool isDebuggerActive() {
+ // We need to find another way to determine this for non-appleclang compilers on macOS
+ return false;
+ }
+ #endif
} // namespace Catch
#elif defined(CATCH_PLATFORM_LINUX)
@@ -7962,6 +10523,8 @@ namespace Catch {
// end catch_decomposer.cpp
// start catch_enforce.cpp
+#include <stdexcept>
+
namespace Catch {
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
[[noreturn]]
@@ -7971,8 +10534,116 @@ namespace Catch {
std::terminate();
}
#endif
+
+ [[noreturn]]
+ void throw_logic_error(std::string const& msg) {
+ throw_exception(std::logic_error(msg));
+ }
+
+ [[noreturn]]
+ void throw_domain_error(std::string const& msg) {
+ throw_exception(std::domain_error(msg));
+ }
+
+ [[noreturn]]
+ void throw_runtime_error(std::string const& msg) {
+ throw_exception(std::runtime_error(msg));
+ }
+
} // namespace Catch;
// end catch_enforce.cpp
+// start catch_enum_values_registry.cpp
+// start catch_enum_values_registry.h
+
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+ namespace Detail {
+
+ std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
+
+ class EnumValuesRegistry : public IMutableEnumValuesRegistry {
+
+ std::vector<std::unique_ptr<EnumInfo>> m_enumInfos;
+
+ EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
+ };
+
+ std::vector<StringRef> parseEnums( StringRef enums );
+
+ } // Detail
+
+} // Catch
+
+// end catch_enum_values_registry.h
+
+#include <map>
+#include <cassert>
+
+namespace Catch {
+
+ IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
+
+ namespace Detail {
+
+ namespace {
+ // Extracts the actual name part of an enum instance
+ // In other words, it returns the Blue part of Bikeshed::Colour::Blue
+ StringRef extractInstanceName(StringRef enumInstance) {
+ // Find last occurrence of ":"
+ size_t name_start = enumInstance.size();
+ while (name_start > 0 && enumInstance[name_start - 1] != ':') {
+ --name_start;
+ }
+ return enumInstance.substr(name_start, enumInstance.size() - name_start);
+ }
+ }
+
+ std::vector<StringRef> parseEnums( StringRef enums ) {
+ auto enumValues = splitStringRef( enums, ',' );
+ std::vector<StringRef> parsed;
+ parsed.reserve( enumValues.size() );
+ for( auto const& enumValue : enumValues ) {
+ parsed.push_back(trim(extractInstanceName(enumValue)));
+ }
+ return parsed;
+ }
+
+ EnumInfo::~EnumInfo() {}
+
+ StringRef EnumInfo::lookup( int value ) const {
+ for( auto const& valueToName : m_values ) {
+ if( valueToName.first == value )
+ return valueToName.second;
+ }
+ return "{** unexpected enum value **}"_sr;
+ }
+
+ std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+ std::unique_ptr<EnumInfo> enumInfo( new EnumInfo );
+ enumInfo->m_name = enumName;
+ enumInfo->m_values.reserve( values.size() );
+
+ const auto valueNames = Catch::Detail::parseEnums( allValueNames );
+ assert( valueNames.size() == values.size() );
+ std::size_t i = 0;
+ for( auto value : values )
+ enumInfo->m_values.emplace_back(value, valueNames[i++]);
+
+ return enumInfo;
+ }
+
+ EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+ m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
+ return *m_enumInfos.back();
+ }
+
+ } // Detail
+} // Catch
+
+// end catch_enum_values_registry.cpp
// start catch_errno_guard.cpp
#include <cerrno>
@@ -7996,7 +10667,7 @@ namespace Catch {
public:
~ExceptionTranslatorRegistry();
virtual void registerTranslator( const IExceptionTranslator* translator );
- virtual std::string translateActiveException() const override;
+ std::string translateActiveException() const override;
std::string tryTranslators() const;
private:
@@ -8061,54 +10732,82 @@ namespace Catch {
}
}
+ std::string ExceptionTranslatorRegistry::tryTranslators() const {
+ if (m_translators.empty()) {
+ std::rethrow_exception(std::current_exception());
+ } else {
+ return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
+ }
+ }
+
#else // ^^ Exceptions are enabled // Exceptions are disabled vv
std::string ExceptionTranslatorRegistry::translateActiveException() const {
CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
}
-#endif
std::string ExceptionTranslatorRegistry::tryTranslators() const {
- if( m_translators.empty() )
- std::rethrow_exception(std::current_exception());
- else
- return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
+ CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
}
+#endif
+
}
// end catch_exception_translator_registry.cpp
// start catch_fatal_condition.cpp
-#if defined(__GNUC__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
+#include <algorithm>
+
+#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+namespace Catch {
+
+ // If neither SEH nor signal handling is required, the handler impls
+ // do not have to do anything, and can be empty.
+ void FatalConditionHandler::engage_platform() {}
+ void FatalConditionHandler::disengage_platform() {}
+ FatalConditionHandler::FatalConditionHandler() = default;
+ FatalConditionHandler::~FatalConditionHandler() = default;
+
+} // end namespace Catch
+
+#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
+#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
+#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace {
- // Report the error condition
+ //! Signals fatal error message to the run context
void reportFatal( char const * const message ) {
Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
}
-}
-#endif // signals/SEH handling
+ //! Minimal size Catch2 needs for its own fatal error handling.
+ //! Picked anecdotally, so it might not be sufficient on all
+ //! platforms, and for all configurations.
+ constexpr std::size_t minStackSizeForErrors = 32 * 1024;
+} // end unnamed namespace
+
+#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch {
+
struct SignalDefs { DWORD id; const char* name; };
// There is no 1-1 mapping between signals and windows exceptions.
// Windows can easily distinguish between SO and SigSegV,
// but SigInt, SigTerm, etc are handled differently.
static SignalDefs signalDefs[] = {
- { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" },
- { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
- { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
- { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
+ { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" },
+ { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" },
+ { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" },
+ { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
};
- LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name);
@@ -8119,38 +10818,50 @@ namespace Catch {
return EXCEPTION_CONTINUE_SEARCH;
}
+ // Since we do not support multiple instantiations, we put these
+ // into global variables and rely on cleaning them up in outlined
+ // constructors/destructors
+ static PVOID exceptionHandlerHandle = nullptr;
+
+ // For MSVC, we reserve part of the stack memory for handling
+ // memory overflow structured exception.
FatalConditionHandler::FatalConditionHandler() {
- isSet = true;
- // 32k seems enough for Catch to handle stack overflow,
- // but the value was found experimentally, so there is no strong guarantee
- guaranteeSize = 32 * 1024;
- exceptionHandlerHandle = nullptr;
+ ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
+ if (!SetThreadStackGuarantee(&guaranteeSize)) {
+ // We do not want to fully error out, because needing
+ // the stack reserve should be rare enough anyway.
+ Catch::cerr()
+ << "Failed to reserve piece of stack."
+ << " Stack overflows will not be reported successfully.";
+ }
+ }
+
+ // We do not attempt to unset the stack guarantee, because
+ // Windows does not support lowering the stack size guarantee.
+ FatalConditionHandler::~FatalConditionHandler() = default;
+
+ void FatalConditionHandler::engage_platform() {
// Register as first handler in current chain
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
- // Pass in guarantee size to be filled
- SetThreadStackGuarantee(&guaranteeSize);
+ if (!exceptionHandlerHandle) {
+ CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
+ }
}
- void FatalConditionHandler::reset() {
- if (isSet) {
- RemoveVectoredExceptionHandler(exceptionHandlerHandle);
- SetThreadStackGuarantee(&guaranteeSize);
- exceptionHandlerHandle = nullptr;
- isSet = false;
+ void FatalConditionHandler::disengage_platform() {
+ if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
+ CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
}
+ exceptionHandlerHandle = nullptr;
}
- FatalConditionHandler::~FatalConditionHandler() {
- reset();
- }
+} // end namespace Catch
-bool FatalConditionHandler::isSet = false;
-ULONG FatalConditionHandler::guaranteeSize = 0;
-PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
+#endif // CATCH_CONFIG_WINDOWS_SEH
-} // namespace Catch
+#if defined( CATCH_CONFIG_POSIX_SIGNALS )
-#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
+#include <signal.h>
namespace Catch {
@@ -8159,10 +10870,6 @@ namespace Catch {
const char* name;
};
- // 32kb for the alternate stack seems to be sufficient. However, this value
- // is experimentally determined, so that's not guaranteed.
- constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
-
static SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" },
{ SIGILL, "SIGILL - Illegal instruction signal" },
@@ -8172,7 +10879,32 @@ namespace Catch {
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
};
- void FatalConditionHandler::handleSignal( int sig ) {
+// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
+// which is zero initialization, but not explicit. We want to avoid
+// that.
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+ static char* altStackMem = nullptr;
+ static std::size_t altStackSize = 0;
+ static stack_t oldSigStack{};
+ static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
+
+ static void restorePreviousSignalHandlers() {
+ // We set signal handlers back to the previous ones. Hopefully
+ // nobody overwrote them in the meantime, and doesn't expect
+ // their signal handlers to live past ours given that they
+ // installed them after ours..
+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, nullptr);
+ }
+
+ static void handleSignal( int sig ) {
char const * name = "<unknown signal>";
for (auto const& def : signalDefs) {
if (sig == def.id) {
@@ -8180,16 +10912,33 @@ namespace Catch {
break;
}
}
- reset();
- reportFatal(name);
+ // We need to restore previous signal handlers and let them do
+ // their thing, so that the users can have the debugger break
+ // when a signal is raised, and so on.
+ restorePreviousSignalHandlers();
+ reportFatal( name );
raise( sig );
}
FatalConditionHandler::FatalConditionHandler() {
- isSet = true;
+ assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
+ if (altStackSize == 0) {
+ altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
+ }
+ altStackMem = new char[altStackSize]();
+ }
+
+ FatalConditionHandler::~FatalConditionHandler() {
+ delete[] altStackMem;
+ // We signal that another instance can be constructed by zeroing
+ // out the pointer.
+ altStackMem = nullptr;
+ }
+
+ void FatalConditionHandler::engage_platform() {
stack_t sigStack;
sigStack.ss_sp = altStackMem;
- sigStack.ss_size = sigStackSize;
+ sigStack.ss_size = altStackSize;
sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = { };
@@ -8201,59 +10950,20 @@ namespace Catch {
}
}
- FatalConditionHandler::~FatalConditionHandler() {
- reset();
- }
-
- void FatalConditionHandler::reset() {
- if( isSet ) {
- // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
- sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
- }
- // Return the old stack
- sigaltstack(&oldSigStack, nullptr);
- isSet = false;
- }
- }
-
- bool FatalConditionHandler::isSet = false;
- struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
- stack_t FatalConditionHandler::oldSigStack = {};
- char FatalConditionHandler::altStackMem[sigStackSize] = {};
-
-} // namespace Catch
-
-#else
-
-namespace Catch {
- void FatalConditionHandler::reset() {}
-}
-
-#endif // signals/SEH handling
-
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
-// end catch_fatal_condition.cpp
-// start catch_generators.cpp
-// start catch_random_number_generator.h
-
-#include <algorithm>
-#include <random>
-
-namespace Catch {
-
- struct IConfig;
+ void FatalConditionHandler::disengage_platform() {
+ restorePreviousSignalHandlers();
+ }
- std::mt19937& rng();
- void seedRng( IConfig const& config );
- unsigned int rngSeed();
+} // end namespace Catch
-}
+#endif // CATCH_CONFIG_POSIX_SIGNALS
+// end catch_fatal_condition.cpp
+// start catch_generators.cpp
-// end catch_random_number_generator.h
#include <limits>
#include <set>
@@ -8261,34 +10971,16 @@ namespace Catch {
IGeneratorTracker::~IGeneratorTracker() {}
-namespace Generators {
-
- GeneratorBase::~GeneratorBase() {}
-
- std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ) {
-
- assert( selectionSize <= sourceSize );
- std::vector<size_t> indices;
- indices.reserve( selectionSize );
- std::uniform_int_distribution<size_t> uid( 0, sourceSize-1 );
+const char* GeneratorException::what() const noexcept {
+ return m_msg;
+}
- std::set<size_t> seen;
- // !TBD: improve this algorithm
- while( indices.size() < selectionSize ) {
- auto index = uid( rng() );
- if( seen.insert( index ).second )
- indices.push_back( index );
- }
- return indices;
- }
+namespace Generators {
- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
- return getResultCapture().acquireGeneratorTracker( lineInfo );
- }
+ GeneratorUntypedBase::~GeneratorUntypedBase() {}
- template<>
- auto all<int>() -> Generator<int> {
- return range( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() );
+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+ return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
}
} // namespace Generators
@@ -8344,10 +11036,16 @@ namespace Catch {
void noMatchingTestCases( std::string const& spec ) override;
+ void reportInvalidArguments(std::string const&arg) override;
+
static std::set<Verbosity> getSupportedVerbosities();
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing(std::string const& name) override;
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
- void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override;
+ void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+ void benchmarkFailed(std::string const&) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
void testRunStarting( TestRunInfo const& testRunInfo ) override;
void testGroupStarting( GroupInfo const& groupInfo ) override;
@@ -8538,7 +11236,7 @@ namespace Catch {
std::size_t listReporters();
- Option<std::size_t> list( Config const& config );
+ Option<std::size_t> list( std::shared_ptr<Config> const& config );
} // end namespace Catch
@@ -8557,7 +11255,7 @@ namespace Catch {
namespace Catch {
std::size_t listTests( Config const& config ) {
- TestSpec testSpec = config.testSpec();
+ TestSpec const& testSpec = config.testSpec();
if( config.hasTestFilters() )
Catch::cout() << "Matching test cases:\n";
else {
@@ -8591,7 +11289,7 @@ namespace Catch {
}
std::size_t listTestsNamesOnly( Config const& config ) {
- TestSpec testSpec = config.testSpec();
+ TestSpec const& testSpec = config.testSpec();
std::size_t matchedTests = 0;
std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
for( auto const& testCaseInfo : matchedTestCases ) {
@@ -8613,14 +11311,23 @@ namespace Catch {
}
std::string TagInfo::all() const {
- std::string out;
- for( auto const& spelling : spellings )
- out += "[" + spelling + "]";
+ size_t size = 0;
+ for (auto const& spelling : spellings) {
+ // Add 2 for the brackes
+ size += spelling.size() + 2;
+ }
+
+ std::string out; out.reserve(size);
+ for (auto const& spelling : spellings) {
+ out += '[';
+ out += spelling;
+ out += ']';
+ }
return out;
}
std::size_t listTags( Config const& config ) {
- TestSpec testSpec = config.testSpec();
+ TestSpec const& testSpec = config.testSpec();
if( config.hasTestFilters() )
Catch::cout() << "Tags for matching test cases:\n";
else {
@@ -8676,15 +11383,16 @@ namespace Catch {
return factories.size();
}
- Option<std::size_t> list( Config const& config ) {
+ Option<std::size_t> list( std::shared_ptr<Config> const& config ) {
Option<std::size_t> listedCount;
- if( config.listTests() )
- listedCount = listedCount.valueOr(0) + listTests( config );
- if( config.listTestNamesOnly() )
- listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
- if( config.listTags() )
- listedCount = listedCount.valueOr(0) + listTags( config );
- if( config.listReporters() )
+ getCurrentMutableContext().setConfig( config );
+ if( config->listTests() )
+ listedCount = listedCount.valueOr(0) + listTests( *config );
+ if( config->listTestNamesOnly() )
+ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config );
+ if( config->listTags() )
+ listedCount = listedCount.valueOr(0) + listTags( *config );
+ if( config->listReporters() )
listedCount = listedCount.valueOr(0) + listReporters();
return listedCount;
}
@@ -8713,6 +11421,29 @@ using Matchers::Impl::MatcherBase;
} // namespace Catch
// end catch_matchers.cpp
+// start catch_matchers_exception.cpp
+
+namespace Catch {
+namespace Matchers {
+namespace Exception {
+
+bool ExceptionMessageMatcher::match(std::exception const& ex) const {
+ return ex.what() == m_message;
+}
+
+std::string ExceptionMessageMatcher::describe() const {
+ return "exception message matches \"" + m_message + "\"";
+}
+
+}
+Exception::ExceptionMessageMatcher Message(std::string const& message) {
+ return Exception::ExceptionMessageMatcher(message);
+}
+
+// namespace Exception
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_exception.cpp
// start catch_matchers_floating.cpp
// start catch_polyfills.hpp
@@ -8741,74 +11472,101 @@ namespace Catch {
} // end namespace Catch
// end catch_to_string.hpp
+#include <algorithm>
+#include <cmath>
#include <cstdlib>
#include <cstdint>
#include <cstring>
+#include <sstream>
+#include <type_traits>
+#include <iomanip>
+#include <limits>
namespace Catch {
-namespace Matchers {
-namespace Floating {
-enum class FloatingPointKind : uint8_t {
- Float,
- Double
-};
-}
-}
-}
-
namespace {
-template <typename T>
-struct Converter;
-
-template <>
-struct Converter<float> {
- static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
- Converter(float f) {
+ int32_t convert(float f) {
+ static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
+ int32_t i;
std::memcpy(&i, &f, sizeof(f));
+ return i;
}
- int32_t i;
-};
-template <>
-struct Converter<double> {
- static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
- Converter(double d) {
+ int64_t convert(double d) {
+ static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
+ int64_t i;
std::memcpy(&i, &d, sizeof(d));
+ return i;
}
- int64_t i;
-};
-template <typename T>
-auto convert(T t) -> Converter<T> {
- return Converter<T>(t);
-}
+ template <typename FP>
+ bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
+ // Comparison with NaN should always be false.
+ // This way we can rule it out before getting into the ugly details
+ if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
+ return false;
+ }
-template <typename FP>
-bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
- // Comparison with NaN should always be false.
- // This way we can rule it out before getting into the ugly details
- if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
- return false;
+ auto lc = convert(lhs);
+ auto rc = convert(rhs);
+
+ if ((lc < 0) != (rc < 0)) {
+ // Potentially we can have +0 and -0
+ return lhs == rhs;
+ }
+
+ // static cast as a workaround for IBM XLC
+ auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
+ return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
+ }
+
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+
+ float nextafter(float x, float y) {
+ return ::nextafterf(x, y);
}
- auto lc = convert(lhs);
- auto rc = convert(rhs);
+ double nextafter(double x, double y) {
+ return ::nextafter(x, y);
+ }
- if ((lc.i < 0) != (rc.i < 0)) {
- // Potentially we can have +0 and -0
- return lhs == rhs;
+#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
+
+template <typename FP>
+FP step(FP start, FP direction, uint64_t steps) {
+ for (uint64_t i = 0; i < steps; ++i) {
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+ start = Catch::nextafter(start, direction);
+#else
+ start = std::nextafter(start, direction);
+#endif
}
+ return start;
+}
- auto ulpDiff = std::abs(lc.i - rc.i);
- return ulpDiff <= maxUlpDiff;
+// Performs equivalent check of std::fabs(lhs - rhs) <= margin
+// But without the subtraction to allow for INFINITY in comparison
+bool marginComparison(double lhs, double rhs, double margin) {
+ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
}
+template <typename FloatingPoint>
+void write(std::ostream& out, FloatingPoint num) {
+ out << std::scientific
+ << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
+ << num;
}
-namespace Catch {
+} // end anonymous namespace
+
namespace Matchers {
namespace Floating {
+
+ enum class FloatingPointKind : uint8_t {
+ Float,
+ Double
+ };
+
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
:m_target{ target }, m_margin{ margin } {
CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
@@ -8825,10 +11583,11 @@ namespace Floating {
return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
}
- WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
+ WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
- CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
- << " ULPs have to be non-negative.");
+ CATCH_ENFORCE(m_type == FloatingPointKind::Double
+ || m_ulps < (std::numeric_limits<uint32_t>::max)(),
+ "Provided ULP is impossibly large for a float comparison.");
}
#if defined(__clang__)
@@ -8853,16 +11612,59 @@ namespace Floating {
#endif
std::string WithinUlpsMatcher::describe() const {
- return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
+ std::stringstream ret;
+
+ ret << "is within " << m_ulps << " ULPs of ";
+
+ if (m_type == FloatingPointKind::Float) {
+ write(ret, static_cast<float>(m_target));
+ ret << 'f';
+ } else {
+ write(ret, m_target);
+ }
+
+ ret << " ([";
+ if (m_type == FloatingPointKind::Double) {
+ write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
+ ret << ", ";
+ write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
+ } else {
+ // We have to cast INFINITY to float because of MinGW, see #1782
+ write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
+ ret << ", ";
+ write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
+ }
+ ret << "])";
+
+ return ret.str();
+ }
+
+ WithinRelMatcher::WithinRelMatcher(double target, double epsilon):
+ m_target(target),
+ m_epsilon(epsilon){
+ CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense.");
+ CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense.");
+ }
+
+ bool WithinRelMatcher::match(double const& matchee) const {
+ const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
+ return marginComparison(matchee, m_target,
+ std::isinf(relMargin)? 0 : relMargin);
+ }
+
+ std::string WithinRelMatcher::describe() const {
+ Catch::ReusableStringStream sstr;
+ sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
+ return sstr.str();
}
}// namespace Floating
-Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
+Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
}
-Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
+Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
}
@@ -8870,9 +11672,24 @@ Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
return Floating::WithinAbsMatcher(target, margin);
}
+Floating::WithinRelMatcher WithinRel(double target, double eps) {
+ return Floating::WithinRelMatcher(target, eps);
+}
+
+Floating::WithinRelMatcher WithinRel(double target) {
+ return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
+}
+
+Floating::WithinRelMatcher WithinRel(float target, float eps) {
+ return Floating::WithinRelMatcher(target, eps);
+}
+
+Floating::WithinRelMatcher WithinRel(float target) {
+ return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
+}
+
} // namespace Matchers
} // namespace Catch
-
// end catch_matchers_floating.cpp
// start catch_matchers_generic.cpp
@@ -9030,28 +11847,43 @@ namespace Catch {
////////////////////////////////////////////////////////////////////////////
ScopedMessage::ScopedMessage( MessageBuilder const& builder )
- : m_info( builder.m_info )
+ : m_info( builder.m_info ), m_moved()
{
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
}
+ ScopedMessage::ScopedMessage( ScopedMessage&& old )
+ : m_info( old.m_info ), m_moved()
+ {
+ old.m_moved = true;
+ }
+
ScopedMessage::~ScopedMessage() {
- if ( !uncaught_exceptions() ){
+ if ( !uncaught_exceptions() && !m_moved ){
getResultCapture().popScopedMessage(m_info);
}
}
Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
auto trimmed = [&] (size_t start, size_t end) {
- while (names[start] == ',' || isspace(names[start])) {
+ while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;
}
- while (names[end] == ',' || isspace(names[end])) {
+ while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) {
--end;
}
return names.substr(start, end - start + 1);
};
+ auto skipq = [&] (size_t start, char quote) {
+ for (auto i = start + 1; i < names.size() ; ++i) {
+ if (names[i] == quote)
+ return i;
+ if (names[i] == '\\')
+ ++i;
+ }
+ CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
+ };
size_t start = 0;
std::stack<char> openings;
@@ -9072,18 +11904,22 @@ namespace Catch {
// case '>':
openings.pop();
break;
+ case '"':
+ case '\'':
+ pos = skipq(pos, c);
+ break;
case ',':
- if (start != pos && openings.size() == 0) {
+ if (start != pos && openings.empty()) {
m_messages.emplace_back(macroName, lineInfo, resultType);
- m_messages.back().message = trimmed(start, pos);
+ m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
m_messages.back().message += " := ";
start = pos;
}
}
}
- assert(openings.size() == 0 && "Mismatched openings");
+ assert(openings.empty() && "Mismatched openings");
m_messages.emplace_back(macroName, lineInfo, resultType);
- m_messages.back().message = trimmed(start, names.size() - 1);
+ m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
m_messages.back().message += " := ";
}
Capturer::~Capturer() {
@@ -9145,6 +11981,22 @@ namespace Catch {
auto str() const -> std::string;
};
+ class RedirectedStreams {
+ public:
+ RedirectedStreams(RedirectedStreams const&) = delete;
+ RedirectedStreams& operator=(RedirectedStreams const&) = delete;
+ RedirectedStreams(RedirectedStreams&&) = delete;
+ RedirectedStreams& operator=(RedirectedStreams&&) = delete;
+
+ RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
+ ~RedirectedStreams();
+ private:
+ std::string& m_redirectedCout;
+ std::string& m_redirectedCerr;
+ RedirectedStdOut m_redirectedStdOut;
+ RedirectedStdErr m_redirectedStdErr;
+ };
+
#if defined(CATCH_CONFIG_NEW_CAPTURE)
// Windows's implementation of std::tmpfile is terrible (it tries
@@ -9236,6 +12088,16 @@ namespace Catch {
{}
auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
+ RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
+ : m_redirectedCout(redirectedCout),
+ m_redirectedCerr(redirectedCerr)
+ {}
+
+ RedirectedStreams::~RedirectedStreams() {
+ m_redirectedCout += m_redirectedStdOut.str();
+ m_redirectedCerr += m_redirectedStdErr.str();
+ }
+
#if defined(CATCH_CONFIG_NEW_CAPTURE)
#if defined(_MSC_VER)
@@ -9243,12 +12105,12 @@ namespace Catch {
if (tmpnam_s(m_buffer)) {
CATCH_RUNTIME_ERROR("Could not get a temp filename");
}
- if (fopen_s(&m_file, m_buffer, "w")) {
+ if (fopen_s(&m_file, m_buffer, "w+")) {
char buffer[100];
if (strerror_s(buffer, errno)) {
CATCH_RUNTIME_ERROR("Could not translate errno to a string");
}
- CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer);
+ CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
}
}
#else
@@ -9351,20 +12213,61 @@ namespace Catch {
namespace Catch {
- std::mt19937& rng() {
- static std::mt19937 s_rng;
- return s_rng;
+namespace {
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4146) // we negate uint32 during the rotate
+#endif
+ // Safe rotr implementation thanks to John Regehr
+ uint32_t rotate_right(uint32_t val, uint32_t count) {
+ const uint32_t mask = 31;
+ count &= mask;
+ return (val >> count) | (val << (-count & mask));
+ }
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+}
+
+ SimplePcg32::SimplePcg32(result_type seed_) {
+ seed(seed_);
+ }
+
+ void SimplePcg32::seed(result_type seed_) {
+ m_state = 0;
+ (*this)();
+ m_state += seed_;
+ (*this)();
}
- void seedRng( IConfig const& config ) {
- if( config.rngSeed() != 0 ) {
- std::srand( config.rngSeed() );
- rng().seed( config.rngSeed() );
+ void SimplePcg32::discard(uint64_t skip) {
+ // We could implement this to run in O(log n) steps, but this
+ // should suffice for our use case.
+ for (uint64_t s = 0; s < skip; ++s) {
+ static_cast<void>((*this)());
}
}
- unsigned int rngSeed() {
- return getCurrentContext().getConfig()->rngSeed();
+ SimplePcg32::result_type SimplePcg32::operator()() {
+ // prepare the output value
+ const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
+ const auto output = rotate_right(xorshifted, m_state >> 59u);
+
+ // advance state
+ m_state = m_state * 6364136223846793005ULL + s_inc;
+
+ return output;
+ }
+
+ bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
+ return lhs.m_state == rhs.m_state;
+ }
+
+ bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
+ return lhs.m_state != rhs.m_state;
}
}
// end catch_random_number_generator.cpp
@@ -9383,6 +12286,8 @@ namespace Catch {
struct IConfig;
std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
@@ -9495,11 +12400,13 @@ namespace Catch {
namespace Catch {
class StartupExceptionRegistry {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
public:
void add(std::exception_ptr const& exception) noexcept;
std::vector<std::exception_ptr> const& getExceptions() const noexcept;
private:
std::vector<std::exception_ptr> m_exceptions;
+#endif
};
} // end namespace Catch
@@ -9582,7 +12489,14 @@ namespace Catch {
m_tagAliasRegistry.add( alias, tag, lineInfo );
}
void registerStartupException() noexcept override {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
m_exceptionRegistry.add(std::current_exception());
+#else
+ CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+#endif
+ }
+ IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
+ return m_enumValuesRegistry;
}
private:
@@ -9591,6 +12505,7 @@ namespace Catch {
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry;
StartupExceptionRegistry m_exceptionRegistry;
+ Detail::EnumValuesRegistry m_enumValuesRegistry;
};
}
@@ -9671,7 +12586,6 @@ namespace Catch {
namespace Generators {
struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
- size_t m_index = static_cast<size_t>( -1 );
GeneratorBasePtr m_generator;
GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
@@ -9683,39 +12597,110 @@ namespace Catch {
std::shared_ptr<GeneratorTracker> tracker;
ITracker& currentTracker = ctx.currentTracker();
- if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ // Under specific circumstances, the generator we want
+ // to acquire is also the current tracker. If this is
+ // the case, we have to avoid looking through current
+ // tracker's children, and instead return the current
+ // tracker.
+ // A case where this check is important is e.g.
+ // for (int i = 0; i < 5; ++i) {
+ // int n = GENERATE(1, 2);
+ // }
+ //
+ // without it, the code above creates 5 nested generators.
+ if (currentTracker.nameAndLocation() == nameAndLocation) {
+ auto thisTracker = currentTracker.parent().findChild(nameAndLocation);
+ assert(thisTracker);
+ assert(thisTracker->isGeneratorTracker());
+ tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker);
+ } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
- assert( childTracker->isIndexTracker() );
+ assert( childTracker->isGeneratorTracker() );
tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
- }
- else {
+ } else {
tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
currentTracker.addChild( tracker );
}
- if( !ctx.completedCycle() && !tracker->isComplete() ) {
- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
- tracker->moveNext();
+ if( !tracker->isComplete() ) {
tracker->open();
}
return *tracker;
}
- void moveNext() {
- m_index++;
- m_children.clear();
- }
-
// TrackerBase interface
- bool isIndexTracker() const override { return true; }
+ bool isGeneratorTracker() const override { return true; }
auto hasGenerator() const -> bool override {
return !!m_generator;
}
void close() override {
TrackerBase::close();
- if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 )
+ // If a generator has a child (it is followed by a section)
+ // and none of its children have started, then we must wait
+ // until later to start consuming its values.
+ // This catches cases where `GENERATE` is placed between two
+ // `SECTION`s.
+ // **The check for m_children.empty cannot be removed**.
+ // doing so would break `GENERATE` _not_ followed by `SECTION`s.
+ const bool should_wait_for_child = [&]() {
+ // No children -> nobody to wait for
+ if ( m_children.empty() ) {
+ return false;
+ }
+ // If at least one child started executing, don't wait
+ if ( std::find_if(
+ m_children.begin(),
+ m_children.end(),
+ []( TestCaseTracking::ITrackerPtr tracker ) {
+ return tracker->hasStarted();
+ } ) != m_children.end() ) {
+ return false;
+ }
+
+ // No children have started. We need to check if they _can_
+ // start, and thus we should wait for them, or they cannot
+ // start (due to filters), and we shouldn't wait for them
+ auto* parent = m_parent;
+ // This is safe: there is always at least one section
+ // tracker in a test case tracking tree
+ while ( !parent->isSectionTracker() ) {
+ parent = &( parent->parent() );
+ }
+ assert( parent &&
+ "Missing root (test case) level section" );
+
+ auto const& parentSection =
+ static_cast<SectionTracker&>( *parent );
+ auto const& filters = parentSection.getFilters();
+ // No filters -> no restrictions on running sections
+ if ( filters.empty() ) {
+ return true;
+ }
+
+ for ( auto const& child : m_children ) {
+ if ( child->isSectionTracker() &&
+ std::find( filters.begin(),
+ filters.end(),
+ static_cast<SectionTracker&>( *child )
+ .trimmedName() ) !=
+ filters.end() ) {
+ return true;
+ }
+ }
+ return false;
+ }();
+
+ // This check is a bit tricky, because m_generator->next()
+ // has a side-effect, where it consumes generator's current
+ // value, but we do not want to invoke the side-effect if
+ // this generator is still waiting for any child to start.
+ if ( should_wait_for_child ||
+ ( m_runState == CompletedSuccessfully &&
+ m_generator->next() ) ) {
+ m_children.clear();
m_runState = Executing;
+ }
}
// IGeneratorTracker interface
@@ -9725,9 +12710,6 @@ namespace Catch {
void setGenerator( GeneratorBasePtr&& generator ) override {
m_generator = std::move( generator );
}
- auto getIndex() const -> size_t override {
- return m_index;
- }
};
GeneratorTracker::~GeneratorTracker() {}
}
@@ -9825,6 +12807,9 @@ namespace Catch {
// and should be let to clear themselves out.
static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+ if (result.getResultType() != ResultWas::Warning)
+ m_messageScopes.clear();
+
// Reset working state
resetAssertionInfo();
m_lastResult = result;
@@ -9848,10 +12833,10 @@ namespace Catch {
return true;
}
- auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+ auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
using namespace Generators;
- GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) );
- assert( tracker.isOpen() );
+ GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
+ TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
m_lastAssertionInfo.lineInfo = lineInfo;
return tracker;
}
@@ -9879,6 +12864,7 @@ namespace Catch {
m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
m_messages.clear();
+ m_messageScopes.clear();
}
void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
@@ -9890,12 +12876,21 @@ namespace Catch {
m_unfinishedSections.push_back(endInfo);
}
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void RunContext::benchmarkPreparing(std::string const& name) {
+ m_reporter->benchmarkPreparing(name);
+ }
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
m_reporter->benchmarkStarting( info );
}
- void RunContext::benchmarkEnded( BenchmarkStats const& stats ) {
+ void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
m_reporter->benchmarkEnded( stats );
}
+ void RunContext::benchmarkFailed(std::string const & error) {
+ m_reporter->benchmarkFailed(error);
+ }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
void RunContext::pushScopedMessage(MessageInfo const & message) {
m_messages.push_back(message);
@@ -9905,6 +12900,10 @@ namespace Catch {
m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
}
+ void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
+ m_messageScopes.emplace_back( builder );
+ }
+
std::string RunContext::getCurrentTestName() const {
return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name
@@ -9926,7 +12925,7 @@ namespace Catch {
// Don't rebuild the result -- the stringification itself can cause more fatal errors
// Instead, fake a result data.
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
- tempResult.message = message;
+ tempResult.message = static_cast<std::string>(message);
AssertionResult result(m_lastAssertionInfo, tempResult);
assertionEnded(result);
@@ -9965,6 +12964,7 @@ namespace Catch {
m_lastAssertionPassed = true;
++m_totals.assertions.passed;
resetAssertionInfo();
+ m_messageScopes.clear();
}
bool RunContext::aborting() const {
@@ -9986,13 +12986,10 @@ namespace Catch {
CATCH_TRY {
if (m_reporter->getPreferences().shouldRedirectStdOut) {
#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
- RedirectedStdOut redirectedStdOut;
- RedirectedStdErr redirectedStdErr;
+ RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
timer.start();
invokeActiveTestCase();
- redirectedCout += redirectedStdOut.str();
- redirectedCerr += redirectedStdErr.str();
#else
OutputRedirect r(redirectedCout, redirectedCerr);
timer.start();
@@ -10019,15 +13016,15 @@ namespace Catch {
m_testCaseTracker->close();
handleUnfinishedSections();
m_messages.clear();
+ m_messageScopes.clear();
SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats);
}
void RunContext::invokeActiveTestCase() {
- FatalConditionHandler fatalConditionHandler; // Handle signals
+ FatalConditionHandlerGuard _(&m_fatalConditionhandler);
m_activeTestCase->invoke();
- fatalConditionHandler.reset();
}
void RunContext::handleUnfinishedSections() {
@@ -10090,7 +13087,7 @@ namespace Catch {
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) );
- data.message = message;
+ data.message = static_cast<std::string>(message);
AssertionResult assertionResult{ m_lastAssertionInfo, data };
assertionEnded( assertionResult );
if( !assertionResult.isOk() )
@@ -10153,6 +13150,18 @@ namespace Catch {
else
CATCH_INTERNAL_ERROR("No result capture instance");
}
+
+ void seedRng(IConfig const& config) {
+ if (config.rngSeed() != 0) {
+ std::srand(config.rngSeed());
+ rng().seed(config.rngSeed());
+ }
+ }
+
+ unsigned int rngSeed() {
+ return getCurrentContext().getConfig()->rngSeed();
+ }
+
}
// end catch_run_context.cpp
// start catch_section.cpp
@@ -10214,7 +13223,7 @@ namespace Catch {
void libIdentify();
int applyCommandLine( int argc, char const * const * argv );
- #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
+ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
int applyCommandLine( int argc, wchar_t const * const * argv );
#endif
@@ -10281,6 +13290,8 @@ namespace Catch {
// end catch_version.h
#include <cstdlib>
#include <iomanip>
+#include <set>
+#include <iterator>
namespace Catch {
@@ -10299,52 +13310,76 @@ namespace Catch {
return createReporter(config->getReporterName(), config);
}
- auto multi = std::unique_ptr<ListeningReporter>(new ListeningReporter);
-
+ // On older platforms, returning std::unique_ptr<ListeningReporter>
+ // when the return type is std::unique_ptr<IStreamingReporter>
+ // doesn't compile without a std::move call. However, this causes
+ // a warning on newer platforms. Thus, we have to work around
+ // it a bit and downcast the pointer manually.
+ auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter);
+ auto& multi = static_cast<ListeningReporter&>(*ret);
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
for (auto const& listener : listeners) {
- multi->addListener(listener->create(Catch::ReporterConfig(config)));
+ multi.addListener(listener->create(Catch::ReporterConfig(config)));
}
- multi->addReporter(createReporter(config->getReporterName(), config));
- return std::move(multi);
+ multi.addReporter(createReporter(config->getReporterName(), config));
+ return ret;
}
- Catch::Totals runTests(std::shared_ptr<Config> const& config) {
- auto reporter = makeReporter(config);
-
- RunContext context(config, std::move(reporter));
-
- Totals totals;
-
- context.testGroupStarting(config->name(), 1, 1);
-
- TestSpec testSpec = config->testSpec();
-
- auto const& allTestCases = getAllTestCasesSorted(*config);
- for (auto const& testCase : allTestCases) {
- if (!context.aborting() && matchTest(testCase, testSpec, *config))
- totals += context.runTest(testCase);
- else
- context.reporter().skipTest(testCase);
+ class TestGroup {
+ public:
+ explicit TestGroup(std::shared_ptr<Config> const& config)
+ : m_config{config}
+ , m_context{config, makeReporter(config)}
+ {
+ auto const& allTestCases = getAllTestCasesSorted(*m_config);
+ m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
+ auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
+
+ if (m_matches.empty() && invalidArgs.empty()) {
+ for (auto const& test : allTestCases)
+ if (!test.isHidden())
+ m_tests.emplace(&test);
+ } else {
+ for (auto const& match : m_matches)
+ m_tests.insert(match.tests.begin(), match.tests.end());
+ }
}
- if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
- ReusableStringStream testConfig;
+ Totals execute() {
+ auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
+ Totals totals;
+ m_context.testGroupStarting(m_config->name(), 1, 1);
+ for (auto const& testCase : m_tests) {
+ if (!m_context.aborting())
+ totals += m_context.runTest(*testCase);
+ else
+ m_context.reporter().skipTest(*testCase);
+ }
+
+ for (auto const& match : m_matches) {
+ if (match.tests.empty()) {
+ m_context.reporter().noMatchingTestCases(match.name);
+ totals.error = -1;
+ }
+ }
- bool first = true;
- for (const auto& input : config->getTestsOrTags()) {
- if (!first) { testConfig << ' '; }
- first = false;
- testConfig << input;
+ if (!invalidArgs.empty()) {
+ for (auto const& invalidArg: invalidArgs)
+ m_context.reporter().reportInvalidArguments(invalidArg);
}
- context.reporter().noMatchingTestCases(testConfig.str());
- totals.error = -1;
+ m_context.testGroupEnded(m_config->name(), totals, 1, 1);
+ return totals;
}
- context.testGroupEnded(config->name(), totals, 1, 1);
- return totals;
- }
+ private:
+ using Tests = std::set<TestCase const*>;
+
+ std::shared_ptr<Config> m_config;
+ RunContext m_context;
+ Tests m_tests;
+ TestSpec::Matches m_matches;
+ };
void applyFilenamesAsTags(Catch::IConfig const& config) {
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
@@ -10381,6 +13416,9 @@ namespace Catch {
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
if ( !exceptions.empty() ) {
+ config();
+ getCurrentMutableContext().setConfig(m_config);
+
m_startupExceptions = true;
Colour colourGuard( Colour::Red );
Catch::cerr() << "Errors occurred during startup!" << '\n';
@@ -10410,7 +13448,7 @@ namespace Catch {
}
void Session::libIdentify() {
Catch::cout()
- << std::left << std::setw(16) << "description: " << "A Catch test executable\n"
+ << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
<< std::left << std::setw(16) << "category: " << "testframework\n"
<< std::left << std::setw(16) << "framework: " << "Catch Test\n"
<< std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
@@ -10422,6 +13460,8 @@ namespace Catch {
auto result = m_cli.parse( clara::Args( argc, argv ) );
if( !result ) {
+ config();
+ getCurrentMutableContext().setConfig(m_config);
Catch::cerr()
<< Colour( Colour::Red )
<< "\nError(s) in input:\n"
@@ -10439,17 +13479,17 @@ namespace Catch {
return 0;
}
-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
+#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
char **utf8Argv = new char *[ argc ];
for ( int i = 0; i < argc; ++i ) {
- int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
+ int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );
utf8Argv[ i ] = new char[ bufSize ];
- WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
+ WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
}
int returnCode = applyCommandLine( argc, utf8Argv );
@@ -10513,10 +13553,15 @@ namespace Catch {
applyFilenamesAsTags( *m_config );
// Handle list request
- if( Option<std::size_t> listed = list( config() ) )
+ if( Option<std::size_t> listed = list( m_config ) )
return static_cast<int>( *listed );
- auto totals = runTests( m_config );
+ TestGroup tests { m_config };
+ auto const totals = tests.execute();
+
+ if( m_config->warnAboutNoTests() && totals.error == -1 )
+ return 2;
+
// Note that on unices only the lower 8 bits are usually used, clamping
// the return value to 255 prevents false negative when some multiple
// of 256 tests has failed
@@ -10564,6 +13609,7 @@ namespace Catch {
// end catch_singletons.cpp
// start catch_startup_exception_registry.cpp
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
namespace Catch {
void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
CATCH_TRY {
@@ -10579,6 +13625,7 @@ void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexce
}
} // end namespace Catch
+#endif
// end catch_startup_exception_registry.cpp
// start catch_stream.cpp
@@ -10593,7 +13640,7 @@ namespace Catch {
Catch::IStream::~IStream() = default;
- namespace detail { namespace {
+ namespace Detail { namespace {
template<typename WriterF, std::size_t bufferSize=256>
class StreamBufImpl : public std::streambuf {
char data[bufferSize];
@@ -10692,15 +13739,15 @@ namespace Catch {
auto makeStream( StringRef const &filename ) -> IStream const* {
if( filename.empty() )
- return new detail::CoutStream();
+ return new Detail::CoutStream();
else if( filename[0] == '%' ) {
if( filename == "%debug" )
- return new detail::DebugOutStream();
+ return new Detail::DebugOutStream();
else
CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
}
else
- return new detail::FileStream( filename );
+ return new Detail::FileStream( filename );
}
// This class encapsulates the idea of a pool of ostringstreams that can be reused.
@@ -10757,12 +13804,13 @@ namespace Catch {
#include <ostream>
#include <cstring>
#include <cctype>
+#include <vector>
namespace Catch {
namespace {
char toLowerCh(char c) {
- return static_cast<char>( std::tolower( c ) );
+ return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) );
}
}
@@ -10797,6 +13845,18 @@ namespace Catch {
return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
}
+ StringRef trim(StringRef ref) {
+ const auto is_ws = [](char c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+ };
+ size_t real_begin = 0;
+ while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; }
+ size_t real_end = ref.size();
+ while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; }
+
+ return ref.substr(real_begin, real_end - real_begin);
+ }
+
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
bool replaced = false;
std::size_t i = str.find( replaceThis );
@@ -10811,6 +13871,21 @@ namespace Catch {
return replaced;
}
+ std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) {
+ std::vector<StringRef> subStrings;
+ std::size_t start = 0;
+ for(std::size_t pos = 0; pos < str.size(); ++pos ) {
+ if( str[pos] == delimiter ) {
+ if( pos - start > 1 )
+ subStrings.push_back( str.substr( start, pos-start ) );
+ start = pos+1;
+ }
+ }
+ if( start < str.size() )
+ subStrings.push_back( str.substr( start, str.size()-start ) );
+ return subStrings;
+ }
+
pluralise::pluralise( std::size_t count, std::string const& label )
: m_count( count ),
m_label( label )
@@ -10827,123 +13902,46 @@ namespace Catch {
// end catch_string_manip.cpp
// start catch_stringref.cpp
-#if defined(__clang__)
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wexit-time-destructors"
-#endif
-
+#include <algorithm>
#include <ostream>
#include <cstring>
#include <cstdint>
-namespace {
- const uint32_t byte_2_lead = 0xC0;
- const uint32_t byte_3_lead = 0xE0;
- const uint32_t byte_4_lead = 0xF0;
-}
-
namespace Catch {
StringRef::StringRef( char const* rawChars ) noexcept
: StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
{}
- StringRef::operator std::string() const {
- return std::string( m_start, m_size );
- }
-
- void StringRef::swap( StringRef& other ) noexcept {
- std::swap( m_start, other.m_start );
- std::swap( m_size, other.m_size );
- std::swap( m_data, other.m_data );
- }
-
auto StringRef::c_str() const -> char const* {
- if( isSubstring() )
- const_cast<StringRef*>( this )->takeOwnership();
+ CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
return m_start;
}
- auto StringRef::currentData() const noexcept -> char const* {
+ auto StringRef::data() const noexcept -> char const* {
return m_start;
}
- auto StringRef::isOwned() const noexcept -> bool {
- return m_data != nullptr;
- }
- auto StringRef::isSubstring() const noexcept -> bool {
- return m_start[m_size] != '\0';
- }
-
- void StringRef::takeOwnership() {
- if( !isOwned() ) {
- m_data = new char[m_size+1];
- memcpy( m_data, m_start, m_size );
- m_data[m_size] = '\0';
- m_start = m_data;
- }
- }
auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
- if( start < m_size )
- return StringRef( m_start+start, size );
- else
+ if (start < m_size) {
+ return StringRef(m_start + start, (std::min)(m_size - start, size));
+ } else {
return StringRef();
- }
- auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
- return
- size() == other.size() &&
- (std::strncmp( m_start, other.m_start, size() ) == 0);
- }
- auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool {
- return !operator==( other );
- }
-
- auto StringRef::operator[](size_type index) const noexcept -> char {
- return m_start[index];
- }
-
- auto StringRef::numberOfCharacters() const noexcept -> size_type {
- size_type noChars = m_size;
- // Make adjustments for uft encodings
- for( size_type i=0; i < m_size; ++i ) {
- char c = m_start[i];
- if( ( c & byte_2_lead ) == byte_2_lead ) {
- noChars--;
- if (( c & byte_3_lead ) == byte_3_lead )
- noChars--;
- if( ( c & byte_4_lead ) == byte_4_lead )
- noChars--;
- }
}
- return noChars;
- }
-
- auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string {
- std::string str;
- str.reserve( lhs.size() + rhs.size() );
- str += lhs;
- str += rhs;
- return str;
- }
- auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string {
- return std::string( lhs ) + std::string( rhs );
}
- auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string {
- return std::string( lhs ) + std::string( rhs );
+ auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
+ return m_size == other.m_size
+ && (std::memcmp( m_start, other.m_start, m_size ) == 0);
}
auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
- return os.write(str.currentData(), str.size());
+ return os.write(str.data(), str.size());
}
auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
- lhs.append(rhs.currentData(), rhs.size());
+ lhs.append(rhs.data(), rhs.size());
return lhs;
}
} // namespace Catch
-
-#if defined(__clang__)
-# pragma clang diagnostic pop
-#endif
// end catch_stringref.cpp
// start catch_tag_alias.cpp
@@ -11046,7 +14044,7 @@ namespace Catch {
void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
CATCH_ENFORCE( !isReservedTag(tag),
"Tag name: [" << tag << "] is not allowed.\n"
- << "Tag names starting with non alpha-numeric characters are reserved\n"
+ << "Tag names starting with non alphanumeric characters are reserved\n"
<< _lineInfo );
}
}
@@ -11062,8 +14060,7 @@ namespace Catch {
std::vector<std::string> tags;
std::string desc, tag;
bool inTag = false;
- std::string _descOrTags = nameAndTags.tags;
- for (char c : _descOrTags) {
+ for (char c : nameAndTags.tags) {
if( !inTag ) {
if( c == '[' )
inTag = true;
@@ -11078,6 +14075,12 @@ namespace Catch {
else if( prop == TestCaseInfo::None )
enforceNotReservedTag( tag, _lineInfo );
+ // Merged hide tags like `[.approvals]` should be added as
+ // `[.][approvals]`. The `[.]` is added at later point, so
+ // we only strip the prefix
+ if (startsWith(tag, '.') && tag.size() > 1) {
+ tag.erase(0, 1);
+ }
tags.push_back( tag );
tag.clear();
inTag = false;
@@ -11087,10 +14090,11 @@ namespace Catch {
}
}
if( isHidden ) {
- tags.push_back( "." );
+ // Add all "hidden" tags to make them behave identically
+ tags.insert( tags.end(), { ".", "!hide" } );
}
- TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo );
+ TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo );
return TestCase( _testCase, std::move(info) );
}
@@ -11182,30 +14186,89 @@ namespace Catch {
// end catch_test_case_info.cpp
// start catch_test_case_registry_impl.cpp
+#include <algorithm>
#include <sstream>
namespace Catch {
- std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+ namespace {
+ struct TestHasher {
+ using hash_t = uint64_t;
+
+ explicit TestHasher( hash_t hashSuffix ):
+ m_hashSuffix{ hashSuffix } {}
+
+ uint32_t operator()( TestCase const& t ) const {
+ // FNV-1a hash with multiplication fold.
+ const hash_t prime = 1099511628211u;
+ hash_t hash = 14695981039346656037u;
+ for ( const char c : t.name ) {
+ hash ^= c;
+ hash *= prime;
+ }
+ hash ^= m_hashSuffix;
+ hash *= prime;
+ const uint32_t low{ static_cast<uint32_t>( hash ) };
+ const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
+ return low * high;
+ }
- std::vector<TestCase> sorted = unsortedTestCases;
+ private:
+ hash_t m_hashSuffix;
+ };
+ } // end unnamed namespace
+ std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
switch( config.runOrder() ) {
- case RunTests::InLexicographicalOrder:
- std::sort( sorted.begin(), sorted.end() );
- break;
- case RunTests::InRandomOrder:
- seedRng( config );
- std::shuffle( sorted.begin(), sorted.end(), rng() );
- break;
case RunTests::InDeclarationOrder:
// already in declaration order
break;
+
+ case RunTests::InLexicographicalOrder: {
+ std::vector<TestCase> sorted = unsortedTestCases;
+ std::sort( sorted.begin(), sorted.end() );
+ return sorted;
+ }
+
+ case RunTests::InRandomOrder: {
+ seedRng( config );
+ TestHasher h{ config.rngSeed() };
+
+ using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
+ std::vector<hashedTest> indexed_tests;
+ indexed_tests.reserve( unsortedTestCases.size() );
+
+ for (auto const& testCase : unsortedTestCases) {
+ indexed_tests.emplace_back(h(testCase), &testCase);
+ }
+
+ std::sort(indexed_tests.begin(), indexed_tests.end(),
+ [](hashedTest const& lhs, hashedTest const& rhs) {
+ if (lhs.first == rhs.first) {
+ return lhs.second->name < rhs.second->name;
+ }
+ return lhs.first < rhs.first;
+ });
+
+ std::vector<TestCase> sorted;
+ sorted.reserve( indexed_tests.size() );
+
+ for (auto const& hashed : indexed_tests) {
+ sorted.emplace_back(*hashed.second);
+ }
+
+ return sorted;
+ }
}
- return sorted;
+ return unsortedTestCases;
}
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
+ return !testCase.throws() || config.allowThrows();
+ }
+
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
- return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+ return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
}
void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
@@ -11222,9 +14285,12 @@ namespace Catch {
std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
std::vector<TestCase> filtered;
filtered.reserve( testCases.size() );
- for( auto const& testCase : testCases )
- if( matchTest( testCase, testSpec, config ) )
- filtered.push_back( testCase );
+ for (auto const& testCase : testCases) {
+ if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
+ (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
+ filtered.push_back(testCase);
+ }
+ }
return filtered;
}
std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
@@ -11263,7 +14329,7 @@ namespace Catch {
}
std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
- std::string className = classOrQualifiedMethodName;
+ std::string className(classOrQualifiedMethodName);
if( startsWith( className, '&' ) )
{
std::size_t lastColons = className.rfind( "::" );
@@ -11300,11 +14366,6 @@ namespace TestCaseTracking {
ITracker::~ITracker() = default;
- TrackerContext& TrackerContext::instance() {
- static TrackerContext s_instance;
- return s_instance;
- }
-
ITracker& TrackerContext::startRun() {
m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
m_currentTracker = nullptr;
@@ -11336,15 +14397,12 @@ namespace TestCaseTracking {
m_currentTracker = tracker;
}
- TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
- : m_nameAndLocation( nameAndLocation ),
+ TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
+ ITracker(nameAndLocation),
m_ctx( ctx ),
m_parent( parent )
{}
- NameAndLocation const& TrackerBase::nameAndLocation() const {
- return m_nameAndLocation;
- }
bool TrackerBase::isComplete() const {
return m_runState == CompletedSuccessfully || m_runState == Failed;
}
@@ -11387,7 +14445,7 @@ namespace TestCaseTracking {
}
bool TrackerBase::isSectionTracker() const { return false; }
- bool TrackerBase::isIndexTracker() const { return false; }
+ bool TrackerBase::isGeneratorTracker() const { return false; }
void TrackerBase::open() {
m_runState = Executing;
@@ -11410,7 +14468,7 @@ namespace TestCaseTracking {
m_runState = CompletedSuccessfully;
break;
case ExecutingChildren:
- if( m_children.empty() || m_children.back()->isComplete() )
+ if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
m_runState = CompletedSuccessfully;
break;
@@ -11445,7 +14503,8 @@ namespace TestCaseTracking {
}
SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
- : TrackerBase( nameAndLocation, ctx, parent )
+ : TrackerBase( nameAndLocation, ctx, parent ),
+ m_trimmed_name(trim(nameAndLocation.name))
{
if( parent ) {
while( !parent->isSectionTracker() )
@@ -11456,6 +14515,17 @@ namespace TestCaseTracking {
}
}
+ bool SectionTracker::isComplete() const {
+ bool complete = true;
+
+ if (m_filters.empty()
+ || m_filters[0] == ""
+ || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
+ complete = TrackerBase::isComplete();
+ }
+ return complete;
+ }
+
bool SectionTracker::isSectionTracker() const { return true; }
SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
@@ -11477,63 +14547,29 @@ namespace TestCaseTracking {
}
void SectionTracker::tryOpen() {
- if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
+ if( !isComplete() )
open();
}
void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
if( !filters.empty() ) {
- m_filters.push_back(""); // Root - should never be consulted
- m_filters.push_back(""); // Test Case - not a section filter
+ m_filters.reserve( m_filters.size() + filters.size() + 2 );
+ m_filters.emplace_back(""); // Root - should never be consulted
+ m_filters.emplace_back(""); // Test Case - not a section filter
m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
}
}
void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
if( filters.size() > 1 )
- m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
+ m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
}
- IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
- : TrackerBase( nameAndLocation, ctx, parent ),
- m_size( size )
- {}
-
- bool IndexTracker::isIndexTracker() const { return true; }
-
- IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
- std::shared_ptr<IndexTracker> tracker;
-
- ITracker& currentTracker = ctx.currentTracker();
- if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
- assert( childTracker );
- assert( childTracker->isIndexTracker() );
- tracker = std::static_pointer_cast<IndexTracker>( childTracker );
- }
- else {
- tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, &currentTracker, size );
- currentTracker.addChild( tracker );
- }
-
- if( !ctx.completedCycle() && !tracker->isComplete() ) {
- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
- tracker->moveNext();
- tracker->open();
- }
-
- return *tracker;
+ std::vector<std::string> const& SectionTracker::getFilters() const {
+ return m_filters;
}
- int IndexTracker::index() const { return m_index; }
-
- void IndexTracker::moveNext() {
- m_index++;
- m_children.clear();
- }
-
- void IndexTracker::close() {
- TrackerBase::close();
- if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
- m_runState = Executing;
+ std::string const& SectionTracker::trimmedName() const {
+ return m_trimmed_name;
}
} // namespace TestCaseTracking
@@ -11541,7 +14577,6 @@ namespace TestCaseTracking {
using TestCaseTracking::ITracker;
using TestCaseTracking::TrackerContext;
using TestCaseTracking::SectionTracker;
-using TestCaseTracking::IndexTracker;
} // namespace Catch
@@ -11586,47 +14621,81 @@ namespace Catch {
namespace Catch {
+ TestSpec::Pattern::Pattern( std::string const& name )
+ : m_name( name )
+ {}
+
TestSpec::Pattern::~Pattern() = default;
- TestSpec::NamePattern::~NamePattern() = default;
- TestSpec::TagPattern::~TagPattern() = default;
- TestSpec::ExcludedPattern::~ExcludedPattern() = default;
- TestSpec::NamePattern::NamePattern( std::string const& name )
- : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ std::string const& TestSpec::Pattern::name() const {
+ return m_name;
+ }
+
+ TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
+ : Pattern( filterString )
+ , m_wildcardPattern( toLower( name ), CaseSensitive::No )
{}
+
bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
- return m_wildcardPattern.matches( toLower( testCase.name ) );
+ return m_wildcardPattern.matches( testCase.name );
}
- TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+ TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
+ : Pattern( filterString )
+ , m_tag( toLower( tag ) )
+ {}
+
bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
return std::find(begin(testCase.lcaseTags),
end(testCase.lcaseTags),
m_tag) != end(testCase.lcaseTags);
}
- TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
- bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+ TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
+ : Pattern( underlyingPattern->name() )
+ , m_underlyingPattern( underlyingPattern )
+ {}
+
+ bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
+ return !m_underlyingPattern->matches( testCase );
+ }
bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
- // All patterns in a filter must match for the filter to be a match
- for( auto const& pattern : m_patterns ) {
- if( !pattern->matches( testCase ) )
- return false;
- }
- return true;
+ return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
+ }
+
+ std::string TestSpec::Filter::name() const {
+ std::string name;
+ for( auto const& p : m_patterns )
+ name += p->name();
+ return name;
}
bool TestSpec::hasFilters() const {
return !m_filters.empty();
}
+
bool TestSpec::matches( TestCaseInfo const& testCase ) const {
- // A TestSpec matches if any filter matches
- for( auto const& filter : m_filters )
- if( filter.matches( testCase ) )
- return true;
- return false;
+ return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
+ }
+
+ TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const
+ {
+ Matches matches( m_filters.size() );
+ std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
+ std::vector<TestCase const*> currentMatches;
+ for( auto const& test : testCases )
+ if( isThrowSafe( test, config ) && filter.matches( test ) )
+ currentMatches.emplace_back( &test );
+ return FilterMatch{ filter.name(), currentMatches };
+ } );
+ return matches;
}
+
+ const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
+ return (m_invalidArgs);
+ }
+
}
// end catch_test_spec.cpp
// start catch_test_spec_parser.cpp
@@ -11638,64 +14707,136 @@ namespace Catch {
TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
m_mode = None;
m_exclusion = false;
- m_start = std::string::npos;
m_arg = m_tagAliases->expandAliases( arg );
m_escapeChars.clear();
+ m_substring.reserve(m_arg.size());
+ m_patternName.reserve(m_arg.size());
+ m_realPatternPos = 0;
+
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
- visitChar( m_arg[m_pos] );
- if( m_mode == Name )
- addPattern<TestSpec::NamePattern>();
+ //if visitChar fails
+ if( !visitChar( m_arg[m_pos] ) ){
+ m_testSpec.m_invalidArgs.push_back(arg);
+ break;
+ }
+ endMode();
return *this;
}
TestSpec TestSpecParser::testSpec() {
addFilter();
return m_testSpec;
}
+ bool TestSpecParser::visitChar( char c ) {
+ if( (m_mode != EscapedName) && (c == '\\') ) {
+ escape();
+ addCharToPattern(c);
+ return true;
+ }else if((m_mode != EscapedName) && (c == ',') ) {
+ return separate();
+ }
- void TestSpecParser::visitChar( char c ) {
- if( m_mode == None ) {
- switch( c ) {
- case ' ': return;
- case '~': m_exclusion = true; return;
- case '[': return startNewMode( Tag, ++m_pos );
- case '"': return startNewMode( QuotedName, ++m_pos );
- case '\\': return escape();
- default: startNewMode( Name, m_pos ); break;
- }
+ switch( m_mode ) {
+ case None:
+ if( processNoneChar( c ) )
+ return true;
+ break;
+ case Name:
+ processNameChar( c );
+ break;
+ case EscapedName:
+ endMode();
+ addCharToPattern(c);
+ return true;
+ default:
+ case Tag:
+ case QuotedName:
+ if( processOtherChar( c ) )
+ return true;
+ break;
}
- if( m_mode == Name ) {
- if( c == ',' ) {
- addPattern<TestSpec::NamePattern>();
- addFilter();
- }
- else if( c == '[' ) {
- if( subString() == "exclude:" )
- m_exclusion = true;
- else
- addPattern<TestSpec::NamePattern>();
- startNewMode( Tag, ++m_pos );
- }
- else if( c == '\\' )
- escape();
+
+ m_substring += c;
+ if( !isControlChar( c ) ) {
+ m_patternName += c;
+ m_realPatternPos++;
}
- else if( m_mode == EscapedName )
- m_mode = Name;
- else if( m_mode == QuotedName && c == '"' )
- addPattern<TestSpec::NamePattern>();
- else if( m_mode == Tag && c == ']' )
- addPattern<TestSpec::TagPattern>();
+ return true;
}
- void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
+ // Two of the processing methods return true to signal the caller to return
+ // without adding the given character to the current pattern strings
+ bool TestSpecParser::processNoneChar( char c ) {
+ switch( c ) {
+ case ' ':
+ return true;
+ case '~':
+ m_exclusion = true;
+ return false;
+ case '[':
+ startNewMode( Tag );
+ return false;
+ case '"':
+ startNewMode( QuotedName );
+ return false;
+ default:
+ startNewMode( Name );
+ return false;
+ }
+ }
+ void TestSpecParser::processNameChar( char c ) {
+ if( c == '[' ) {
+ if( m_substring == "exclude:" )
+ m_exclusion = true;
+ else
+ endMode();
+ startNewMode( Tag );
+ }
+ }
+ bool TestSpecParser::processOtherChar( char c ) {
+ if( !isControlChar( c ) )
+ return false;
+ m_substring += c;
+ endMode();
+ return true;
+ }
+ void TestSpecParser::startNewMode( Mode mode ) {
m_mode = mode;
- m_start = start;
+ }
+ void TestSpecParser::endMode() {
+ switch( m_mode ) {
+ case Name:
+ case QuotedName:
+ return addNamePattern();
+ case Tag:
+ return addTagPattern();
+ case EscapedName:
+ revertBackToLastMode();
+ return;
+ case None:
+ default:
+ return startNewMode( None );
+ }
}
void TestSpecParser::escape() {
- if( m_mode == None )
- m_start = m_pos;
+ saveLastMode();
m_mode = EscapedName;
- m_escapeChars.push_back( m_pos );
+ m_escapeChars.push_back(m_realPatternPos);
+ }
+ bool TestSpecParser::isControlChar( char c ) const {
+ switch( m_mode ) {
+ default:
+ return false;
+ case None:
+ return c == '~';
+ case Name:
+ return c == '[';
+ case EscapedName:
+ return true;
+ case QuotedName:
+ return c == '"';
+ case Tag:
+ return c == '[' || c == ']';
+ }
}
- std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
void TestSpecParser::addFilter() {
if( !m_currentFilter.m_patterns.empty() ) {
@@ -11704,6 +14845,86 @@ namespace Catch {
}
}
+ void TestSpecParser::saveLastMode() {
+ lastMode = m_mode;
+ }
+
+ void TestSpecParser::revertBackToLastMode() {
+ m_mode = lastMode;
+ }
+
+ bool TestSpecParser::separate() {
+ if( (m_mode==QuotedName) || (m_mode==Tag) ){
+ //invalid argument, signal failure to previous scope.
+ m_mode = None;
+ m_pos = m_arg.size();
+ m_substring.clear();
+ m_patternName.clear();
+ m_realPatternPos = 0;
+ return false;
+ }
+ endMode();
+ addFilter();
+ return true; //success
+ }
+
+ std::string TestSpecParser::preprocessPattern() {
+ std::string token = m_patternName;
+ for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
+ token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
+ m_escapeChars.clear();
+ if (startsWith(token, "exclude:")) {
+ m_exclusion = true;
+ token = token.substr(8);
+ }
+
+ m_patternName.clear();
+ m_realPatternPos = 0;
+
+ return token;
+ }
+
+ void TestSpecParser::addNamePattern() {
+ auto token = preprocessPattern();
+
+ if (!token.empty()) {
+ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
+ if (m_exclusion)
+ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+ m_currentFilter.m_patterns.push_back(pattern);
+ }
+ m_substring.clear();
+ m_exclusion = false;
+ m_mode = None;
+ }
+
+ void TestSpecParser::addTagPattern() {
+ auto token = preprocessPattern();
+
+ if (!token.empty()) {
+ // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
+ // we have to create a separate hide tag and shorten the real one
+ if (token.size() > 1 && token[0] == '.') {
+ token.erase(token.begin());
+ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
+ if (m_exclusion) {
+ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+ }
+ m_currentFilter.m_patterns.push_back(pattern);
+ }
+
+ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
+
+ if (m_exclusion) {
+ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+ }
+ m_currentFilter.m_patterns.push_back(pattern);
+ }
+ m_substring.clear();
+ m_exclusion = false;
+ m_mode = None;
+ }
+
TestSpec parseTestSpec( std::string const& arg ) {
return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
}
@@ -11744,7 +14965,7 @@ namespace Catch {
// is terrible and we should move on.
// TBD: How to signal that the measured resolution is probably wrong?
if (ticks > startTime + 3 * nanosecondsInSecond) {
- return sum / i;
+ return sum / ( i + 1u );
}
}
@@ -11805,13 +15026,11 @@ namespace Detail {
enum Arch { Big, Little };
static Arch which() {
- union _{
- int asInt;
- char asChar[sizeof (int)];
- } u;
-
- u.asInt = 1;
- return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+ int one = 1;
+ // If the lowest byte we read is non-zero, we can assume
+ // that little endian format is used.
+ auto value = *reinterpret_cast<char*>(&one);
+ return value ? Little : Big;
}
};
}
@@ -11935,6 +15154,13 @@ std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
}
#endif
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+#include <cstddef>
+std::string StringMaker<std::byte>::convert(std::byte value) {
+ return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
+}
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+
std::string StringMaker<int>::convert(int value) {
return ::Catch::Detail::stringify(static_cast<long long>(value));
}
@@ -11997,11 +15223,16 @@ std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {
return "nullptr";
}
+int StringMaker<float>::precision = 5;
+
std::string StringMaker<float>::convert(float value) {
- return fpToString(value, 5) + 'f';
+ return fpToString(value, precision) + 'f';
}
+
+int StringMaker<double>::precision = 10;
+
std::string StringMaker<double>::convert(double value) {
- return fpToString(value, 10);
+ return fpToString(value, precision);
}
std::string ratio_string<std::atto>::symbol() { return "a"; }
@@ -12075,11 +15306,48 @@ namespace Catch {
// end catch_totals.cpp
// start catch_uncaught_exceptions.cpp
+// start catch_config_uncaught_exceptions.hpp
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+
+#if defined(_MSC_VER)
+# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+# endif
+#endif
+
+#include <exception>
+
+#if defined(__cpp_lib_uncaught_exceptions) \
+ && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif // __cpp_lib_uncaught_exceptions
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \
+ && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \
+ && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+
+# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
+
+#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+// end catch_config_uncaught_exceptions.hpp
#include <exception>
namespace Catch {
bool uncaught_exceptions() {
-#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ return false;
+#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
return std::uncaught_exceptions() > 0;
#else
return std::uncaught_exception();
@@ -12119,7 +15387,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 2, 5, 0, "", 0 );
+ static Version version( 2, 13, 8, "", 0 );
return version;
}
@@ -12127,14 +15395,12 @@ namespace Catch {
// end catch_version.cpp
// start catch_wildcard_pattern.cpp
-#include <sstream>
-
namespace Catch {
WildcardPattern::WildcardPattern( std::string const& pattern,
CaseSensitive::Choice caseSensitivity )
: m_caseSensitivity( caseSensitivity ),
- m_pattern( adjustCase( pattern ) )
+ m_pattern( normaliseString( pattern ) )
{
if( startsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 1 );
@@ -12149,28 +15415,27 @@ namespace Catch {
bool WildcardPattern::matches( std::string const& str ) const {
switch( m_wildcard ) {
case NoWildcard:
- return m_pattern == adjustCase( str );
+ return m_pattern == normaliseString( str );
case WildcardAtStart:
- return endsWith( adjustCase( str ), m_pattern );
+ return endsWith( normaliseString( str ), m_pattern );
case WildcardAtEnd:
- return startsWith( adjustCase( str ), m_pattern );
+ return startsWith( normaliseString( str ), m_pattern );
case WildcardAtBothEnds:
- return contains( adjustCase( str ), m_pattern );
+ return contains( normaliseString( str ), m_pattern );
default:
CATCH_INTERNAL_ERROR( "Unknown enum" );
}
}
- std::string WildcardPattern::adjustCase( std::string const& str ) const {
- return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+ std::string WildcardPattern::normaliseString( std::string const& str ) const {
+ return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
}
}
// end catch_wildcard_pattern.cpp
// start catch_xmlwriter.cpp
#include <iomanip>
-
-using uchar = unsigned char;
+#include <type_traits>
namespace Catch {
@@ -12203,13 +15468,37 @@ namespace {
}
void hexEscapeChar(std::ostream& os, unsigned char c) {
+ std::ios_base::fmtflags f(os.flags());
os << "\\x"
<< std::uppercase << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<int>(c);
+ os.flags(f);
+ }
+
+ bool shouldNewline(XmlFormatting fmt) {
+ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
+ }
+
+ bool shouldIndent(XmlFormatting fmt) {
+ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
}
} // anonymous namespace
+ XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
+ return static_cast<XmlFormatting>(
+ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
+ static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
+ );
+ }
+
+ XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
+ return static_cast<XmlFormatting>(
+ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
+ static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
+ );
+ }
+
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
: m_str( str ),
m_forWhat( forWhat )
@@ -12220,7 +15509,7 @@ namespace {
// (see: http://www.w3.org/TR/xml/#syntax)
for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
- uchar c = m_str[idx];
+ unsigned char c = m_str[idx];
switch (c) {
case '<': os << "&lt;"; break;
case '&': os << "&amp;"; break;
@@ -12280,7 +15569,7 @@ namespace {
bool valid = true;
uint32_t value = headerValue(c);
for (std::size_t n = 1; n < encBytes; ++n) {
- uchar nc = m_str[idx + n];
+ unsigned char nc = m_str[idx + n];
valid &= ((nc & 0xC0) == 0x80);
value = (value << 6) | (nc & 0x3F);
}
@@ -12314,13 +15603,17 @@ namespace {
return os;
}
- XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
- : m_writer( writer )
+ XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt )
+ : m_writer( writer ),
+ m_fmt(fmt)
{}
XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
- : m_writer( other.m_writer ){
+ : m_writer( other.m_writer ),
+ m_fmt(other.m_fmt)
+ {
other.m_writer = nullptr;
+ other.m_fmt = XmlFormatting::None;
}
XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
if ( m_writer ) {
@@ -12328,16 +15621,19 @@ namespace {
}
m_writer = other.m_writer;
other.m_writer = nullptr;
+ m_fmt = other.m_fmt;
+ other.m_fmt = XmlFormatting::None;
return *this;
}
XmlWriter::ScopedElement::~ScopedElement() {
- if( m_writer )
- m_writer->endElement();
+ if (m_writer) {
+ m_writer->endElement(m_fmt);
+ }
}
- XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
- m_writer->writeText( text, indent );
+ XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
+ m_writer->writeText( text, fmt );
return *this;
}
@@ -12347,37 +15643,47 @@ namespace {
}
XmlWriter::~XmlWriter() {
- while( !m_tags.empty() )
+ while (!m_tags.empty()) {
endElement();
+ }
+ newlineIfNecessary();
}
- XmlWriter& XmlWriter::startElement( std::string const& name ) {
+ XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
ensureTagClosed();
newlineIfNecessary();
- m_os << m_indent << '<' << name;
+ if (shouldIndent(fmt)) {
+ m_os << m_indent;
+ m_indent += " ";
+ }
+ m_os << '<' << name;
m_tags.push_back( name );
- m_indent += " ";
m_tagIsOpen = true;
+ applyFormatting(fmt);
return *this;
}
- XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
- ScopedElement scoped( this );
- startElement( name );
+ XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
+ ScopedElement scoped( this, fmt );
+ startElement( name, fmt );
return scoped;
}
- XmlWriter& XmlWriter::endElement() {
- newlineIfNecessary();
- m_indent = m_indent.substr( 0, m_indent.size()-2 );
+ XmlWriter& XmlWriter::endElement(XmlFormatting fmt) {
+ m_indent = m_indent.substr(0, m_indent.size() - 2);
+
if( m_tagIsOpen ) {
m_os << "/>";
m_tagIsOpen = false;
+ } else {
+ newlineIfNecessary();
+ if (shouldIndent(fmt)) {
+ m_os << m_indent;
+ }
+ m_os << "</" << m_tags.back() << ">";
}
- else {
- m_os << m_indent << "</" << m_tags.back() << ">";
- }
- m_os << std::endl;
+ m_os << std::flush;
+ applyFormatting(fmt);
m_tags.pop_back();
return *this;
}
@@ -12393,22 +15699,26 @@ namespace {
return *this;
}
- XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
+ XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
if( !text.empty() ){
bool tagWasOpen = m_tagIsOpen;
ensureTagClosed();
- if( tagWasOpen && indent )
+ if (tagWasOpen && shouldIndent(fmt)) {
m_os << m_indent;
+ }
m_os << XmlEncode( text );
- m_needsNewline = true;
+ applyFormatting(fmt);
}
return *this;
}
- XmlWriter& XmlWriter::writeComment( std::string const& text ) {
+ XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
ensureTagClosed();
- m_os << m_indent << "<!--" << text << "-->";
- m_needsNewline = true;
+ if (shouldIndent(fmt)) {
+ m_os << m_indent;
+ }
+ m_os << "<!--" << text << "-->";
+ applyFormatting(fmt);
return *this;
}
@@ -12424,11 +15734,16 @@ namespace {
void XmlWriter::ensureTagClosed() {
if( m_tagIsOpen ) {
- m_os << ">" << std::endl;
+ m_os << '>' << std::flush;
+ newlineIfNecessary();
m_tagIsOpen = false;
}
}
+ void XmlWriter::applyFormatting(XmlFormatting fmt) {
+ m_needsNewline = shouldNewline(fmt);
+ }
+
void XmlWriter::writeDeclaration() {
m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
}
@@ -12469,11 +15784,37 @@ namespace Catch {
#ifdef _MSC_VER
sprintf_s(buffer, "%.3f", duration);
#else
- sprintf(buffer, "%.3f", duration);
+ std::sprintf(buffer, "%.3f", duration);
#endif
return std::string(buffer);
}
+ bool shouldShowDuration( IConfig const& config, double duration ) {
+ if ( config.showDurations() == ShowDurations::Always ) {
+ return true;
+ }
+ if ( config.showDurations() == ShowDurations::Never ) {
+ return false;
+ }
+ const double min = config.minDuration();
+ return min >= 0 && duration >= min;
+ }
+
+ std::string serializeFilters( std::vector<std::string> const& container ) {
+ ReusableStringStream oss;
+ bool first = true;
+ for (auto&& filter : container)
+ {
+ if (!first)
+ oss << ' ';
+ else
+ first = false;
+
+ oss << filter;
+ }
+ return oss.str();
+ }
+
TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)
:StreamingReporterBase(_config) {}
@@ -12689,24 +16030,25 @@ private:
if (itMessage == messages.end())
return;
- // using messages.end() directly yields (or auto) compilation error:
- std::vector<MessageInfo>::const_iterator itEnd = messages.end();
- const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+ const auto itEnd = messages.cend();
+ const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
{
Colour colourGuard(colour);
stream << " with " << pluralise(N, "message") << ':';
}
- for (; itMessage != itEnd; ) {
+ while (itMessage != itEnd) {
// If this assertion is a warning ignore any INFO messages
if (printInfoMessages || itMessage->type != ResultWas::Info) {
- stream << " '" << itMessage->message << '\'';
- if (++itMessage != itEnd) {
+ printMessage();
+ if (itMessage != itEnd) {
Colour colourGuard(dimColour());
stream << " and";
}
+ continue;
}
+ ++itMessage;
}
}
@@ -12724,10 +16066,6 @@ private:
return "Reports test results on a single line, suitable for IDEs";
}
- ReporterPreferences CompactReporter::getPreferences() const {
- return m_reporterPrefs;
- }
-
void CompactReporter::noMatchingTestCases( std::string const& spec ) {
stream << "No test cases matched '" << spec << '\'' << std::endl;
}
@@ -12754,8 +16092,9 @@ private:
}
void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
- if (m_config->showDurations() == ShowDurations::Always) {
- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ double dur = _sectionStats.durationInSeconds;
+ if ( shouldShowDuration( *m_config, dur ) ) {
+ stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
}
}
@@ -12779,8 +16118,13 @@ private:
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
- // Note that 4062 (not all labels are handled
- // and default is missing) is enabled
+ // Note that 4062 (not all labels are handled and default is missing) is enabled
+#endif
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+// For simplicity, benchmarking-only helpers are always enabled
+# pragma clang diagnostic ignored "-Wunused-function"
#endif
namespace Catch {
@@ -12962,11 +16306,11 @@ class Duration {
static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
- uint64_t m_inNanoseconds;
+ double m_inNanoseconds;
Unit m_units;
public:
- explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
+ explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
: m_inNanoseconds(inNanoseconds),
m_units(units) {
if (m_units == Unit::Auto) {
@@ -12995,7 +16339,7 @@ public:
case Unit::Minutes:
return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
default:
- return static_cast<double>(m_inNanoseconds);
+ return m_inNanoseconds;
}
}
auto unitsAsString() const -> std::string {
@@ -13003,7 +16347,7 @@ public:
case Unit::Nanoseconds:
return "ns";
case Unit::Microseconds:
- return "µs";
+ return "us";
case Unit::Milliseconds:
return "ms";
case Unit::Seconds:
@@ -13016,7 +16360,7 @@ public:
}
friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
- return os << duration.value() << " " << duration.unitsAsString();
+ return os << duration.value() << ' ' << duration.unitsAsString();
}
};
} // end anon namespace
@@ -13041,10 +16385,16 @@ public:
if (!m_isOpen) {
m_isOpen = true;
*this << RowBreak();
- for (auto const& info : m_columnInfos)
- *this << info.name << ColumnBreak();
- *this << RowBreak();
- m_os << Catch::getLineOfChars<'-'>() << "\n";
+
+ Columns headerCols;
+ Spacer spacer(2);
+ for (auto const& info : m_columnInfos) {
+ headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
+ headerCols += spacer;
+ }
+ m_os << headerCols << '\n';
+
+ m_os << Catch::getLineOfChars<'-'>() << '\n';
}
}
void close() {
@@ -13063,30 +16413,29 @@ public:
friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
auto colStr = tp.m_oss.str();
- // This takes account of utf8 encodings
- auto strSize = Catch::StringRef(colStr).numberOfCharacters();
+ const auto strSize = colStr.size();
tp.m_oss.str("");
tp.open();
if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
tp.m_currentColumn = -1;
- tp.m_os << "\n";
+ tp.m_os << '\n';
}
tp.m_currentColumn++;
auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
- auto padding = (strSize + 2 < static_cast<std::size_t>(colInfo.width))
- ? std::string(colInfo.width - (strSize + 2), ' ')
+ auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
+ ? std::string(colInfo.width - (strSize + 1), ' ')
: std::string();
if (colInfo.justification == ColumnInfo::Left)
- tp.m_os << colStr << padding << " ";
+ tp.m_os << colStr << padding << ' ';
else
- tp.m_os << padding << colStr << " ";
+ tp.m_os << padding << colStr << ' ';
return tp;
}
friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
if (tp.m_currentColumn > 0) {
- tp.m_os << "\n";
+ tp.m_os << '\n';
tp.m_currentColumn = -1;
}
return tp;
@@ -13096,12 +16445,26 @@ public:
ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
: StreamingReporterBase(config),
m_tablePrinter(new TablePrinter(config.stream(),
- {
- { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
- { "iters", 8, ColumnInfo::Right },
- { "elapsed ns", 14, ColumnInfo::Right },
- { "average", 14, ColumnInfo::Right }
- })) {}
+ [&config]() -> std::vector<ColumnInfo> {
+ if (config.fullConfig()->benchmarkNoAnalysis())
+ {
+ return{
+ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
+ { " samples", 14, ColumnInfo::Right },
+ { " iterations", 14, ColumnInfo::Right },
+ { " mean", 14, ColumnInfo::Right }
+ };
+ }
+ else
+ {
+ return{
+ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
+ { "samples mean std dev", 14, ColumnInfo::Right },
+ { "iterations low mean low std dev", 14, ColumnInfo::Right },
+ { "estimated high mean high std dev", 14, ColumnInfo::Right }
+ };
+ }
+ }())) {}
ConsoleReporter::~ConsoleReporter() = default;
std::string ConsoleReporter::getDescription() {
@@ -13112,6 +16475,10 @@ void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
stream << "No test cases matched '" << spec << '\'' << std::endl;
}
+void ConsoleReporter::reportInvalidArguments(std::string const&arg){
+ stream << "Invalid Filter: " << arg << std::endl;
+}
+
void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
@@ -13132,6 +16499,7 @@ bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
}
void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
+ m_tablePrinter->close();
m_headerPrinted = false;
StreamingReporterBase::sectionStarting(_sectionInfo);
}
@@ -13146,8 +16514,9 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
stream << "\nNo assertions in test case";
stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
}
- if (m_config->showDurations() == ShowDurations::Always) {
- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ double dur = _sectionStats.durationInSeconds;
+ if (shouldShowDuration(*m_config, dur)) {
+ stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
}
if (m_headerPrinted) {
m_headerPrinted = false;
@@ -13155,28 +16524,53 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
StreamingReporterBase::sectionEnded(_sectionStats);
}
-void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
- lazyPrintWithoutClosingBenchmarkTable();
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+void ConsoleReporter::benchmarkPreparing(std::string const& name) {
+ lazyPrintWithoutClosingBenchmarkTable();
- auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) );
+ auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
- bool firstLine = true;
- for (auto line : nameCol) {
- if (!firstLine)
- (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
- else
- firstLine = false;
+ bool firstLine = true;
+ for (auto line : nameCol) {
+ if (!firstLine)
+ (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
+ else
+ firstLine = false;
- (*m_tablePrinter) << line << ColumnBreak();
+ (*m_tablePrinter) << line << ColumnBreak();
+ }
+}
+
+void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
+ (*m_tablePrinter) << info.samples << ColumnBreak()
+ << info.iterations << ColumnBreak();
+ if (!m_config->benchmarkNoAnalysis())
+ (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
+}
+void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
+ if (m_config->benchmarkNoAnalysis())
+ {
+ (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
+ }
+ else
+ {
+ (*m_tablePrinter) << ColumnBreak()
+ << Duration(stats.mean.point.count()) << ColumnBreak()
+ << Duration(stats.mean.lower_bound.count()) << ColumnBreak()
+ << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
+ << Duration(stats.standardDeviation.point.count()) << ColumnBreak()
+ << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
+ << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
}
}
-void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) {
- Duration average(stats.elapsedTimeInNanoseconds / stats.iterations);
+
+void ConsoleReporter::benchmarkFailed(std::string const& error) {
+ Colour colour(Colour::Red);
(*m_tablePrinter)
- << stats.iterations << ColumnBreak()
- << stats.elapsedTimeInNanoseconds << ColumnBreak()
- << average << ColumnBreak();
+ << "Benchmark failed (" << error << ')'
+ << ColumnBreak() << RowBreak();
}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
m_tablePrinter->close();
@@ -13198,6 +16592,10 @@ void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
stream << std::endl;
StreamingReporterBase::testRunEnded(_testRunStats);
}
+void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
+ StreamingReporterBase::testRunStarting(_testInfo);
+ printTestFilters();
+}
void ConsoleReporter::lazyPrint() {
@@ -13251,11 +16649,9 @@ void ConsoleReporter::printTestCaseAndSectionHeader() {
SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
- if (!lineInfo.empty()) {
- stream << getLineOfChars<'-'>() << '\n';
- Colour colourGuard(Colour::FileName);
- stream << lineInfo << '\n';
- }
+ stream << getLineOfChars<'-'>() << '\n';
+ Colour colourGuard(Colour::FileName);
+ stream << lineInfo << '\n';
stream << getLineOfChars<'.'>() << '\n' << std::endl;
}
@@ -13379,6 +16775,13 @@ void ConsoleReporter::printSummaryDivider() {
stream << getLineOfChars<'-'>() << '\n';
}
+void ConsoleReporter::printTestFilters() {
+ if (m_config->testSpec().hasFilters()) {
+ Colour guard(Colour::BrightYellow);
+ stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
+ }
+}
+
CATCH_REGISTER_REPORTER("console", ConsoleReporter)
} // end namespace Catch
@@ -13386,6 +16789,10 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
// end catch_reporter_console.cpp
// start catch_reporter_junit.cpp
@@ -13393,6 +16800,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
#include <sstream>
#include <ctime>
#include <algorithm>
+#include <iomanip>
namespace Catch {
@@ -13420,7 +16828,7 @@ namespace Catch {
#else
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
#endif
- return std::string(timeStamp);
+ return std::string(timeStamp, timeStampSize-1);
}
std::string fileNameTag(const std::vector<std::string> &tags) {
@@ -13431,6 +16839,17 @@ namespace Catch {
return it->substr(1);
return std::string();
}
+
+ // Formats the duration in seconds to 3 decimal places.
+ // This is done because some genius defined Maven Surefire schema
+ // in a way that only accepts 3 decimal places, and tools like
+ // Jenkins use that schema for validation JUnit reporter output.
+ std::string formatDuration( double seconds ) {
+ ReusableStringStream rss;
+ rss << std::fixed << std::setprecision( 3 ) << seconds;
+ return rss.str();
+ }
+
} // anonymous namespace
JunitReporter::JunitReporter( ReporterConfig const& _config )
@@ -13490,6 +16909,7 @@ namespace Catch {
void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+
TestGroupStats const& stats = groupNode.value;
xml.writeAttribute( "name", stats.groupInfo.name );
xml.writeAttribute( "errors", unexpectedExceptions );
@@ -13499,15 +16919,30 @@ namespace Catch {
if( m_config->showDurations() == ShowDurations::Never )
xml.writeAttribute( "time", "" );
else
- xml.writeAttribute( "time", suiteTime );
+ xml.writeAttribute( "time", formatDuration( suiteTime ) );
xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+ // Write properties if there are any
+ if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
+ auto properties = xml.scopedElement("properties");
+ if (m_config->hasTestFilters()) {
+ xml.scopedElement("property")
+ .writeAttribute("name", "filters")
+ .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
+ }
+ if (m_config->rngSeed() != 0) {
+ xml.scopedElement("property")
+ .writeAttribute("name", "random-seed")
+ .writeAttribute("value", m_config->rngSeed());
+ }
+ }
+
// Write test cases
for( auto const& child : groupNode.children )
writeTestCase( *child );
- xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false );
- xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false );
+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
}
void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
@@ -13529,12 +16964,13 @@ namespace Catch {
if ( !m_config->name().empty() )
className = m_config->name() + "." + className;
- writeSection( className, "", rootSection );
+ writeSection( className, "", rootSection, stats.testInfo.okToFail() );
}
- void JunitReporter::writeSection( std::string const& className,
- std::string const& rootName,
- SectionNode const& sectionNode ) {
+ void JunitReporter::writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode,
+ bool testOkToFail) {
std::string name = trim( sectionNode.stats.sectionInfo.name );
if( !rootName.empty() )
name = rootName + '/' + name;
@@ -13551,20 +16987,30 @@ namespace Catch {
xml.writeAttribute( "classname", className );
xml.writeAttribute( "name", name );
}
- xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
+ xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) );
+ // This is not ideal, but it should be enough to mimic gtest's
+ // junit output.
+ // Ideally the JUnit reporter would also handle `skipTest`
+ // events and write those out appropriately.
+ xml.writeAttribute( "status", "run" );
+
+ if (sectionNode.stats.assertions.failedButOk) {
+ xml.scopedElement("skipped")
+ .writeAttribute("message", "TEST_CASE tagged with !mayfail");
+ }
writeAssertions( sectionNode );
if( !sectionNode.stdOut.empty() )
- xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
if( !sectionNode.stdErr.empty() )
- xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
}
for( auto const& childNode : sectionNode.childSections )
if( className.empty() )
- writeSection( name, "", *childNode );
+ writeSection( name, "", *childNode, testOkToFail );
else
- writeSection( className, name, *childNode );
+ writeSection( className, name, *childNode, testOkToFail );
}
void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
@@ -13582,11 +17028,7 @@ namespace Catch {
elementName = "error";
break;
case ResultWas::ExplicitFailure:
- elementName = "failure";
- break;
case ResultWas::ExpressionFailed:
- elementName = "failure";
- break;
case ResultWas::DidntThrowException:
elementName = "failure";
break;
@@ -13604,10 +17046,25 @@ namespace Catch {
XmlWriter::ScopedElement e = xml.scopedElement( elementName );
- xml.writeAttribute( "message", result.getExpandedExpression() );
+ xml.writeAttribute( "message", result.getExpression() );
xml.writeAttribute( "type", result.getTestMacroName() );
ReusableStringStream rss;
+ if (stats.totals.assertions.total() > 0) {
+ rss << "FAILED" << ":\n";
+ if (result.hasExpression()) {
+ rss << " ";
+ rss << result.getExpressionInMacro();
+ rss << '\n';
+ }
+ if (result.hasExpandedExpression()) {
+ rss << "with expansion:\n";
+ rss << Column(result.getExpandedExpression()).indent(2) << '\n';
+ }
+ } else {
+ rss << '\n';
+ }
+
if( !result.getMessage().empty() )
rss << result.getMessage() << '\n';
for( auto const& msg : stats.infoMessages )
@@ -13615,7 +17072,7 @@ namespace Catch {
rss << msg.message << '\n';
rss << "at " << result.getSourceInfo();
- xml.writeText( rss.str(), false );
+ xml.writeText( rss.str(), XmlFormatting::Newline );
}
}
@@ -13659,19 +17116,41 @@ namespace Catch {
m_reporter->noMatchingTestCases( spec );
}
+ void ListeningReporter::reportInvalidArguments(std::string const&arg){
+ for ( auto const& listener : m_listeners ) {
+ listener->reportInvalidArguments( arg );
+ }
+ m_reporter->reportInvalidArguments( arg );
+ }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void ListeningReporter::benchmarkPreparing( std::string const& name ) {
+ for (auto const& listener : m_listeners) {
+ listener->benchmarkPreparing(name);
+ }
+ m_reporter->benchmarkPreparing(name);
+ }
void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
for ( auto const& listener : m_listeners ) {
listener->benchmarkStarting( benchmarkInfo );
}
m_reporter->benchmarkStarting( benchmarkInfo );
}
- void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
+ void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
for ( auto const& listener : m_listeners ) {
listener->benchmarkEnded( benchmarkStats );
}
m_reporter->benchmarkEnded( benchmarkStats );
}
+ void ListeningReporter::benchmarkFailed( std::string const& error ) {
+ for (auto const& listener : m_listeners) {
+ listener->benchmarkFailed(error);
+ }
+ m_reporter->benchmarkFailed(error);
+ }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
for ( auto const& listener : m_listeners ) {
listener->testRunStarting( testRunInfo );
@@ -13802,6 +17281,8 @@ namespace Catch {
m_xml.startElement( "Catch" );
if( !m_config->name().empty() )
m_xml.writeAttribute( "name", m_config->name() );
+ if (m_config->testSpec().hasFilters())
+ m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
if( m_config->rngSeed() != 0 )
m_xml.scopedElement( "Randomness" )
.writeAttribute( "seed", m_config->rngSeed() );
@@ -13937,9 +17418,9 @@ namespace Catch {
e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
if( !testCaseStats.stdOut.empty() )
- m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
+ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
if( !testCaseStats.stdErr.empty() )
- m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
m_xml.endElement();
}
@@ -13951,6 +17432,10 @@ namespace Catch {
.writeAttribute( "successes", testGroupStats.totals.assertions.passed )
.writeAttribute( "failures", testGroupStats.totals.assertions.failed )
.writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+ m_xml.scopedElement( "OverallResultsCases")
+ .writeAttribute( "successes", testGroupStats.totals.testCases.passed )
+ .writeAttribute( "failures", testGroupStats.totals.testCases.failed )
+ .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk );
m_xml.endElement();
}
@@ -13960,9 +17445,58 @@ namespace Catch {
.writeAttribute( "successes", testRunStats.totals.assertions.passed )
.writeAttribute( "failures", testRunStats.totals.assertions.failed )
.writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+ m_xml.scopedElement( "OverallResultsCases")
+ .writeAttribute( "successes", testRunStats.totals.testCases.passed )
+ .writeAttribute( "failures", testRunStats.totals.testCases.failed )
+ .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk );
+ m_xml.endElement();
+ }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void XmlReporter::benchmarkPreparing(std::string const& name) {
+ m_xml.startElement("BenchmarkResults")
+ .writeAttribute("name", name);
+ }
+
+ void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
+ m_xml.writeAttribute("samples", info.samples)
+ .writeAttribute("resamples", info.resamples)
+ .writeAttribute("iterations", info.iterations)
+ .writeAttribute("clockResolution", info.clockResolution)
+ .writeAttribute("estimatedDuration", info.estimatedDuration)
+ .writeComment("All values in nano seconds");
+ }
+
+ void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
+ m_xml.startElement("mean")
+ .writeAttribute("value", benchmarkStats.mean.point.count())
+ .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count())
+ .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count())
+ .writeAttribute("ci", benchmarkStats.mean.confidence_interval);
+ m_xml.endElement();
+ m_xml.startElement("standardDeviation")
+ .writeAttribute("value", benchmarkStats.standardDeviation.point.count())
+ .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count())
+ .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count())
+ .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval);
+ m_xml.endElement();
+ m_xml.startElement("outliers")
+ .writeAttribute("variance", benchmarkStats.outlierVariance)
+ .writeAttribute("lowMild", benchmarkStats.outliers.low_mild)
+ .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe)
+ .writeAttribute("highMild", benchmarkStats.outliers.high_mild)
+ .writeAttribute("highSevere", benchmarkStats.outliers.high_severe);
+ m_xml.endElement();
m_xml.endElement();
}
+ void XmlReporter::benchmarkFailed(std::string const &error) {
+ m_xml.scopedElement("failed").
+ writeAttribute("message", error);
+ m_xml.endElement();
+ }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
CATCH_REGISTER_REPORTER( "xml", XmlReporter )
} // end namespace Catch
@@ -13988,7 +17522,7 @@ namespace Catch {
#ifndef __OBJC__
-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
// Standard C/C++ Win32 Unicode wmain entry point
extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
#else
@@ -14036,7 +17570,7 @@ int main (int argc, char * const argv[]) {
#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
-#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ )
+#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
@@ -14050,7 +17584,7 @@ int main (int argc, char * const argv[]) {
#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
-#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ )
+#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
@@ -14065,6 +17599,7 @@ int main (int argc, char * const argv[]) {
#endif // CATCH_CONFIG_DISABLE_MATCHERS
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
@@ -14082,10 +17617,22 @@ int main (int argc, char * const argv[]) {
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
#else
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
#endif
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
@@ -14106,6 +17653,13 @@ int main (int argc, char * const argv[]) {
#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+#define CATCH_BENCHMARK(...) \
+ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+#define CATCH_BENCHMARK_ADVANCED(name) \
+ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
#else
@@ -14141,6 +17695,7 @@ int main (int argc, char * const argv[]) {
#endif // CATCH_CONFIG_DISABLE_MATCHERS
#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )
@@ -14157,10 +17712,26 @@ int main (int argc, char * const argv[]) {
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
+#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
#else
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
#endif
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
@@ -14186,6 +17757,13 @@ int main (int argc, char * const argv[]) {
#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+#define BENCHMARK(...) \
+ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+#define BENCHMARK_ADVANCED(name) \
+ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
using Catch::Detail::Approx;
#else // CATCH_CONFIG_DISABLE
@@ -14225,12 +17803,13 @@ using Catch::Detail::Approx;
#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
#endif // CATCH_CONFIG_DISABLE_MATCHERS
-#define CATCH_INFO( msg ) (void)(0)
-#define CATCH_WARN( msg ) (void)(0)
-#define CATCH_CAPTURE( msg ) (void)(0)
+#define CATCH_INFO( msg ) (void)(0)
+#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
+#define CATCH_WARN( msg ) (void)(0)
+#define CATCH_CAPTURE( msg ) (void)(0)
-#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
#define CATCH_METHOD_AS_TEST_CASE( method, ... )
#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define CATCH_SECTION( ... )
@@ -14239,19 +17818,31 @@ using Catch::Detail::Approx;
#define CATCH_FAIL_CHECK( ... ) (void)(0)
#define CATCH_SUCCEED( ... ) (void)(0)
-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
#else
-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
#endif
// "BDD-style" convenience wrappers
-#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className )
#define CATCH_GIVEN( desc )
#define CATCH_AND_GIVEN( desc )
#define CATCH_WHEN( desc )
@@ -14297,11 +17888,12 @@ using Catch::Detail::Approx;
#endif // CATCH_CONFIG_DISABLE_MATCHERS
#define INFO( msg ) (void)(0)
+#define UNSCOPED_INFO( msg ) (void)(0)
#define WARN( msg ) (void)(0)
#define CAPTURE( msg ) (void)(0)
-#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
#define METHOD_AS_TEST_CASE( method, ... )
#define REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define SECTION( ... )
@@ -14309,14 +17901,26 @@ using Catch::Detail::Approx;
#define FAIL( ... ) (void)(0)
#define FAIL_CHECK( ... ) (void)(0)
#define SUCCEED( ... ) (void)(0)
-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
#else
-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
#endif
#define STATIC_REQUIRE( ... ) (void)(0)
@@ -14327,8 +17931,8 @@ using Catch::Detail::Approx;
#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
// "BDD-style" convenience wrappers
-#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) )
-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ) )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className )
#define GIVEN( desc )
#define AND_GIVEN( desc )
@@ -14359,4 +17963,3 @@ using Catch::Detail::Approx;
// end catch_reenable_warnings.h
// end catch.hpp
#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-
diff --git a/src/teakra/src/interpreter.h b/src/teakra/src/interpreter.h
index 72724ee..aac4916 100644
--- a/src/teakra/src/interpreter.h
+++ b/src/teakra/src/interpreter.h
@@ -1,4 +1,5 @@
#pragma once
+#include <utility>
#include <atomic>
#include <stdexcept>
#include <tuple>
diff --git a/src/teakra/src/teakra.cpp b/src/teakra/src/teakra.cpp
index 76bc79f..7b4c028 100644
--- a/src/teakra/src/teakra.cpp
+++ b/src/teakra/src/teakra.cpp
@@ -50,7 +50,7 @@ struct Teakra::Impl {
}
void Reset() {
- shared_memory.raw.fill(0);
+ shared_memory.raw.fill(0); // BAD!!!!
miu.Reset();
apbp_from_cpu.Reset();
apbp_from_dsp.Reset();
diff --git a/src/types.h b/src/types.h
index b723566..47c1b9e 100644
--- a/src/types.h
+++ b/src/types.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
diff --git a/src/version.h b/src/version.h
index 0bebb99..f387820 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2021 Arisotura
+ Copyright 2016-2022 melonDS team
This file is part of melonDS.
@@ -19,7 +19,7 @@
#ifndef VERSION_H
#define VERSION_H
-#define MELONDS_URL "http://melonds.kuribo64.net/"
+#define MELONDS_URL "https://melonds.kuribo64.net/"
#endif // VERSION_H