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:
Tim Allen 2012-02-16 01:01:22 +11:00
parent 10fd29e7bb
commit a37ce1cb2f
22 changed files with 157 additions and 144 deletions

View File

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

View File

@ -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"

View File

@ -5,7 +5,6 @@
#define SMP_CPP
namespace SNES {
#include "disassembler.cpp"
SMP smp;
#include "algorithms.cpp"

View File

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

View File

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

View File

@ -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() {

View File

@ -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();

View File

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

View File

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

View File

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

View File

@ -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)) {

View File

@ -86,7 +86,7 @@ ConsoleWindow::ConsoleWindow() {
onClose = [] { application->quit = true; };
menuEmulationReloadCartridge.onActivate = [&] {
interface->loadCartridge(interface->fileName);
interface->loadCartridge(interface->pathName);
};
menuEmulationPowerCycle.onActivate = [&] {

View File

@ -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");

View File

@ -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) {

View File

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

View File

@ -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();

View File

@ -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() {

View File

@ -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) {

View File

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

View File

@ -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 = "";
}
//

View File

@ -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) {

View File

@ -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();