mirror of https://github.com/bsnes-emu/bsnes.git
Update to v076r03 release.
byuu says: Changelog: - paths.cfg work completed - save states/archives and cheat files for multi-slot games are more intelligent now For paths.cfg, there are three types of entries. Each have different special prefixes. Folder paths: sfc, bs, st, gb, filter, shader By default, bsnes will remember the last path you loaded a file of said type from. It will be prefixed with "recent/" in the file. Specify an explicit hard-coded path to override this. BIOS paths: satellaviewBios, sufamiTurboBios, superGameBoyBios Remembers an explicit hard-coded path to the BIOS you selected last. I was thinking that a nice feature would be for the "Load Special" windows to pop open the slot A load dialog if a BIOS was selected. Select a game from this popup and it loads directly, cancel it to get the regular window to override the BIOS. Save paths: srm, rtc, bsa, bst, cht, log Paths to write various files that the emulator generates. Note: srm groups bsp, bss and sav for now. Was being lazy. There are four special prefixes for these: "base/" -- gets replaced with the executable path "user/" -- gets replaced with the same folder where bsnes.cfg goes (%APPDATA%/bsnes or ~/.config/bsnes) -- good for hiding files "./" -- gets replaced with the current ROM path "../" -- gets replaced with the folder above the current ROM path If you want to go up two folders or more, then use a hard-coded path. If that's not good enough, kill yourself because God hates you.
This commit is contained in:
parent
8d64f9b155
commit
6c4e3ec790
|
@ -13,6 +13,14 @@ public:
|
|||
PAL,
|
||||
};
|
||||
|
||||
enum class Slot : unsigned {
|
||||
Base,
|
||||
Bsx,
|
||||
SufamiTurboA,
|
||||
SufamiTurboB,
|
||||
GameBoy,
|
||||
};
|
||||
|
||||
MappedRAM rom;
|
||||
MappedRAM ram;
|
||||
|
||||
|
@ -46,9 +54,9 @@ public:
|
|||
const string id;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
unsigned slot;
|
||||
NonVolatileRAM() : id(""), data(0), size(0), slot(0) {}
|
||||
NonVolatileRAM(const string id, uint8_t *data, unsigned size, unsigned slot = 0)
|
||||
Slot slot;
|
||||
NonVolatileRAM() : id(""), data(0), size(0), slot(Slot::Base) {}
|
||||
NonVolatileRAM(const string id, uint8_t *data, unsigned size, Slot slot = Slot::Base)
|
||||
: id(id), data(data), size(size), slot(slot) {}
|
||||
};
|
||||
linear_vector<NonVolatileRAM> nvram;
|
||||
|
|
|
@ -11,13 +11,13 @@ void SufamiTurbo::load() {
|
|||
slotB.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
|
||||
|
||||
if(slotA.rom.data()) {
|
||||
cartridge.nvram.append({ "srm", slotA.ram.data(), slotA.ram.size(), 1 });
|
||||
cartridge.nvram.append({ "srm", 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({ "srm", slotB.ram.data(), slotB.ram.size(), 2 });
|
||||
cartridge.nvram.append({ "srm", slotB.ram.data(), slotB.ram.size(), Cartridge::Slot::SufamiTurboB });
|
||||
} else {
|
||||
slotB.rom.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "076.02";
|
||||
static const char Version[] = "076.03";
|
||||
static const unsigned SerializerVersion = 18;
|
||||
}
|
||||
}
|
||||
|
@ -39,10 +39,10 @@ using namespace nall;
|
|||
#endif
|
||||
|
||||
namespace SNES {
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
|
|
|
@ -14,10 +14,10 @@ bool Cartridge::loadNormal(const char *basename) {
|
|||
bool Cartridge::loadBsxSlotted(const char *basename, const char *slotname) {
|
||||
unload();
|
||||
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
|
||||
loadCartridge(SNES::bsxflash.memory, slotAXML, slotname);
|
||||
loadCartridge(SNES::bsxflash.memory, bsxXML, slotname);
|
||||
SNES::cartridge.basename = baseName = nall::basename(basename);
|
||||
slotAName = nall::basename(slotname);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, { baseXML, slotAXML });
|
||||
bsxName = nall::basename(slotname);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, { baseXML, bsxXML });
|
||||
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
|
||||
utility.cartridgeLoaded();
|
||||
return true;
|
||||
|
@ -26,10 +26,10 @@ bool Cartridge::loadBsxSlotted(const char *basename, const char *slotname) {
|
|||
bool Cartridge::loadBsx(const char *basename, const char *slotname) {
|
||||
unload();
|
||||
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
|
||||
loadCartridge(SNES::bsxflash.memory, slotAXML, slotname);
|
||||
loadCartridge(SNES::bsxflash.memory, bsxXML, slotname);
|
||||
SNES::cartridge.basename = baseName = nall::basename(basename);
|
||||
slotAName = nall::basename(slotname);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, { baseXML, slotAXML });
|
||||
bsxName = nall::basename(slotname);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, { baseXML, bsxXML });
|
||||
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
|
||||
utility.cartridgeLoaded();
|
||||
return true;
|
||||
|
@ -38,12 +38,12 @@ bool Cartridge::loadBsx(const char *basename, const char *slotname) {
|
|||
bool Cartridge::loadSufamiTurbo(const char *basename, const char *slotAname, const char *slotBname) {
|
||||
unload();
|
||||
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
|
||||
loadCartridge(SNES::sufamiturbo.slotA.rom, slotAXML, slotAname);
|
||||
loadCartridge(SNES::sufamiturbo.slotB.rom, slotBXML, slotBname);
|
||||
loadCartridge(SNES::sufamiturbo.slotA.rom, sufamiTurboAXML, slotAname);
|
||||
loadCartridge(SNES::sufamiturbo.slotB.rom, sufamiTurboBXML, slotBname);
|
||||
SNES::cartridge.basename = baseName = nall::basename(basename);
|
||||
slotAName = nall::basename(slotAname);
|
||||
slotBName = nall::basename(slotBname);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, { baseXML, slotAXML, slotBXML });
|
||||
sufamiTurboAName = nall::basename(slotAname);
|
||||
sufamiTurboBName = nall::basename(slotBname);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, { baseXML, sufamiTurboAXML, sufamiTurboBXML });
|
||||
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
|
||||
utility.cartridgeLoaded();
|
||||
return true;
|
||||
|
@ -69,11 +69,11 @@ bool Cartridge::loadSuperGameBoy(const char *basename, const char *slotname) {
|
|||
if(data) delete[] data;
|
||||
|
||||
SNES::cartridge.basename = baseName = nall::basename(basename);
|
||||
slotAName = nall::basename(slotname);
|
||||
gameBoyName = nall::basename(slotname);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, { baseXML, "" });
|
||||
|
||||
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
|
||||
if(GameBoy::cartridge.info.battery && fp.open(string(slotAName, ".sav"), file::mode::read)) {
|
||||
if(GameBoy::cartridge.info.battery && fp.open(path.load(SNES::Cartridge::Slot::GameBoy, "sav"), file::mode::read)) {
|
||||
fp.read(GameBoy::cartridge.ramdata, min(GameBoy::cartridge.ramsize, fp.size()));
|
||||
fp.close();
|
||||
}
|
||||
|
@ -89,14 +89,14 @@ void Cartridge::unload() {
|
|||
foreach(memory, SNES::cartridge.nvram) saveMemory(memory);
|
||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||
file fp;
|
||||
if(GameBoy::cartridge.info.battery && fp.open(string(slotAName, ".sav"), file::mode::write)) {
|
||||
if(GameBoy::cartridge.info.battery && fp.open(path.load(SNES::Cartridge::Slot::GameBoy, "sav"), file::mode::write)) {
|
||||
fp.write(GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize);
|
||||
fp.close();
|
||||
}
|
||||
}
|
||||
|
||||
utility.cartridgeUnloaded();
|
||||
baseName = slotAName = slotBName = "";
|
||||
baseName = bsxName = sufamiTurboAName = sufamiTurboBName = gameBoyName = "";
|
||||
}
|
||||
|
||||
bool Cartridge::loadCartridge(SNES::MappedRAM &memory, string &XML, const char *filename) {
|
||||
|
@ -133,8 +133,7 @@ bool Cartridge::loadCartridge(SNES::MappedRAM &memory, string &XML, const char *
|
|||
|
||||
bool Cartridge::loadMemory(SNES::Cartridge::NonVolatileRAM &memory) {
|
||||
if(memory.size == 0) return true;
|
||||
lstring filenames = { baseName, slotAName, slotBName };
|
||||
string filename = string(filenames[memory.slot], ".", memory.id);
|
||||
string filename = path.load(memory.slot, memory.id);
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::read) == false) return false;
|
||||
fp.read(memory.data, min(memory.size, fp.size()));
|
||||
|
@ -144,8 +143,7 @@ bool Cartridge::loadMemory(SNES::Cartridge::NonVolatileRAM &memory) {
|
|||
|
||||
bool Cartridge::saveMemory(SNES::Cartridge::NonVolatileRAM &memory) {
|
||||
if(memory.size == 0) return true;
|
||||
lstring filenames = { baseName, slotAName, slotBName };
|
||||
string filename = string(filenames[memory.slot], ".", memory.id);
|
||||
string filename = path.load(memory.slot, memory.id);
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
fp.write(memory.data, memory.size);
|
||||
|
|
|
@ -6,8 +6,8 @@ struct Cartridge {
|
|||
bool loadSuperGameBoy(const char *basename, const char *slotname);
|
||||
void unload();
|
||||
|
||||
string baseName, slotAName, slotBName;
|
||||
string baseXML, slotAXML, slotBXML;
|
||||
string baseName, bsxName, sufamiTurboAName, sufamiTurboBName, gameBoyName;
|
||||
string baseXML, bsxXML, sufamiTurboAXML, sufamiTurboBXML, gameBoyXML;
|
||||
bool patchApplied;
|
||||
|
||||
private:
|
||||
|
|
|
@ -54,8 +54,7 @@ void Console::write(const string &text, bool echo) {
|
|||
|
||||
void Console::tracerEnable(bool state) {
|
||||
if(state == true) {
|
||||
string filename = { cartridge.baseName, ".log" };
|
||||
logfile.open(filename, file::mode::write);
|
||||
logfile.open(path.load(SNES::Cartridge::Slot::Cartridge, "log"), file::mode::write);
|
||||
} else {
|
||||
logfile.close();
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ string Path::load(const string &path) {
|
|||
if(path == "filter") value = filter;
|
||||
if(path == "shader") value = shader;
|
||||
|
||||
if(value.beginswith(":")) value.ltrim<1>(":");
|
||||
if(value.beginswith("recent/")) value.ltrim<1>("recent/");
|
||||
if(value == "") value = base;
|
||||
return value;
|
||||
}
|
||||
|
@ -48,12 +48,56 @@ void Path::save(const string &path, const string &value) {
|
|||
|
||||
if(output) {
|
||||
string &s = *output;
|
||||
if(s.beginswith(":") == false && s != "") return;
|
||||
s = { ":", value };
|
||||
if(s.beginswith("recent/") == false && s != "") return;
|
||||
s = { "recent/", value };
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string Path::load(SNES::Cartridge::Slot slot, const string &type, const string &suffix) {
|
||||
string romPath;
|
||||
switch(slot) {
|
||||
case SNES::Cartridge::Slot::Base: romPath = cartridge.baseName; break;
|
||||
case SNES::Cartridge::Slot::Bsx: romPath = cartridge.bsxName; break;
|
||||
case SNES::Cartridge::Slot::SufamiTurboA: romPath = cartridge.sufamiTurboAName; break;
|
||||
case SNES::Cartridge::Slot::SufamiTurboB: romPath = cartridge.sufamiTurboBName; break;
|
||||
case SNES::Cartridge::Slot::GameBoy: romPath = cartridge.gameBoyName; break;
|
||||
}
|
||||
|
||||
string path = romPath;
|
||||
if(type == "srm" && srm != "") path = string(srm, notdir(path));
|
||||
if(type == "bsp" && srm != "") path = string(srm, notdir(path));
|
||||
if(type == "bss" && srm != "") path = string(srm, notdir(path));
|
||||
if(type == "sav" && srm != "") path = string(srm, notdir(path));
|
||||
if(type == "rtc" && rtc != "") path = string(rtc, notdir(path));
|
||||
if(type == "bsa" && bsa != "") path = string(bsa, notdir(path));
|
||||
if(type == "bst" && bst != "") path = string(bst, notdir(path));
|
||||
if(type == "cht" && cht != "") path = string(cht, notdir(path));
|
||||
if(type == "log" && log != "") path = string(log, notdir(path));
|
||||
|
||||
if(path.beginswith("user/")) {
|
||||
path.ltrim<1>("user/");
|
||||
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
|
||||
path = string(user, ".config/bsnes/", path);
|
||||
#else
|
||||
path = string(user, "bsnes/", path);
|
||||
#endif
|
||||
} else if(path.beginswith("base/")) {
|
||||
path.ltrim<1>("base/");
|
||||
path = string(base, path);
|
||||
} else if(path.beginswith("./")) {
|
||||
path.ltrim<1>("./");
|
||||
path = string(dir(romPath), path);
|
||||
} else if(path.beginswith("../")) {
|
||||
string base = dir(romPath);
|
||||
base.rtrim<1>("/");
|
||||
path.ltrim<1>("../");
|
||||
path = string(dir(base), path);
|
||||
}
|
||||
|
||||
return { path, suffix, ".", type };
|
||||
}
|
||||
|
||||
void Path::load() {
|
||||
configuration::load(home("paths.cfg"));
|
||||
}
|
||||
|
@ -74,4 +118,12 @@ Path::Path() {
|
|||
attach(satellaviewBios = "", "satellaviewBios");
|
||||
attach(sufamiTurboBios = "", "sufamiTurboBios");
|
||||
attach(superGameBoyBios = "", "superGameBoyBios");
|
||||
|
||||
attach(srm = "", "srm");
|
||||
attach(rtc = "", "rtc");
|
||||
|
||||
attach(bsa = "", "bsa");
|
||||
attach(bst = "", "bst");
|
||||
attach(cht = "", "cht");
|
||||
attach(log = "", "log");
|
||||
}
|
||||
|
|
|
@ -14,9 +14,18 @@ struct Path : public configuration {
|
|||
string sufamiTurboBios;
|
||||
string superGameBoyBios;
|
||||
|
||||
string srm;
|
||||
string rtc;
|
||||
|
||||
string bsa;
|
||||
string bst;
|
||||
string cht;
|
||||
string log;
|
||||
|
||||
string home(const string &filename);
|
||||
string load(const string &path);
|
||||
void save(const string &path, const string &value);
|
||||
string load(SNES::Cartridge::Slot slot, const string &type, const string &suffix = "");
|
||||
|
||||
void load();
|
||||
void save();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CheatEditor cheatEditor;
|
||||
|
||||
void CheatEditor::load(string filename) {
|
||||
void CheatEditor::load() {
|
||||
SNES::cheat.reset();
|
||||
cheatList.reset();
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
|
@ -12,7 +12,7 @@ void CheatEditor::load(string filename) {
|
|||
|
||||
unsigned n = 0;
|
||||
string data;
|
||||
data.readfile(string(filename, ".cht"));
|
||||
data.readfile(path.load(utility.stateSlot(), "cht"));
|
||||
xml_element document = xml_parse(data);
|
||||
foreach(head, document.element) {
|
||||
if(head.name == "cartridge") {
|
||||
|
@ -43,7 +43,7 @@ void CheatEditor::load(string filename) {
|
|||
synchronize();
|
||||
}
|
||||
|
||||
void CheatEditor::save(string filename) {
|
||||
void CheatEditor::save() {
|
||||
signed lastSave = -1;
|
||||
for(signed i = 127; i >= 0; i--) {
|
||||
if(cheatText[i][CheatCode] != "" || cheatText[i][CheatDesc] != "") {
|
||||
|
@ -52,12 +52,12 @@ void CheatEditor::save(string filename) {
|
|||
}
|
||||
}
|
||||
if(lastSave == -1) {
|
||||
unlink(string(filename, ".cht"));
|
||||
unlink(path.load(utility.stateSlot(), "cht"));
|
||||
return;
|
||||
}
|
||||
|
||||
file fp;
|
||||
if(fp.open(string(filename, ".cht"), file::mode::write)) {
|
||||
if(fp.open(path.load(utility.stateSlot(), "cht"), file::mode::write)) {
|
||||
fp.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
fp.print(string("<cartridge sha256=\"", SNES::cartridge.sha256(), "\">\n"));
|
||||
for(unsigned i = 0; i <= lastSave; i++) {
|
||||
|
|
|
@ -13,8 +13,8 @@ struct CheatEditor : TopLevelWindow {
|
|||
Button clearAllButton;
|
||||
Button clearButton;
|
||||
|
||||
void load(string filename);
|
||||
void save(string filename);
|
||||
void load();
|
||||
void save();
|
||||
void create();
|
||||
|
||||
private:
|
||||
|
|
|
@ -59,9 +59,8 @@ void StateManager::load() {
|
|||
stateList.append("");
|
||||
}
|
||||
|
||||
string filename = { cartridge.baseName, ".bsa" };
|
||||
file fp;
|
||||
if(fp.open(string(cartridge.baseName, ".bsa"), file::mode::read)) {
|
||||
if(fp.open(path.load(utility.stateSlot(), "bsa"), file::mode::read)) {
|
||||
if(fp.readl(4) == 0x31415342) {
|
||||
if(fp.readl(4) == SNES::Info::SerializerVersion) {
|
||||
for(unsigned i = 0; i < 32; i++) {
|
||||
|
@ -85,10 +84,10 @@ void StateManager::save() {
|
|||
}
|
||||
|
||||
if(hasSave == false) {
|
||||
unlink(string(cartridge.baseName, ".bsa"));
|
||||
unlink(path.load(utility.stateSlot(), "bsa"));
|
||||
} else {
|
||||
file fp;
|
||||
if(fp.open(string(cartridge.baseName, ".bsa"), file::mode::write)) {
|
||||
if(fp.open(path.load(utility.stateSlot(), "bsa"), file::mode::write)) {
|
||||
fp.writel(0x31415342, 4); //'BSA1'
|
||||
fp.writel(SNES::Info::SerializerVersion, 4);
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ void Utility::setShader() {
|
|||
|
||||
void Utility::cartridgeLoaded() {
|
||||
SNES::system.power();
|
||||
cheatEditor.load(cartridge.baseName);
|
||||
cheatEditor.load();
|
||||
stateManager.load();
|
||||
mainWindow.synchronize();
|
||||
utility.setTitle(notdir(cartridge.baseName));
|
||||
|
@ -162,13 +162,21 @@ void Utility::cartridgeLoaded() {
|
|||
|
||||
void Utility::cartridgeUnloaded() {
|
||||
SNES::cartridge.unload();
|
||||
cheatEditor.save(cartridge.baseName);
|
||||
cheatEditor.save();
|
||||
stateManager.save();
|
||||
mainWindow.synchronize();
|
||||
}
|
||||
|
||||
SNES::Cartridge::Slot Utility::stateSlot() {
|
||||
SNES::Cartridge::Slot slot = SNES::Cartridge::Slot::Base;
|
||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::Bsx) slot = SNES::Cartridge::Slot::Bsx;
|
||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SufamiTurbo) slot = SNES::Cartridge::Slot::SufamiTurboA;
|
||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) slot = SNES::Cartridge::Slot::GameBoy;
|
||||
return slot;
|
||||
}
|
||||
|
||||
void Utility::saveState(unsigned slot) {
|
||||
string filename = { cartridge.baseName, "-", slot, ".bst" };
|
||||
string filename = path.load(stateSlot(), "bst", { "-", slot });
|
||||
SNES::system.runtosave();
|
||||
serializer s = SNES::system.serialize();
|
||||
file fp;
|
||||
|
@ -182,7 +190,7 @@ void Utility::saveState(unsigned slot) {
|
|||
}
|
||||
|
||||
void Utility::loadState(unsigned slot) {
|
||||
string filename = { cartridge.baseName, "-", slot, ".bst" };
|
||||
string filename = path.load(stateSlot(), "bst", { "-", slot });
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::read)) {
|
||||
unsigned size = fp.size();
|
||||
|
|
|
@ -15,6 +15,7 @@ struct Utility : property<Utility> {
|
|||
void cartridgeLoaded();
|
||||
void cartridgeUnloaded();
|
||||
|
||||
SNES::Cartridge::Slot stateSlot();
|
||||
void saveState(unsigned slot);
|
||||
void loadState(unsigned slot);
|
||||
|
||||
|
|
Loading…
Reference in New Issue