From 38ec77b8038c826313f42149a8cb114dcabeed8e Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 5 Jul 2015 14:42:11 -0700 Subject: [PATCH 1/5] Qt: Menu option to make portable --- src/gba/supervisor/config.c | 24 ++++++++++++++++++++++++ src/gba/supervisor/config.h | 1 + src/platform/qt/ConfigController.cpp | 16 ++++++++++++++++ src/platform/qt/ConfigController.h | 1 + src/platform/qt/Window.cpp | 4 ++++ 5 files changed, 46 insertions(+) diff --git a/src/gba/supervisor/config.c b/src/gba/supervisor/config.c index c32d72e42..e8a92dd11 100644 --- a/src/gba/supervisor/config.c +++ b/src/gba/supervisor/config.c @@ -126,6 +126,30 @@ bool GBAConfigSave(const struct GBAConfig* config) { return ConfigurationWrite(&config->configTable, path); } +void GBAConfigMakePortable(const struct GBAConfig* config) { + struct VFile* portable; +#ifndef _WIN32 + char out[PATH_MAX]; + getcwd(out, PATH_MAX); + strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out)); + portable = VFileOpen(out, O_WRONLY | O_CREAT); +#else + wchar_t wpath[MAX_PATH]; + wchar_t wprojectName[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH); + HMODULE hModule = GetModuleHandleW(NULL); + GetModuleFileNameW(hModule, wpath, MAX_PATH); + PathRemoveFileSpecW(wpath); + WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0); + StringCchCatA(out, outLength, "\\portable.ini"); + portable = VFileOpen(out, O_WRONLY | O_CREAT); +#endif + if (portable) { + portable->close(portable); + GBAConfigSave(config); + } +} + void GBAConfigDirectory(char* out, size_t outLength) { struct VFile* portable; #ifndef _WIN32 diff --git a/src/gba/supervisor/config.h b/src/gba/supervisor/config.h index 86c5a93cc..c4183e122 100644 --- a/src/gba/supervisor/config.h +++ b/src/gba/supervisor/config.h @@ -52,6 +52,7 @@ void GBAConfigDeinit(struct GBAConfig*); bool GBAConfigLoad(struct GBAConfig*); bool GBAConfigSave(const struct GBAConfig*); +void GBAConfigMakePortable(const struct GBAConfig*); void GBAConfigDirectory(char* out, size_t outLength); const char* GBAConfigGetValue(const struct GBAConfig*, const char* key); diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 1996160a9..a5859fc83 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -258,3 +258,19 @@ void ConfigController::write() { GBAConfigSave(&m_config); m_settings->sync(); } + +void ConfigController::makePortable() { + GBAConfigMakePortable(&m_config); + + char path[PATH_MAX]; + GBAConfigDirectory(path, sizeof(path)); + QString fileName(path); + fileName.append(QDir::separator()); + fileName.append("qt.ini"); + QSettings* settings2 = new QSettings(fileName, QSettings::IniFormat, this); + for (const auto& key : m_settings->allKeys()) { + settings2->setValue(key, m_settings->value(key)); + } + delete m_settings; + m_settings = settings2; +} diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index eb26fa896..3b0de11b2 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -89,6 +89,7 @@ public slots: void setOption(const char* key, const QVariant& value); void setQtOption(const QString& key, const QVariant& value, const QString& group = QString()); + void makePortable(); void write(); private: diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 2feb07d6d..e94aec4d1 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -642,6 +642,10 @@ void Window::setupMenu(QMenuBar* menubar) { fileMenu->addSeparator(); + addControlledAction(fileMenu, fileMenu->addAction(tr("Make portable"), m_config, SLOT(makePortable())), "makePortable"); + + fileMenu->addSeparator(); + QAction* loadState = new QAction(tr("&Load state"), fileMenu); loadState->setShortcut(tr("F10")); connect(loadState, &QAction::triggered, [this]() { this->openStateWindow(LoadSave::LOAD); }); From fd809b3b39cfb362eae63bffc03dc90f58b1ece7 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 5 Jul 2015 15:00:29 -0700 Subject: [PATCH 2/5] GBA: Fix Windows build --- src/gba/supervisor/config.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gba/supervisor/config.c b/src/gba/supervisor/config.c index e8a92dd11..7e355e222 100644 --- a/src/gba/supervisor/config.c +++ b/src/gba/supervisor/config.c @@ -134,14 +134,15 @@ void GBAConfigMakePortable(const struct GBAConfig* config) { strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out)); portable = VFileOpen(out, O_WRONLY | O_CREAT); #else + char out[MAX_PATH]; wchar_t wpath[MAX_PATH]; wchar_t wprojectName[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH); HMODULE hModule = GetModuleHandleW(NULL); GetModuleFileNameW(hModule, wpath, MAX_PATH); PathRemoveFileSpecW(wpath); - WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0); - StringCchCatA(out, outLength, "\\portable.ini"); + WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0); + StringCchCatA(out, MAX_PATH, "\\portable.ini"); portable = VFileOpen(out, O_WRONLY | O_CREAT); #endif if (portable) { From b4c3440bc4b8bf85cd91f3571c91f4a6a958c7aa Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 6 Jul 2015 23:12:06 -0700 Subject: [PATCH 3/5] GBA Cheats: Fix Pro Action Replay and GameShark issues when used together --- CHANGES | 1 + src/gba/cheats/gameshark.c | 1 + src/gba/cheats/parv3.c | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index d6dc7eec6..eb315fd5b 100644 --- a/CHANGES +++ b/CHANGES @@ -57,6 +57,7 @@ Bugfixes: - ARM7: ARMHotplugDetach should call deinit - Qt: Fix window being too tall after exiting fullscreen - Qt: Fix a missing va_end call in the log handler lambda within the GameController constructor + - GBA Cheats: Fix Pro Action Replay and GameShark issues when used together Misc: - Qt: Handle saving input settings better - Debugger: Free watchpoints in addition to breakpoints diff --git a/src/gba/cheats/gameshark.c b/src/gba/cheats/gameshark.c index 0dd6f68b2..996de638d 100644 --- a/src/gba/cheats/gameshark.c +++ b/src/gba/cheats/gameshark.c @@ -197,6 +197,7 @@ bool GBACheatAddGameShark(struct GBACheatSet* set, uint32_t op1, uint32_t op2) { switch (set->gsaVersion) { case 0: + case 3: GBACheatSetGameSharkVersion(set, 1); // Fall through case 1: diff --git a/src/gba/cheats/parv3.c b/src/gba/cheats/parv3.c index 8ec3b67e4..c5e01b66d 100644 --- a/src/gba/cheats/parv3.c +++ b/src/gba/cheats/parv3.c @@ -297,9 +297,10 @@ bool GBACheatAddProActionReplay(struct GBACheatSet* set, uint32_t op1, uint32_t switch (set->gsaVersion) { case 0: + case 1: GBACheatSetGameSharkVersion(set, 3); // Fall through - case 1: + case 3: GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds); return GBACheatAddProActionReplayRaw(set, o1, o2); } From 250d3b940dc06961a320811678dac67e5a77484f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 7 Jul 2015 00:26:31 -0700 Subject: [PATCH 4/5] VFS: Add sync method to force syncing with backing --- CHANGES | 1 + src/util/vfs.h | 1 + src/util/vfs/vfs-fd.c | 9 +++++++++ src/util/vfs/vfs-file.c | 13 +++++++++++++ src/util/vfs/vfs-lzma.c | 9 +++++++++ src/util/vfs/vfs-zip.c | 9 +++++++++ 6 files changed, 42 insertions(+) diff --git a/CHANGES b/CHANGES index eb315fd5b..3b37272d5 100644 --- a/CHANGES +++ b/CHANGES @@ -98,6 +98,7 @@ Misc: - GBA Hardware: Backport generic RTC source into core - All: Proper handling of Unicode file paths - GBA Video: Slightly optimize mode 0 mosaic rendering + - VFS: Add sync method to force syncing with backing 0.2.1: (2015-05-13) Bugfixes: diff --git a/src/util/vfs.h b/src/util/vfs.h index 3f74d721a..6fca9013c 100644 --- a/src/util/vfs.h +++ b/src/util/vfs.h @@ -39,6 +39,7 @@ struct VFile { void (*unmap)(struct VFile* vf, void* memory, size_t size); void (*truncate)(struct VFile* vf, size_t size); ssize_t (*size)(struct VFile* vf); + bool (*sync)(struct VFile* vf, const void* buffer, size_t size); }; struct VDirEntry { diff --git a/src/util/vfs/vfs-fd.c b/src/util/vfs/vfs-fd.c index 383d6bbff..3dd5fa8b6 100644 --- a/src/util/vfs/vfs-fd.c +++ b/src/util/vfs/vfs-fd.c @@ -30,6 +30,7 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags); static void _vfdUnmap(struct VFile* vf, void* memory, size_t size); static void _vfdTruncate(struct VFile* vf, size_t size); static ssize_t _vfdSize(struct VFile* vf); +static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size); struct VFile* VFileOpenFD(const char* path, int flags) { if (!path) { @@ -66,6 +67,7 @@ struct VFile* VFileFromFD(int fd) { vfd->d.unmap = _vfdUnmap; vfd->d.truncate = _vfdTruncate; vfd->d.size = _vfdSize; + vfd->d.sync = _vfdSync; return &vfd->d; } @@ -166,3 +168,10 @@ static ssize_t _vfdSize(struct VFile* vf) { } return stat.st_size; } + +static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) { + UNUSED(buffer); + UNUSED(size); + struct VFileFD* vfd = (struct VFileFD*) vf; + return fsync(vfd->fd) == 0; +} diff --git a/src/util/vfs/vfs-file.c b/src/util/vfs/vfs-file.c index 70b676e63..d140b08af 100644 --- a/src/util/vfs/vfs-file.c +++ b/src/util/vfs/vfs-file.c @@ -24,6 +24,7 @@ static void* _vffMap(struct VFile* vf, size_t size, int flags); static void _vffUnmap(struct VFile* vf, void* memory, size_t size); static void _vffTruncate(struct VFile* vf, size_t size); static ssize_t _vffSize(struct VFile* vf); +static bool _vffSync(struct VFile* vf, const void* buffer, size_t size); struct VFile* VFileFOpen(const char* path, const char* mode) { if (!path && !mode) { @@ -57,6 +58,7 @@ struct VFile* VFileFromFILE(FILE* file) { vff->d.unmap = _vffUnmap; vff->d.truncate = _vffTruncate; vff->d.size = _vffSize; + vff->d.sync = _vffSync; return &vff->d; } @@ -140,3 +142,14 @@ static ssize_t _vffSize(struct VFile* vf) { fseek(vff->file, pos, SEEK_SET); return size; } + +static bool _vffSync(struct VFile* vf, const void* buffer, size_t size) { + struct VFileFILE* vff = (struct VFileFILE*) vf; + if (buffer && size) { + long pos = ftell(vff->file); + fseek(vff->file, 0, SEEK_SET); + fwrite(buffer, size, 1, vff->file); + fseek(vff->file, pos, SEEK_SET); + } + return fflush(vff->file) == 0; +} diff --git a/src/util/vfs/vfs-lzma.c b/src/util/vfs/vfs-lzma.c index 21e580fdb..c9dafe7b3 100644 --- a/src/util/vfs/vfs-lzma.c +++ b/src/util/vfs/vfs-lzma.c @@ -56,6 +56,7 @@ static void* _vf7zMap(struct VFile* vf, size_t size, int flags); static void _vf7zUnmap(struct VFile* vf, void* memory, size_t size); static void _vf7zTruncate(struct VFile* vf, size_t size); static ssize_t _vf7zSize(struct VFile* vf); +static bool _vf7zSync(struct VFile* vf, const void* buffer, size_t size); static bool _vd7zClose(struct VDir* vd); static void _vd7zRewind(struct VDir* vd); @@ -291,6 +292,7 @@ struct VFile* _vd7zOpenFile(struct VDir* vd, const char* path, int mode) { vf->d.unmap = _vf7zUnmap; vf->d.truncate = _vf7zTruncate; vf->d.size = _vf7zSize; + vf->d.sync = _vf7zSync; return &vf->d; } @@ -308,4 +310,11 @@ const char* _vde7zName(struct VDirEntry* vde) { return vde7z->utf8; } +bool _vf7zSync(struct VFile* vf, const void* memory, size_t size) { + UNUSED(vf); + UNUSED(memory); + UNUSED(size); + return false; +} + #endif diff --git a/src/util/vfs/vfs-zip.c b/src/util/vfs/vfs-zip.c index 5a1de8781..feda1cea5 100644 --- a/src/util/vfs/vfs-zip.c +++ b/src/util/vfs/vfs-zip.c @@ -43,6 +43,7 @@ static void* _vfzMap(struct VFile* vf, size_t size, int flags); static void _vfzUnmap(struct VFile* vf, void* memory, size_t size); static void _vfzTruncate(struct VFile* vf, size_t size); static ssize_t _vfzSize(struct VFile* vf); +static bool _vfzSync(struct VFile* vf, const void* buffer, size_t size); static bool _vdzClose(struct VDir* vd); static void _vdzRewind(struct VDir* vd); @@ -289,6 +290,7 @@ struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) { vfz->d.unmap = _vfzUnmap; vfz->d.truncate = _vfzTruncate; vfz->d.size = _vfzSize; + vfz->d.sync = _vfzSync; return &vfz->d; } @@ -302,4 +304,11 @@ const char* _vdezName(struct VDirEntry* vde) { return s.name; } +bool _vfzSync(struct VFile* vf, const void* memory, size_t size) { + UNUSED(vf); + UNUSED(memory); + UNUSED(size); + return false; +} + #endif From 4b3df31e4970c2db065e3ed801ef179ce4b9955a Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 7 Jul 2015 00:30:10 -0700 Subject: [PATCH 5/5] GBA: Savedata is now synced shortly after data finishes being written --- CHANGES | 1 + src/gba/gba.c | 2 ++ src/gba/memory.c | 1 + src/gba/savedata.c | 39 +++++++++++++++++++++++++++++++++++++++ src/gba/savedata.h | 10 ++++++++++ 5 files changed, 53 insertions(+) diff --git a/CHANGES b/CHANGES index 3b37272d5..8c8f164c1 100644 --- a/CHANGES +++ b/CHANGES @@ -99,6 +99,7 @@ Misc: - All: Proper handling of Unicode file paths - GBA Video: Slightly optimize mode 0 mosaic rendering - VFS: Add sync method to force syncing with backing + - GBA: Savedata is now synced shortly after data finishes being written 0.2.1: (2015-05-13) Bugfixes: diff --git a/src/gba/gba.c b/src/gba/gba.c index 1be44c506..500068e4b 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -756,6 +756,8 @@ void GBAFrameStarted(struct GBA* gba) { } void GBAFrameEnded(struct GBA* gba) { + GBASavedataClean(&gba->memory.savedata, gba->video.frameCounter); + if (gba->rr) { gba->rr->nextFrame(gba->rr); } diff --git a/src/gba/memory.c b/src/gba/memory.c index 32fa3f917..9e7a478bb 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -818,6 +818,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo GBASavedataWriteFlash(&memory->savedata, address, value); } else if (memory->savedata.type == SAVEDATA_SRAM) { memory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value; + memory->savedata.dirty |= SAVEDATA_DIRT_NEW; } else if (memory->hw.devices & HW_TILT) { GBAHardwareTiltWrite(&memory->hw, address & OFFSET_MASK, value); } else { diff --git a/src/gba/savedata.c b/src/gba/savedata.c index 0321b36bd..f283a8e75 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -20,6 +20,7 @@ // Other games vary from very little, with a fairly solid 20500 cycle count. (Observed on a SST (D4BF) chip). // An average estimation is as follows. #define FLASH_SETTLE_CYCLES 18000 +#define CLEANUP_THRESHOLD 15 static void _flashSwitchBank(struct GBASavedata* savedata, int bank); static void _flashErase(struct GBASavedata* savedata); @@ -33,6 +34,8 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { savedata->vf = vf; savedata->realVf = vf; savedata->mapMode = MAP_WRITE; + savedata->dirty = 0; + savedata->dirtAge = 0; } void GBASavedataDeinit(struct GBASavedata* savedata) { @@ -252,6 +255,7 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8 case FLASH_STATE_RAW: switch (savedata->command) { case FLASH_COMMAND_PROGRAM: + savedata->dirty |= SAVEDATA_DIRT_NEW; savedata->currentBank[address] = value; savedata->command = FLASH_COMMAND_NONE; break; @@ -359,6 +363,7 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32 uint8_t current = savedata->data[savedata->writeAddress >> 3]; current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7))); current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7)); + savedata->dirty |= SAVEDATA_DIRT_NEW; savedata->data[savedata->writeAddress >> 3] = current; ++savedata->writeAddress; } else { @@ -401,6 +406,38 @@ uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) { return 0; } +void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) { + if (savedata->dirty & SAVEDATA_DIRT_NEW) { + savedata->dirty &= ~SAVEDATA_DIRT_NEW; + if (!(savedata->dirty & SAVEDATA_DIRT_SEEN)) { + savedata->dirtAge = frameCount; + savedata->dirty |= SAVEDATA_DIRT_SEEN; + } + } else if ((savedata->dirty & SAVEDATA_DIRT_SEEN) && frameCount - savedata->dirtAge > CLEANUP_THRESHOLD) { + size_t size; + switch (savedata->type) { + case SAVEDATA_EEPROM: + size = SIZE_CART_EEPROM; + break; + case SAVEDATA_SRAM: + size = SIZE_CART_SRAM; + break; + case SAVEDATA_FLASH512: + size = SIZE_CART_FLASH512; + break; + case SAVEDATA_FLASH1M: + size = SIZE_CART_FLASH1M; + break; + default: + size = 0; + break; + } + savedata->vf->sync(savedata->vf, savedata->data, size); + savedata->dirty = 0; + GBALog(0, GBA_LOG_INFO, "Savedata synced"); + } +} + void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state, bool includeData) { state->savedata.type = savedata->type; state->savedata.command = savedata->command; @@ -451,6 +488,7 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) { void _flashErase(struct GBASavedata* savedata) { GBALog(0, GBA_LOG_DEBUG, "Performing flash chip erase"); + savedata->dirty |= SAVEDATA_DIRT_NEW; size_t size = SIZE_CART_FLASH512; if (savedata->type == SAVEDATA_FLASH1M) { size = SIZE_CART_FLASH1M; @@ -460,6 +498,7 @@ void _flashErase(struct GBASavedata* savedata) { void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) { GBALog(0, GBA_LOG_DEBUG, "Performing flash sector erase at 0x%04x", sectorStart); + savedata->dirty |= SAVEDATA_DIRT_NEW; size_t size = 0x1000; if (savedata->type == SAVEDATA_FLASH1M) { GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart); diff --git a/src/gba/savedata.h b/src/gba/savedata.h index a3030eb03..a260fefe3 100644 --- a/src/gba/savedata.h +++ b/src/gba/savedata.h @@ -51,6 +51,11 @@ enum FlashManufacturer { FLASH_MFG_SANYO = 0x1362 }; +enum SavedataDirty { + SAVEDATA_DIRT_NEW = 1, + SAVEDATA_DIRT_SEEN = 2 +}; + enum { SAVEDATA_FLASH_BASE = 0x0E005555, @@ -77,6 +82,9 @@ struct GBASavedata { unsigned settling; int dust; + enum SavedataDirty dirty; + uint32_t dirtAge; + enum FlashStateMachine flashState; }; @@ -98,6 +106,8 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8 uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata); void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize); +void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount); + struct GBASerializedState; void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state, bool includeData); void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state, bool includeData);