all repos — mgba @ 33ca1e2e9ce98d63ecbd6ff3e815197ade0c6a1b

mGBA Game Boy Advance Emulator

Merge branch 'master' into port/psp2
Jeffrey Pfau jeffrey@endrift.com
Tue, 07 Jul 2015 00:30:29 -0700
commit

33ca1e2e9ce98d63ecbd6ff3e815197ade0c6a1b

parent

a0a38caabee69d143d926192d4a3ab6fae6ac2e0

M CHANGESCHANGES

@@ -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:
M src/gba/cheats/gameshark.csrc/gba/cheats/gameshark.c

@@ -197,6 +197,7 @@ GBACheatRegisterLine(set, line);

switch (set->gsaVersion) { case 0: + case 3: GBACheatSetGameSharkVersion(set, 1); // Fall through case 1:
M src/gba/cheats/parv3.csrc/gba/cheats/parv3.c

@@ -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); }
M src/gba/gba.csrc/gba/gba.c

@@ -756,6 +756,8 @@ }

} void GBAFrameEnded(struct GBA* gba) { + GBASavedataClean(&gba->memory.savedata, gba->video.frameCounter); + if (gba->rr) { gba->rr->nextFrame(gba->rr); }
M src/gba/memory.csrc/gba/memory.c

@@ -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 {
M src/gba/savedata.csrc/gba/savedata.c

@@ -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);
M src/gba/savedata.hsrc/gba/savedata.h

@@ -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);
M src/gba/supervisor/config.csrc/gba/supervisor/config.c

@@ -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
M src/gba/supervisor/config.hsrc/gba/supervisor/config.h

@@ -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);
M src/platform/qt/ConfigController.cppsrc/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; +}
M src/platform/qt/ConfigController.hsrc/platform/qt/ConfigController.h

@@ -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:
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -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); });
M src/util/vfs.hsrc/util/vfs.h

@@ -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 {
M src/util/vfs/vfs-fd.csrc/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 @@ 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; +}
M src/util/vfs/vfs-file.csrc/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 @@ 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; +}
M src/util/vfs/vfs-lzma.csrc/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 @@ 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
M src/util/vfs/vfs-zip.csrc/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 @@ 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