GBA: Create GameShark snapshots

This commit is contained in:
Jeffrey Pfau 2015-04-15 04:17:28 -07:00
parent 3ff8467ba7
commit 688be6948b
6 changed files with 148 additions and 4 deletions

View File

@ -6,6 +6,7 @@ Features:
- 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

View File

@ -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;
}

View File

@ -311,6 +311,20 @@ void GameController::importSharkport(const QString& path) {
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;

View File

@ -99,6 +99,7 @@ public slots:
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);

View File

@ -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() {
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, InputController::KEYBOARD);
openView(keyEditor);
@ -620,10 +635,15 @@ void Window::setupMenu(QMenuBar* menubar) {
}
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);

View File

@ -66,6 +66,7 @@ public slots:
void saveConfig();
void importSharkport();
void exportSharkport();
void openKeymapWindow();
void openSettingsWindow();