GBA: Support for loading Gameshark snapshots

This commit is contained in:
Jeffrey Pfau 2015-04-15 03:58:58 -07:00
parent 27a178fe3c
commit 3ff8467ba7
6 changed files with 201 additions and 0 deletions

145
src/gba/sharkport.c Normal file
View File

@ -0,0 +1,145 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "sharkport.h"
#include "gba/gba.h"
#include "util/vfs.h"
static const char* const SHARKPORT_HEADER = "SharkPortSave";
bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf) {
char buffer[0x1C];
if (vf->read(vf, buffer, 4) < 4) {
return false;
}
uint32_t size;
LOAD_32(size, 0, buffer);
if (size != strlen(SHARKPORT_HEADER)) {
return false;
}
if (vf->read(vf, buffer, size) < size) {
return false;
}
if (memcmp(SHARKPORT_HEADER, buffer, size) != 0) {
return false;
}
if (vf->read(vf, buffer, 4) < 4) {
return false;
}
LOAD_32(size, 0, buffer);
if (size != 0x000F0000) {
// What is this value?
return false;
}
// Skip first three fields
if (vf->read(vf, buffer, 4) < 4) {
return false;
}
LOAD_32(size, 0, buffer);
if (vf->seek(vf, size, SEEK_CUR) < 0) {
return false;
}
if (vf->read(vf, buffer, 4) < 4) {
return false;
}
LOAD_32(size, 0, buffer);
if (vf->seek(vf, size, SEEK_CUR) < 0) {
return false;
}
if (vf->read(vf, buffer, 4) < 4) {
return false;
}
LOAD_32(size, 0, buffer);
if (vf->seek(vf, size, SEEK_CUR) < 0) {
return false;
}
// Read payload
if (vf->read(vf, buffer, 4) < 4) {
return false;
}
LOAD_32(size, 0, buffer);
if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) {
return false;
}
char* payload = malloc(size);
if (vf->read(vf, payload, size) < size) {
goto cleanup;
}
struct GBACartridge* cart = (struct GBACartridge*) gba->memory.rom;
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 (memcmp(buffer, payload, 0x1C) != 0) {
goto cleanup;
}
uint32_t checksum;
if (vf->read(vf, buffer, 4) < 4) {
goto cleanup;
}
LOAD_32(checksum, 0, buffer);
uint32_t calcChecksum = 0;
uint32_t i;
for (i = 0; i < size; ++i) {
calcChecksum += payload[i] << (calcChecksum % 24);
}
if (calcChecksum != checksum) {
goto cleanup;
}
uint32_t copySize = size - 0x1C;
switch (gba->memory.savedata.type) {
case SAVEDATA_SRAM:
if (copySize > SIZE_CART_SRAM) {
copySize = SIZE_CART_SRAM;
}
break;
case SAVEDATA_FLASH512:
if (copySize > SIZE_CART_FLASH512) {
GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming);
}
// Fall through
case SAVEDATA_FLASH1M:
if (copySize > SIZE_CART_FLASH1M) {
copySize = SIZE_CART_FLASH1M;
}
break;
case SAVEDATA_EEPROM:
if (copySize > SIZE_CART_EEPROM) {
copySize = SAVEDATA_EEPROM;
}
break;
case SAVEDATA_FORCE_NONE:
case SAVEDATA_AUTODETECT:
goto cleanup;
}
memcpy(gba->memory.savedata.data, &payload[0x1C], copySize);
free(payload);
return true;
cleanup:
free(payload);
return false;
}

17
src/gba/sharkport.h Normal file
View File

@ -0,0 +1,17 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GBA_SHARKPORT_H
#define GBA_SHARKPORT_H
#include "util/common.h"
struct GBA;
struct VFile;
bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf);
bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf);
#endif

View File

@ -18,6 +18,7 @@ extern "C" {
#include "gba/audio.h"
#include "gba/gba.h"
#include "gba/serialize.h"
#include "gba/sharkport.h"
#include "gba/renderers/video-software.h"
#include "gba/supervisor/config.h"
#include "util/vfs.h"
@ -296,6 +297,20 @@ void GameController::loadPatch(const QString& path) {
}
}
void GameController::importSharkport(const QString& path) {
if (!m_gameOpen) {
return;
}
VFile* vf = VFileOpen(path.toLocal8Bit().constData(), O_RDONLY);
if (!vf) {
return;
}
threadInterrupt();
GBASavedataImportSharkPort(m_threadContext.gba, vf);
threadContinue();
vf->close(vf);
}
void GameController::closeGame() {
if (!m_gameOpen) {
return;

View File

@ -98,6 +98,7 @@ public slots:
void setSkipBIOS(bool);
void setUseBIOS(bool);
void loadPatch(const QString& path);
void importSharkport(const QString& path);
void openGame();
void closeGame();
void setPaused(bool paused);

View File

@ -255,6 +255,21 @@ void Window::openView(QWidget* widget) {
widget->show();
}
void Window::importSharkport() {
bool doPause = m_controller->isLoaded() && !m_controller->isPaused();
if (doPause) {
m_controller->setPaused(true);
}
QString filename = QFileDialog::getOpenFileName(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->importSharkport(filename);
}
}
void Window::openKeymapWindow() {
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, InputController::KEYBOARD);
openView(keyEditor);
@ -604,6 +619,12 @@ void Window::setupMenu(QMenuBar* menubar) {
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");
fileMenu->addSeparator();
QAction* multiWindow = new QAction(tr("New multiplayer window"), fileMenu);
connect(multiWindow, &QAction::triggered, [this]() {

View File

@ -65,6 +65,8 @@ public slots:
void loadConfig();
void saveConfig();
void importSharkport();
void openKeymapWindow();
void openSettingsWindow();
void openShortcutWindow();