aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorArisotura <thetotalworm@gmail.com>2021-10-28 18:47:13 +0200
committerGitHub <noreply@github.com>2021-10-28 18:47:13 +0200
commitff3f661bb54dcb31e2533967aa231d827d2be4b7 (patch)
treef6b9d4ea0fc42f234bb1dd4f1dc6b0db9069333e /src
parenta8613af2bd3ba0cc9d52b6a5d63899cda7ca2864 (diff)
DLDI/SD folder-sync apparatus (#1251)
guess we can finally have DLDI that isn't obtuse
Diffstat (limited to 'src')
-rw-r--r--src/ARM.cpp2
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/Config.cpp9
-rw-r--r--src/Config.h4
-rw-r--r--src/DSi_NAND.cpp67
-rw-r--r--src/DSi_SD.cpp76
-rw-r--r--src/DSi_SD.h6
-rw-r--r--src/FATStorage.cpp1112
-rw-r--r--src/FATStorage.h101
-rw-r--r--src/NDSCart.cpp111
-rw-r--r--src/NDSCart.h7
-rw-r--r--src/Platform.h24
-rw-r--r--src/fatfs/diskio.c25
-rw-r--r--src/fatfs/ff.c92
-rw-r--r--src/fatfs/ff.h52
-rw-r--r--src/frontend/Util_ROM.cpp8
-rw-r--r--src/frontend/qt_sdl/EmuSettingsDialog.cpp139
-rw-r--r--src/frontend/qt_sdl/EmuSettingsDialog.h8
-rw-r--r--src/frontend/qt_sdl/EmuSettingsDialog.ui213
-rw-r--r--src/frontend/qt_sdl/Platform.cpp45
-rw-r--r--src/frontend/qt_sdl/PlatformConfig.cpp28
-rw-r--r--src/frontend/qt_sdl/PlatformConfig.h14
22 files changed, 1917 insertions, 227 deletions
diff --git a/src/ARM.cpp b/src/ARM.cpp
index fc9c266..03844b6 100644
--- a/src/ARM.cpp
+++ b/src/ARM.cpp
@@ -461,8 +461,6 @@ void ARM::UpdateMode(u32 oldmode, u32 newmode)
break;
}
- #undef SWAP
-
if (Num == 0)
{
/*if ((newmode & 0x1F) == 0x10)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cc9ef0f..d8250ae 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -26,6 +26,7 @@ add_library(core STATIC
DSi_NWifi.cpp
DSi_SD.cpp
DSi_SPI_TSC.cpp
+ FATStorage.cpp
FIFO.h
GBACart.cpp
GPU.cpp
diff --git a/src/Config.cpp b/src/Config.cpp
index 67b2f24..8909caa 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -32,8 +32,6 @@ int ExternalBIOSEnable;
char BIOS9Path[1024];
char BIOS7Path[1024];
char FirmwarePath[1024];
-int DLDIEnable;
-char DLDISDPath[1024];
char FirmwareUsername[64];
int FirmwareLanguage;
@@ -47,8 +45,6 @@ char DSiBIOS9Path[1024];
char DSiBIOS7Path[1024];
char DSiFirmwarePath[1024];
char DSiNANDPath[1024];
-int DSiSDEnable;
-char DSiSDPath[1024];
int RandomizeMAC;
int AudioBitrate;
@@ -68,9 +64,6 @@ ConfigEntry ConfigFile[] =
{"BIOS7Path", 1, BIOS7Path, 0, "", 1023},
{"FirmwarePath", 1, FirmwarePath, 0, "", 1023},
- {"DLDIEnable", 0, &DLDIEnable, 0, NULL, 0},
- {"DLDISDPath", 1, DLDISDPath, 0, "", 1023},
-
{"FirmwareUsername", 1, FirmwareUsername, 0, "melonDS", 63},
{"FirmwareLanguage", 0, &FirmwareLanguage, 1, NULL, 0},
{"FirmwareOverrideSettings", 0, &FirmwareOverrideSettings, false, NULL, 0},
@@ -83,8 +76,6 @@ ConfigEntry ConfigFile[] =
{"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023},
{"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023},
{"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023},
- {"DSiSDEnable", 0, &DSiSDEnable, 0, NULL, 0},
- {"DSiSDPath", 1, DSiSDPath, 0, "", 1023},
{"RandomizeMAC", 0, &RandomizeMAC, 0, NULL, 0},
{"AudioBitrate", 0, &AudioBitrate, 0, NULL, 0},
diff --git a/src/Config.h b/src/Config.h
index bf7214e..5525034 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -45,8 +45,6 @@ extern int ExternalBIOSEnable;
extern char BIOS9Path[1024];
extern char BIOS7Path[1024];
extern char FirmwarePath[1024];
-extern int DLDIEnable;
-extern char DLDISDPath[1024];
extern char FirmwareUsername[64];
extern int FirmwareLanguage;
@@ -60,8 +58,6 @@ extern char DSiBIOS9Path[1024];
extern char DSiBIOS7Path[1024];
extern char DSiFirmwarePath[1024];
extern char DSiNANDPath[1024];
-extern int DSiSDEnable;
-extern char DSiSDPath[1024];
extern int RandomizeMAC;
extern int AudioBitrate;
diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp
index 917578e..f4f68d4 100644
--- a/src/DSi_NAND.cpp
+++ b/src/DSi_NAND.cpp
@@ -52,7 +52,10 @@ bool Init(FILE* nandfile, u8* es_keyY)
if (!nandfile)
return false;
- ff_disk_open(FF_ReadNAND, FF_WriteNAND);
+ fseek(nandfile, 0, SEEK_END);
+ u64 nandlen = ftell(nandfile);
+
+ ff_disk_open(FF_ReadNAND, FF_WriteNAND, (LBA_t)(nandlen>>9));
FRESULT res;
res = f_mount(&CurFS, "0:", 0);
@@ -438,7 +441,7 @@ bool ESDecrypt(u8* data, u32 len)
void ReadHardwareInfo(u8* dataS, u8* dataN)
{
- FIL file;
+ FF_FIL file;
FRESULT res;
u32 nread;
@@ -460,11 +463,11 @@ void ReadHardwareInfo(u8* dataS, u8* dataN)
void ReadUserData(u8* data)
{
- FIL file;
+ FF_FIL file;
FRESULT res;
u32 nread;
- FIL f1, f2;
+ FF_FIL f1, f2;
int v1, v2;
res = f_open(&f1, "0:/shared1/TWLCFG0.dat", FA_OPEN_EXISTING | FA_READ);
@@ -516,7 +519,7 @@ void PatchTSC()
char filename[64];
sprintf(filename, "0:/shared1/TWLCFG%d.dat", i);
- FIL file;
+ FF_FIL file;
res = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ | FA_WRITE);
if (res != FR_OK)
{
@@ -554,8 +557,8 @@ void PatchTSC()
void debug_listfiles(const char* path)
{
- DIR dir;
- FILINFO info;
+ FF_DIR dir;
+ FF_FILINFO info;
FRESULT res;
res = f_opendir(&dir, path);
@@ -564,8 +567,8 @@ void debug_listfiles(const char* path)
for (;;)
{
res = f_readdir(&dir, &info);
- if (res != FR_OK) return;
- if (!info.fname[0]) return;
+ if (res != FR_OK) break;
+ if (!info.fname[0]) break;
char fullname[512];
sprintf(fullname, "%s/%s", path, info.fname);
@@ -582,7 +585,7 @@ void debug_listfiles(const char* path)
bool ImportFile(const char* path, const char* in)
{
- FIL file;
+ FF_FIL file;
FILE* fin;
FRESULT res;
@@ -601,14 +604,14 @@ bool ImportFile(const char* path, const char* in)
return false;
}
- u8 buf[0x200];
- for (u32 i = 0; i < len; i += 0x200)
+ u8 buf[0x1000];
+ for (u32 i = 0; i < len; i += 0x1000)
{
u32 blocklen;
- if ((i + 0x200) > len)
+ if ((i + 0x1000) > len)
blocklen = len - i;
else
- blocklen = 0x200;
+ blocklen = 0x1000;
u32 nwrite;
fread(buf, blocklen, 1, fin);
@@ -623,7 +626,7 @@ bool ImportFile(const char* path, const char* in)
bool ExportFile(const char* path, const char* out)
{
- FIL file;
+ FF_FIL file;
FILE* fout;
FRESULT res;
@@ -640,14 +643,14 @@ bool ExportFile(const char* path, const char* out)
return false;
}
- u8 buf[0x200];
- for (u32 i = 0; i < len; i += 0x200)
+ u8 buf[0x1000];
+ for (u32 i = 0; i < len; i += 0x1000)
{
u32 blocklen;
- if ((i + 0x200) > len)
+ if ((i + 0x1000) > len)
blocklen = len - i;
else
- blocklen = 0x200;
+ blocklen = 0x1000;
u32 nread;
f_read(&file, buf, blocklen, &nread);
@@ -662,7 +665,7 @@ bool ExportFile(const char* path, const char* out)
void RemoveFile(const char* path)
{
- FILINFO info;
+ FF_FILINFO info;
FRESULT res = f_stat(path, &info);
if (res != FR_OK) return;
@@ -674,8 +677,8 @@ void RemoveFile(const char* path)
void RemoveDir(const char* path)
{
- DIR dir;
- FILINFO info;
+ FF_DIR dir;
+ FF_FILINFO info;
FRESULT res;
res = f_stat(path, &info);
@@ -731,7 +734,7 @@ u32 GetTitleVersion(u32 category, u32 titleid)
FRESULT res;
char path[256];
sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid);
- FIL file;
+ FF_FIL file;
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
if (res != FR_OK)
return 0xFFFFFFFF;
@@ -749,7 +752,7 @@ u32 GetTitleVersion(u32 category, u32 titleid)
void ListTitles(u32 category, std::vector<u32>& titlelist)
{
FRESULT res;
- DIR titledir;
+ FF_DIR titledir;
char path[256];
sprintf(path, "0:/title/%08x", category);
@@ -762,7 +765,7 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
for (;;)
{
- FILINFO info;
+ FF_FILINFO info;
f_readdir(&titledir, &info);
if (!info.fname[0])
break;
@@ -779,7 +782,7 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
continue;
sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
- FILINFO appinfo;
+ FF_FILINFO appinfo;
res = f_stat(path, &appinfo);
if (res != FR_OK)
continue;
@@ -814,7 +817,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND
char path[256];
sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
- FIL file;
+ FF_FIL file;
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
if (res != FR_OK)
return;
@@ -842,7 +845,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND
bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version)
{
- FIL file;
+ FF_FIL file;
FRESULT res;
u32 nwrite;
@@ -908,7 +911,7 @@ bool CreateSaveFile(const char* path, u32 len)
if (len == 0x4000) totsec16 = 27;
else totsec16 = len >> 9;
- FIL file;
+ FF_FIL file;
FRESULT res;
u32 nwrite;
@@ -966,11 +969,11 @@ bool ImportTitle(const char* appfile, u8* tmd, bool readonly)
printf("Title ID: %08x/%08x\n", titleid0, titleid1);
FRESULT res;
- DIR ticketdir;
- FILINFO info;
+ FF_DIR ticketdir;
+ FF_FILINFO info;
char fname[128];
- FIL file;
+ FF_FIL file;
u32 nwrite;
// ticket
diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp
index 01b6442..20c735d 100644
--- a/src/DSi_SD.cpp
+++ b/src/DSi_SD.cpp
@@ -112,9 +112,20 @@ void DSi_SDHost::Reset()
DSi_MMCStorage* sd;
DSi_MMCStorage* mmc;
- if (Config::DSiSDEnable)
+ if (Platform::GetConfigBool(Platform::DSiSD_Enable))
{
- sd = new DSi_MMCStorage(this, false, DSi::SDIOFile);
+ std::string folderpath;
+ if (Platform::GetConfigBool(Platform::DSiSD_FolderSync))
+ folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath);
+ else
+ folderpath = "";
+
+ sd = new DSi_MMCStorage(this,
+ false,
+ Platform::GetConfigString(Platform::DSiSD_ImagePath),
+ (u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024,
+ Platform::GetConfigBool(Platform::DSiSD_ReadOnly),
+ folderpath);
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
sd->SetCID(sd_cid);
}
@@ -427,7 +438,10 @@ u16 DSi_SDHost::Read(u32 addr)
if (!Num)
{
if (Ports[0]) // basic check of whether the SD card is inserted
- ret |= 0x00B0;
+ {
+ ret |= 0x0030;
+ if (!Ports[0]->ReadOnly) ret |= 0x0080;
+ }
else
ret |= 0x0008;
}
@@ -714,14 +728,34 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard")
-DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file) : DSi_SDDevice(host)
+DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file)
+ : DSi_SDDevice(host)
{
Internal = internal;
File = file;
+ SD = nullptr;
+}
+
+DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir)
+ : DSi_SDDevice(host)
+{
+ Internal = internal;
+ File = nullptr;
+
+ SD = new FATStorage(filename, size, readonly, sourcedir);
+ SD->Open();
+
+ ReadOnly = readonly;
}
DSi_MMCStorage::~DSi_MMCStorage()
-{}
+{
+ if (SD)
+ {
+ SD->Close();
+ delete SD;
+ }
+}
void DSi_MMCStorage::Reset()
{
@@ -947,13 +981,17 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
len = Host->GetTransferrableLen(len);
u8 data[0x200];
- if (File)
+ if (SD)
+ {
+ SD->ReadSectors((u32)(addr >> 9), 1, data);
+ }
+ else if (File)
{
fseek(File, addr, SEEK_SET);
- fread(data, 1, len, File);
+ fread(&data[addr & 0x1FF], 1, len, File);
}
- return Host->DataRX(data, len);
+ return Host->DataRX(&data[addr & 0x1FF], len);
}
u32 DSi_MMCStorage::WriteBlock(u64 addr)
@@ -962,12 +1000,26 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
len = Host->GetTransferrableLen(len);
u8 data[0x200];
- if ((len = Host->DataTX(data, len)))
+ if (len < 0x200)
{
- if (File)
+ if (SD)
{
- fseek(File, addr, SEEK_SET);
- fwrite(data, 1, len, File);
+ SD->ReadSectors((u32)(addr >> 9), 1, data);
+ }
+ }
+ if ((len = Host->DataTX(&data[addr & 0x1FF], len)))
+ {
+ if (!ReadOnly)
+ {
+ if (SD)
+ {
+ SD->WriteSectors((u32)(addr >> 9), 1, data);
+ }
+ else if (File)
+ {
+ fseek(File, addr, SEEK_SET);
+ fwrite(&data[addr & 0x1FF], 1, len, File);
+ }
}
}
diff --git a/src/DSi_SD.h b/src/DSi_SD.h
index 407a7f1..1c0f7ce 100644
--- a/src/DSi_SD.h
+++ b/src/DSi_SD.h
@@ -21,6 +21,7 @@
#include <string.h>
#include "FIFO.h"
+#include "FATStorage.h"
class DSi_SDDevice;
@@ -102,7 +103,7 @@ private:
class DSi_SDDevice
{
public:
- DSi_SDDevice(DSi_SDHost* host) { Host = host; IRQ = false; }
+ DSi_SDDevice(DSi_SDHost* host) { Host = host; IRQ = false; ReadOnly = false; }
virtual ~DSi_SDDevice() {}
virtual void Reset() = 0;
@@ -111,6 +112,7 @@ public:
virtual void ContinueTransfer() = 0;
bool IRQ;
+ bool ReadOnly;
protected:
DSi_SDHost* Host;
@@ -121,6 +123,7 @@ 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, u64 size, bool readonly, std::string sourcedir);
~DSi_MMCStorage();
void Reset();
@@ -135,6 +138,7 @@ public:
private:
bool Internal;
FILE* File;
+ FATStorage* SD;
u8 CID[16];
u8 CSD[16];
diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp
new file mode 100644
index 0000000..b59f502
--- /dev/null
+++ b/src/FATStorage.cpp
@@ -0,0 +1,1112 @@
+/*
+ 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 <string.h>
+#include <dirent.h>
+#include <inttypes.h>
+#include <vector>
+
+#include "FATStorage.h"
+#include "Platform.h"
+
+namespace fs = std::filesystem;
+
+
+// really, Windows?
+#ifdef __WIN32__
+ #define melon_fseek _fseeki64
+ #define melon_ftell _ftelli64
+#else
+ #define melon_fseek fseek
+ #define melon_ftell ftell
+#endif // __WIN32__
+
+
+FATStorage::FATStorage(std::string filename, u64 size, bool readonly, std::string sourcedir)
+{
+ ReadOnly = readonly;
+ Load(filename, size, sourcedir);
+
+ File = nullptr;
+}
+
+FATStorage::~FATStorage()
+{
+ if (!ReadOnly) Save();
+}
+
+
+bool FATStorage::Open()
+{
+ File = Platform::OpenLocalFile(FilePath.c_str(), "r+b");
+ if (!File)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void FATStorage::Close()
+{
+ if (File) fclose(File);
+ File = nullptr;
+}
+
+
+bool FATStorage::InjectFile(std::string path, u8* data, u32 len)
+{
+ if (!File) return false;
+ if (FF_File) return false;
+
+ FF_File = File;
+ FF_FileSize = FileSize;
+ ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9));
+
+ FRESULT res;
+ FATFS fs;
+
+ res = f_mount(&fs, "0:", 1);
+ if (res != FR_OK)
+ {
+ ff_disk_close();
+ FF_File = nullptr;
+ return false;
+ }
+
+ path = "0:/" + path;
+ FF_FIL file;
+ res = f_open(&file, path.c_str(), FA_CREATE_ALWAYS | FA_WRITE);
+ if (res != FR_OK)
+ {
+ f_unmount("0:");
+ ff_disk_close();
+ FF_File = nullptr;
+ return false;
+ }
+
+ u32 nwrite;
+ f_write(&file, data, len, &nwrite);
+ f_close(&file);
+ printf("burped hard: %d/%d\n", nwrite, len);
+
+ f_unmount("0:");
+ ff_disk_close();
+ FF_File = nullptr;
+ return nwrite==len;
+}
+
+
+u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data)
+{
+ return ReadSectorsInternal(File, FileSize, start, num, data);
+}
+
+u32 FATStorage::WriteSectors(u32 start, u32 num, u8* data)
+{
+ if (ReadOnly) return 0;
+ return WriteSectorsInternal(File, FileSize, start, num, data);
+}
+
+
+FILE* FATStorage::FF_File;
+u64 FATStorage::FF_FileSize;
+
+UINT FATStorage::FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num)
+{
+ return ReadSectorsInternal(FF_File, FF_FileSize, sector, num, buf);
+}
+
+UINT FATStorage::FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num)
+{
+ return WriteSectorsInternal(FF_File, FF_FileSize, sector, num, buf);
+}
+
+
+u32 FATStorage::ReadSectorsInternal(FILE* file, u64 filelen, u32 start, u32 num, u8* data)
+{
+ if (!file) return 0;
+
+ u64 addr = start * 0x200ULL;
+ u32 len = num * 0x200;
+
+ if ((addr+len) > filelen)
+ {
+ if (addr >= filelen) return 0;
+ len = filelen - addr;
+ num = len >> 9;
+ }
+
+ melon_fseek(file, addr, SEEK_SET);
+
+ u32 res = fread(data, 0x200, num, file);
+ if (res < num)
+ {
+ if (feof(file))
+ {
+ memset(&data[0x200*res], 0, 0x200*(num-res));
+ return num;
+ }
+ }
+
+ return res;
+}
+
+u32 FATStorage::WriteSectorsInternal(FILE* file, u64 filelen, u32 start, u32 num, u8* data)
+{
+ if (!file) return 0;
+
+ u64 addr = start * 0x200ULL;
+ u32 len = num * 0x200;
+
+ if ((addr+len) > filelen)
+ {
+ if (addr >= filelen) return 0;
+ len = filelen - addr;
+ num = len >> 9;
+ }
+
+ melon_fseek(file, addr, SEEK_SET);
+
+ u32 res = fwrite(data, 0x200, num, file);
+ return res;
+}
+
+
+void FATStorage::LoadIndex()
+{
+ DirIndex.clear();
+ FileIndex.clear();
+
+ FILE* f = Platform::OpenLocalFile(IndexPath.c_str(), "r");
+ if (!f) return;
+
+ char linebuf[1536];
+ while (!feof(f))
+ {
+ if (fgets(linebuf, 1536, f) == nullptr)
+ break;
+
+ if (linebuf[0] == 'S')
+ {
+ u64 fsize;
+ int ret = sscanf(linebuf, "SIZE %" PRIu64, &fsize);
+ if (ret < 1) continue;
+
+ FileSize = fsize;
+ }
+ else if (linebuf[0] == 'D')
+ {
+ u32 readonly;
+ char fpath[1536] = {0};
+ int ret = sscanf(linebuf, "DIR %u %[^\t\r\n]",
+ &readonly, fpath);
+ if (ret < 2) continue;
+
+ for (int i = 0; i < 1536 && fpath[i] != '\0'; i++)
+ {
+ if (fpath[i] == '\\')
+ fpath[i] = '/';
+ }
+
+ DirIndexEntry entry;
+ entry.Path = fpath;
+ entry.IsReadOnly = readonly!=0;
+
+ DirIndex[entry.Path] = entry;
+ }
+ else if (linebuf[0] == 'F')
+ {
+ u32 readonly;
+ u64 fsize;
+ s64 lastmodified;
+ u32 lastmod_internal;
+ char fpath[1536] = {0};
+ int ret = sscanf(linebuf, "FILE %u %" PRIu64 " %" PRId64 " %u %[^\t\r\n]",
+ &readonly, &fsize, &lastmodified, &lastmod_internal, fpath);
+ if (ret < 5) continue;
+
+ for (int i = 0; i < 1536 && fpath[i] != '\0'; i++)
+ {
+ if (fpath[i] == '\\')
+ fpath[i] = '/';
+ }
+
+ FileIndexEntry entry;
+ entry.Path = fpath;
+ entry.IsReadOnly = readonly!=0;
+ entry.Size = fsize;
+ entry.LastModified = lastmodified;
+ entry.LastModifiedInternal = lastmod_internal;
+
+ FileIndex[entry.Path] = entry;
+ }
+ }
+
+ fclose(f);
+
+ // ensure the indexes are sane
+
+ std::vector<std::string> removelist;
+
+ for (const auto& [key, val] : DirIndex)
+ {
+ std::string path = val.Path;
+
+ if ((path.find("/./") != std::string::npos) ||
+ (path.find("/../") != std::string::npos) ||
+ (path.substr(0,2) == "./") ||
+ (path.substr(0,3) == "../"))
+ {
+ removelist.push_back(key);
+ continue;
+ }
+
+ int sep = path.rfind('/');
+ if (sep == std::string::npos) continue;
+
+ path = path.substr(0, sep);
+ if (DirIndex.count(path) < 1)
+ {
+ removelist.push_back(key);
+ }
+ }
+
+ for (const auto& key : removelist)
+ {
+ DirIndex.erase(key);
+ }
+
+ removelist.clear();
+
+ for (const auto& [key, val] : FileIndex)
+ {
+ std::string path = val.Path;
+
+ if ((path.find("/./") != std::string::npos) ||
+ (path.find("/../") != std::string::npos) ||
+ (path.substr(0,2) == "./") ||
+ (path.substr(0,3) == "../"))
+ {
+ removelist.push_back(key);
+ continue;
+ }
+
+ int sep = path.rfind('/');
+ if (sep == std::string::npos) continue;
+
+ path = path.substr(0, sep);
+ if (DirIndex.count(path) < 1)
+ {
+ removelist.push_back(key);
+ }
+ }
+
+ for (const auto& key : removelist)
+ {
+ FileIndex.erase(key);
+ }
+}
+
+void FATStorage::SaveIndex()
+{
+ FILE* f = Platform::OpenLocalFile(IndexPath.c_str(), "w");
+ if (!f) return;
+
+ fprintf(f, "SIZE %" PRIu64 "\r\n", FileSize);
+
+ for (const auto& [key, val] : DirIndex)
+ {
+ fprintf(f, "DIR %u %s\r\n",
+ val.IsReadOnly?1:0, val.Path.c_str());
+ }
+
+ for (const auto& [key, val] : FileIndex)
+ {
+ fprintf(f, "FILE %u %" PRIu64 " %" PRId64 " %u %s\r\n",
+ val.IsReadOnly?1:0, val.Size, val.LastModified, val.LastModifiedInternal, val.Path.c_str());
+ }
+
+ fclose(f);
+}
+
+
+bool FATStorage::ExportFile(std::string path, fs::path out)
+{
+ FF_FIL file;
+ FILE* fout;
+ FRESULT res;
+
+ res = f_open(&file, path.c_str(), FA_OPEN_EXISTING | FA_READ);
+ if (res != FR_OK)
+ return false;
+
+ u32 len = f_size(&file);
+
+ if (fs::exists(out))
+ {
+ std::error_code err;
+ fs::permissions(out,
+ fs::perms::owner_read | fs::perms::owner_write,
+ fs::perm_options::add,
+ err);
+ }
+
+ fout = Platform::OpenFile(out.u8string().c_str(), "wb");
+ if (!fout)
+ {
+ f_close(&file);
+ return false;
+ }
+
+ u8 buf[0x1000];
+ for (u32 i = 0; i < len; i += 0x1000)
+ {
+ u32 blocklen;
+ if ((i + 0x1000) > len)
+ blocklen = len - i;
+ else
+ blocklen = 0x1000;
+
+ u32 nread;
+ f_read(&file, buf, blocklen, &nread);
+ fwrite(buf, blocklen, 1, fout);
+ }
+
+ fclose(fout);
+ f_close(&file);
+
+ return true;
+}
+
+void FATStorage::ExportDirectory(std::string path, std::string outbase, int level)
+{
+ if (level >= 32) return;
+
+ FF_DIR dir;
+ FF_FILINFO info;
+ FRESULT res;
+
+ std::string fullpath = "0:/" + path;
+ res = f_opendir(&dir, fullpath.c_str());
+ if (res != FR_OK) return;
+
+ std::vector<std::string> subdirlist;
+
+ for (;;)
+ {
+ res = f_readdir(&dir, &info);
+ if (res != FR_OK) break;
+ if (!info.fname[0]) break;
+
+ std::string fullpath = path + info.fname;
+ fs::path outpath = fs::u8path(outbase + "/" + fullpath);
+
+ if (info.fattrib & AM_DIR)
+ {
+ if (DirIndex.count(fullpath) < 1)
+ {
+ std::error_code err;
+ fs::create_directory(outpath, err);
+
+ DirIndexEntry entry;
+ entry.Path = fullpath;
+ entry.IsReadOnly = (info.fattrib & AM_RDO) != 0;
+
+ DirIndex[entry.Path] = entry;
+ }
+
+ subdirlist.push_back(fullpath);
+ }
+ else
+ {
+ bool doexport = false;
+
+ if (FileIndex.count(fullpath) < 1)
+ {
+ doexport = true;
+
+ FileIndexEntry entry;
+ entry.Path = fullpath;
+ entry.IsReadOnly = (info.fattrib & AM_RDO) != 0;
+ entry.Size = info.fsize;
+ entry.LastModifiedInternal = (info.fdate << 16) | info.ftime;
+
+ FileIndex[entry.Path] = entry;
+ }
+ else
+ {
+ u32 lastmod = (info.fdate << 16) | info.ftime;
+
+ FileIndexEntry& entry = FileIndex[fullpath];
+ if ((info.fsize != entry.Size) || (lastmod != entry.LastModifiedInternal))
+ doexport = true;
+
+ entry.Size = info.fsize;
+ entry.LastModifiedInternal = lastmod;
+ }
+
+ if (doexport)
+ {
+ if (ExportFile("0:/"+fullpath, outpath))
+ {
+ fs::file_time_type modtime = fs::last_write_time(outpath);
+ s64 modtime_raw = std::chrono::duration_cast<std::chrono::seconds>(modtime.time_since_epoch()).count();
+
+ FileIndexEntry& entry = FileIndex[fullpath];
+ entry.LastModified = modtime_raw;
+ }
+ else
+ {
+ // ??????
+ }
+ }
+ }
+
+ std::error_code err;
+ fs::permissions(outpath,
+ fs::perms::owner_read | fs::perms::owner_write,
+ (info.fattrib & AM_RDO) ? fs::perm_options::remove : fs::perm_options::add,
+ err);
+ }
+
+ f_closedir(&dir);
+
+ for (auto& entry : subdirlist)
+ {
+ ExportDirectory(entry+"/", outbase, level+1);
+ }
+}
+
+bool FATStorage::DeleteHostDirectory(std::string path, std::string outbase, int level)
+{
+ if (level >= 32) return false;
+
+ std::vector<std::string> filedeletelist;
+ std::vector<std::string> dirdeletelist;
+
+ int outlen = outbase.length();
+ for (auto& entry : fs::directory_iterator(outbase + "/" + path))
+ {
+ std::string fullpath = entry.path().string();
+ std::string innerpath = fullpath.substr(outlen);
+ if (innerpath[0] == '/' || innerpath[0] == '\\')
+ innerpath = innerpath.substr(1);
+
+ int ilen = innerpath.length();
+ for (int i = 0; i < ilen; i++)
+ {
+ if (innerpath[i] == '\\')
+ innerpath[i] = '/';
+ }
+
+ if (entry.is_directory())
+ {
+ dirdeletelist.push_back(innerpath);
+ }
+ else
+ {
+ filedeletelist.push_back(innerpath);
+ }
+ }
+
+ for (const auto& key : filedeletelist)
+ {
+ fs::path fullpath = fs::u8path(outbase + "/" + key);
+ std::error_code err;
+ fs::permissions(fullpath,
+ fs::perms::owner_read | fs::perms::owner_write,
+ fs::perm_options::add,
+ err);
+ if (!fs::remove(fullpath))
+ return false;
+
+ FileIndex.erase(key);
+ }
+
+ for (const auto& key : dirdeletelist)
+ {
+ if (!DeleteHostDirectory(key, outbase, level+1))
+ return false;
+ }
+
+ {
+ fs::path fullpath = fs::u8path(outbase + "/" + path);
+
+ std::error_code err;
+ fs::permissions(fullpath,
+ fs::perms::owner_read | fs::perms::owner_write,
+ fs::perm_options::add,
+ err);
+ if (!fs::remove(fullpath))
+ return false;
+
+ DirIndex.erase(path);
+ }
+
+ return true;
+}
+
+void FATStorage::ExportChanges(std::string outbase)
+{
+ // reflect changes in the FAT volume to the host filesystem
+ // * delete directories and files that exist in the index but not in the volume
+ // * copy files to the host FS if they exist within the index and their size or
+ // internal last-modified time is different
+ // * index and copy directories and files that exist in the volume but not in
+ // the index
+
+ std::vector<std::string> deletelist;
+
+ for (const auto& [key, val] : FileIndex)
+ {
+ std::string innerpath = "0:/" + val.Path;
+ FF_FILINFO finfo;
+ FRESULT res = f_stat(innerpath.c_str(), &finfo);
+ if (res == FR_OK)
+ {
+ if (finfo.fattrib & AM_DIR)
+ {
+ deletelist.push_back(key);
+ }
+ }
+ else if (res == FR_NO_FILE || res == FR_NO_PATH)
+ {
+ deletelist.push_back(key);
+ }
+ }
+
+ for (const auto& key : deletelist)
+ {
+ fs::path fullpath = fs::u8path(outbase + "/" + key);
+
+ std::error_code err;
+ fs::permissions(fullpath,
+ fs::perms::owner_read | fs::perms::owner_write,
+ fs::perm_options::add,
+ err);
+ fs::remove(fullpath);
+
+ FileIndex.erase(key);
+ }
+
+ deletelist.clear();
+
+ for (const auto& [key, val] : DirIndex)
+ {
+ std::string innerpath = "0:/" + val.Path;
+ FF_FILINFO finfo;
+ FRESULT res = f_stat(innerpath.c_str(), &finfo);
+ if (res == FR_OK)
+ {
+ if (!(finfo.fattrib & AM_DIR))
+ {
+ deletelist.push_back(key);
+ }
+ }
+ else if (res == FR_NO_FILE || res == FR_NO_PATH)
+ {
+ deletelist.push_back(key);
+ }
+ }
+
+ for (const auto& key : deletelist)
+ {
+ DeleteHostDirectory(key, outbase, 0);
+ }
+
+ ExportDirectory("", outbase, 0);
+}
+
+
+bool FATStorage::CanFitFile(u32 len)
+{
+ FATFS* fs;
+ DWORD freeclusters;
+ FRESULT res;
+
+ res = f_getfree("0:", &freeclusters, &fs);
+ if (res != FR_OK) return false;
+
+ u32 clustersize = fs->csize * 0x200;
+
+ len = (len + clustersize - 1) / clustersize;
+ return (freeclusters >= len);
+}
+
+bool FATStorage::DeleteDirectory(std::string path, int level)
+{
+ if (level >= 32) return false;
+ if (path.length() < 1) return false;
+
+ FF_DIR dir;
+ FF_FILINFO info;
+ FRESULT res;
+
+ std::string fullpath = "0:/" + path;
+ f_chmod(fullpath.c_str(), 0, AM_RDO);
+ res = f_opendir(&dir, fullpath.c_str());
+ if (res != FR_OK) return false;
+
+ std::vector<std::string> deletelist;
+ std::vector<std::string> subdirlist;
+ int survivors = 0;
+
+ for (;;)
+ {
+ res = f_readdir(&dir, &info);
+ if (res != FR_OK) break;
+ if (!info.fname[0]) break;
+
+ std::string fullpath = path + info.fname;
+
+ if (info.fattrib & AM_DIR)
+ {
+ subdirlist.push_back(fullpath);
+ }
+ else
+ {
+ deletelist.push_back(fullpath);
+ }
+ }
+
+ f_closedir(&dir);
+
+ for (auto& entry : deletelist)
+ {
+ std::string fullpath = "0:/" + entry;
+ f_chmod(fullpath.c_str(), 0, AM_RDO);
+ res = f_unlink(fullpath.c_str());
+ if (res != FR_OK) return false;
+ }
+
+ for (auto& entry : subdirlist)
+ {
+ if (!DeleteDirectory(entry+"/", level+1)) return false;
+
+ std::string fullpath = "0:/" + entry;
+ f_chmod(fullpath.c_str(), 0, AM_RDO);
+ res = f_unlink(fullpath.c_str());
+ if (res != FR_OK) return false;
+ }
+
+ res = f_unlink(fullpath.c_str());
+ if (res != FR_OK) return false;
+
+ return true;
+}
+
+void FATStorage::CleanupDirectory(std::string sourcedir, std::string path, int level)
+{
+ if (level >= 32) return;
+
+ FF_DIR dir;
+ FF_FILINFO info;
+ FRESULT res;
+
+ std::string fullpath = "0:/" + path;
+ res = f_opendir(&dir, fullpath.c_str());
+ if (res != FR_OK) return;
+
+ std::vector<std::string> filedeletelist;
+ std::vector<std::string> dirdeletelist;
+ std::vector<std::string> subdirlist;
+
+ for (;;)
+ {
+ res = f_readdir(&dir, &info);
+ if (res != FR_OK) break;
+ if (!info.fname[0]) break;
+
+ std::string fullpath = path + info.fname;
+
+ if (info.fattrib & AM_DIR)
+ {
+ if (DirIndex.count(fullpath) < 1)
+ dirdeletelist.push_back(fullpath);
+ else if (!fs::is_directory(fs::u8path(sourcedir+"/"+fullpath)))
+ {
+ DirIndex.erase(fullpath);
+ dirdeletelist.push_back(fullpath);
+ }
+ else
+ subdirlist.push_back(fullpath);
+ }
+ else
+ {
+ if (FileIndex.count(fullpath) < 1)
+ filedeletelist.push_back(fullpath);
+ else if (!fs::is_regular_file(fs::u8path(sourcedir+"/"+fullpath)))
+ {
+ FileIndex.erase(fullpath);
+ filedeletelist.push_back(fullpath);
+ }
+ }
+ }
+
+ f_closedir(&dir);
+
+ for (auto& entry : filedeletelist)
+ {
+ std::string fullpath = "0:/" + entry;
+ f_chmod(fullpath.c_str(), 0, AM_RDO);
+ f_unlink(fullpath.c_str());
+ }
+
+ for (auto& entry : dirdeletelist)
+ {
+ DeleteDirectory(entry+"/", level+1);
+ }
+
+ for (auto& entry : subdirlist)
+ {
+ CleanupDirectory(sourcedir, entry+"/", level+1);
+ }
+}
+
+bool FATStorage::ImportFile(std::string path, fs::path in)
+{
+ FF_FIL file;
+ FILE* fin;
+ FRESULT res;
+
+ fin = Platform::OpenFile(in.u8string().c_str(), "rb");
+ if (!fin)
+ return false;
+
+ fseek(fin, 0, SEEK_END);
+ u32 len = (u32)ftell(fin);
+ fseek(fin, 0, SEEK_SET);
+
+ if (!CanFitFile(len))
+ {
+ fclose(fin);
+ return false;
+ }
+
+ res = f_open(&file, path.c_str(), FA_CREATE_ALWAYS | FA_WRITE);
+ if (res != FR_OK)
+ {
+ fclose(fin);
+ return false;
+ }
+
+ u8 buf[0x1000];
+ for (u32 i = 0; i < len; i += 0x1000)
+ {
+ u32 blocklen;
+ if ((i + 0x1000) > len)
+ blocklen = len - i;
+ else
+ blocklen = 0x1000;
+
+ u32 nwrite;
+ fread(buf, blocklen, 1, fin);
+ f_write(&file, buf, blocklen, &nwrite);
+ }
+
+ fclose(fin);
+ f_close(&file);
+
+ return true;
+}
+
+bool FATStorage::ImportDirectory(std::string sourcedir)
+{
+ // remove whatever isn't in the index
+ CleanupDirectory(sourcedir, "", 0);
+
+ int srclen = sourcedir.length();
+
+ // iterate through the host directory:
+ // * directories will be added if they aren't in the index
+ // * files will be added if they aren't in the index, or if the size or last-modified-date don't match
+ for (auto& entry : fs::recursive_directory_iterator(sourcedir))
+ {
+ std::string fullpath = entry.path().u8string();
+ std::string innerpath = fullpath.substr(srclen);
+ if (innerpath[0] == '/' || innerpath[0] == '\\')
+ innerpath = innerpath.substr(1);
+
+ int ilen = innerpath.length();
+ for (int i = 0; i < ilen; i++)
+ {
+ if (innerpath[i] == '\\')
+ innerpath[i] = '/';
+ }
+
+ bool readonly = (entry.status().permissions() & fs::perms::owner_write) == fs::perms::none;
+
+ if (entry.is_directory())
+ {
+ if (DirIndex.count(innerpath) < 1)
+ {
+ DirIndexEntry ientry;
+ ientry.Path = innerpath;
+ ientry.IsReadOnly = readonly;
+
+ innerpath = "0:/" + innerpath;
+ FRESULT res = f_mkdir(innerpath.c_str());
+ if (res == FR_OK)
+ {
+ DirIndex[ientry.Path] = ientry;
+ }
+ }
+ }
+ else if (entry.is_regular_file())
+ {
+ u64 filesize = entry.file_size();
+
+ auto lastmodified = entry.last_write_time();
+ s64 lastmodified_raw = std::chrono::duration_cast<std::chrono::seconds>(lastmodified.time_since_epoch()).count();
+
+ bool import = false;
+ if (FileIndex.count(innerpath) < 1)
+ {
+ import = true;
+ }
+ else
+ {
+ FileIndexEntry& chk = FileIndex[innerpath];
+ if (chk.Size != filesize) import = true;
+ if (chk.LastModified != lastmodified_raw) import = true;
+ }
+
+ if (import)
+ {
+ FileIndexEntry ientry;
+ ientry.Path = innerpath;
+ ientry.IsReadOnly = readonly;
+ ientry.Size = filesize;
+ ientry.LastModified = lastmodified_raw;
+
+ innerpath = "0:/" + innerpath;
+ if (ImportFile(innerpath, entry.path()))
+ {
+ FF_FILINFO finfo;
+ f_stat(innerpath.c_str(), &finfo);
+
+ ientry.LastModifiedInternal = (finfo.fdate << 16) | finfo.ftime;
+
+ FileIndex[ientry.Path] = ientry;
+ }
+ }
+ }
+
+ f_chmod(innerpath.c_str(), readonly?AM_RDO:0, AM_RDO);
+ }
+
+ SaveIndex();
+
+ return true;
+}
+
+u64 FATStorage::GetDirectorySize(std::string sourcedir)
+{
+ u64 ret = 0;
+ u32 csize = 0x1000; // this is an estimate
+
+ for (auto& entry : fs::recursive_directory_iterator(sourcedir))
+ {
+ if (entry.is_directory())
+ {
+ ret += csize;
+ }
+ else if (entry.is_regular_file())
+ {
+ u64 filesize = entry.file_size();
+
+ filesize = (filesize + (csize-1)) & ~(csize-1);
+ ret += filesize;
+ }
+ }
+
+ return ret;
+}
+
+bool FATStorage::Load(std::string filename, u64 size, std::string sourcedir)
+{
+ FilePath = filename;
+ FileSize = size;
+ SourceDir = sourcedir;
+
+ bool hasdir = !sourcedir.empty();
+
+ // 'auto' size management: (size=0)
+ // * if an index exists: the size from the index is used
+ // * if no index, and an image file exists: the file size is used
+ // * if no image: if sourcing from a directory, size is calculated from that
+ // with a minimum 128MB extra, otherwise size is defaulted to 512MB
+
+ bool isnew = false;
+ FF_File = Platform::OpenLocalFile(filename.c_str(), "r+b");
+ if (!FF_File)
+ {
+ FF_File = Platform::OpenLocalFile(filename.c_str(), "w+b");
+ if (!FF_File)
+ return false;
+
+ isnew = true;
+ }
+
+ IndexPath = FilePath + ".idx";
+ if (isnew)
+ {
+ DirIndex.clear();
+ FileIndex.clear();
+ SaveIndex();
+ }
+ else
+ {
+ LoadIndex();
+
+ if (FileSize == 0)
+ {
+ melon_fseek(FF_File, 0, SEEK_END);
+ FileSize = melon_ftell(FF_File);
+ printf("FILESIZE DETERMINED TO BE %016llX (%d) (%d)\n", FileSize, sizeof(long), errno);
+ }
+ }
+
+ bool needformat = false;
+ FATFS fs;
+ FRESULT res;
+
+ if (FileSize == 0)
+ {
+ needformat = true;
+ }
+ else
+ {
+ FF_FileSize = FileSize;
+ ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9));
+
+ res = f_mount(&fs, "0:", 1);
+ if (res != FR_OK)
+ {
+ needformat = true;
+ }
+ else if (size > 0 && size != FileSize)
+ {
+ needformat = true;
+ }
+ }
+
+ if (needformat)
+ {
+ FileSize = size;
+ if (FileSize == 0)
+ {
+ if (hasdir)
+ {
+ FileSize = GetDirectorySize(sourcedir);
+ FileSize += 0x8000000ULL; // 128MB leeway
+
+ // make it a power of two
+ FileSize |= (FileSize >> 1);
+ FileSize |= (FileSize >> 2);
+ FileSize |= (FileSize >> 4);
+ FileSize |= (FileSize >> 8);
+ FileSize |= (FileSize >> 16);
+ FileSize |= (FileSize >> 32);
+ FileSize++;
+ }
+ else
+ FileSize = 0x20000000ULL; // 512MB
+ }
+
+ FF_FileSize = FileSize;
+ ff_disk_close();
+ ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9));
+
+ DirIndex.clear();
+ FileIndex.clear();
+ SaveIndex();
+
+ FF_MKFS_PARM fsopt;
+
+ // FAT type: we force it to FAT32 for any volume that is 1GB or more
+ // libfat attempts to determine the FAT type from the volume size and other parameters
+ // which can lead to it trying to interpret a FAT16 volume as FAT32
+ if (FileSize >= 0x40000000ULL)
+ fsopt.fmt = FM_FAT32;
+ else
+ fsopt.fmt = FM_FAT;
+
+ fsopt.au_size = 0;
+ fsopt.align = 1;
+ fsopt.n_fat = 1;
+ fsopt.n_root = 512;
+
+ BYTE workbuf[FF_MAX_SS];
+ res = f_mkfs("0:", &fsopt, workbuf, sizeof(workbuf));
+
+ if (res == FR_OK)
+ res = f_mount(&fs, "0:", 1);
+ }
+
+ if (res == FR_OK)
+ {
+ if (hasdir)
+ ImportDirectory(sourcedir);
+ }
+
+ f_unmount("0:");
+
+ ff_disk_close();
+ fclose(FF_File);
+ FF_File = nullptr;
+
+ return true;
+}
+
+bool FATStorage::Save()
+{
+ FF_File = Platform::OpenLocalFile(FilePath.c_str(), "r+b");
+ if (!FF_File)
+ {
+ return false;
+ }
+
+ FF_FileSize = FileSize;
+ ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9));
+
+ FRESULT res;
+ FATFS fs;
+
+ res = f_mount(&fs, "0:", 1);
+ if (res != FR_OK)
+ {
+ ff_disk_close();
+ fclose(FF_File);
+ FF_File = nullptr;
+ return false;
+ }
+
+ ExportChanges(SourceDir);
+
+ SaveIndex();
+
+ f_unmount("0:");
+
+ ff_disk_close();
+ fclose(FF_File);
+ FF_File = nullptr;
+
+ return true;
+}
diff --git a/src/FATStorage.h b/src/FATStorage.h
new file mode 100644
index 0000000..3f3afa4
--- /dev/null
+++ b/src/FATStorage.h
@@ -0,0 +1,101 @@
+/*
+ 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 FATSTORAGE_H
+#define FATSTORAGE_H
+
+#include <stdio.h>
+#include <string>
+#include <map>
+#include <filesystem>
+
+#include "types.h"
+#include "fatfs/ff.h"
+
+
+class FATStorage
+{
+public:
+ FATStorage(std::string filename, u64 size, bool readonly, std::string sourcedir);
+ ~FATStorage();
+
+ bool Open();
+ void Close();
+
+ bool InjectFile(std::string path, u8* data, u32 len);
+
+ u32 ReadSectors(u32 start, u32 num, u8* data);
+ u32 WriteSectors(u32 start, u32 num, u8* data);
+
+private:
+ std::string FilePath;
+ std::string IndexPath;
+ std::string SourceDir;
+ bool ReadOnly;
+
+ FILE* File;
+ u64 FileSize;
+
+ static FILE* FF_File;
+ static u64 FF_FileSize;
+ static UINT FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num);
+ static UINT FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num);
+
+ static u32 ReadSectorsInternal(FILE* file, u64 filelen, u32 start, u32 num, u8* data);
+ static u32 WriteSectorsInternal(FILE* file, u64 filelen, u32 start, u32 num, u8* data);
+
+ void LoadIndex();
+ void SaveIndex();
+
+ bool ExportFile(std::string path, std::filesystem::path out);
+ void ExportDirectory(std::string path, std::string outbase, int level);
+ bool DeleteHostDirectory(std::string path, std::string outbase, int level);
+ void ExportChanges(std::string outbase);
+
+ bool CanFitFile(u32 len);
+ bool DeleteDirectory(std::string path, int level);
+ void CleanupDirectory(std::string sourcedir, std::string path, int level);
+ bool ImportFile(std::string path, std::filesystem::path in);
+ bool ImportDirectory(std::string sourcedir);
+ u64 GetDirectorySize(std::string sourcedir);
+
+ bool Load(std::string filename, u64 size, std::string sourcedir);
+ bool Save();
+
+ typedef struct
+ {
+ std::string Path;
+ bool IsReadOnly;
+
+ } DirIndexEntry;
+
+ typedef struct
+ {
+ std::string Path;
+ bool IsReadOnly;
+ u64 Size;
+ s64 LastModified;
+ u32 LastModifiedInternal;
+
+ } FileIndexEntry;
+
+ std::map<std::string, DirIndexEntry> DirIndex;
+ std::map<std::string, FileIndexEntry> FileIndex;
+};
+
+#endif // FATSTORAGE_H
diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index a88ba59..3d231ac 100644
--- a/src/NDSCart.cpp
+++ b/src/NDSCart.cpp
@@ -24,7 +24,6 @@
#include "ARM.h"
#include "DSi_AES.h"
#include "Platform.h"
-#include "Config.h"
#include "ROMList.h"
#include "melonDLDI.h"
#include "NDSCart_SRAMManager.h"
@@ -52,6 +51,7 @@ u32 TransferDir;
u8 TransferCmd[8];
bool CartInserted;
+char CartName[256];
u8* CartROM;
u32 CartROMSize;
u32 CartID;
@@ -202,7 +202,7 @@ void CartCommon::SetupDirectBoot()
{
CmdEncMode = 2;
DataEncMode = 2;
- DSiMode = false; // TODO!!
+ DSiMode = IsDSi && NDS::ConsoleType==1;
}
void CartCommon::DoSavestate(Savestate* file)
@@ -1164,30 +1164,73 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)
{
- if (Config::DLDIEnable)
+ ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly);
+
+ if (Platform::GetConfigBool(Platform::DLDI_Enable))
{
- ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI));
- SDFile = Platform::OpenLocalFile(Config::DLDISDPath, "r+b");
+ std::string folderpath;
+ if (Platform::GetConfigBool(Platform::DLDI_FolderSync))
+ folderpath = Platform::GetConfigString(Platform::DLDI_FolderPath);
+ else
+ folderpath = "";
+
+ ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), ReadOnly);
+ SD = new FATStorage(Platform::GetConfigString(Platform::DLDI_ImagePath),
+ (u64)Platform::GetConfigInt(Platform::DLDI_ImageSize) * 1024 * 1024,
+ ReadOnly,
+ folderpath);
+ SD->Open();
}
else
- SDFile = nullptr;
+ SD = nullptr;
}
CartHomebrew::~CartHomebrew()
{
- if (SDFile) fclose(SDFile);
+ if (SD)
+ {
+ SD->Close();
+ delete SD;
+ }
}
void CartHomebrew::Reset()
{
CartCommon::Reset();
+}
- if (SDFile) fclose(SDFile);
+void CartHomebrew::SetupDirectBoot()
+{
+ CartCommon::SetupDirectBoot();
- if (Config::DLDIEnable)
- SDFile = Platform::OpenLocalFile(Config::DLDISDPath, "r+b");
- else
- SDFile = nullptr;
+ if (SD)
+ {
+ // add the ROM to the SD volume
+
+ if (!SD->InjectFile(CartName, CartROM, CartROMSize))
+ return;
+
+ // setup argv command line
+
+ char argv[512] = {0};
+ int argvlen;
+
+ strncpy(argv, "fat:/", 511);
+ strncat(argv, CartName, 511);
+ argvlen = strlen(argv);
+
+ void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32;
+
+ u32 argvbase = Header.ARM9RAMAddress + Header.ARM9Size;
+ argvbase = (argvbase + 0xF) & ~0xF;
+
+ for (u32 i = 0; i <= argvlen; i+=4)
+ writefn(argvbase+i, *(u32*)&argv[i]);
+
+ writefn(0x02FFFE70, 0x5F617267);
+ writefn(0x02FFFE74, argvbase);
+ writefn(0x02FFFE78, argvlen+1);
+ }
}
void CartHomebrew::DoSavestate(Savestate* file)
@@ -1220,13 +1263,7 @@ int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len)
case 0xC0: // SD read
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
- u64 addr = sector * 0x200ULL;
-
- if (SDFile)
- {
- fseek(SDFile, addr, SEEK_SET);
- fread(data, len, 1, SDFile);
- }
+ if (SD) SD->ReadSectors(sector, len>>9, data);
}
return 0;
@@ -1249,13 +1286,7 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
case 0xC1:
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
- u64 addr = sector * 0x200ULL;
-
- if (SDFile)
- {
- fseek(SDFile, addr, SEEK_SET);
- fwrite(data, len, 1, SDFile);
- }
+ if (SD && (!ReadOnly)) SD->WriteSectors(sector, len>>9, data);
}
break;
@@ -1264,7 +1295,7 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
}
}
-void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen)
+void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
{
u32 offset = *(u32*)&ROM[0x20];
u32 size = *(u32*)&ROM[0x2C];
@@ -1381,6 +1412,19 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen)
memset(&binary[dldioffset+fixstart], 0, fixend-fixstart);
}
+ if (readonly)
+ {
+ // clear the can-write feature flag
+ binary[dldioffset+0x64] &= ~0x02;
+
+ // make writeSectors() return failure
+ u32 writesec_addr = *(u32*)&binary[dldioffset+0x74];
+ writesec_addr -= memaddr;
+ writesec_addr += dldioffset;
+ *(u32*)&binary[writesec_addr+0x00] = 0xE3A00000; // mov r0, #0
+ *(u32*)&binary[writesec_addr+0x04] = 0xE12FFF1E; // bx lr
+ }
+
printf("applied DLDI patch\n");
}
@@ -1673,6 +1717,16 @@ bool LoadROM(const char* path, const char* sram, bool direct)
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);
@@ -1694,6 +1748,9 @@ bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct)
{
NDS::Reset();
+ // TODO: make it more meaningful?
+ strncpy(CartName, "rom.nds", 256);
+
u32 len = filelength;
CartROMSize = 0x200;
while (CartROMSize < len)
diff --git a/src/NDSCart.h b/src/NDSCart.h
index ecfb209..9f39988 100644
--- a/src/NDSCart.h
+++ b/src/NDSCart.h
@@ -21,6 +21,7 @@
#include "types.h"
#include "NDS_Header.h"
+#include "FATStorage.h"
namespace NDSCart
{
@@ -171,6 +172,7 @@ public:
~CartHomebrew() override;
void Reset() override;
+ void SetupDirectBoot() override;
void DoSavestate(Savestate* file) override;
@@ -178,10 +180,11 @@ public:
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
private:
- void ApplyDLDIPatch(const u8* patch, u32 len);
+ void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly);
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
- FILE* SDFile;
+ FATStorage* SD;
+ bool ReadOnly;
};
extern u16 SPICnt;
diff --git a/src/Platform.h b/src/Platform.h
index 76029eb..8399138 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -22,6 +22,7 @@
#include "types.h"
#include <functional>
+#include <string>
namespace Platform
{
@@ -31,6 +32,29 @@ void DeInit();
void StopEmu();
+// configuration values
+
+enum ConfigEntry
+{
+ DLDI_Enable,
+ DLDI_ImagePath,
+ DLDI_ImageSize,
+ DLDI_ReadOnly,
+ DLDI_FolderSync,
+ DLDI_FolderPath,
+
+ DSiSD_Enable,
+ DSiSD_ImagePath,
+ DSiSD_ImageSize,
+ DSiSD_ReadOnly,
+ DSiSD_FolderSync,
+ DSiSD_FolderPath,
+};
+
+int GetConfigInt(ConfigEntry entry);
+bool GetConfigBool(ConfigEntry entry);
+std::string GetConfigString(ConfigEntry entry);
+
// fopen() wrappers
// * OpenFile():
// simple fopen() wrapper that supports UTF8.
diff --git a/src/fatfs/diskio.c b/src/fatfs/diskio.c
index 225d9bf..9ed4a76 100644
--- a/src/fatfs/diskio.c
+++ b/src/fatfs/diskio.c
@@ -13,15 +13,17 @@
static ff_disk_read_cb ReadCb;
static ff_disk_write_cb WriteCb;
+static LBA_t SectorCount;
static DSTATUS Status = STA_NOINIT | STA_NODISK;
-void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb)
+void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt)
{
if (!readcb) return;
ReadCb = readcb;
WriteCb = writecb;
+ SectorCount = seccnt;
Status &= ~STA_NODISK;
if (!writecb) Status |= STA_PROTECT;
@@ -32,6 +34,7 @@ void ff_disk_close()
{
ReadCb = (void*)0;
WriteCb = (void*)0;
+ SectorCount = 0;
Status &= ~STA_PROTECT;
Status |= STA_NODISK;
@@ -123,12 +126,28 @@ DRESULT disk_ioctl (
{
switch (cmd)
{
- case 0: // sync
+ case CTRL_SYNC:
// TODO: fflush?
return RES_OK;
+
+ case GET_SECTOR_COUNT:
+ *(LBA_t*)buff = SectorCount;
+ return RES_OK;
+
+ case GET_SECTOR_SIZE:
+ *(WORD*)buff = 0x200;
+ return RES_OK;
+
+ case GET_BLOCK_SIZE:
+ *(DWORD*)buff = 1;
+ return RES_OK;
+
+ case CTRL_TRIM:
+ // TODO??
+ return RES_OK;
}
- //printf("disk_ioctl(%02X, %02X, %p)\n", pdrv, cmd, buff);
+ //printf("FatFS: unknown disk_ioctl(%02X, %02X, %p)\n", pdrv, cmd, buff);
return RES_PARERR;
}
diff --git a/src/fatfs/ff.c b/src/fatfs/ff.c
index d209605..9d21294 100644
--- a/src/fatfs/ff.c
+++ b/src/fatfs/ff.c
@@ -1595,7 +1595,7 @@ static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:D
/*-----------------------------------------------------------------------*/
static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */
- FIL* fp, /* Pointer to the file object */
+ FF_FIL* fp, /* Pointer to the file object */
FSIZE_t ofs /* File offset to be converted to cluster# */
)
{
@@ -1664,7 +1664,7 @@ static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
/*-----------------------------------------------------------------------*/
static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
- DIR* dp, /* Pointer to directory object */
+ FF_DIR* dp, /* Pointer to directory object */
DWORD ofs /* Offset of directory table */
)
{
@@ -1712,7 +1712,7 @@ static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
/*-----------------------------------------------------------------------*/
static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
- DIR* dp, /* Pointer to the directory object */
+ FF_DIR* dp, /* Pointer to the directory object */
int stretch /* 0: Do not stretch table, 1: Stretch table if needed */
)
{
@@ -1773,7 +1773,7 @@ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DEN
/*-----------------------------------------------------------------------*/
static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
- DIR* dp, /* Pointer to the directory object */
+ FF_DIR* dp, /* Pointer to the directory object */
UINT n_ent /* Number of contiguous entries to allocate */
)
{
@@ -2273,7 +2273,7 @@ static void create_xdir (
#define DIR_READ_LABEL(dp) dir_read(dp, 1)
static FRESULT dir_read (
- DIR* dp, /* Pointer to the directory object */
+ FF_DIR* dp, /* Pointer to the directory object */
int vol /* Filtered by 0:file/directory or 1:volume label */
)
{
@@ -2351,7 +2351,7 @@ static FRESULT dir_read (
/*-----------------------------------------------------------------------*/
static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
- DIR* dp /* Pointer to the directory object with the file name */
+ FF_DIR* dp /* Pointer to the directory object with the file name */
)
{
FRESULT res;
@@ -2432,7 +2432,7 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
/*-----------------------------------------------------------------------*/
static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
- DIR* dp /* Target directory with object name to be created */
+ FF_DIR* dp /* Target directory with object name to be created */
)
{
FRESULT res;
@@ -2538,7 +2538,7 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too
/*-----------------------------------------------------------------------*/
static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
- DIR* dp /* Directory object pointing the entry to be removed */
+ FF_DIR* dp /* Directory object pointing the entry to be removed */
)
{
FRESULT res;
@@ -2584,8 +2584,8 @@ static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
/*-----------------------------------------------------------------------*/
static void get_fileinfo (
- DIR* dp, /* Pointer to the directory object */
- FILINFO* fno /* Pointer to the file information to be filled */
+ FF_DIR* dp, /* Pointer to the directory object */
+ FF_FILINFO* fno /* Pointer to the file information to be filled */
)
{
UINT si, di;
@@ -2799,7 +2799,7 @@ static int pattern_match ( /* 0:mismatched, 1:matched */
/*-----------------------------------------------------------------------*/
static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
- DIR* dp, /* Pointer to the directory object */
+ FF_DIR* dp, /* Pointer to the directory object */
const TCHAR** path /* Pointer to pointer to the segment in the path string */
)
{
@@ -3001,7 +3001,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
/*-----------------------------------------------------------------------*/
static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
- DIR* dp, /* Directory object to return last directory and found object */
+ FF_DIR* dp, /* Directory object to return last directory and found object */
const TCHAR* path /* Full-path string to find a file or directory */
)
{
@@ -3651,13 +3651,13 @@ FRESULT f_mount (
/*-----------------------------------------------------------------------*/
FRESULT f_open (
- FIL* fp, /* Pointer to the blank file object */
+ FF_FIL* fp, /* Pointer to the blank file object */
const TCHAR* path, /* Pointer to the file name */
BYTE mode /* Access mode and open mode flags */
)
{
FRESULT res;
- DIR dj;
+ FF_DIR dj;
FATFS *fs;
#if !FF_FS_READONLY
DWORD cl, bcs, clst, tm;
@@ -3848,7 +3848,7 @@ FRESULT f_open (
/*-----------------------------------------------------------------------*/
FRESULT f_read (
- FIL* fp, /* Open file to be read */
+ FF_FIL* fp, /* Open file to be read */
void* buff, /* Data buffer to store the read data */
UINT btr, /* Number of bytes to read */
UINT* br /* Number of bytes read */
@@ -3948,7 +3948,7 @@ FRESULT f_read (
/*-----------------------------------------------------------------------*/
FRESULT f_write (
- FIL* fp, /* Open file to be written */
+ FF_FIL* fp, /* Open file to be written */
const void* buff, /* Data to be written */
UINT btw, /* Number of bytes to write */
UINT* bw /* Number of bytes written */
@@ -4069,7 +4069,7 @@ FRESULT f_write (
/*-----------------------------------------------------------------------*/
FRESULT f_sync (
- FIL* fp /* Open file to be synced */
+ FF_FIL* fp /* Open file to be synced */
)
{
FRESULT res;
@@ -4150,7 +4150,7 @@ FRESULT f_sync (
/*-----------------------------------------------------------------------*/
FRESULT f_close (
- FIL* fp /* Open file to be closed */
+ FF_FIL* fp /* Open file to be closed */
)
{
FRESULT res;
@@ -4365,7 +4365,7 @@ FRESULT f_getcwd (
/*-----------------------------------------------------------------------*/
FRESULT f_lseek (
- FIL* fp, /* Pointer to the file object */
+ FF_FIL* fp, /* Pointer to the file object */
FSIZE_t ofs /* File pointer from top of file */
)
{
@@ -4529,7 +4529,7 @@ FRESULT f_lseek (
/*-----------------------------------------------------------------------*/
FRESULT f_opendir (
- DIR* dp, /* Pointer to directory object to create */
+ FF_DIR* dp, /* Pointer to directory object to create */
const TCHAR* path /* Pointer to the directory path */
)
{
@@ -4595,7 +4595,7 @@ FRESULT f_opendir (
/*-----------------------------------------------------------------------*/
FRESULT f_closedir (
- DIR *dp /* Pointer to the directory object to be closed */
+ FF_DIR *dp /* Pointer to the directory object to be closed */
)
{
FRESULT res;
@@ -4625,8 +4625,8 @@ FRESULT f_closedir (
/*-----------------------------------------------------------------------*/
FRESULT f_readdir (
- DIR* dp, /* Pointer to the open directory object */
- FILINFO* fno /* Pointer to file information to return */
+ FF_DIR* dp, /* Pointer to the open directory object */
+ FF_FILINFO* fno /* Pointer to file information to return */
)
{
FRESULT res;
@@ -4661,8 +4661,8 @@ FRESULT f_readdir (
/*-----------------------------------------------------------------------*/
FRESULT f_findnext (
- DIR* dp, /* Pointer to the open directory object */
- FILINFO* fno /* Pointer to the file information structure */
+ FF_DIR* dp, /* Pointer to the open directory object */
+ FF_FILINFO* fno /* Pointer to the file information structure */
)
{
FRESULT res;
@@ -4686,8 +4686,8 @@ FRESULT f_findnext (
/*-----------------------------------------------------------------------*/
FRESULT f_findfirst (
- DIR* dp, /* Pointer to the blank directory object */
- FILINFO* fno, /* Pointer to the file information structure */
+ FF_DIR* dp, /* Pointer to the blank directory object */
+ FF_FILINFO* fno, /* Pointer to the file information structure */
const TCHAR* path, /* Pointer to the directory to open */
const TCHAR* pattern /* Pointer to the matching pattern */
)
@@ -4714,11 +4714,11 @@ FRESULT f_findfirst (
FRESULT f_stat (
const TCHAR* path, /* Pointer to the file path */
- FILINFO* fno /* Pointer to file information to return */
+ FF_FILINFO* fno /* Pointer to file information to return */
)
{
FRESULT res;
- DIR dj;
+ FF_DIR dj;
DEF_NAMBUF
@@ -4840,7 +4840,7 @@ FRESULT f_getfree (
/*-----------------------------------------------------------------------*/
FRESULT f_truncate (
- FIL* fp /* Pointer to the file object */
+ FF_FIL* fp /* Pointer to the file object */
)
{
FRESULT res;
@@ -4894,7 +4894,7 @@ FRESULT f_unlink (
)
{
FRESULT res;
- DIR dj, sdj;
+ FF_DIR dj, sdj;
DWORD dclst = 0;
FATFS *fs;
#if FF_FS_EXFAT
@@ -4988,7 +4988,7 @@ FRESULT f_mkdir (
)
{
FRESULT res;
- DIR dj;
+ FF_DIR dj;
FFOBJID sobj;
FATFS *fs;
DWORD dcl, pcl, tm;
@@ -5073,7 +5073,7 @@ FRESULT f_rename (
)
{
FRESULT res;
- DIR djo, djn;
+ FF_DIR djo, djn;
FATFS *fs;
BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir;
LBA_t sect;
@@ -5121,7 +5121,7 @@ FRESULT f_rename (
#endif
{ /* At FAT/FAT32 volume */
memcpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */
- memcpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */
+ memcpy(&djn, &djo, sizeof (FF_DIR)); /* Duplicate the directory object */
res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
if (res == FR_OK) { /* Is new name already in use by any other object? */
res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
@@ -5184,7 +5184,7 @@ FRESULT f_chmod (
)
{
FRESULT res;
- DIR dj;
+ FF_DIR dj;
FATFS *fs;
DEF_NAMBUF
@@ -5226,11 +5226,11 @@ FRESULT f_chmod (
FRESULT f_utime (
const TCHAR* path, /* Pointer to the file/directory name */
- const FILINFO* fno /* Pointer to the timestamp to be set */
+ const FF_FILINFO* fno /* Pointer to the timestamp to be set */
)
{
FRESULT res;
- DIR dj;
+ FF_DIR dj;
FATFS *fs;
DEF_NAMBUF
@@ -5489,7 +5489,7 @@ FRESULT f_setlabel (
/*-----------------------------------------------------------------------*/
FRESULT f_expand (
- FIL* fp, /* Pointer to the file object */
+ FF_FIL* fp, /* Pointer to the file object */
FSIZE_t fsz, /* File size to be expanded to */
BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */
)
@@ -5579,7 +5579,7 @@ FRESULT f_expand (
/*-----------------------------------------------------------------------*/
FRESULT f_forward (
- FIL* fp, /* Pointer to the file object */
+ FF_FIL* fp, /* Pointer to the file object */
UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
UINT btf, /* Number of bytes to forward */
UINT* bf /* Pointer to number of bytes forwarded */
@@ -5801,14 +5801,14 @@ static FRESULT create_partition (
FRESULT f_mkfs (
const TCHAR* path, /* Logical drive number */
- const MKFS_PARM* opt, /* Format options */
+ const FF_MKFS_PARM* opt, /* Format options */
void* work, /* Pointer to working buffer (null: use heap memory) */
UINT len /* Size of working buffer [byte] */
)
{
static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */
static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */
- static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
+ static const FF_MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart;
WORD ss; /* Sector size */
DWORD sz_buf, sz_blk, n_clst, pau, nsect, n, vsn;
@@ -6338,7 +6338,7 @@ FRESULT f_fdisk (
TCHAR* f_gets (
TCHAR* buff, /* Pointer to the buffer to store read string */
int len, /* Size of string buffer (items) */
- FIL* fp /* Pointer to the file object */
+ FF_FIL* fp /* Pointer to the file object */
)
{
int nc = 0;
@@ -6629,7 +6629,7 @@ static int putc_flush (putbuff* pb)
/* Initialize write buffer */
-static void putc_init (putbuff* pb, FIL* fp)
+static void putc_init (putbuff* pb, FF_FIL* fp)
{
memset(pb, 0, sizeof (putbuff));
pb->fp = fp;
@@ -6639,7 +6639,7 @@ static void putc_init (putbuff* pb, FIL* fp)
int f_putc (
TCHAR c, /* A character to be output */
- FIL* fp /* Pointer to the file object */
+ FF_FIL* fp /* Pointer to the file object */
)
{
putbuff pb;
@@ -6659,7 +6659,7 @@ int f_putc (
int f_puts (
const TCHAR* str, /* Pointer to the string to be output */
- FIL* fp /* Pointer to the file object */
+ FF_FIL* fp /* Pointer to the file object */
)
{
putbuff pb;
@@ -6799,7 +6799,7 @@ static void ftoa (
int f_printf (
- FIL* fp, /* Pointer to the file object */
+ FF_FIL* fp, /* Pointer to the file object */
const TCHAR* fmt, /* Pointer to the format string */
... /* Optional arguments... */
)
diff --git a/src/fatfs/ff.h b/src/fatfs/ff.h
index 6df78a8..800e47b 100644
--- a/src/fatfs/ff.h
+++ b/src/fatfs/ff.h
@@ -219,7 +219,7 @@ typedef struct {
#if !FF_FS_TINY
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
#endif
-} FIL;
+} FF_FIL;
@@ -238,7 +238,7 @@ typedef struct {
#if FF_USE_FIND
const TCHAR* pat; /* Pointer to the name matching pattern */
#endif
-} DIR;
+} FF_DIR;
@@ -255,7 +255,7 @@ typedef struct {
#else
TCHAR fname[12 + 1]; /* File name */
#endif
-} FILINFO;
+} FF_FILINFO;
@@ -267,7 +267,7 @@ typedef struct {
UINT align; /* Data area alignment (sector) */
UINT n_root; /* Number of root directory entries */
DWORD au_size; /* Cluster size (byte) */
-} MKFS_PARM;
+} FF_MKFS_PARM;
@@ -301,40 +301,40 @@ typedef enum {
/*--------------------------------------------------------------*/
/* FatFs module application interface */
-FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
-FRESULT f_close (FIL* fp); /* Close an open file object */
-FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
-FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
-FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
-FRESULT f_truncate (FIL* fp); /* Truncate the file */
-FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
-FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
-FRESULT f_closedir (DIR* dp); /* Close an open directory */
-FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
-FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
-FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
+FRESULT f_open (FF_FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
+FRESULT f_close (FF_FIL* fp); /* Close an open file object */
+FRESULT f_read (FF_FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
+FRESULT f_write (FF_FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
+FRESULT f_lseek (FF_FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
+FRESULT f_truncate (FF_FIL* fp); /* Truncate the file */
+FRESULT f_sync (FF_FIL* fp); /* Flush cached data of the writing file */
+FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */
+FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */
+FRESULT f_readdir (FF_DIR* dp, FF_FILINFO* fno); /* Read a directory item */
+FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
+FRESULT f_findnext (FF_DIR* dp, FF_FILINFO* fno); /* Find next file */
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
-FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
+FRESULT f_stat (const TCHAR* path, FF_FILINFO* fno); /* Get file status */
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
-FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
+FRESULT f_utime (const TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
-FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
-FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
+FRESULT f_forward (FF_FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
+FRESULT f_expand (FF_FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
-FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
+FRESULT f_mkfs (const TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp); /* Set current code page */
-int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
-int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
-int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
-TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
+int f_putc (TCHAR c, FF_FIL* fp); /* Put a character to the file */
+int f_puts (const TCHAR* str, FF_FIL* cp); /* Put a string to the file */
+int f_printf (FF_FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
@@ -420,7 +420,7 @@ int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
typedef UINT (*ff_disk_read_cb)(BYTE* buff, LBA_t sector, UINT count);
typedef UINT (*ff_disk_write_cb)(BYTE* buff, LBA_t sector, UINT count);
-void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb);
+void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt);
void ff_disk_close();
diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp
index 29247e4..8f100c7 100644
--- a/src/frontend/Util_ROM.cpp
+++ b/src/frontend/Util_ROM.cpp
@@ -222,14 +222,14 @@ int SetupDSiNAND()
DSi::SDMMCFile = f;
- if (Config::DSiSDEnable)
+ /*if (Config::DSiSDEnable)
{
f = Platform::OpenLocalFile(Config::DSiSDPath, "r+b");
if (f)
DSi::SDIOFile = f;
else
DSi::SDIOFile = Platform::OpenLocalFile(Config::DSiSDPath, "w+b");
- }
+ }*/
return Load_OK;
}
@@ -509,7 +509,7 @@ void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[
{
for (int x = 0; x < 32; x++)
{
- for (int y = 0; y < 32/2; y++)
+ for (int y = 0; y < 32/2; y++)
{
std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]);
}
@@ -519,7 +519,7 @@ void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[
{
for (int x = 0; x < 32/2; x++)
{
- for (int y = 0; y < 32; y++)
+ for (int y = 0; y < 32; y++)
{
std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]);
}
diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
index 31e2e52..9413846 100644
--- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
@@ -48,15 +48,10 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->txtBIOS7Path->setText(Config::BIOS7Path);
ui->txtFirmwarePath->setText(Config::FirmwarePath);
- ui->cbDLDIEnable->setChecked(Config::DLDIEnable != 0);
- ui->txtDLDISDPath->setText(Config::DLDISDPath);
-
ui->txtDSiBIOS9Path->setText(Config::DSiBIOS9Path);
ui->txtDSiBIOS7Path->setText(Config::DSiBIOS7Path);
ui->txtDSiFirmwarePath->setText(Config::DSiFirmwarePath);
ui->txtDSiNANDPath->setText(Config::DSiNANDPath);
- ui->cbDSiSDEnable->setChecked(Config::DSiSDEnable != 0);
- ui->txtDSiSDPath->setText(Config::DSiSDPath);
ui->cbxConsoleType->addItem("DS");
ui->cbxConsoleType->addItem("DSi (experimental)");
@@ -83,6 +78,45 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
on_chkEnableJIT_toggled();
on_chkExternalBIOS_toggled();
+
+ const int imgsizes[] = {256, 512, 1024, 2048, 4096, 0};
+
+ ui->cbxDLDISize->addItem("Auto");
+ ui->cbxDSiSDSize->addItem("Auto");
+
+ for (int i = 0; imgsizes[i] != 0; i++)
+ {
+ int sizedisp = imgsizes[i];
+ QString sizelbl;
+ if (sizedisp >= 1024)
+ {
+ sizedisp >>= 10;
+ sizelbl = QString("%1 GB").arg(sizedisp);
+ }
+ else
+ {
+ sizelbl = QString("%1 MB").arg(sizedisp);
+ }
+
+ ui->cbxDLDISize->addItem(sizelbl);
+ ui->cbxDSiSDSize->addItem(sizelbl);
+ }
+
+ ui->cbDLDIEnable->setChecked(Config::DLDIEnable != 0);
+ ui->txtDLDISDPath->setText(Config::DLDISDPath);
+ ui->cbxDLDISize->setCurrentIndex(Config::DLDISize);
+ ui->cbDLDIReadOnly->setChecked(Config::DLDIReadOnly != 0);
+ ui->cbDLDIFolder->setChecked(Config::DLDIFolderSync != 0);
+ ui->txtDLDIFolder->setText(Config::DLDIFolderPath);
+ on_cbDLDIEnable_toggled();
+
+ ui->cbDSiSDEnable->setChecked(Config::DSiSDEnable != 0);
+ ui->txtDSiSDPath->setText(Config::DSiSDPath);
+ ui->cbxDSiSDSize->setCurrentIndex(Config::DSiSDSize);
+ ui->cbDSiSDReadOnly->setChecked(Config::DSiSDReadOnly != 0);
+ ui->cbDSiSDFolder->setChecked(Config::DSiSDFolderSync != 0);
+ ui->txtDSiSDFolder->setText(Config::DSiSDFolderPath);
+ on_cbDSiSDEnable_toggled();
}
EmuSettingsDialog::~EmuSettingsDialog()
@@ -154,14 +188,25 @@ void EmuSettingsDialog::done(int r)
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;
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;
+ std::string dldiFolderPath = ui->txtDLDIFolder->text().toStdString();
+
std::string dsiBios9Path = ui->txtDSiBIOS9Path->text().toStdString();
std::string dsiBios7Path = ui->txtDSiBIOS7Path->text().toStdString();
std::string dsiFirmwarePath = ui->txtDSiFirmwarePath->text().toStdString();
std::string dsiNANDPath = ui->txtDSiNANDPath->text().toStdString();
+
int dsiSDEnable = ui->cbDSiSDEnable->isChecked() ? 1:0;
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;
+ std::string dsiSDFolderPath = ui->txtDSiSDFolder->text().toStdString();
if (consoleType != Config::ConsoleType
|| directBoot != Config::DirectBoot
@@ -178,12 +223,20 @@ void EmuSettingsDialog::done(int r)
|| strcmp(Config::FirmwarePath, firmwarePath.c_str()) != 0
|| dldiEnable != Config::DLDIEnable
|| strcmp(Config::DLDISDPath, dldiSDPath.c_str()) != 0
+ || 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
|| dsiSDEnable != Config::DSiSDEnable
- || strcmp(Config::DSiSDPath, dsiSDPath.c_str()) != 0)
+ || strcmp(Config::DSiSDPath, dsiSDPath.c_str()) != 0
+ || dsiSDSize != Config::DSiSDSize
+ || dsiSDReadOnly != Config::DSiSDReadOnly
+ || dsiSDFolderSync != Config::DSiSDFolderSync
+ || strcmp(Config::DSiSDFolderPath, dsiSDFolderPath.c_str()) != 0)
{
if (RunningSomething
&& QMessageBox::warning(this, "Reset necessary to apply changes",
@@ -195,15 +248,25 @@ void EmuSettingsDialog::done(int r)
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::DLDIEnable = dldiEnable;
strncpy(Config::DLDISDPath, dldiSDPath.c_str(), 1023); Config::DLDISDPath[1023] = '\0';
+ Config::DLDISize = dldiSize;
+ Config::DLDIReadOnly = dldiReadOnly;
+ Config::DLDIFolderSync = dldiFolderSync;
+ strncpy(Config::DLDIFolderPath, dldiFolderPath.c_str(), 1023); Config::DLDIFolderPath[1023] = '\0';
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::DSiSDEnable = dsiSDEnable;
strncpy(Config::DSiSDPath, dsiSDPath.c_str(), 1023); Config::DSiSDPath[1023] = '\0';
+ Config::DSiSDSize = dsiSDSize;
+ Config::DSiSDReadOnly = dsiSDReadOnly;
+ Config::DSiSDFolderSync = dsiSDFolderSync;
+ strncpy(Config::DSiSDFolderPath, dsiSDFolderPath.c_str(), 1023); Config::DSiSDFolderPath[1023] = '\0';
#ifdef JIT_ENABLED
Config::JIT_Enable = jitEnable;
@@ -287,6 +350,20 @@ void EmuSettingsDialog::on_btnDSiBIOS7Browse_clicked()
ui->txtDSiBIOS7Path->setText(file);
}
+void EmuSettingsDialog::on_cbDLDIEnable_toggled()
+{
+ bool disabled = !ui->cbDLDIEnable->isChecked();
+ ui->txtDLDISDPath->setDisabled(disabled);
+ ui->btnDLDISDBrowse->setDisabled(disabled);
+ ui->cbxDLDISize->setDisabled(disabled);
+ ui->cbDLDIReadOnly->setDisabled(disabled);
+ ui->cbDLDIFolder->setDisabled(disabled);
+
+ if (!disabled) disabled = !ui->cbDLDIFolder->isChecked();
+ ui->txtDLDIFolder->setDisabled(disabled);
+ ui->btnDLDIFolderBrowse->setDisabled(disabled);
+}
+
void EmuSettingsDialog::on_btnDLDISDBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
@@ -299,6 +376,24 @@ void EmuSettingsDialog::on_btnDLDISDBrowse_clicked()
ui->txtDLDISDPath->setText(file);
}
+void EmuSettingsDialog::on_cbDLDIFolder_toggled()
+{
+ bool disabled = !ui->cbDLDIFolder->isChecked();
+ ui->txtDLDIFolder->setDisabled(disabled);
+ ui->btnDLDIFolderBrowse->setDisabled(disabled);
+}
+
+void EmuSettingsDialog::on_btnDLDIFolderBrowse_clicked()
+{
+ QString dir = QFileDialog::getExistingDirectory(this,
+ "Select DLDI SD folder...",
+ EmuDirectory);
+
+ if (dir.isEmpty()) return;
+
+ ui->txtDLDIFolder->setText(dir);
+}
+
void EmuSettingsDialog::on_btnDSiFirmwareBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
@@ -323,6 +418,20 @@ void EmuSettingsDialog::on_btnDSiNANDBrowse_clicked()
ui->txtDSiNANDPath->setText(file);
}
+void EmuSettingsDialog::on_cbDSiSDEnable_toggled()
+{
+ bool disabled = !ui->cbDSiSDEnable->isChecked();
+ ui->txtDSiSDPath->setDisabled(disabled);
+ ui->btnDSiSDBrowse->setDisabled(disabled);
+ ui->cbxDSiSDSize->setDisabled(disabled);
+ ui->cbDSiSDReadOnly->setDisabled(disabled);
+ ui->cbDSiSDFolder->setDisabled(disabled);
+
+ if (!disabled) disabled = !ui->cbDSiSDFolder->isChecked();
+ ui->txtDSiSDFolder->setDisabled(disabled);
+ ui->btnDSiSDFolderBrowse->setDisabled(disabled);
+}
+
void EmuSettingsDialog::on_btnDSiSDBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
@@ -335,6 +444,24 @@ void EmuSettingsDialog::on_btnDSiSDBrowse_clicked()
ui->txtDSiSDPath->setText(file);
}
+void EmuSettingsDialog::on_cbDSiSDFolder_toggled()
+{
+ bool disabled = !ui->cbDSiSDFolder->isChecked();
+ ui->txtDSiSDFolder->setDisabled(disabled);
+ ui->btnDSiSDFolderBrowse->setDisabled(disabled);
+}
+
+void EmuSettingsDialog::on_btnDSiSDFolderBrowse_clicked()
+{
+ QString dir = QFileDialog::getExistingDirectory(this,
+ "Select DSi SD folder...",
+ EmuDirectory);
+
+ if (dir.isEmpty()) return;
+
+ ui->txtDSiSDFolder->setText(dir);
+}
+
void EmuSettingsDialog::on_chkEnableJIT_toggled()
{
bool disabled = !ui->chkEnableJIT->isChecked();
diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h
index b650959..60e2160 100644
--- a/src/frontend/qt_sdl/EmuSettingsDialog.h
+++ b/src/frontend/qt_sdl/EmuSettingsDialog.h
@@ -58,13 +58,21 @@ private slots:
void on_btnBIOS9Browse_clicked();
void on_btnBIOS7Browse_clicked();
void on_btnFirmwareBrowse_clicked();
+
+ void on_cbDLDIEnable_toggled();
void on_btnDLDISDBrowse_clicked();
+ void on_cbDLDIFolder_toggled();
+ void on_btnDLDIFolderBrowse_clicked();
void on_btnDSiBIOS9Browse_clicked();
void on_btnDSiBIOS7Browse_clicked();
void on_btnDSiFirmwareBrowse_clicked();
void on_btnDSiNANDBrowse_clicked();
+
+ void on_cbDSiSDEnable_toggled();
void on_btnDSiSDBrowse_clicked();
+ void on_cbDSiSDFolder_toggled();
+ void on_btnDSiSDFolderBrowse_clicked();
void on_chkEnableJIT_toggled();
void on_chkExternalBIOS_toggled();
diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.ui b/src/frontend/qt_sdl/EmuSettingsDialog.ui
index 41b6e6e..92e2792 100644
--- a/src/frontend/qt_sdl/EmuSettingsDialog.ui
+++ b/src/frontend/qt_sdl/EmuSettingsDialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>575</width>
- <height>260</height>
+ <height>351</height>
</rect>
</property>
<property name="sizePolicy">
@@ -203,6 +203,20 @@
<string>DSi-mode</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>DSi ARM9 BIOS:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QPathInput" name="txtDSiFirmwarePath">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode firmware (used for DS-mode backwards compatibility)&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 128 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
<item row="1" column="1">
<widget class="QPathInput" name="txtDSiBIOS7Path">
<property name="whatsThis">
@@ -210,41 +224,72 @@
</property>
</widget>
</item>
- <item row="5" column="2">
- <widget class="QPushButton" name="btnDSiSDBrowse">
+ <item row="9" column="0">
+ <widget class="QCheckBox" name="cbDSiSDFolder">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sync the emulated SD card to the given folder. The folder's contents will be copied to the SD image, and any change made to the SD image will be reflected to the folder.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
<property name="text">
- <string>Browse...</string>
+ <string>Sync SD to folder:</string>
</property>
</widget>
</item>
- <item row="3" column="0">
- <widget class="QLabel" name="label_8">
+ <item row="2" column="2">
+ <widget class="QPushButton" name="btnDSiFirmwareBrowse">
<property name="text">
- <string>DSi NAND:</string>
+ <string>Browse...</string>
</property>
</widget>
</item>
- <item row="3" column="2">
- <widget class="QPushButton" name="btnDSiNANDBrowse">
+ <item row="6" column="1">
+ <widget class="QPathInput" name="txtDSiSDPath">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;SD image file for emulating the DSi's SD card. A blank image file will be created if it doesn't already exist.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_10">
<property name="text">
- <string>Browse...</string>
+ <string>SD card image:</string>
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_6">
+ <item row="7" column="1">
+ <widget class="QComboBox" name="cbxDSiSDSize">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Size of the SD image.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If set to Auto:&lt;/p&gt;&lt;p&gt;* if an image file exists, the volume size will be that of the image file&lt;/p&gt;&lt;p&gt;* if no image file exists and folder sync is enabled, the volume size will be determined from the synced folder's contents&lt;/p&gt;&lt;p&gt;* otherwise, the volume size will default to 512 MB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_13">
<property name="text">
- <string>DSi ARM7 BIOS:</string>
+ <string>Image size:</string>
</property>
</widget>
</item>
- <item row="2" column="2">
- <widget class="QPushButton" name="btnDSiFirmwareBrowse">
+ <item row="6" column="2">
+ <widget class="QPushButton" name="btnDSiSDBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>DSi NAND:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <widget class="QLineEdit" name="txtDSiSDFolder">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sync the emulated SD card to the given folder. The folder's contents will be copied to the SD image, and any change made to the SD image will be reflected to the folder.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
<item row="3" column="1">
<widget class="QPathInput" name="txtDSiNANDPath">
<property name="whatsThis">
@@ -259,13 +304,6 @@
</property>
</widget>
</item>
- <item row="5" column="0">
- <widget class="QLabel" name="label_10">
- <property name="text">
- <string>DSi SD card:</string>
- </property>
- </widget>
- </item>
<item row="0" column="1">
<widget class="QPathInput" name="txtDSiBIOS9Path">
<property name="sizePolicy">
@@ -279,6 +317,16 @@
</property>
</widget>
</item>
+ <item row="5" column="0" colspan="3">
+ <widget class="QCheckBox" name="cbDSiSDEnable">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Simulate a SD card being inserted in the DSi's SD slot.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Enable DSi SD card</string>
+ </property>
+ </widget>
+ </item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
@@ -286,44 +334,51 @@
</property>
</widget>
</item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_5">
+ <item row="1" column="2">
+ <widget class="QPushButton" name="btnDSiBIOS7Browse">
<property name="text">
- <string>DSi ARM9 BIOS:</string>
+ <string>Browse...</string>
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QPathInput" name="txtDSiFirmwarePath">
+ <item row="8" column="0">
+ <widget class="QCheckBox" name="cbDSiSDReadOnly">
<property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode firmware (used for DS-mode backwards compatibility)&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 128 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make the emulated SD card read-only.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Read-only SD</string>
</property>
</widget>
</item>
- <item row="5" column="1">
- <widget class="QPathInput" name="txtDSiSDPath">
- <property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;SD image file for emulating the DSi's SD card&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <item row="3" column="2">
+ <widget class="QPushButton" name="btnDSiNANDBrowse">
+ <property name="text">
+ <string>Browse...</string>
</property>
</widget>
</item>
- <item row="4" column="0" colspan="3">
- <widget class="QCheckBox" name="cbDSiSDEnable">
- <property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Simulate a SD card being inserted in the DSi's SD slot. Requires a SD card image.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_6">
<property name="text">
- <string>Enable DSi SD card</string>
+ <string>DSi ARM7 BIOS:</string>
</property>
</widget>
</item>
- <item row="1" column="2">
- <widget class="QPushButton" name="btnDSiBIOS7Browse">
+ <item row="9" column="2">
+ <widget class="QPushButton" name="btnDSiSDFolderBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
+ <item row="4" column="0" colspan="3">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
@@ -399,34 +454,69 @@
<string>DLDI</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
- <item row="0" column="0" colspan="3">
- <widget class="QCheckBox" name="cbDLDIEnable">
+ <item row="1" column="2">
+ <widget class="QPushButton" name="btnDLDISDBrowse">
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QPushButton" name="btnDLDIFolderBrowse">
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="txtDLDIFolder">
<property name="whatsThis">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable the built-in DLDI driver, to let homebrew access files from a given SD image.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sync the emulated SD card to the given folder. The folder's contents will be copied to the SD image, and any change made to the SD image will be reflected to the folder.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_12">
<property name="text">
- <string>Enable DLDI (for homebrew)</string>
+ <string>Image size:</string>
</property>
</widget>
</item>
- <item row="1" column="2">
- <widget class="QPushButton" name="btnDLDISDBrowse">
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="cbDLDIFolder">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sync the emulated SD card to the given folder. The folder's contents will be copied to the SD image, and any change made to the SD image will be reflected to the folder.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
<property name="text">
- <string>Browse...</string>
+ <string>Sync SD to folder:</string>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QPathInput" name="txtDLDISDPath"/>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="cbxDLDISize">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Size of the SD image.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If set to Auto:&lt;/p&gt;&lt;p&gt;* if an image file exists, the volume size will be that of the image file&lt;/p&gt;&lt;p&gt;* if no image file exists and folder sync is enabled, the volume size will be determined from the synced folder's contents&lt;/p&gt;&lt;p&gt;* otherwise, the volume size will default to 512 MB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_11">
+ <item row="0" column="0" colspan="3">
+ <widget class="QCheckBox" name="cbDLDIEnable">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable the built-in DLDI driver, to let homebrew access files from an emulated SD card.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
<property name="text">
- <string>DLDI SD card:</string>
+ <string>Enable DLDI (for homebrew)</string>
</property>
</widget>
</item>
- <item row="2" column="0">
+ <item row="1" column="1">
+ <widget class="QPathInput" name="txtDLDISDPath">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;SD card image file to be used for DLDI. A blank image file will be created if it doesn't already exist.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -439,6 +529,23 @@
</property>
</spacer>
</item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>SD card image:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="3">
+ <widget class="QCheckBox" name="cbDLDIReadOnly">
+ <property name="whatsThis">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make the emulated SD card read-only.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Read-only SD</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</widget>
diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp
index 81d86d3..08190d6 100644
--- a/src/frontend/qt_sdl/Platform.cpp
+++ b/src/frontend/qt_sdl/Platform.cpp
@@ -127,6 +127,51 @@ void StopEmu()
}
+int GetConfigInt(ConfigEntry entry)
+{
+ const int imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
+
+ switch (entry)
+ {
+ case DLDI_ImageSize: return imgsizes[Config::DLDISize];
+
+ case DSiSD_ImageSize: return imgsizes[Config::DSiSDSize];
+ }
+
+ return 0;
+}
+
+bool GetConfigBool(ConfigEntry entry)
+{
+ switch (entry)
+ {
+ case DLDI_Enable: return Config::DLDIEnable != 0;
+ case DLDI_ReadOnly: return Config::DLDIReadOnly != 0;
+ case DLDI_FolderSync: return Config::DLDIFolderSync != 0;
+
+ case DSiSD_Enable: return Config::DSiSDEnable != 0;
+ case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0;
+ case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0;
+ }
+
+ return false;
+}
+
+std::string GetConfigString(ConfigEntry entry)
+{
+ switch (entry)
+ {
+ case DLDI_ImagePath: return Config::DLDISDPath;
+ case DLDI_FolderPath: return Config::DLDIFolderPath;
+
+ case DSiSD_ImagePath: return Config::DSiSDPath;
+ case DSiSD_FolderPath: return Config::DSiSDFolderPath;
+ }
+
+ return "";
+}
+
+
FILE* OpenFile(const char* path, const char* mode, bool mustexist)
{
QFile f(path);
diff --git a/src/frontend/qt_sdl/PlatformConfig.cpp b/src/frontend/qt_sdl/PlatformConfig.cpp
index 40f55ba..e01e4c9 100644
--- a/src/frontend/qt_sdl/PlatformConfig.cpp
+++ b/src/frontend/qt_sdl/PlatformConfig.cpp
@@ -63,6 +63,20 @@ int ShowOSD;
int ConsoleType;
int DirectBoot;
+int DLDIEnable;
+char DLDISDPath[1024];
+int DLDISize;
+int DLDIReadOnly;
+int DLDIFolderSync;
+char DLDIFolderPath[1024];
+
+int DSiSDEnable;
+char DSiSDPath[1024];
+int DSiSDSize;
+int DSiSDReadOnly;
+int DSiSDFolderSync;
+char DSiSDFolderPath[1024];
+
int SocketBindAnyAddr;
char LANDevice[128];
int DirectLAN;
@@ -172,6 +186,20 @@ ConfigEntry PlatformConfigFile[] =
{"ConsoleType", 0, &ConsoleType, 0, NULL, 0},
{"DirectBoot", 0, &DirectBoot, 1, NULL, 0},
+ {"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},
+
{"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},
{"LANDevice", 1, LANDevice, 0, "", 127},
{"DirectLAN", 0, &DirectLAN, 0, NULL, 0},
diff --git a/src/frontend/qt_sdl/PlatformConfig.h b/src/frontend/qt_sdl/PlatformConfig.h
index 202e36a..3e7bf85 100644
--- a/src/frontend/qt_sdl/PlatformConfig.h
+++ b/src/frontend/qt_sdl/PlatformConfig.h
@@ -79,6 +79,20 @@ extern int ShowOSD;
extern int ConsoleType;
extern int DirectBoot;
+extern int DLDIEnable;
+extern char DLDISDPath[1024];
+extern int DLDISize;
+extern int DLDIReadOnly;
+extern int DLDIFolderSync;
+extern char DLDIFolderPath[1024];
+
+extern int DSiSDEnable;
+extern char DSiSDPath[1024];
+extern int DSiSDSize;
+extern int DSiSDReadOnly;
+extern int DSiSDFolderSync;
+extern char DSiSDFolderPath[1024];
+
extern int SocketBindAnyAddr;
extern char LANDevice[128];
extern int DirectLAN;