mirror of https://github.com/bsnes-emu/bsnes.git
Update to v086r01 release.
byuu says: The goals for v087 are to have a unified cartridge-folder concept, as well as a more functional SNES debugger. Starting with the cartridge folders. What I have so far: Code: NES: - program.rom - character.rom - program.ram - … SNES: - program.rom - program.rtc - data.rom (SPC7110) - { dsp1.rom, dsp1b.rom, cx4.rom, … } - msu1.rom - track-#.pcm Game Boy, Game Boy Color: - program.rom - program.ram - program.rtc Sub-cartridges (BS-X, Sufami Turbo, …) are stored as separate folders Folder names must be UTF-8 based, with all-lowercase extensions File names must be all-lowercase SNES: - "program.ram" (.srm, .sts) - "msu1.rom" (name.msu) - "track-#.pcm" (name-#.pcm) - "upd96050.ram" -> "name.ram" - "bsx.ram" (.bss) - "bsx.psram" (.bsp) - "serial.so" -> "libserial.so" (broken) Need: - Super Game Boy (not even sure how this loads and saves memory, it's obviously broken) And I need to think of some way of handling multi-cart loaded games. Eg Satellaview-slotted and Sufami Turbo. It was { base + slot ( + slot ... } }, but this gets trickier with folders and fixed names. Actual markup for the NES needs to change as well.
This commit is contained in:
parent
10fd29e7bb
commit
a37ce1cb2f
|
@ -1,7 +1,7 @@
|
|||
#ifndef BASE_HPP
|
||||
#define BASE_HPP
|
||||
|
||||
const char Version[] = "086";
|
||||
const char Version[] = "086.01";
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/algorithm.hpp>
|
||||
|
|
|
@ -145,7 +145,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
|||
);
|
||||
|
||||
else if(has_cx4) markup.append(
|
||||
" <hitachidsp model='HG51B169' frequency='20000000' firmware='cx4.bin' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n"
|
||||
" <hitachidsp model='HG51B169' frequency='20000000' firmware='cx4.rom' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n"
|
||||
" <rom>\n"
|
||||
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
|
||||
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
|
||||
|
@ -420,7 +420,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
|||
);
|
||||
|
||||
if(has_dsp1) {
|
||||
markup.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n");
|
||||
markup.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp1b.rom' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n");
|
||||
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
|
||||
" <dr>\n"
|
||||
" <map address='20-3f:8000-bfff'/>\n"
|
||||
|
@ -455,7 +455,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
|||
}
|
||||
|
||||
if(has_dsp2) markup.append(
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n"
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp2.rom' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='20-3f:8000-bfff'/>\n"
|
||||
" <map address='a0-bf:8000-bfff'/>\n"
|
||||
|
@ -468,7 +468,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
|||
);
|
||||
|
||||
if(has_dsp3) markup.append(
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n"
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp3.rom' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='20-3f:8000-bfff'/>\n"
|
||||
" <map address='a0-bf:8000-bfff'/>\n"
|
||||
|
@ -481,7 +481,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
|||
);
|
||||
|
||||
if(has_dsp4) markup.append(
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n"
|
||||
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp4.rom' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='30-3f:8000-bfff'/>\n"
|
||||
" <map address='b0-bf:8000-bfff'/>\n"
|
||||
|
@ -494,7 +494,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
|||
);
|
||||
|
||||
if(has_st010) markup.append(
|
||||
" <necdsp model='uPD96050' frequency='10000000' firmware='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n"
|
||||
" <necdsp model='uPD96050' frequency='10000000' firmware='st0010.rom' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='60:0000'/>\n"
|
||||
" <map address='e0:0000'/>\n"
|
||||
|
@ -511,7 +511,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
|||
);
|
||||
|
||||
if(has_st011) markup.append(
|
||||
" <necdsp model='uPD96050' frequency='15000000' firmware='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n"
|
||||
" <necdsp model='uPD96050' frequency='15000000' firmware='st0011.rom' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n"
|
||||
" <dr>\n"
|
||||
" <map address='60:0000'/>\n"
|
||||
" <map address='e0:0000'/>\n"
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#define SMP_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "disassembler.cpp"
|
||||
SMP smp;
|
||||
|
||||
#include "algorithms.cpp"
|
||||
|
|
|
@ -38,7 +38,7 @@ void Cartridge::load(Mode cartridge_mode, const char *markup) {
|
|||
|
||||
if(ram_size > 0) {
|
||||
ram.map(allocate<uint8>(ram_size, 0xff), ram_size);
|
||||
nvram.append({ ".srm", ram.data(), ram.size() });
|
||||
nvram.append({ "program.ram", ram.data(), ram.size() });
|
||||
}
|
||||
|
||||
rom.write_protect(true);
|
||||
|
|
|
@ -223,7 +223,7 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
|
|||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
|
||||
string path = { dir(interface->path(Slot::Base, ".dsp")), firmware };
|
||||
string path = interface->path(Slot::Base, firmware);
|
||||
unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384);
|
||||
unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048);
|
||||
unsigned filesize = promsize * 3 + dromsize * 2;
|
||||
|
@ -288,7 +288,7 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
|
|||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
|
||||
string path = { dir(interface->path(Slot::Base, ".dsp")), firmware };
|
||||
string path = interface->path(Slot::Base, firmware);
|
||||
file fp;
|
||||
if(fp.open(path, file::mode::read) == false) {
|
||||
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is missing." });
|
||||
|
@ -496,7 +496,7 @@ void Cartridge::parse_markup_setarisc(XML::Node &root) {
|
|||
|
||||
void Cartridge::parse_markup_msu1(XML::Node &root) {
|
||||
if(root.exists() == false) {
|
||||
has_msu1 = file::exists(interface->path(Cartridge::Slot::Base, ".msu"));
|
||||
has_msu1 = file::exists(interface->path(Cartridge::Slot::Base, "msu1.rom"));
|
||||
if(has_msu1) {
|
||||
Mapping m({ &MSU1::mmio_read, &msu1 }, { &MSU1::mmio_write, &msu1 });
|
||||
m.banklo = 0x00, m.bankhi = 0x3f, m.addrlo = 0x2000, m.addrhi = 0x2007;
|
||||
|
|
|
@ -8,11 +8,11 @@ void BSXCartridge::init() {
|
|||
void BSXCartridge::load() {
|
||||
sram.map(allocate<uint8>(32 * 1024, 0xff), 32 * 1024);
|
||||
sram.write_protect(false);
|
||||
cartridge.nvram.append({ ".bss", sram.data(), sram.size() });
|
||||
cartridge.nvram.append({ "bsx.ram", sram.data(), sram.size() });
|
||||
|
||||
psram.map(allocate<uint8>(512 * 1024, 0xff), 512 * 1024);
|
||||
psram.write_protect(false);
|
||||
cartridge.nvram.append({ ".bsp", psram.data(), psram.size() });
|
||||
cartridge.nvram.append({ "bsx.psram", psram.data(), psram.size() });
|
||||
}
|
||||
|
||||
void BSXCartridge::unload() {
|
||||
|
|
|
@ -50,7 +50,7 @@ void MSU1::init() {
|
|||
|
||||
void MSU1::load() {
|
||||
if(datafile.open()) datafile.close();
|
||||
datafile.open(interface->path(Cartridge::Slot::Base, ".msu"), file::mode::read);
|
||||
datafile.open(interface->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read);
|
||||
}
|
||||
|
||||
void MSU1::unload() {
|
||||
|
@ -110,7 +110,7 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
|
|||
case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0);
|
||||
case 5: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
|
||||
if(audiofile.open()) audiofile.close();
|
||||
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
uint32 header = audiofile.readm(4);
|
||||
if(header != 0x4d535531) { //verify 'MSU1' header
|
||||
audiofile.close();
|
||||
|
|
|
@ -16,12 +16,12 @@ void MSU1::serialize(serializer &s) {
|
|||
s.integer(mmio.audio_play);
|
||||
|
||||
if(datafile.open()) datafile.close();
|
||||
if(datafile.open(interface->path(Cartridge::Slot::Base, ".msu"), file::mode::read)) {
|
||||
if(datafile.open(interface->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read)) {
|
||||
datafile.seek(mmio.data_offset);
|
||||
}
|
||||
|
||||
if(audiofile.open()) audiofile.close();
|
||||
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
audiofile.seek(mmio.audio_offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ void NECDSP::init() {
|
|||
|
||||
void NECDSP::load() {
|
||||
if(revision == Revision::uPD96050) {
|
||||
cartridge.nvram.append({ ".nec", (uint8_t*)dataRAM, 4096 });
|
||||
cartridge.nvram.append({ "upd96050.ram", (uint8_t*)dataRAM, 4096 });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,13 @@ void SufamiTurbo::load() {
|
|||
slotB.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
|
||||
|
||||
if(slotA.rom.data()) {
|
||||
cartridge.nvram.append({ ".sts", slotA.ram.data(), slotA.ram.size(), Cartridge::Slot::SufamiTurboA });
|
||||
cartridge.nvram.append({ "program.ram", slotA.ram.data(), slotA.ram.size(), Cartridge::Slot::SufamiTurboA });
|
||||
} else {
|
||||
slotA.rom.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
|
||||
}
|
||||
|
||||
if(slotB.rom.data()) {
|
||||
cartridge.nvram.append({ ".sts", slotB.ram.data(), slotB.ram.size(), Cartridge::Slot::SufamiTurboB });
|
||||
cartridge.nvram.append({ "program.ram", slotB.ram.data(), slotB.ram.size(), Cartridge::Slot::SufamiTurboB });
|
||||
} else {
|
||||
slotB.rom.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ void Serial::latch(bool data) {
|
|||
|
||||
Serial::Serial(bool port) : Controller(port) {
|
||||
enable = false;
|
||||
string basename = interface->path(Cartridge::Slot::Base, "");
|
||||
string basename = interface->path(Cartridge::Slot::Base, "serial.so");
|
||||
string name = notdir(basename);
|
||||
string path = dir(basename);
|
||||
if(open(name, path)) {
|
||||
|
|
|
@ -86,7 +86,7 @@ ConsoleWindow::ConsoleWindow() {
|
|||
onClose = [] { application->quit = true; };
|
||||
|
||||
menuEmulationReloadCartridge.onActivate = [&] {
|
||||
interface->loadCartridge(interface->fileName);
|
||||
interface->loadCartridge(interface->pathName);
|
||||
};
|
||||
|
||||
menuEmulationPowerCycle.onActivate = [&] {
|
||||
|
|
|
@ -20,7 +20,7 @@ void Debugger::loadUsage() {
|
|||
//then it is possible that the memory map has changed.
|
||||
//will print invalidation message when files do not exist as well.
|
||||
|
||||
if(file::timestamp(interface->fileName, file::time::modify) >=
|
||||
if(file::timestamp({ interface->pathName, "program.rom" }, file::time::modify) >=
|
||||
file::timestamp({ interface->pathName, "debug/usage.cpu" }, file::time::modify)
|
||||
) {
|
||||
print("CPU usage invalidated\n");
|
||||
|
@ -31,7 +31,7 @@ void Debugger::loadUsage() {
|
|||
}
|
||||
}
|
||||
|
||||
if(file::timestamp(interface->fileName, file::time::modify) >=
|
||||
if(file::timestamp({ interface->pathName, "program.rom" }, file::time::modify) >=
|
||||
file::timestamp({ interface->pathName, "debug/usage.apu" }, file::time::modify)
|
||||
) {
|
||||
print("APU usage invalidated\n");
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include "../base.hpp"
|
||||
Interface *interface = nullptr;
|
||||
|
||||
bool Interface::loadCartridge(const string &filename) {
|
||||
bool Interface::loadCartridge(const string &foldername) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
if(file::read({ foldername, "program.rom" }, data, size) == false) return false;
|
||||
|
||||
if(SNES::cartridge.loaded()) {
|
||||
saveMemory();
|
||||
|
@ -12,23 +12,25 @@ bool Interface::loadCartridge(const string &filename) {
|
|||
debugger->print("Cartridge unloaded\n");
|
||||
}
|
||||
|
||||
fileName = filename;
|
||||
baseName = nall::basename(fileName);
|
||||
pathName = dir(baseName);
|
||||
pathName = foldername;
|
||||
mkdir(string(pathName, "debug/"), 0755);
|
||||
|
||||
string markup;
|
||||
markup.readfile({ baseName, ".xml" });
|
||||
markup.readfile({ pathName, "manifest.xml" });
|
||||
if(markup.empty()) markup = SnesCartridge(data, size).markup;
|
||||
|
||||
SNES::cartridge.rom.copy(data, size);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
|
||||
SNES::system.power();
|
||||
|
||||
string name = pathName;
|
||||
name.rtrim<1>("/");
|
||||
name = notdir(name);
|
||||
|
||||
delete[] data;
|
||||
videoWindow->setTitle(notdir(baseName));
|
||||
videoWindow->setTitle(name);
|
||||
SNES::video.generate(SNES::Video::Format::RGB24);
|
||||
debugger->print("Loaded ", fileName, "\n");
|
||||
debugger->print("Loaded ", pathName, "program.rom\n");
|
||||
loadMemory();
|
||||
debugger->print(markup, "\n");
|
||||
debugger->suspend();
|
||||
|
@ -38,7 +40,7 @@ bool Interface::loadCartridge(const string &filename) {
|
|||
void Interface::loadMemory() {
|
||||
for(auto &memory : SNES::cartridge.nvram) {
|
||||
if(memory.size == 0) continue;
|
||||
string filename = { baseName, memory.id };
|
||||
string filename = { pathName, memory.id };
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size)) {
|
||||
|
@ -54,7 +56,7 @@ void Interface::loadMemory() {
|
|||
void Interface::saveMemory() {
|
||||
for(auto &memory : SNES::cartridge.nvram) {
|
||||
if(memory.size == 0) continue;
|
||||
string filename = { baseName, memory.id };
|
||||
string filename = { pathName, memory.id };
|
||||
if(file::write(filename, memory.data, memory.size)) {
|
||||
debugger->print("Saved ", filename, "\n");
|
||||
}
|
||||
|
@ -64,7 +66,7 @@ void Interface::saveMemory() {
|
|||
}
|
||||
|
||||
bool Interface::loadState(unsigned slot) {
|
||||
string filename = { baseName, "-", slot, ".bst" };
|
||||
string filename = { pathName, "state-", slot, ".bst" };
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
|
@ -78,7 +80,7 @@ bool Interface::loadState(unsigned slot) {
|
|||
bool Interface::saveState(unsigned slot) {
|
||||
SNES::system.runtosave();
|
||||
serializer s = SNES::system.serialize();
|
||||
string filename = { baseName, "-", slot, ".bst" };
|
||||
string filename = { pathName, "state-", slot, ".bst" };
|
||||
bool result = file::write(filename, s.data(), s.size());
|
||||
if(result) debugger->print("Saved state to ", filename, "\n");
|
||||
return result;
|
||||
|
@ -144,7 +146,7 @@ int16_t Interface::inputPoll(bool port, SNES::Input::Device device, unsigned ind
|
|||
}
|
||||
|
||||
string Interface::path(SNES::Cartridge::Slot slot, const string &hint) {
|
||||
return { baseName, hint };
|
||||
return { pathName, hint };
|
||||
}
|
||||
|
||||
void Interface::message(const string &text) {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
struct Interface : SNES::Interface {
|
||||
string fileName;
|
||||
string baseName;
|
||||
string pathName;
|
||||
|
||||
bool loadCartridge(const string &filename);
|
||||
bool loadCartridge(const string &foldername);
|
||||
void loadMemory();
|
||||
void saveMemory();
|
||||
bool loadState(unsigned slot);
|
||||
|
|
|
@ -38,10 +38,7 @@ Application::Application(int argc, char **argv) {
|
|||
//if(!directory::exists(foldername)) foldername = "/media/sdb1/root/cartridges/The Legend of Zelda - A Link to the Past (US).sfc/";
|
||||
if(!directory::exists(foldername)) foldername = DialogWindow::folderSelect(Window::None, settings->folderpath);
|
||||
if(!foldername.endswith(".sfc/")) return;
|
||||
lstring contents = directory::files(foldername, "*.sfc");
|
||||
if(contents.size() != 1) return;
|
||||
string filename = { foldername, contents[0] };
|
||||
if(!file::exists(filename)) return;
|
||||
if(!directory::exists(foldername)) return;
|
||||
|
||||
//save path for later; remove cartridge name from path
|
||||
settings->folderpath = foldername;
|
||||
|
@ -76,7 +73,7 @@ Application::Application(int argc, char **argv) {
|
|||
audio.set(Audio::Synchronize, settings->synchronizeAudio);
|
||||
audio.set(Audio::Frequency, 32000u);
|
||||
|
||||
if(interface->loadCartridge(filename) == false) return;
|
||||
if(interface->loadCartridge(foldername) == false) return;
|
||||
cpuDebugger->updateDisassembly();
|
||||
smpDebugger->updateDisassembly();
|
||||
memoryEditor->selectSource();
|
||||
|
|
|
@ -12,21 +12,18 @@ bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const s
|
|||
unsigned size;
|
||||
|
||||
if(filename.endswith("/")) {
|
||||
string basename = filename;
|
||||
basename.rtrim<1>("/");
|
||||
basename.append("/", notdir(basename));
|
||||
if(file::read(basename, data, size) == false) return false;
|
||||
interface->baseName = nall::basename(basename);
|
||||
if(file::read({ filename, "program.rom" }, data, size) == false) return false;
|
||||
interface->base = { true, filename };
|
||||
} else {
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
interface->baseName = nall::basename(filename);
|
||||
interface->base.name = { false, nall::basename(filename) };
|
||||
}
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->applyPatch(interface->baseName, data, size);
|
||||
//interface->applyPatch(interface->baseName, data, size);
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||
|
||||
GameBoyCartridge info(data, size);
|
||||
if(markup.empty()) markup = info.markup;
|
||||
|
@ -37,7 +34,7 @@ bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const s
|
|||
|
||||
if(GameBoy::cartridge.ramsize) {
|
||||
filemap fp;
|
||||
if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) {
|
||||
if(fp.open(interface->base.filename("program.ram", ".sav"), filemap::mode::read)) {
|
||||
memcpy(GameBoy::cartridge.ramdata, fp.data(), min(GameBoy::cartridge.ramsize, fp.size()));
|
||||
}
|
||||
}
|
||||
|
@ -50,11 +47,11 @@ bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const s
|
|||
|
||||
void InterfaceGameBoy::unloadCartridge() {
|
||||
if(GameBoy::cartridge.ramsize) {
|
||||
file::write({ interface->baseName, ".sav" }, GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize);
|
||||
file::write(interface->base.filename("program.ram", ".sav"), GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize);
|
||||
}
|
||||
|
||||
GameBoy::cartridge.unload();
|
||||
interface->baseName = "";
|
||||
interface->base.name = "";
|
||||
}
|
||||
|
||||
void InterfaceGameBoy::power() {
|
||||
|
|
|
@ -22,6 +22,12 @@ Filter::~Filter() {
|
|||
delete[] data;
|
||||
}
|
||||
|
||||
string CartridgePath::filename(const string &folderName, const string &fileName) const {
|
||||
if(name.empty()) return "";
|
||||
if(folder) return { name, folderName };
|
||||
return { name, fileName };
|
||||
}
|
||||
|
||||
void Interface::bindControllers() {
|
||||
switch(mode()) {
|
||||
case Mode::NES:
|
||||
|
@ -79,8 +85,8 @@ void Interface::loadCartridge(Mode mode) {
|
|||
}
|
||||
|
||||
bindControllers();
|
||||
cheatEditor->load({ baseName, ".cht" });
|
||||
stateManager->load({ baseName, ".bsa" }, 0u);
|
||||
cheatEditor->load(base.filename("cheats.xml", ".cht"));
|
||||
stateManager->load(base.filename("states.bsa", ".bsa"), 0u);
|
||||
dipSwitches->load();
|
||||
utility->showMessage({ "Loaded ", notdir(baseName) });
|
||||
}
|
||||
|
@ -97,8 +103,8 @@ bool Interface::loadCartridge(const string &filename) {
|
|||
void Interface::unloadCartridge() {
|
||||
if(cartridgeLoaded() == false) return;
|
||||
cheatDatabase->setVisible(false);
|
||||
cheatEditor->save({ baseName, ".cht" });
|
||||
stateManager->save({ baseName, ".bsa" }, 0u);
|
||||
cheatEditor->save(base.filename("cheats.xml", ".cht"));
|
||||
stateManager->save(base.filename("states.bsa", ".bsa"), 0u);
|
||||
setCheatCodes();
|
||||
|
||||
switch(mode()) {
|
||||
|
@ -108,7 +114,6 @@ void Interface::unloadCartridge() {
|
|||
}
|
||||
|
||||
interface->baseName = "";
|
||||
interface->slotName.reset();
|
||||
utility->setMode(mode = Mode::None);
|
||||
}
|
||||
|
||||
|
@ -140,7 +145,7 @@ bool Interface::unserialize(serializer &s) {
|
|||
}
|
||||
|
||||
bool Interface::saveState(unsigned slot) {
|
||||
string filename = { baseName, "-", slot, ".bst" };
|
||||
string filename = base.filename({ "state-", slot, ".bst" }, { "-", slot, ".bst" });
|
||||
serializer s = serialize();
|
||||
bool result = file::write(filename, s.data(), s.size());
|
||||
utility->showMessage(result == true ? string{ "Saved state ", slot } : "Failed to save state");
|
||||
|
@ -148,7 +153,7 @@ bool Interface::saveState(unsigned slot) {
|
|||
}
|
||||
|
||||
bool Interface::loadState(unsigned slot) {
|
||||
string filename = { baseName, "-", slot, ".bst" };
|
||||
string filename = base.filename({ "state-", slot, ".bst" }, { "-", slot, ".bst" });
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) {
|
||||
|
|
|
@ -9,6 +9,12 @@ struct InterfaceCore {
|
|||
virtual bool unserialize(serializer&) = 0;
|
||||
};
|
||||
|
||||
struct CartridgePath {
|
||||
bool folder;
|
||||
string name;
|
||||
string filename(const string &folderName, const string &fileName) const;
|
||||
};
|
||||
|
||||
#include "nes/nes.hpp"
|
||||
#include "snes/snes.hpp"
|
||||
#include "gameboy/gameboy.hpp"
|
||||
|
@ -58,8 +64,11 @@ struct Interface : property<Interface> {
|
|||
bool applyPatch(const string &filename, uint8_t *&data, unsigned &size);
|
||||
void videoRefresh(const uint32_t *input, unsigned inputPitch, unsigned width, unsigned height);
|
||||
|
||||
CartridgePath base;
|
||||
vector<CartridgePath> slot;
|
||||
|
||||
//deprecated
|
||||
string baseName; // = "/path/to/cartridge" (no extension)
|
||||
lstring slotName;
|
||||
|
||||
InterfaceCore *core;
|
||||
InterfaceNES nes;
|
||||
|
|
|
@ -27,38 +27,31 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||
unsigned size;
|
||||
|
||||
if(filename.endswith("/")) {
|
||||
string basename = filename;
|
||||
basename.rtrim<1>("/");
|
||||
basename.append("/", notdir(nall::basename(basename)));
|
||||
|
||||
if(file::exists({ basename, ".prg" }) && file::exists({ basename, ".chr" })) {
|
||||
unsigned prgsize = file::size({ basename, ".prg" });
|
||||
unsigned chrsize = file::size({ basename, ".chr" });
|
||||
if(file::exists({ filename, "program.rom" }) && file::exists({ filename, "character.rom" })) {
|
||||
unsigned prgsize = file::size({ filename, "program.rom" });
|
||||
unsigned chrsize = file::size({ filename, "character.rom" });
|
||||
data = new uint8_t[size = prgsize + chrsize];
|
||||
nall::file fp;
|
||||
fp.open({ basename, ".prg" }, file::mode::read);
|
||||
fp.open({ filename, "program.rom" }, file::mode::read);
|
||||
fp.read(data, fp.size());
|
||||
fp.close();
|
||||
fp.open({ basename, ".chr" }, file::mode::read);
|
||||
fp.open({ filename, "character.rom" }, file::mode::read);
|
||||
fp.read(data + prgsize, fp.size());
|
||||
fp.close();
|
||||
} else if(file::exists({ basename, ".fc" })) {
|
||||
file::read({ basename, ".fc" }, data, size);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
interface->baseName = basename;
|
||||
interface->base = { true, filename };
|
||||
} else {
|
||||
file::read(filename, data, size);
|
||||
interface->baseName = nall::basename(filename);
|
||||
interface->base = { false, nall::basename(filename) };
|
||||
}
|
||||
|
||||
interface->unloadCartridge();
|
||||
interface->applyPatch(interface->baseName, data, size);
|
||||
//interface->applyPatch(interface->base.filename("patch.bps", ".bps"), data, size);
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||
|
||||
NES::cartridge.load(markup, data, size);
|
||||
NES::system.power();
|
||||
|
@ -66,7 +59,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||
|
||||
if(NES::cartridge.ram_size()) {
|
||||
filemap fp;
|
||||
if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) {
|
||||
if(fp.open(interface->base.filename("program.ram", ".sav"), filemap::mode::read)) {
|
||||
memcpy(NES::cartridge.ram_data(), fp.data(), min(NES::cartridge.ram_size(), fp.size()));
|
||||
}
|
||||
}
|
||||
|
@ -78,10 +71,10 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||
|
||||
void InterfaceNES::unloadCartridge() {
|
||||
if(NES::cartridge.ram_size()) {
|
||||
file::write({ interface->baseName, ".sav" }, NES::cartridge.ram_data(), NES::cartridge.ram_size());
|
||||
file::write(interface->base.filename("program.ram", ".sav"), NES::cartridge.ram_data(), NES::cartridge.ram_size());
|
||||
}
|
||||
NES::cartridge.unload();
|
||||
interface->baseName = "";
|
||||
interface->base.name = "";
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -30,29 +30,32 @@ bool InterfaceSNES::cartridgeLoaded() {
|
|||
return SNES::cartridge.loaded();
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadFile(string &filename, uint8_t *&data, unsigned &size) {
|
||||
bool InterfaceSNES::loadCartridge(const string &filename, CartridgePath &cartridge, uint8_t *&data, unsigned &size) {
|
||||
auto backup = cartridge;
|
||||
string suffix;
|
||||
if(filename.endswith("/")) {
|
||||
filename.rtrim<1>("/");
|
||||
filename.append("/", notdir(filename));
|
||||
cartridge = { true, filename };
|
||||
} else {
|
||||
suffix = { ".", extension(filename) };
|
||||
cartridge = { false, nall::basename(filename) };
|
||||
}
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
filename = nall::basename(filename);
|
||||
interface->applyPatch(filename, data, size);
|
||||
if(file::read(cartridge.filename("program.rom", suffix), data, size) == false) {
|
||||
cartridge = backup;
|
||||
return false;
|
||||
}
|
||||
//interface->applyPatch(filename, data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadCartridge(string basename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(loadFile(basename, data, size) == false) return false;
|
||||
|
||||
if(loadCartridge(basename, interface->base, data, size) == false) return false;
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = basename;
|
||||
interface->slotName = { basename };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
if(markup == "") markup = SnesCartridge(data, size).markup;
|
||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||
if(markup.empty()) markup = SnesCartridge(data, size).markup;
|
||||
|
||||
SNES::cartridge.rom.copy(data, size);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
|
||||
|
@ -69,17 +72,13 @@ bool InterfaceSNES::loadCartridge(string basename) {
|
|||
bool InterfaceSNES::loadSatellaviewSlottedCartridge(string basename, string slotname) {
|
||||
uint8_t *data[2];
|
||||
unsigned size[2];
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotname, data[1], size[1]);
|
||||
|
||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
||||
loadCartridge(slotname, interface->slot(0), data[1], size[1]);
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = basename;
|
||||
if(data[1]) interface->baseName.append("+", notdir(slotname));
|
||||
interface->slotName = { basename, slotname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
|
||||
|
||||
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
||||
|
@ -98,17 +97,13 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(string basename, string slot
|
|||
bool InterfaceSNES::loadSatellaviewCartridge(string basename, string slotname) {
|
||||
uint8_t *data[2];
|
||||
unsigned size[2];
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotname, data[1], size[1]);
|
||||
|
||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
||||
loadCartridge(slotname, interface->slot(0), data[1], size[1]);
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = basename;
|
||||
if(data[1]) interface->baseName.append("+", notdir(slotname));
|
||||
interface->slotName = { basename, slotname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
|
||||
|
||||
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
||||
|
@ -127,20 +122,14 @@ bool InterfaceSNES::loadSatellaviewCartridge(string basename, string slotname) {
|
|||
bool InterfaceSNES::loadSufamiTurboCartridge(string basename, string slotAname, string slotBname) {
|
||||
uint8_t *data[3];
|
||||
unsigned size[3];
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotAname, data[1], size[1]);
|
||||
loadFile(slotBname, data[2], size[2]);
|
||||
|
||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
||||
loadCartridge(slotAname, interface->slot(0), data[1], size[1]);
|
||||
loadCartridge(slotBname, interface->slot(1), data[2], size[2]);
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = basename;
|
||||
if(data[1] && data[2]) interface->baseName = { slotAname, "+", notdir(slotBname) };
|
||||
else if(data[1]) interface->baseName = slotAname;
|
||||
else if(data[2]) interface->baseName = slotBname;
|
||||
interface->slotName = { basename, slotAname, slotBname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
|
||||
|
||||
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||
if(data[1]) SNES::sufamiturbo.slotA.rom.copy(data[1], size[1]);
|
||||
|
@ -161,21 +150,17 @@ bool InterfaceSNES::loadSufamiTurboCartridge(string basename, string slotAname,
|
|||
bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname) {
|
||||
uint8_t *data[2];
|
||||
unsigned size[2];
|
||||
if(loadFile(basename, data[0], size[0]) == false) return false;
|
||||
loadFile(slotname, data[1], size[1]);
|
||||
|
||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
||||
loadCartridge(slotname, interface->slot(0), data[1], size[1]);
|
||||
interface->unloadCartridge();
|
||||
interface->baseName = basename;
|
||||
if(data[1]) interface->baseName = slotname;
|
||||
interface->slotName = { basename, slotname };
|
||||
|
||||
string markup;
|
||||
markup.readfile({ interface->baseName, ".xml" });
|
||||
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
|
||||
|
||||
string gbMarkup;
|
||||
gbMarkup.readfile({ nall::basename(slotname), ".xml" });
|
||||
if(gbMarkup == "") gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
|
||||
gbMarkup.readfile(interface->slot[0].filename("manifest.xml", ".xml"));
|
||||
if(gbMarkup.empty()) gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
|
||||
|
||||
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, gbMarkup, data[1], size[1]);
|
||||
|
@ -194,7 +179,8 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname)
|
|||
void InterfaceSNES::unloadCartridge() {
|
||||
saveMemory();
|
||||
SNES::cartridge.unload();
|
||||
interface->baseName = "";
|
||||
interface->base.name = "";
|
||||
interface->slot.reset();
|
||||
}
|
||||
|
||||
void InterfaceSNES::power() {
|
||||
|
@ -209,14 +195,29 @@ void InterfaceSNES::run() {
|
|||
SNES::system.run();
|
||||
}
|
||||
|
||||
//slot[] array = Cartridge::Slot to slot# conversion:
|
||||
//{ Base, Bsx, SufamiTurbo, SufamiTurboA, SufamiTurboB, GameBoy }
|
||||
string InterfaceSNES::memoryName(SNES::Cartridge::NonVolatileRAM &memory) {
|
||||
if(memory.slot == SNES::Cartridge::Slot::Base) {
|
||||
if(memory.id == "program.ram") return interface->base.filename("program.ram", ".srm");
|
||||
if(memory.id == "upd96050.ram") return interface->base.filename("upd96050.ram", ".nec");
|
||||
if(memory.id == "bsx.ram") return interface->base.filename("bsx.ram", ".bss");
|
||||
if(memory.id == "bsx.psram") return interface->base.filename("bsx.psram", ".bsp");
|
||||
}
|
||||
if(memory.slot == SNES::Cartridge::Slot::SufamiTurboA) {
|
||||
if(memory.id == "program.ram") return interface->slot[0].filename("program.ram", ".sts");
|
||||
}
|
||||
if(memory.slot == SNES::Cartridge::Slot::SufamiTurboB) {
|
||||
if(memory.id == "program.ram") return interface->slot[1].filename("program.ram", ".sts");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void InterfaceSNES::loadMemory() {
|
||||
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
||||
for(auto &memory : SNES::cartridge.nvram) {
|
||||
if(memory.size == 0) continue;
|
||||
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
||||
|
||||
string filename = memoryName(memory);
|
||||
if(filename.empty()) continue;
|
||||
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size)) {
|
||||
|
@ -227,10 +228,12 @@ void InterfaceSNES::loadMemory() {
|
|||
}
|
||||
|
||||
void InterfaceSNES::saveMemory() {
|
||||
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
||||
for(auto &memory : SNES::cartridge.nvram) {
|
||||
if(memory.size == 0) continue;
|
||||
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
||||
|
||||
string filename = memoryName(memory);
|
||||
if(filename.empty()) continue;
|
||||
|
||||
file::write(filename, memory.data, memory.size);
|
||||
}
|
||||
}
|
||||
|
@ -356,8 +359,17 @@ int16_t InterfaceSNES::inputPoll(bool port, SNES::Input::Device device, unsigned
|
|||
}
|
||||
|
||||
string InterfaceSNES::path(SNES::Cartridge::Slot slot, const string &hint) {
|
||||
static unsigned index[] = { 0, 0, 0, 1, 2, 1 };
|
||||
return { interface->slotName[index[(unsigned)slot]], hint };
|
||||
if(slot == SNES::Cartridge::Slot::Base) {
|
||||
if(hint == "msu1.rom") return interface->base.filename("msu1.rom", ".msu");
|
||||
if(hint.wildcard("track-*.pcm")) {
|
||||
string track = hint;
|
||||
track.trim<1>("track-", ".pcm");
|
||||
return interface->base.filename(hint, { "-", decimal(track), ".pcm" });
|
||||
}
|
||||
if(hint == "serial.so") return { dir(interface->base.name), "libserial.so" };
|
||||
if(hint.endswith(".rom")) return { dir(interface->base.name), hint };
|
||||
}
|
||||
return { dir(interface->base.name), hint };
|
||||
}
|
||||
|
||||
void InterfaceSNES::message(const string &text) {
|
||||
|
|
|
@ -4,7 +4,7 @@ struct InterfaceSNES : InterfaceCore, SNES::Interface {
|
|||
void setController(bool port, unsigned device);
|
||||
|
||||
bool cartridgeLoaded();
|
||||
bool loadFile(string &filename, uint8_t *&data, unsigned &size);
|
||||
bool loadCartridge(const string &filename, CartridgePath &cartridge, uint8_t *&data, unsigned &size);
|
||||
bool loadCartridge(string basename);
|
||||
bool loadSatellaviewSlottedCartridge(string basename, string slotname);
|
||||
bool loadSatellaviewCartridge(string basename, string slotname);
|
||||
|
@ -16,6 +16,7 @@ struct InterfaceSNES : InterfaceCore, SNES::Interface {
|
|||
void reset();
|
||||
void run();
|
||||
|
||||
string memoryName(SNES::Cartridge::NonVolatileRAM &memory);
|
||||
void loadMemory();
|
||||
void saveMemory();
|
||||
|
||||
|
|
Loading…
Reference in New Issue