mirror of https://github.com/mgba-emu/mgba.git
GBA: Create GameShark snapshots
This commit is contained in:
parent
3ff8467ba7
commit
688be6948b
1
CHANGES
1
CHANGES
|
@ -6,6 +6,7 @@ Features:
|
||||||
- Volume control
|
- Volume control
|
||||||
- More shortcuts are editable (e.g. quick save/load, solar sensor)
|
- More shortcuts are editable (e.g. quick save/load, solar sensor)
|
||||||
- Rewind now shows the frame after rewinding
|
- Rewind now shows the frame after rewinding
|
||||||
|
- Import/Export of GameShark/Action Replay snapshots
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- GBA: Fix timers not updating timing when writing to only the reload register
|
- GBA: Fix timers not updating timing when writing to only the reload register
|
||||||
- All: Fix sanitize-deb script not cleaning up after itself
|
- All: Fix sanitize-deb script not cleaning up after itself
|
||||||
|
|
|
@ -143,3 +143,110 @@ cleanup:
|
||||||
free(payload);
|
free(payload);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf) {
|
||||||
|
char buffer[0x1C];
|
||||||
|
uint32_t size = strlen(SHARKPORT_HEADER);
|
||||||
|
STORE_32(size, 0, buffer);
|
||||||
|
if (vf->write(vf, buffer, 4) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (vf->write(vf, SHARKPORT_HEADER, size) < size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = 0x000F0000;
|
||||||
|
STORE_32(size, 0, buffer);
|
||||||
|
if (vf->write(vf, buffer, 4) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
|
||||||
|
size = sizeof(cart->title);
|
||||||
|
STORE_32(size, 0, buffer);
|
||||||
|
if (vf->write(vf, buffer, 4) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (vf->write(vf, cart->title, size) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t t = time(0);
|
||||||
|
struct tm* tm = localtime(&t);
|
||||||
|
size = strftime(&buffer[4], sizeof(buffer) - 4, "%m/%d/%Y %I:%M:%S %p", tm);
|
||||||
|
STORE_32(size, 0, buffer);
|
||||||
|
if (vf->write(vf, buffer, size + 4) < size + 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last field is blank
|
||||||
|
size = 0;
|
||||||
|
STORE_32(size, 0, buffer);
|
||||||
|
if (vf->write(vf, buffer, 4) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write payload
|
||||||
|
size = 0x1C;
|
||||||
|
switch (gba->memory.savedata.type) {
|
||||||
|
case SAVEDATA_SRAM:
|
||||||
|
size += SIZE_CART_SRAM;
|
||||||
|
break;
|
||||||
|
case SAVEDATA_FLASH512:
|
||||||
|
size += SIZE_CART_FLASH512;
|
||||||
|
break;
|
||||||
|
case SAVEDATA_FLASH1M:
|
||||||
|
size += SIZE_CART_FLASH1M;
|
||||||
|
break;
|
||||||
|
case SAVEDATA_EEPROM:
|
||||||
|
size += SIZE_CART_EEPROM;
|
||||||
|
break;
|
||||||
|
case SAVEDATA_FORCE_NONE:
|
||||||
|
case SAVEDATA_AUTODETECT:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
STORE_32(size, 0, buffer);
|
||||||
|
if (vf->write(vf, buffer, 4) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size -= 0x1C;
|
||||||
|
|
||||||
|
memcpy(buffer, cart->title, 16);
|
||||||
|
buffer[0x10] = 0;
|
||||||
|
buffer[0x11] = 0;
|
||||||
|
buffer[0x12] = cart->checksum;
|
||||||
|
buffer[0x13] = cart->maker;
|
||||||
|
buffer[0x14] = 1;
|
||||||
|
buffer[0x15] = 0;
|
||||||
|
buffer[0x16] = 0;
|
||||||
|
buffer[0x17] = 0;
|
||||||
|
buffer[0x18] = 0;
|
||||||
|
buffer[0x19] = 0;
|
||||||
|
buffer[0x1A] = 0;
|
||||||
|
buffer[0x1B] = 0;
|
||||||
|
if (vf->write(vf, buffer, 0x1C) < 0x1C) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < 0x1C; ++i) {
|
||||||
|
checksum += buffer[i] << (checksum % 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vf->write(vf, gba->memory.savedata.data, size) < size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < size; ++i) {
|
||||||
|
checksum += ((char) gba->memory.savedata.data[i]) << (checksum % 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
STORE_32(checksum, 0, buffer);
|
||||||
|
if (vf->write(vf, buffer, 4) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -311,6 +311,20 @@ void GameController::importSharkport(const QString& path) {
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameController::exportSharkport(const QString& path) {
|
||||||
|
if (!m_gameOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VFile* vf = VFileOpen(path.toLocal8Bit().constData(), O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
|
if (!vf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
threadInterrupt();
|
||||||
|
GBASavedataExportSharkPort(m_threadContext.gba, vf);
|
||||||
|
threadContinue();
|
||||||
|
vf->close(vf);
|
||||||
|
}
|
||||||
|
|
||||||
void GameController::closeGame() {
|
void GameController::closeGame() {
|
||||||
if (!m_gameOpen) {
|
if (!m_gameOpen) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -99,6 +99,7 @@ public slots:
|
||||||
void setUseBIOS(bool);
|
void setUseBIOS(bool);
|
||||||
void loadPatch(const QString& path);
|
void loadPatch(const QString& path);
|
||||||
void importSharkport(const QString& path);
|
void importSharkport(const QString& path);
|
||||||
|
void exportSharkport(const QString& path);
|
||||||
void openGame();
|
void openGame();
|
||||||
void closeGame();
|
void closeGame();
|
||||||
void setPaused(bool paused);
|
void setPaused(bool paused);
|
||||||
|
|
|
@ -270,6 +270,21 @@ void Window::importSharkport() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::exportSharkport() {
|
||||||
|
bool doPause = m_controller->isLoaded() && !m_controller->isPaused();
|
||||||
|
if (doPause) {
|
||||||
|
m_controller->setPaused(true);
|
||||||
|
}
|
||||||
|
QString filename = QFileDialog::getSaveFileName(this, tr("Select save"), m_config->getQtOption("lastDirectory").toString(), tr("GameShark saves (*.sps *.xps)"));
|
||||||
|
if (doPause) {
|
||||||
|
m_controller->setPaused(false);
|
||||||
|
}
|
||||||
|
if (!filename.isEmpty()) {
|
||||||
|
m_config->setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||||
|
m_controller->exportSharkport(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Window::openKeymapWindow() {
|
void Window::openKeymapWindow() {
|
||||||
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, InputController::KEYBOARD);
|
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, InputController::KEYBOARD);
|
||||||
openView(keyEditor);
|
openView(keyEditor);
|
||||||
|
@ -620,10 +635,15 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
QAction* loadSharkport = new QAction(tr("Import GameShark Save"), fileMenu);
|
QAction* importShark = new QAction(tr("Import GameShark Save"), fileMenu);
|
||||||
connect(loadSharkport, SIGNAL(triggered()), this, SLOT(importSharkport()));
|
connect(importShark, SIGNAL(triggered()), this, SLOT(importSharkport()));
|
||||||
m_gameActions.append(loadSharkport);
|
m_gameActions.append(importShark);
|
||||||
addControlledAction(fileMenu, loadSharkport, "loadSharkport");
|
addControlledAction(fileMenu, importShark, "importShark");
|
||||||
|
|
||||||
|
QAction* exportShark = new QAction(tr("Export GameShark Save"), fileMenu);
|
||||||
|
connect(exportShark, SIGNAL(triggered()), this, SLOT(exportSharkport()));
|
||||||
|
m_gameActions.append(exportShark);
|
||||||
|
addControlledAction(fileMenu, exportShark, "exportShark");
|
||||||
|
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
QAction* multiWindow = new QAction(tr("New multiplayer window"), fileMenu);
|
QAction* multiWindow = new QAction(tr("New multiplayer window"), fileMenu);
|
||||||
|
|
|
@ -66,6 +66,7 @@ public slots:
|
||||||
void saveConfig();
|
void saveConfig();
|
||||||
|
|
||||||
void importSharkport();
|
void importSharkport();
|
||||||
|
void exportSharkport();
|
||||||
|
|
||||||
void openKeymapWindow();
|
void openKeymapWindow();
|
||||||
void openSettingsWindow();
|
void openSettingsWindow();
|
||||||
|
|
Loading…
Reference in New Issue