Merge branch 'master' into port/psp2
jump to
@@ -57,6 +57,7 @@ - GBA Audio: Fix 8-bit writes to audio channel 3 frequency
- 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@@ -97,6 +98,8 @@ - GBA Audio: Implement audio reset for channels A/B
- 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 + - GBA: Savedata is now synced shortly after data finishes being written 0.2.1: (2015-05-13) Bugfixes:
@@ -197,6 +197,7 @@ GBACheatRegisterLine(set, line);
switch (set->gsaVersion) { case 0: + case 3: GBACheatSetGameSharkVersion(set, 1); // Fall through case 1:
@@ -297,9 +297,10 @@ GBACheatRegisterLine(set, line);
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); }
@@ -756,6 +756,8 @@ }
} void GBAFrameEnded(struct GBA* gba) { + GBASavedataClean(&gba->memory.savedata, gba->video.frameCounter); + if (gba->rr) { gba->rr->nextFrame(gba->rr); }
@@ -818,6 +818,7 @@ if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) {
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 {
@@ -20,6 +20,7 @@ // Some games may vary anywhere between about 2000 cycles to up to 30000 cycles. (Observed on a Macronix (09C2) chip).
// 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 @@ savedata->flashState = FLASH_STATE_RAW;
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 @@ switch (savedata->flashState) {
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 @@ } else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) {
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 @@ }
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 _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 _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);
@@ -51,6 +51,11 @@ FLASH_MFG_PANASONIC = 0x1B32,
FLASH_MFG_SANYO = 0x1362 }; +enum SavedataDirty { + SAVEDATA_DIRT_NEW = 1, + SAVEDATA_DIRT_SEEN = 2 +}; + enum { SAVEDATA_FLASH_BASE = 0x0E005555,@@ -77,6 +82,9 @@ bool realisticTiming;
unsigned settling; int dust; + enum SavedataDirty dirty; + uint32_t dirtAge; + enum FlashStateMachine flashState; };@@ -97,6 +105,8 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value);
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);
@@ -126,6 +126,31 @@ strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
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 + 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, MAX_PATH, 0, 0); + StringCchCatA(out, MAX_PATH, "\\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
@@ -52,6 +52,7 @@
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);
@@ -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; +}
@@ -89,6 +89,7 @@ void setOption(const char* key, const char* value);
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:
@@ -642,6 +642,10 @@ m_mruMenu = fileMenu->addMenu(tr("Recent"));
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); });
@@ -39,6 +39,7 @@ void* (*map)(struct VFile* vf, size_t size, int flags);
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 {
@@ -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 @@ vfd->d.map = _vfdMap;
vfd->d.unmap = _vfdUnmap; vfd->d.truncate = _vfdTruncate; vfd->d.size = _vfdSize; + vfd->d.sync = _vfdSync; return &vfd->d; }@@ -166,3 +168,10 @@ return -1;
} 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; +}
@@ -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 @@ vff->d.map = _vffMap;
vff->d.unmap = _vffUnmap; vff->d.truncate = _vffTruncate; vff->d.size = _vffSize; + vff->d.sync = _vffSync; return &vff->d; }@@ -140,3 +142,14 @@ ssize_t size = ftell(vff->file);
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; +}
@@ -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 @@ vf->d.map = _vf7zMap;
vf->d.unmap = _vf7zUnmap; vf->d.truncate = _vf7zTruncate; vf->d.size = _vf7zSize; + vf->d.sync = _vf7zSync; return &vf->d; }@@ -306,6 +308,13 @@ free(name);
} return vde7z->utf8; +} + +bool _vf7zSync(struct VFile* vf, const void* memory, size_t size) { + UNUSED(vf); + UNUSED(memory); + UNUSED(size); + return false; } #endif
@@ -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 @@ vfz->d.map = _vfzMap;
vfz->d.unmap = _vfzUnmap; vfz->d.truncate = _vfzTruncate; vfz->d.size = _vfzSize; + vfz->d.sync = _vfzSync; return &vfz->d; }@@ -300,6 +302,13 @@ if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
return 0; } return s.name; +} + +bool _vfzSync(struct VFile* vf, const void* memory, size_t size) { + UNUSED(vf); + UNUSED(memory); + UNUSED(size); + return false; } #endif