From 2dd35f984d9848c53b33ea93c013df9157ee6b52 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 6 Mar 2018 09:42:10 +1100 Subject: [PATCH] Update to v106r10 release. byuu says: Changelog: - manifest: memory/battery now resides under type at memory/type/battery - genius: volatile option changed to battery; auto-disables when not RAM or RTC type - higan: added new Emulator::Game class to parse manifests for all emulated systems consistently - Super Famicom: board manifest appended to manifest viewer now - Super Famicom: cartridge class updated to use Emulator::Game objects - hiro: improve suppression of userland callbacks once Application::quit() is called - this fixes a crash in genius when closing the window with a tree view item selected My intention is to remove Emulator::Interface::sha256(), as it's not really useful. They'll be removed from save states as well. I never bothered validating the SHA256 within them, because that'd be really annoying for ROM hackers. I also intend to rename Emulator::Interface::title() to label() instead. Most everything is still broken. The SNES still needs all the board definitions updated, all the other cores need to move to using Emulator::Game. --- genius/genius.cpp | 17 ++-- genius/genius.hpp | 4 +- higan/emulator/emulator.hpp | 3 +- higan/emulator/game.hpp | 87 +++++++++++++++++++ higan/sfc/cartridge/cartridge.cpp | 66 ++++++++------ higan/sfc/cartridge/cartridge.hpp | 25 ++---- higan/sfc/cartridge/load.cpp | 115 ++++++++++++++----------- higan/sfc/cartridge/save.cpp | 51 ++++++----- higan/sfc/coprocessor/msu1/msu1.cpp | 4 +- hiro/cocoa/object.hpp | 2 +- hiro/gtk/object.hpp | 2 +- hiro/qt/object.hpp | 2 +- hiro/windows/object.hpp | 2 +- icarus/heuristics/bs-memory.cpp | 2 +- icarus/heuristics/famicom.cpp | 2 +- icarus/heuristics/game-boy-advance.cpp | 2 +- icarus/heuristics/game-boy.cpp | 2 +- icarus/heuristics/game-gear.cpp | 2 +- icarus/heuristics/heuristics.cpp | 16 +--- icarus/heuristics/heuristics.hpp | 8 +- icarus/heuristics/master-system.cpp | 2 +- icarus/heuristics/mega-drive.cpp | 2 +- icarus/heuristics/pc-engine.cpp | 2 +- icarus/heuristics/sufami-turbo.cpp | 2 +- icarus/heuristics/super-famicom.cpp | 2 +- icarus/heuristics/supergrafx.cpp | 2 +- icarus/heuristics/wonderswan.cpp | 2 +- 27 files changed, 258 insertions(+), 170 deletions(-) create mode 100644 higan/emulator/game.hpp diff --git a/genius/genius.cpp b/genius/genius.cpp index d79af809..0722c642 100644 --- a/genius/genius.cpp +++ b/genius/genius.cpp @@ -132,7 +132,7 @@ auto ListWindow::loadDatabase(string location) -> void { component.memory.manufacturer = object["manufacturer"].text(); component.memory.part = object["part"].text(); component.memory.note = object["note"].text(); - component.memory.isVolatile = (bool)object["volatile"]; + component.memory.battery = (bool)object["type/battery"]; } if(object.name() == "oscillator") { component.type = Component::Type::Oscillator; @@ -185,6 +185,8 @@ auto ListWindow::saveDatabase(string location) -> void { if(component.type == Component::Type::Memory) { fp.print(" memory\n"); fp.print(" type: ", component.memory.type, "\n"); + if(component.memory.battery) + fp.print(" battery\n"); fp.print(" size: ", component.memory.size, "\n"); fp.print(" category: ", component.memory.category, "\n"); if(component.memory.manufacturer) @@ -193,8 +195,6 @@ auto ListWindow::saveDatabase(string location) -> void { fp.print(" part: ", component.memory.part, "\n"); if(component.memory.note) fp.print(" note: ", component.memory.note, "\n"); - if(component.memory.isVolatile) - fp.print(" volatile\n"); } if(component.type == Component::Type::Oscillator) { @@ -370,7 +370,7 @@ auto GameWindow::reloadList() -> void { string index = {"[", counter++, "] "}; if(component.type == Component::Type::Memory) { item.setText({index, "Memory"}); - item.append(TreeViewItem().setText({"Type: ", component.memory.type})); + item.append(TreeViewItem().setText({"Type: ", component.memory.type, component.memory.battery ? " + Battery" : ""})); item.append(TreeViewItem().setText({"Size: ", component.memory.size})); item.append(TreeViewItem().setText({"Category: ", component.memory.category})); if(component.memory.manufacturer) @@ -379,8 +379,6 @@ auto GameWindow::reloadList() -> void { item.append(TreeViewItem().setText({"Part: ", component.memory.part})); if(component.memory.note) item.append(TreeViewItem().setText({"Note: ", component.memory.note})); - if(component.memory.isVolatile) - item.append(TreeViewItem().setText({"Volatile"})); } if(component.type == Component::Type::Oscillator) { @@ -480,7 +478,7 @@ MemoryWindow::MemoryWindow() { partEdit.onChange([&] { modified = true, updateWindow(); }); noteLabel.setText("Note:").setAlignment(1.0); noteEdit.onChange([&] { modified = true, updateWindow(); }); - volatileOption.setText("Volatile").onToggle([&] { modified = true, updateWindow(); }); + batteryOption.setText("Battery").onToggle([&] { modified = true, updateWindow(); }); acceptButton.setText("Accept").onActivate([&] { accept(); }); cancelButton.setText("Cancel").onActivate([&] { cancel(); }); @@ -501,7 +499,7 @@ auto MemoryWindow::show(Memory memory) -> void { manufacturerEdit.setText(memory.manufacturer); partEdit.setText(memory.part); noteEdit.setText(memory.note); - volatileOption.setChecked(memory.isVolatile); + batteryOption.setChecked(memory.battery); updateWindow(); setCentered(*gameWindow); @@ -517,7 +515,7 @@ auto MemoryWindow::accept() -> void { memory.manufacturer = manufacturerEdit.text().strip(); memory.part = partEdit.text().strip(); memory.note = noteEdit.text().strip(); - memory.isVolatile = volatileOption.checked(); + memory.battery = batteryOption.checked() && (memory.type == "RAM" || memory.type == "RTC"); Component component{Component::Type::Memory}; component.memory = memory; @@ -550,6 +548,7 @@ auto MemoryWindow::updateWindow() -> void { manufacturerEdit.setBackgroundColor(manufacturerEdit.text().strip() ? Color{} : (Color{255, 255, 240})); partEdit.setBackgroundColor(partEdit.text().strip() ? Color{} : (Color{255, 255, 240})); noteEdit.setBackgroundColor(noteEdit.text().strip() ? Color{} : (Color{255, 255, 240})); + batteryOption.setEnabled(typeEdit.text().strip() == "RAM" || typeEdit.text().strip() == "RTC"); acceptButton.setEnabled(valid); setTitle({modified ? "*" : "", create ? "Add New Memory" : "Modify Memory Details"}); } diff --git a/genius/genius.hpp b/genius/genius.hpp index 5ed3abab..250ed370 100644 --- a/genius/genius.hpp +++ b/genius/genius.hpp @@ -5,7 +5,7 @@ struct Memory { string manufacturer; string part; string note; - bool isVolatile = false; + boolean battery; }; struct Oscillator { @@ -151,7 +151,7 @@ private: LineEdit noteEdit{¬eLayout, Size{~0, 0}}; HorizontalLayout controlLayout{&layout, Size{~0, 0}}; Widget controlSpacer{&controlLayout, Size{~0, 0}}; - CheckLabel volatileOption{&controlLayout, Size{0, 0}}; + CheckLabel batteryOption{&controlLayout, Size{0, 0}}; Button acceptButton{&controlLayout, Size{80, 0}}; Button cancelButton{&controlLayout, Size{80, 0}}; }; diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index dd9de104..45d4eaf2 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.09"; + static const string Version = "106.10"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; @@ -38,3 +38,4 @@ namespace Emulator { #include "platform.hpp" #include "interface.hpp" +#include "game.hpp" diff --git a/higan/emulator/game.hpp b/higan/emulator/game.hpp new file mode 100644 index 00000000..9a765664 --- /dev/null +++ b/higan/emulator/game.hpp @@ -0,0 +1,87 @@ +#pragma once + +namespace Emulator { + +struct Game { + struct Memory; + struct Oscillator; + + inline auto load(string_view) -> void; + inline auto memory(string_view) -> maybe; + inline auto oscillator(natural = 0) -> maybe; + + struct Memory { + explicit operator bool() const { return type; } + inline auto name() const -> string; + + string type; + boolean battery; + natural size; + string category; + string manufacturer; + string part; + }; + + struct Oscillator { + explicit operator bool() const { return frequency; } + + natural frequency; + }; + + Markup::Node document; + string sha256; + string label; + string name; + string region; + string revision; + string board; + vector memoryList; + vector oscillatorList; +}; + +auto Game::load(string_view text) -> void { + document = BML::unserialize(text); + + sha256 = document["game/sha256"].text(); + label = document["game/label"].text(); + name = document["game/name"].text(); + region = document["game/region"].text(); + revision = document["game/revision"].text(); + board = document["game/board"].text(); + + for(auto node : document.find("game/board/memory")) { + Memory memory; + memory.type = node["type"].text(); + memory.battery = (bool)node["type/battery"]; + memory.size = node["size"].natural(); + memory.category = node["category"].text(); + memory.manufacturer = node["manufacturer"].text(); + memory.part = node["part"].text(); + memoryList.append(memory); + } + + for(auto node : document.find("game/board/oscillator")) { + Oscillator oscillator; + oscillator.frequency = node["frequency"].natural(); + oscillatorList.append(oscillator); + } +} + +auto Game::memory(string_view name) -> maybe { + for(auto& memory : memoryList) { + if(memory.name() == name) return memory; + } + return nothing; +} + +auto Game::oscillator(natural index) -> maybe { + if(index < oscillatorList.size()) return oscillatorList[index]; + return nothing; +} + +auto Game::Memory::name() const -> string { + if(part) return string{part, ".", category, ".", type}.downcase(); + return string{category, ".", type}.downcase(); +} + +} diff --git a/higan/sfc/cartridge/cartridge.cpp b/higan/sfc/cartridge/cartridge.cpp index 10c9ed5c..5992f86f 100644 --- a/higan/sfc/cartridge/cartridge.cpp +++ b/higan/sfc/cartridge/cartridge.cpp @@ -8,26 +8,32 @@ namespace SuperFamicom { Cartridge cartridge; auto Cartridge::manifest() const -> string { - string manifest = information.manifest.cartridge; - if(information.manifest.gameBoy) manifest.append("\n[[Game Boy]]\n\n", information.manifest.gameBoy); - if(information.manifest.bsMemory) manifest.append("\n[[BS Memory]]\n\n", information.manifest.bsMemory); - if(information.manifest.sufamiTurboA) manifest.append("\n[[Sufami Turbo - Slot A]]\n\n", information.manifest.sufamiTurboA); - if(information.manifest.sufamiTurboB) manifest.append("\n[[Sufami Turbo - Slot B]]\n\n", information.manifest.sufamiTurboB); + string manifest = BML::serialize(game.document); + manifest.append("\n", BML::serialize(board)); + if(slotGameBoy.document) manifest.append("\n", BML::serialize(slotGameBoy.document)); + if(slotBSMemory.document) manifest.append("\n", BML::serialize(slotBSMemory.document)); + if(slotSufamiTurboA.document) manifest.append("\n", BML::serialize(slotSufamiTurboA.document)); + if(slotSufamiTurboB.document) manifest.append("\n", BML::serialize(slotSufamiTurboB.document)); return manifest; } auto Cartridge::title() const -> string { - string title = information.title.cartridge; - if(information.title.gameBoy) title.append(" + ", information.title.gameBoy); - if(information.title.bsMemory) title.append(" + ", information.title.bsMemory); - if(information.title.sufamiTurboA) title.append(" + ", information.title.sufamiTurboA); - if(information.title.sufamiTurboB) title.append(" + ", information.title.sufamiTurboB); - return title; + auto label = game.label; + if(slotGameBoy.label) label.append(" + ", slotGameBoy.label); + if(slotBSMemory.label) label.append(" + ", slotBSMemory.label); + if(slotSufamiTurboA.label) label.append(" + ", slotSufamiTurboA.label); + if(slotSufamiTurboB.label) label.append(" + ", slotSufamiTurboB.label); + return label; } auto Cartridge::load() -> bool { information = {}; has = {}; + game = {}; + slotGameBoy = {}; + slotBSMemory = {}; + slotSufamiTurboA = {}; + slotSufamiTurboB = {}; if(auto loaded = platform->load(ID::SuperFamicom, "Super Famicom", "sfc", {"Auto", "NTSC", "PAL"})) { information.pathID = loaded.pathID(); @@ -35,10 +41,9 @@ auto Cartridge::load() -> bool { } else return false; if(auto fp = platform->open(ID::SuperFamicom, "manifest.bml", File::Read, File::Required)) { - information.manifest.cartridge = fp->reads(); + game.load(fp->reads()); } else return false; - auto document = BML::unserialize(information.manifest.cartridge); - loadCartridge(document); + loadCartridge(game.document); //Game Boy if(cartridge.has.ICD) { @@ -91,9 +96,8 @@ auto Cartridge::loadGameBoy() -> bool { #if defined(SFC_SUPERGAMEBOY) //invoked from ICD::load() information.sha256 = GameBoy::cartridge.sha256(); - information.manifest.gameBoy = GameBoy::cartridge.manifest(); - information.title.gameBoy = GameBoy::cartridge.title(); - loadGameBoy(BML::unserialize(information.manifest.gameBoy)); + slotGameBoy.load(GameBoy::cartridge.manifest()); + loadGameBoy(slotGameBoy.document); return true; #endif return false; @@ -101,34 +105,40 @@ auto Cartridge::loadGameBoy() -> bool { auto Cartridge::loadBSMemory() -> bool { if(auto fp = platform->open(bsmemory.pathID, "manifest.bml", File::Read, File::Required)) { - information.manifest.bsMemory = fp->reads(); + slotBSMemory.load(fp->reads()); } else return false; - loadBSMemory(BML::unserialize(string{information.manifest.bsMemory}.replace("type: ", "type:"))); + loadBSMemory(slotBSMemory.document); return true; } auto Cartridge::loadSufamiTurboA() -> bool { if(auto fp = platform->open(sufamiturboA.pathID, "manifest.bml", File::Read, File::Required)) { - information.manifest.sufamiTurboA = fp->reads(); + slotSufamiTurboA.load(fp->reads()); } else return false; - loadSufamiTurboA(BML::unserialize(string{information.manifest.sufamiTurboA}.replace("type: ", "type:"))); + loadSufamiTurboA(slotSufamiTurboA.document); return true; } auto Cartridge::loadSufamiTurboB() -> bool { if(auto fp = platform->open(sufamiturboB.pathID, "manifest.bml", File::Read, File::Required)) { - information.manifest.sufamiTurboB = fp->reads(); + slotSufamiTurboB.load(fp->reads()); } else return false; - loadSufamiTurboB(BML::unserialize(string{information.manifest.sufamiTurboB}.replace("type: ", "type:"))); + loadSufamiTurboB(slotSufamiTurboB.document); return true; } auto Cartridge::save() -> void { - saveCartridge(BML::unserialize(information.manifest.cartridge)); - saveGameBoy(BML::unserialize(information.manifest.gameBoy)); - saveBSMemory(BML::unserialize(information.manifest.bsMemory)); - saveSufamiTurboA(BML::unserialize(string{information.manifest.sufamiTurboA}.replace("type: ", "type:"))); - saveSufamiTurboB(BML::unserialize(string{information.manifest.sufamiTurboB}.replace("type: ", "type:"))); + saveCartridge(game.document); + if(has.GameBoySlot) { + saveGameBoy(slotGameBoy.document); + } + if(has.BSMemorySlot) { + saveBSMemory(slotBSMemory.document); + } + if(has.SufamiTurboSlots) { + saveSufamiTurboA(slotSufamiTurboA.document); + saveSufamiTurboB(slotSufamiTurboB.document); + } } auto Cartridge::unload() -> void { diff --git a/higan/sfc/cartridge/cartridge.hpp b/higan/sfc/cartridge/cartridge.hpp index a0225f66..1eeeaa05 100644 --- a/higan/sfc/cartridge/cartridge.hpp +++ b/higan/sfc/cartridge/cartridge.hpp @@ -18,22 +18,6 @@ struct Cartridge { uint pathID = 0; string region; string sha256; - - struct Manifest { - string cartridge; - string gameBoy; - string bsMemory; - string sufamiTurboA; - string sufamiTurboB; - } manifest; - - struct Title { - string cartridge; - string gameBoy; - string bsMemory; - string sufamiTurboA; - string sufamiTurboB; - } title; } information; struct Has { @@ -59,6 +43,13 @@ struct Cartridge { } has; private: + Emulator::Game game; + Emulator::Game slotGameBoy; + Emulator::Game slotBSMemory; + Emulator::Game slotSufamiTurboA; + Emulator::Game slotSufamiTurboB; + Markup::Node board; + //cartridge.cpp auto loadGameBoy() -> bool; auto loadBSMemory() -> bool; @@ -66,7 +57,7 @@ private: auto loadSufamiTurboB() -> bool; //load.cpp - auto loadBoard(Markup::Node) -> Markup::Node; + auto loadBoard(string) -> Markup::Node; auto loadCartridge(Markup::Node) -> void; auto loadGameBoy(Markup::Node) -> void; auto loadBSMemory(Markup::Node) -> void; diff --git a/higan/sfc/cartridge/load.cpp b/higan/sfc/cartridge/load.cpp index d6f92d8b..f183c83b 100644 --- a/higan/sfc/cartridge/load.cpp +++ b/higan/sfc/cartridge/load.cpp @@ -1,7 +1,6 @@ -auto Cartridge::loadBoard(Markup::Node node) -> Markup::Node { +auto Cartridge::loadBoard(string board) -> Markup::Node { string output; - auto board = node["game/board"].text(); if(board.beginsWith("SNSP-")) board.replace("SNSP-", "SHVC-", 1L); if(board.beginsWith("MAXI-")) board.replace("MAXI-", "SHVC-", 1L); if(board.beginsWith("MJSC-")) board.replace("MJSC-", "SHVC-", 1L); @@ -26,10 +25,10 @@ auto Cartridge::loadBoard(Markup::Node node) -> Markup::Node { } auto Cartridge::loadCartridge(Markup::Node node) -> void { - information.title.cartridge = node["game/label"].text(); + board = loadBoard(game.board); if(region() == "Auto") { - auto region = node["game/region"].text(); + auto region = game.region; if(region.endsWith("BRA") || region.endsWith("CAN") || region.endsWith("HKG") @@ -46,15 +45,13 @@ auto Cartridge::loadCartridge(Markup::Node node) -> void { } } - auto board = node["board"]; - if(!board) board = loadBoard(node); - if(board["bsmemory"] || board["mcc/bsmemory"] || board["sa1/bsmemory"]) { if(auto loaded = platform->load(ID::BSMemory, "BS Memory", "bs")) { bsmemory.pathID = loaded.pathID(); loadBSMemory(); } } + if(board["sufamiturbo"]) { if(auto loaded = platform->load(ID::SufamiTurboA, "Sufami Turbo", "st")) { sufamiturboA.pathID = loaded.pathID(); @@ -88,17 +85,18 @@ auto Cartridge::loadGameBoy(Markup::Node node) -> void { } auto Cartridge::loadBSMemory(Markup::Node node) -> void { - information.title.bsMemory = node["game/label"].text(); - bsmemory.readonly = node["game/memory/type"].text() == "ROM"; - - loadMemory(bsmemory.memory, node["game/memory"], File::Required, bsmemory.pathID); + if(node["game/board/memory/type"].text() == "ROM") { + bsmemory.readonly = true; + loadMemory(bsmemory.memory, node["game/board/memory(type=ROM)"], File::Required, bsmemory.pathID); + } else { + bsmemory.readonly = false; + loadMemory(bsmemory.memory, node["game/board/memory(type=Flash)"], File::Required, bsmemory.pathID); + } } auto Cartridge::loadSufamiTurboA(Markup::Node node) -> void { - information.title.sufamiTurboA = node["game/label"].text(); - - loadMemory(sufamiturboA.rom, node["game/memory(type=ROM)" ], File::Required, sufamiturboA.pathID); - loadMemory(sufamiturboA.ram, node["game/memory(type=NVRAM)"], File::Optional, sufamiturboA.pathID); + loadMemory(sufamiturboA.rom, node["game/board/memory(type=ROM)"], File::Required, sufamiturboA.pathID); + loadMemory(sufamiturboA.ram, node["game/board/memory(type=RAM)"], File::Optional, sufamiturboA.pathID); if(auto loaded = platform->load(ID::SufamiTurboB, "Sufami Turbo", "st")) { sufamiturboB.pathID = loaded.pathID(); @@ -107,10 +105,8 @@ auto Cartridge::loadSufamiTurboA(Markup::Node node) -> void { } auto Cartridge::loadSufamiTurboB(Markup::Node node) -> void { - information.title.sufamiTurboB = node["game/label"].text(); - - loadMemory(sufamiturboB.rom, node["game/memory(type=ROM)" ], File::Required, sufamiturboB.pathID); - loadMemory(sufamiturboB.ram, node["game/memory(type=NVRAM)"], File::Optional, sufamiturboB.pathID); + loadMemory(sufamiturboB.rom, node["game/board/memory(type=ROM)"], File::Required, sufamiturboB.pathID); + loadMemory(sufamiturboB.ram, node["game/board/memory(type=RAM)"], File::Optional, sufamiturboB.pathID); } // @@ -227,14 +223,20 @@ auto Cartridge::loadSuperFX(Markup::Node node) -> void { auto Cartridge::loadARMDSP(Markup::Node node) -> void { has.ARMDSP = true; - if(auto fp = platform->open(ID::SuperFamicom, node["prom"]["name"].text(), File::Read, File::Required)) { - for(auto n : range(128 * 1024)) armdsp.programROM[n] = fp->read(); + if(auto memory = game.memory(node["prom/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read, File::Required)) { + for(auto n : range(128 * 1024)) armdsp.programROM[n] = fp->read(); + } } - if(auto fp = platform->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) { - for(auto n : range( 32 * 1024)) armdsp.dataROM[n] = fp->read(); + if(auto memory = game.memory(node["drom/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read, File::Required)) { + for(auto n : range( 32 * 1024)) armdsp.dataROM[n] = fp->read(); + } } - if(auto fp = platform->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) { - for(auto n : range( 16 * 1024)) armdsp.programRAM[n] = fp->read(); + if(auto memory = game.memory(node["ram/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read)) { + for(auto n : range( 16 * 1024)) armdsp.programRAM[n] = fp->read(); + } } for(auto leaf : node.find("map")) loadMap(leaf, {&ArmDSP::read, &armdsp}, {&ArmDSP::write, &armdsp}); @@ -253,11 +255,15 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void { for(auto& word : hitachidsp.dataROM) word = 0x000000; for(auto& word : hitachidsp.dataRAM) word = 0x00; - if(auto fp = platform->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) { - for(auto n : range(1 * 1024)) hitachidsp.dataROM[n] = fp->readl(3); + if(auto memory = game.memory(node["drom/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read, File::Required)) { + for(auto n : range(1 * 1024)) hitachidsp.dataROM[n] = fp->readl(3); + } } - if(auto fp = platform->open(ID::SuperFamicom, node["dram"]["name"].text(), File::Read)) { - for(auto n : range(3 * 1024)) hitachidsp.dataRAM[n] = fp->readl(1); + if(auto memory = game.memory(node["dram/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read)) { + for(auto n : range(3 * 1024)) hitachidsp.dataRAM[n] = fp->readl(1); + } } for(auto leaf : node.find("map")) loadMap(leaf, {&HitachiDSP::dspRead, &hitachidsp}, {&HitachiDSP::dspWrite, &hitachidsp}); @@ -284,14 +290,20 @@ auto Cartridge::loadNECDSP(Markup::Node node) -> void { if(necdsp.revision == NECDSP::Revision::uPD7725 ) memory::assign(size, 2048, 1024, 256); if(necdsp.revision == NECDSP::Revision::uPD96050) memory::assign(size, 16384, 2048, 2048); - if(auto fp = platform->open(ID::SuperFamicom, node["prom"]["name"].text(), File::Read, File::Required)) { - for(auto n : range(size[0])) necdsp.programROM[n] = fp->readl(3); + if(auto memory = game.memory(node["prom/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read, File::Required)) { + for(auto n : range(size[0])) necdsp.programROM[n] = fp->readl(3); + } } - if(auto fp = platform->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) { - for(auto n : range(size[1])) necdsp.dataROM[n] = fp->readl(2); + if(auto memory = game.memory(node["drom/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read, File::Required)) { + for(auto n : range(size[1])) necdsp.dataROM[n] = fp->readl(2); + } } - if(auto fp = platform->open(ID::SuperFamicom, node["dram"]["name"].text(), File::Read)) { - for(auto n : range(size[2])) necdsp.dataRAM[n] = fp->readl(2); + if(auto memory = game.memory(node["dram/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read)) { + for(auto n : range(size[2])) necdsp.dataRAM[n] = fp->readl(2); + } } for(auto leaf : node.find("map")) loadMap(leaf, {&NECDSP::read, &necdsp}, {&NECDSP::write, &necdsp}); @@ -302,10 +314,12 @@ auto Cartridge::loadEpsonRTC(Markup::Node node) -> void { has.EpsonRTC = true; epsonrtc.initialize(); - if(auto fp = platform->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) { - uint8 data[16] = {0}; - for(auto& byte : data) byte = fp->read(); - epsonrtc.load(data); + if(auto memory = game.memory(node["ram/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read)) { + uint8 data[16] = {0}; + for(auto& byte : data) byte = fp->read(); + epsonrtc.load(data); + } } for(auto leaf : node.find("map")) loadMap(leaf, {&EpsonRTC::read, &epsonrtc}, {&EpsonRTC::write, &epsonrtc}); @@ -315,10 +329,12 @@ auto Cartridge::loadSharpRTC(Markup::Node node) -> void { has.SharpRTC = true; sharprtc.initialize(); - if(auto fp = platform->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) { - uint8 data[16] = {0}; - for(auto& byte : data) byte = fp->read(); - sharprtc.load(data); + if(auto memory = game.memory(node["ram/name"].text())) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Read)) { + uint8 data[16] = {0}; + for(auto& byte : data) byte = fp->read(); + sharprtc.load(data); + } } for(auto leaf : node.find("map")) loadMap(leaf, {&SharpRTC::read, &sharprtc}, {&SharpRTC::write, &sharprtc}); @@ -366,12 +382,13 @@ auto Cartridge::loadMSU1(Markup::Node node) -> void { auto Cartridge::loadMemory(MappedRAM& ram, Markup::Node node, bool required, maybe id) -> void { if(!id) id = pathID(); - auto name = node["name"].text(); - auto size = node["size"].natural(); - ram.allocate(size); - if(auto fp = platform->open(id(), name, File::Read, required)) { - ram.allocate(fp->size()); //TODO: temporary hack - fp->read(ram.data(), ram.size()); + if(auto memory = game.memory(node["name"].text())) { + ram.allocate(memory->size); + if(memory->type == "RAM" && !memory->battery) return; + if(memory->type == "RTC" && !memory->battery) return; + if(auto fp = platform->open(id(), memory->name(), File::Read, required)) { + fp->read(ram.data(), ram.size()); + } } } diff --git a/higan/sfc/cartridge/save.cpp b/higan/sfc/cartridge/save.cpp index a72e14db..67dfbfcf 100644 --- a/higan/sfc/cartridge/save.cpp +++ b/higan/sfc/cartridge/save.cpp @@ -1,6 +1,4 @@ auto Cartridge::saveCartridge(Markup::Node node) -> void { - auto board = node["board"]; - if(auto node = board["ram"]) saveRAM(node); if(auto node = board["mcc"]) saveMCC(node); if(auto node = board["event"]) saveEvent(node); @@ -23,11 +21,11 @@ auto Cartridge::saveBSMemory(Markup::Node node) -> void { } auto Cartridge::saveSufamiTurboA(Markup::Node node) -> void { - saveMemory(sufamiturboA.ram, node["game/memory(type=NVRAM)"], sufamiturboA.pathID); + saveMemory(sufamiturboA.ram, node["game/board/memory(type=RAM)"], sufamiturboA.pathID); } auto Cartridge::saveSufamiTurboB(Markup::Node node) -> void { - saveMemory(sufamiturboB.ram, node["game/memory(type=NVRAM)"], sufamiturboB.pathID); + saveMemory(sufamiturboB.ram, node["game/board/memory(type=RAM)"], sufamiturboB.pathID); } // @@ -54,9 +52,9 @@ auto Cartridge::saveSuperFX(Markup::Node node) -> void { } auto Cartridge::saveARMDSP(Markup::Node node) -> void { - if(!node["ram/volatile"]) { - if(auto name = node["ram/name"].text()) { - if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) { + if(auto memory = game.memory(node["ram/name"].text())) { + if(memory->battery) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Write)) { for(auto n : range(16 * 1024)) fp->write(armdsp.programRAM[n]); } } @@ -66,9 +64,9 @@ auto Cartridge::saveARMDSP(Markup::Node node) -> void { auto Cartridge::saveHitachiDSP(Markup::Node node) -> void { saveMemory(hitachidsp.ram, node["ram"]); - if(!node["dram/volatile"]) { - if(auto name = node["dram/name"].text()) { - if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) { + if(auto memory = game.memory(node["dram/name"].text())) { + if(memory->battery) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Write)) { for(auto n : range(3 * 1024)) fp->write(hitachidsp.dataRAM[n]); } } @@ -76,10 +74,10 @@ auto Cartridge::saveHitachiDSP(Markup::Node node) -> void { } auto Cartridge::saveNECDSP(Markup::Node node) -> void { - if(!node["dram/volatile"]) { - uint size = necdsp.revision == NECDSP::Revision::uPD7725 ? 256 : 2048; - if(auto name = node["dram/name"].text()) { - if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) { + uint size = necdsp.revision == NECDSP::Revision::uPD7725 ? 256 : 2048; + if(auto memory = game.memory(node["dram/name"].text())) { + if(memory->battery) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Write)) { for(auto n : range(size)) fp->writel(necdsp.dataRAM[n], 2); } } @@ -87,9 +85,9 @@ auto Cartridge::saveNECDSP(Markup::Node node) -> void { } auto Cartridge::saveEpsonRTC(Markup::Node node) -> void { - if(!node["ram/volatile"]) { - if(auto name = node["ram/name"].text()) { - if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) { + if(auto memory = game.memory(node["ram/name"].text())) { + if(memory->battery) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Write)) { uint8 data[16] = {0}; epsonrtc.save(data); fp->write(data, 16); @@ -99,9 +97,9 @@ auto Cartridge::saveEpsonRTC(Markup::Node node) -> void { } auto Cartridge::saveSharpRTC(Markup::Node node) -> void { - if(!node["ram/volatile"]) { - if(auto name = node["ram/name"].text()) { - if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) { + if(auto memory = game.memory(node["ram/name"].text())) { + if(memory->battery) { + if(auto fp = platform->open(ID::SuperFamicom, memory->name(), File::Write)) { uint8 data[16] = {0}; sharprtc.save(data); fp->write(data, 16); @@ -124,12 +122,13 @@ auto Cartridge::saveOBC1(Markup::Node node) -> void { // -auto Cartridge::saveMemory(MappedRAM& memory, Markup::Node node, maybe id) -> void { +auto Cartridge::saveMemory(MappedRAM& ram, Markup::Node node, maybe id) -> void { if(!id) id = pathID(); - if(!node || node["volatile"]) return; - auto name = node["name"].text(); - auto size = node["size"].natural(); - if(auto fp = platform->open(id(), name, File::Write)) { - fp->write(memory.data(), memory.size()); + if(auto memory = game.memory(node["name"].text())) { + if(memory->type == "RAM" && !memory->battery) return; + if(memory->type == "RTC" && !memory->battery) return; + if(auto fp = platform->open(id(), memory->name(), File::Write)) { + fp->write(ram.data(), ram.size()); + } } } diff --git a/higan/sfc/coprocessor/msu1/msu1.cpp b/higan/sfc/coprocessor/msu1/msu1.cpp index 88ca56a9..7fa447fd 100644 --- a/higan/sfc/coprocessor/msu1/msu1.cpp +++ b/higan/sfc/coprocessor/msu1/msu1.cpp @@ -72,7 +72,7 @@ auto MSU1::power() -> void { auto MSU1::dataOpen() -> void { dataFile.reset(); - auto document = BML::unserialize(cartridge.information.manifest.cartridge); + auto document = Markup::Node(); //todo: fix this string name = document["board/msu1/rom/name"].text(); if(!name) name = "msu1.rom"; if(dataFile = platform->open(ID::SuperFamicom, name, File::Read)) { @@ -82,7 +82,7 @@ auto MSU1::dataOpen() -> void { auto MSU1::audioOpen() -> void { audioFile.reset(); - auto document = BML::unserialize(cartridge.information.manifest.cartridge); + auto document = Markup::Node(); //todo: fix this string name = {"track-", io.audioTrack, ".pcm"}; for(auto track : document.find("board/msu1/track")) { if(track["number"].natural() != io.audioTrack) continue; diff --git a/hiro/cocoa/object.hpp b/hiro/cocoa/object.hpp index 056ed0b7..e1fc4dbb 100644 --- a/hiro/cocoa/object.hpp +++ b/hiro/cocoa/object.hpp @@ -18,7 +18,7 @@ struct pObject { virtual auto setFont(const Font& font) -> void; virtual auto setVisible(bool visible) -> void; - auto locked() const -> bool { return locks != 0; } + auto locked() const -> bool { return locks != 0 || Application::state.quit; } auto lock() -> void { ++locks; } auto unlock() -> void { --locks; } diff --git a/hiro/gtk/object.hpp b/hiro/gtk/object.hpp index 056ed0b7..e1fc4dbb 100644 --- a/hiro/gtk/object.hpp +++ b/hiro/gtk/object.hpp @@ -18,7 +18,7 @@ struct pObject { virtual auto setFont(const Font& font) -> void; virtual auto setVisible(bool visible) -> void; - auto locked() const -> bool { return locks != 0; } + auto locked() const -> bool { return locks != 0 || Application::state.quit; } auto lock() -> void { ++locks; } auto unlock() -> void { --locks; } diff --git a/hiro/qt/object.hpp b/hiro/qt/object.hpp index ec5b878a..ade3c070 100644 --- a/hiro/qt/object.hpp +++ b/hiro/qt/object.hpp @@ -18,7 +18,7 @@ struct pObject { virtual auto setFont(const Font& font) -> void; virtual auto setVisible(bool visible) -> void; - auto locked() const -> bool { return locks != 0; } + auto locked() const -> bool { return locks != 0 || Application::state.quit; } auto lock() -> void { locks++; } auto unlock() -> void { locks--; } diff --git a/hiro/windows/object.hpp b/hiro/windows/object.hpp index c30c8ade..0b8d3eb1 100644 --- a/hiro/windows/object.hpp +++ b/hiro/windows/object.hpp @@ -18,7 +18,7 @@ struct pObject { virtual auto setGroup(sGroup group) -> void; virtual auto setVisible(bool visible) -> void; - auto locked() const -> bool { return locks != 0; } + auto locked() const -> bool { return locks != 0 || Application::state.quit; } auto lock() -> void { ++locks; } auto unlock() -> void { --locks; } diff --git a/icarus/heuristics/bs-memory.cpp b/icarus/heuristics/bs-memory.cpp index 705c37bc..6a33f1fd 100644 --- a/icarus/heuristics/bs-memory.cpp +++ b/icarus/heuristics/bs-memory.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct BSMemory : Heuristics { +struct BSMemory { BSMemory(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/famicom.cpp b/icarus/heuristics/famicom.cpp index 632eee6c..3e34e48e 100644 --- a/icarus/heuristics/famicom.cpp +++ b/icarus/heuristics/famicom.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct Famicom : Heuristics { +struct Famicom { Famicom(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/game-boy-advance.cpp b/icarus/heuristics/game-boy-advance.cpp index b9f756b6..2b0ce0dd 100644 --- a/icarus/heuristics/game-boy-advance.cpp +++ b/icarus/heuristics/game-boy-advance.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct GameBoyAdvance : Heuristics { +struct GameBoyAdvance { GameBoyAdvance(vector& buffer, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/game-boy.cpp b/icarus/heuristics/game-boy.cpp index 850cd52f..69b6562b 100644 --- a/icarus/heuristics/game-boy.cpp +++ b/icarus/heuristics/game-boy.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct GameBoy : Heuristics { +struct GameBoy { GameBoy(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/game-gear.cpp b/icarus/heuristics/game-gear.cpp index 2a316279..769dcc23 100644 --- a/icarus/heuristics/game-gear.cpp +++ b/icarus/heuristics/game-gear.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct GameGear : Heuristics { +struct GameGear { GameGear(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/heuristics.cpp b/icarus/heuristics/heuristics.cpp index dc2e7d26..d046df45 100644 --- a/icarus/heuristics/heuristics.cpp +++ b/icarus/heuristics/heuristics.cpp @@ -4,6 +4,8 @@ auto Memory::text() const -> string { string output; output.append(" memory\n"); output.append(" type: ", _type, "\n"); +if(_battery) + output.append(" battery\n"); output.append(" size: 0x", hex(_size), "\n"); output.append(" category: ", _category, "\n"); if(_manufacturer) @@ -12,8 +14,6 @@ if(_part) output.append(" part: ", _part, "\n"); if(_note) output.append(" note: ", _note, "\n"); -if(_battery) - output.append(" battery\n"); return output; } @@ -26,16 +26,4 @@ if(_note) return output; } -//deprecated -auto Heuristics::memory(string type, uint size, string name, string metadata) const -> string { - string output; - output.append(" memory\n"); - output.append(" type: ", type, "\n"); - output.append(" size: 0x", hex(size), "\n"); - output.append(" name: ", name, "\n"); -if(metadata) - output.append(" metadata: ", metadata, "\n"); - return output; -} - } diff --git a/icarus/heuristics/heuristics.hpp b/icarus/heuristics/heuristics.hpp index 50e21b1e..1db99e56 100644 --- a/icarus/heuristics/heuristics.hpp +++ b/icarus/heuristics/heuristics.hpp @@ -2,20 +2,20 @@ namespace Heuristics { struct Memory { auto& type(string type) { _type = type; return *this; } + auto& battery(boolean battery = true) { _battery = battery; return *this; } auto& size(natural size) { _size = size; return *this; } auto& category(string category) { _category = category; return *this; } auto& manufacturer(string manufacturer) { _manufacturer = manufacturer; return *this; } auto& part(string part) { _part = part; return *this; } - auto& battery(boolean battery = true) { _battery = battery; return *this; } auto& note(string note) { _note = note; return *this; } auto text() const -> string; string _type; + boolean _battery; natural _size; string _category; string _manufacturer; string _part; - boolean _battery; string _note; }; @@ -28,8 +28,4 @@ struct Oscillator { string _note; }; -struct Heuristics { - auto memory(string type, uint size, string name, string metadata = {}) const -> string; -}; - } diff --git a/icarus/heuristics/master-system.cpp b/icarus/heuristics/master-system.cpp index 33099d73..8883df30 100644 --- a/icarus/heuristics/master-system.cpp +++ b/icarus/heuristics/master-system.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct MasterSystem : Heuristics { +struct MasterSystem { MasterSystem(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/mega-drive.cpp b/icarus/heuristics/mega-drive.cpp index 4fa71a79..776eef45 100644 --- a/icarus/heuristics/mega-drive.cpp +++ b/icarus/heuristics/mega-drive.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct MegaDrive : Heuristics { +struct MegaDrive { MegaDrive(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/pc-engine.cpp b/icarus/heuristics/pc-engine.cpp index c1d35ea3..e0f844c7 100644 --- a/icarus/heuristics/pc-engine.cpp +++ b/icarus/heuristics/pc-engine.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct PCEngine : Heuristics { +struct PCEngine { PCEngine(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/sufami-turbo.cpp b/icarus/heuristics/sufami-turbo.cpp index 67eacd26..7fee656a 100644 --- a/icarus/heuristics/sufami-turbo.cpp +++ b/icarus/heuristics/sufami-turbo.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct SufamiTurbo : Heuristics { +struct SufamiTurbo { SufamiTurbo(vector& data, string location); explicit operator bool() const; diff --git a/icarus/heuristics/super-famicom.cpp b/icarus/heuristics/super-famicom.cpp index 3a087de8..f4cdd94a 100644 --- a/icarus/heuristics/super-famicom.cpp +++ b/icarus/heuristics/super-famicom.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct SuperFamicom : Heuristics { +struct SuperFamicom { SuperFamicom(vector& data, string location); explicit operator bool() const; diff --git a/icarus/heuristics/supergrafx.cpp b/icarus/heuristics/supergrafx.cpp index 89cb0d3a..49e4df4e 100644 --- a/icarus/heuristics/supergrafx.cpp +++ b/icarus/heuristics/supergrafx.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct SuperGrafx : Heuristics { +struct SuperGrafx { SuperGrafx(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; diff --git a/icarus/heuristics/wonderswan.cpp b/icarus/heuristics/wonderswan.cpp index a30cde92..9c55c3aa 100644 --- a/icarus/heuristics/wonderswan.cpp +++ b/icarus/heuristics/wonderswan.cpp @@ -1,6 +1,6 @@ namespace Heuristics { -struct WonderSwan : Heuristics { +struct WonderSwan { WonderSwan(vector& buffer, string location); explicit operator bool() const; auto manifest() const -> string;