all repos — mgba @ 688be6948bedf51f1cabf6404b6dec8116e04599

mGBA Game Boy Advance Emulator

GBA: Create GameShark snapshots
Jeffrey Pfau jeffrey@endrift.com
Wed, 15 Apr 2015 04:17:28 -0700
commit

688be6948bedf51f1cabf6404b6dec8116e04599

parent

3ff8467ba7df94e7a256ef7ccb45426cc495cc82

M CHANGESCHANGES

@@ -6,6 +6,7 @@ - Palette viewer

- Volume control - More shortcuts are editable (e.g. quick save/load, solar sensor) - Rewind now shows the frame after rewinding + - Import/Export of GameShark/Action Replay snapshots Bugfixes: - GBA: Fix timers not updating timing when writing to only the reload register - All: Fix sanitize-deb script not cleaning up after itself
M src/gba/sharkport.csrc/gba/sharkport.c

@@ -143,3 +143,110 @@ cleanup:

free(payload); 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; +}
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -311,6 +311,20 @@ threadContinue();

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() { if (!m_gameOpen) { return;
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -99,6 +99,7 @@ void setSkipBIOS(bool);

void setUseBIOS(bool); void loadPatch(const QString& path); void importSharkport(const QString& path); + void exportSharkport(const QString& path); void openGame(); void closeGame(); void setPaused(bool paused);
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -270,6 +270,21 @@ m_controller->importSharkport(filename);

} } +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() { GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, InputController::KEYBOARD); openView(keyEditor);

@@ -620,10 +635,15 @@ addControlledAction(quickSaveMenu, quickSave, QString("quickSave.%1").arg(i));

} fileMenu->addSeparator(); - QAction* loadSharkport = new QAction(tr("Import GameShark Save"), fileMenu); - connect(loadSharkport, SIGNAL(triggered()), this, SLOT(importSharkport())); - m_gameActions.append(loadSharkport); - addControlledAction(fileMenu, loadSharkport, "loadSharkport"); + QAction* importShark = new QAction(tr("Import GameShark Save"), fileMenu); + connect(importShark, SIGNAL(triggered()), this, SLOT(importSharkport())); + m_gameActions.append(importShark); + 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(); QAction* multiWindow = new QAction(tr("New multiplayer window"), fileMenu);
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -66,6 +66,7 @@ void loadConfig();

void saveConfig(); void importSharkport(); + void exportSharkport(); void openKeymapWindow(); void openSettingsWindow();