mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into port/psp2
This commit is contained in:
commit
33ca1e2e9c
3
CHANGES
3
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
|
||||
|
@ -97,6 +98,8 @@ 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
|
||||
- GBA: Savedata is now synced shortly after data finishes being written
|
||||
|
||||
0.2.1: (2015-05-13)
|
||||
Bugfixes:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -126,6 +126,31 @@ 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
|
||||
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 @@ 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);
|
||||
|
|
|
@ -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 @@ 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:
|
||||
|
|
|
@ -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); });
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue