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:
Tim Allen 2011-03-04 19:57:00 +11:00
parent 8d64f9b155
commit 6c4e3ec790
13 changed files with 125 additions and 51 deletions

View File

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

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({ "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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@ struct Utility : property<Utility> {
void cartridgeLoaded();
void cartridgeUnloaded();
SNES::Cartridge::Slot stateSlot();
void saveState(unsigned slot);
void loadState(unsigned slot);