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
|
||||
- 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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -66,6 +66,7 @@ public slots:
|
|||
void saveConfig();
|
||||
|
||||
void importSharkport();
|
||||
void exportSharkport();
|
||||
|
||||
void openKeymapWindow();
|
||||
void openSettingsWindow();
|
||||
|
|
Loading…
Reference in New Issue