* added Super Game Boy save RAM/RTC support
This commit is contained in:
byuu 2019-07-26 01:19:47 +09:00
parent c2a181dbc5
commit 2f67beeba9
16 changed files with 143 additions and 72 deletions

View File

@ -1,6 +1,5 @@
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <emulator/audio/audio.cpp> #include <emulator/audio/audio.cpp>
#include <emulator/resource/resource.cpp>
namespace Emulator { namespace Emulator {

View File

@ -28,11 +28,10 @@ using namespace nall;
#include <emulator/memory/readable.hpp> #include <emulator/memory/readable.hpp>
#include <emulator/memory/writable.hpp> #include <emulator/memory/writable.hpp>
#include <emulator/audio/audio.hpp> #include <emulator/audio/audio.hpp>
#include <emulator/resource/resource.hpp>
namespace Emulator { namespace Emulator {
static const string Name = "bsnes"; static const string Name = "bsnes";
static const string Version = "107.14"; static const string Version = "107.15";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org"; static const string Website = "https://byuu.org";

View File

@ -1,6 +0,0 @@
all:
sourcery resource.bml resource.cpp resource.hpp
clean:
rm resource.cpp
rm resource.hpp

View File

@ -1,5 +0,0 @@
namespace name=Resource
namespace name=Sprite
binary name=CrosshairRed file=sprite/crosshair-red.png
binary name=CrosshairGreen file=sprite/crosshair-green.png
binary name=CrosshairBlue file=sprite/crosshair-blue.png

View File

@ -1,45 +0,0 @@
#include "resource.hpp"
namespace Resource {
namespace Sprite {
const unsigned char CrosshairRed[342] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
196,1,149,43,14,27,0,0,0,248,73,68,65,84,88,133,205,87,65,14,196,32,8,132,102,255,255,101,246,176,177,139,
148,81,80,27,229,212,70,102,6,212,0,50,229,77,26,107,156,37,139,2,228,241,209,39,11,113,71,156,68,139,106,128,
56,255,198,175,203,223,114,16,79,68,253,138,90,99,141,113,112,80,231,131,196,11,83,52,19,43,196,53,135,147,7,38,
150,104,244,212,32,86,235,228,236,20,6,200,207,191,117,215,70,12,242,94,139,133,166,236,173,236,67,252,111,139,67,157,
237,71,48,27,192,244,142,93,228,23,148,144,184,228,131,96,254,3,164,4,176,213,108,37,52,5,208,53,47,227,81,28,
49,153,102,163,88,96,149,68,150,193,21,223,59,128,68,43,69,13,103,4,199,246,8,34,151,240,209,249,38,112,251,47,
97,177,209,74,152,246,95,93,9,211,51,160,181,99,142,128,104,115,55,124,59,136,115,7,146,237,51,33,2,71,166,226,
94,23,13,77,214,104,44,103,174,163,143,86,189,244,187,224,232,151,81,21,132,39,210,33,91,246,54,132,193,44,226,219,
107,95,57,136,120,253,172,254,16,23,0,0,0,0,73,69,78,68,174,66,96,130,
};
const unsigned char CrosshairGreen[329] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
196,1,149,43,14,27,0,0,0,235,73,68,65,84,88,133,213,87,65,18,195,32,8,196,78,31,230,211,253,153,61,180,
52,18,145,1,193,97,178,39,141,44,139,24,69,11,216,209,133,177,98,117,166,37,92,162,77,176,170,118,223,26,163,78,
68,71,145,198,244,169,157,57,35,84,248,43,222,255,109,154,254,113,140,114,102,222,18,239,165,120,251,181,42,0,232,103,
114,217,85,226,163,27,124,232,163,87,142,115,153,82,137,71,98,233,247,21,44,228,194,169,217,171,252,159,22,95,234,164,
47,129,55,128,144,140,237,166,63,132,151,190,4,247,147,16,103,35,157,90,220,140,119,121,80,224,94,108,0,164,227,119,
182,221,229,13,182,82,193,225,176,42,56,59,188,105,9,52,5,3,109,58,243,205,202,203,255,9,17,251,91,202,169,227,
205,128,235,198,19,17,64,40,82,171,225,233,32,158,113,33,65,164,222,9,105,16,50,81,55,238,88,210,212,119,1,0,
238,241,241,126,143,125,62,216,173,151,209,35,222,134,235,96,98,252,229,226,3,112,72,179,236,202,138,114,18,0,0,0,
0,73,69,78,68,174,66,96,130,
};
const unsigned char CrosshairBlue[332] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
196,1,149,43,14,27,0,0,0,238,73,68,65,84,88,133,213,87,91,18,195,32,8,196,78,15,232,81,189,161,253,9,
25,52,98,121,57,76,246,43,137,44,11,24,69,11,232,209,55,99,69,235,76,74,184,69,107,229,245,91,27,220,137,124,
75,140,58,21,165,34,181,246,199,251,100,167,174,200,32,124,137,119,124,134,177,252,116,108,224,44,120,44,190,156,56,102,
163,204,228,182,107,173,80,31,93,225,67,30,189,112,124,85,41,145,120,36,88,191,159,96,33,23,78,101,47,242,127,90,
156,213,73,159,2,111,0,33,21,179,150,63,132,151,62,5,243,78,136,217,236,118,173,85,198,86,30,20,152,154,13,192,
118,251,125,216,90,121,212,118,215,112,86,224,26,142,133,247,152,2,73,195,64,155,190,248,166,229,229,255,132,8,243,146,
242,234,120,43,224,58,241,68,4,16,138,212,110,120,58,136,119,28,72,16,169,103,194,33,136,63,68,209,184,103,74,83,
239,5,0,215,26,167,231,123,124,103,130,53,221,140,94,113,55,100,131,9,242,151,139,31,79,50,234,237,105,206,30,22,
0,0,0,0,73,69,78,68,174,66,96,130,
};
}
}

View File

@ -1,7 +0,0 @@
namespace Resource {
namespace Sprite {
extern const unsigned char CrosshairRed[342];
extern const unsigned char CrosshairGreen[329];
extern const unsigned char CrosshairBlue[332];
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

View File

@ -250,6 +250,48 @@ typedef union {
} vba64; } vba64;
} GB_rtc_save_t; } GB_rtc_save_t;
int GB_save_battery_size(GB_gameboy_t *gb)
{
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
GB_rtc_save_t rtc_save_size;
return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0);
}
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
{
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
if (size < GB_save_battery_size(gb)) return EIO;
memcpy(buffer, gb->mbc_ram, gb->mbc_ram_size);
if (gb->cartridge_type->has_rtc) {
GB_rtc_save_t rtc_save = {{{{0,}},},};
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds;
rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes;
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
rtc_save.vba64.rtc_real.days = gb->rtc_real.days;
rtc_save.vba64.rtc_real.high = gb->rtc_real.high;
rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds;
rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes;
rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours;
rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days;
rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high;
#ifdef GB_BIG_ENDIAN
rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second);
#else
rtc_save.vba64.last_rtc_second = gb->last_rtc_second;
#endif
memcpy(buffer + gb->mbc_ram_size, &rtc_save.vba64, sizeof(rtc_save.vba64));
}
errno = 0;
return errno;
}
int GB_save_battery(GB_gameboy_t *gb, const char *path) int GB_save_battery(GB_gameboy_t *gb, const char *path)
{ {
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
@ -293,6 +335,79 @@ int GB_save_battery(GB_gameboy_t *gb, const char *path)
return errno; return errno;
} }
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size)
{
memcpy(gb->mbc_ram, buffer, MIN(gb->mbc_ram_size, size));
if (size <= gb->mbc_ram_size) {
goto reset_rtc;
}
GB_rtc_save_t rtc_save;
memcpy(&rtc_save, buffer + gb->mbc_ram_size, MIN(sizeof(rtc_save), size));
switch (size - gb->mbc_ram_size) {
case sizeof(rtc_save.sameboy_legacy):
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second;
break;
case sizeof(rtc_save.vba32):
gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba32.rtc_real.days;
gb->rtc_real.high = rtc_save.vba32.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba32.last_rtc_second;
#endif
break;
case sizeof(rtc_save.vba64):
gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba64.rtc_real.days;
gb->rtc_real.high = rtc_save.vba64.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba64.last_rtc_second;
#endif
break;
default:
goto reset_rtc;
}
if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */
goto reset_rtc;
}
if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time,
so if the value we read is lower it means it wasn't
really RTC data. */
goto reset_rtc;
}
goto exit;
reset_rtc:
gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
exit:
return;
}
/* Loading will silently stop if the format is incomplete */ /* Loading will silently stop if the format is incomplete */
void GB_load_battery(GB_gameboy_t *gb, const char *path) void GB_load_battery(GB_gameboy_t *gb, const char *path)
{ {

View File

@ -671,7 +671,11 @@ void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer,
int GB_load_rom(GB_gameboy_t *gb, const char *path); int GB_load_rom(GB_gameboy_t *gb, const char *path);
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size); void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
int GB_save_battery_size(GB_gameboy_t *gb);
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
int GB_save_battery(GB_gameboy_t *gb, const char *path); int GB_save_battery(GB_gameboy_t *gb, const char *path);
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
void GB_load_battery(GB_gameboy_t *gb, const char *path); void GB_load_battery(GB_gameboy_t *gb, const char *path);
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip); void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);

View File

@ -128,7 +128,7 @@ auto Cartridge::loadSufamiTurboB() -> bool {
auto Cartridge::save() -> void { auto Cartridge::save() -> void {
saveCartridge(game.document); saveCartridge(game.document);
if(has.GameBoySlot) { if(has.GameBoySlot) {
saveCartridgeGameBoy(slotGameBoy.document); icd.save();
} }
if(has.BSMemorySlot) { if(has.BSMemorySlot) {
saveCartridgeBSMemory(slotBSMemory.document); saveCartridgeBSMemory(slotBSMemory.document);

View File

@ -98,7 +98,6 @@ private:
//save.cpp //save.cpp
auto saveCartridge(Markup::Node) -> void; auto saveCartridge(Markup::Node) -> void;
auto saveCartridgeGameBoy(Markup::Node) -> void;
auto saveCartridgeBSMemory(Markup::Node) -> void; auto saveCartridgeBSMemory(Markup::Node) -> void;
auto saveCartridgeSufamiTurboA(Markup::Node) -> void; auto saveCartridgeSufamiTurboA(Markup::Node) -> void;
auto saveCartridgeSufamiTurboB(Markup::Node) -> void; auto saveCartridgeSufamiTurboB(Markup::Node) -> void;

View File

@ -13,9 +13,6 @@ auto Cartridge::saveCartridge(Markup::Node node) -> void {
if(auto node = board["processor(identifier=OBC1)"]) saveOBC1(node); if(auto node = board["processor(identifier=OBC1)"]) saveOBC1(node);
} }
auto Cartridge::saveCartridgeGameBoy(Markup::Node node) -> void {
}
auto Cartridge::saveCartridgeBSMemory(Markup::Node node) -> void { auto Cartridge::saveCartridgeBSMemory(Markup::Node node) -> void {
if(auto memory = Emulator::Game::Memory{node["game/board/memory(type=Flash,content=Program)"]}) { if(auto memory = Emulator::Game::Memory{node["game/board/memory(type=Flash,content=Program)"]}) {
if(auto fp = platform->open(bsmemory.pathID, memory.name(), File::Write)) { if(auto fp = platform->open(bsmemory.pathID, memory.name(), File::Write)) {

View File

@ -101,11 +101,31 @@ auto ICD::load() -> bool {
cartridge.information.sha256 = Hash::SHA256({data, (uint64_t)size}).digest(); cartridge.information.sha256 = Hash::SHA256({data, (uint64_t)size}).digest();
fp->read(data, size); fp->read(data, size);
GB_load_rom_from_buffer(&sameboy, data, size); GB_load_rom_from_buffer(&sameboy, data, size);
free(data);
} else return unload(), false; } else return unload(), false;
if(auto fp = platform->open(pathID(), "save.ram", File::Read)) {
auto size = fp->size();
auto data = (uint8_t*)malloc(size);
fp->read(data, size);
GB_load_battery_from_buffer(&sameboy, data, size);
free(data);
}
return true; return true;
} }
auto ICD::save() -> void {
if(auto size = GB_save_battery_size(&sameboy)) {
auto data = (uint8_t*)malloc(size);
GB_save_battery_to_buffer(&sameboy, data, size);
if(auto fp = platform->open(pathID(), "save.ram", File::Write)) {
fp->write(data, size);
}
free(data);
}
}
auto ICD::unload() -> void { auto ICD::unload() -> void {
save();
GB_free(&sameboy); GB_free(&sameboy);
} }

View File

@ -8,6 +8,7 @@ struct ICD : Emulator::Platform, Thread {
auto main() -> void; auto main() -> void;
auto load() -> bool; auto load() -> bool;
auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power(bool reset = false) -> void; auto power(bool reset = false) -> void;