diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index f9a2a936..cd864acf 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -13,7 +13,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.52"; + static const string Version = "106.53"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index c2e48d91..4017b6c3 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -87,6 +87,12 @@ struct Interface { //cheat functions virtual auto cheats(const vector& = {}) -> void {} + //configuration + virtual auto configuration() -> string { return {}; } + virtual auto configuration(string name) -> string { return {}; } + virtual auto configure(string configuration = "") -> bool { return false; } + virtual auto configure(string name, string value) -> bool { return false; } + //settings virtual auto cap(const string& name) -> bool { return false; } virtual auto get(const string& name) -> any { return {}; } diff --git a/higan/md/cartridge/cartridge.cpp b/higan/md/cartridge/cartridge.cpp index 9f01ddaa..70bcf96f 100644 --- a/higan/md/cartridge/cartridge.cpp +++ b/higan/md/cartridge/cartridge.cpp @@ -5,144 +5,145 @@ namespace MegaDrive { Cartridge cartridge; #include "serialization.cpp" -auto Cartridge::region() const -> string { - return game.region; -} - auto Cartridge::hashes() const -> vector { vector hashes; - hashes.append(game.hash); - if(lockOn.hash) hashes.append(lockOn.hash); + hashes.append(information.hash); + if(slot) for(auto& hash : slot->hashes()) hashes.append(hash); return hashes; } auto Cartridge::manifests() const -> vector { vector manifests; - manifests.append(game.manifest); - if(lockOn.manifest) manifests.append(lockOn.manifest); + manifests.append(information.manifest); + if(slot) for(auto& manifest : slot->manifests()) manifests.append(manifest); return manifests; } auto Cartridge::titles() const -> vector { vector titles; - titles.append(game.title); - if(lockOn.title) titles.append(lockOn.title); + titles.append(information.title); + if(slot) for(auto& title : slot->titles()) titles.append(title); return titles; } auto Cartridge::load() -> bool { - game = {}; - lockOn = {}; - read.reset(); - write.reset(); + unload(); - if(!loadGame()) { - game = {}; - return false; + if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"Auto", "NTSC-J", "NTSC-U", "PAL"})) { + information.pathID = loaded.pathID; + information.region = loaded.option; + } else return false; + + if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + information.document = BML::unserialize(information.manifest); + information.hash = information.document["game/sha256"].text(); + information.title = information.document["game/label"].text(); + + if(!loadROM(rom, information.document["game/board/memory(type=ROM,content=Program)"])) { + return unload(), false; } - read = {&Cartridge::readGame, this}; - write = {&Cartridge::writeGame, this}; + if(!loadROM(patch, information.document["game/board/memory(type=ROM,content=Patch)"])) { + patch.reset(); + } - if(game.patch.size) { + if(!loadRAM(ram, information.document["game/board/memory(type=RAM,content=Save)"])) { + ram.reset(); + } + + if(information.region == "Auto") { + if(auto region = information.document["game/region"].text()) { + information.region = region.upcase(); + } else { + information.region = "NTSC-J"; + } + } + + read = {&Cartridge::readLinear, this}; + write = {&Cartridge::writeLinear, this}; + + if(rom.size > 0x200000) { + read = {&Cartridge::readBanked, this}; + write = {&Cartridge::writeBanked, this}; + } + + if(patch) { + slot = new Cartridge{depth + 1}; + if(!slot->load()) slot.reset(); read = {&Cartridge::readLockOn, this}; write = {&Cartridge::writeLockOn, this}; - if(!loadLockOn()) lockOn = {}; + } + + if(rom.data[0x120>>1]==0x4761 || rom.data[0x120>>1]==0x6147) { + slot = new Cartridge{depth + 1}; + if(!slot->load()) slot.reset(); + read = {&Cartridge::readGameGenie, this}; + write = {&Cartridge::writeGameGenie, this}; + } + + //easter egg: power draw increases with each successively stacked cartridge + //simulate increasing address/data line errors as stacking increases + if(depth >= 3) { + auto reader = read; + auto writer = write; + auto scramble = [=](uint32 value) -> uint32 { + uint chance = max(1, (1 << 19) >> depth) - 1; + if((random() & chance) == 0) value ^= 1 << (random() & 31); + return value; + }; + read = [=](uint22 address) -> uint16 { + return scramble(reader(scramble(address))); + }; + write = [=](uint22 address, uint16 data) -> void { + writer(scramble(address), scramble(data)); + }; } return true; } -auto Cartridge::loadGame() -> bool { - if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"Auto", "NTSC-J", "NTSC-U", "PAL"})) { - game.pathID = loaded.pathID; - game.region = loaded.option; - } else return false; - - if(auto fp = platform->open(game.pathID, "manifest.bml", File::Read, File::Required)) { - game.manifest = fp->reads(); - } else return false; - - game.document = BML::unserialize(game.manifest); - game.hash = game.document["game/sha256"].text(); - game.title = game.document["game/label"].text(); - - if(!loadROM(game.rom, game.pathID, game.document["game/board/memory(type=ROM,content=Program)"])) { - game.rom.reset(); - return false; - } - - if(!loadROM(game.patch, game.pathID, game.document["game/board/memory(type=ROM,content=Patch)"])) { - game.patch.reset(); - } - - if(!loadRAM(game.ram, game.pathID, game.document["game/board/memory(type=RAM,content=Save)"])) { - game.ram.reset(); - } - - if(game.region == "Auto") { - if(auto region = game.document["game/region"].text()) { - game.region = region.upcase(); - } else { - game.region = "NTSC-J"; - } - } - - return true; +auto Cartridge::save() -> void { + saveRAM(ram, information.document["game/board/memory(type=RAM,content=Save)"]); + if(slot) slot->save(); } -auto Cartridge::loadLockOn() -> bool { - if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md")) { - lockOn.pathID = loaded.pathID; - } else return false; - - if(auto fp = platform->open(lockOn.pathID, "manifest.bml", File::Read, File::Required)) { - lockOn.manifest = fp->reads(); - } else return false; - - lockOn.document = BML::unserialize(lockOn.manifest); - lockOn.hash = lockOn.document["game/sha256"].text(); - lockOn.title = lockOn.document["game/label"].text(); - - if(!loadROM(lockOn.rom, lockOn.pathID, lockOn.document["game/board/memory(type=ROM,content=Program)"])) { - lockOn.rom.reset(); - return false; - } - - if(!loadRAM(lockOn.ram, lockOn.pathID, lockOn.document["game/board/memory(type=RAM,content=Save)"])) { - lockOn.ram.reset(); - } - - if(lockOn.rom.size >= 0x200) { - string name; - name.resize(48); - for(uint n : range(24)) { - name.get()[n * 2 + 0] = lockOn.rom.data[0x120 / 2 + n].byte(1); - name.get()[n * 2 + 1] = lockOn.rom.data[0x120 / 2 + n].byte(0); - } - name.strip(); - while(name.find(" ")) name.replace(" ", " "); - lockOn.patch = name == "SONIC THE HEDGEHOG 2"; - } - - return true; +auto Cartridge::unload() -> void { + rom.reset(); + patch.reset(); + ram.reset(); + read.reset(); + write.reset(); + if(slot) slot->unload(); + slot.reset(); } -auto Cartridge::loadROM(Memory& rom, uint pathID, Markup::Node memory) -> bool { +auto Cartridge::power() -> void { + ramEnable = 1; + ramWritable = 1; + for(uint n : range(8)) romBank[n] = n; + gameGenie = {}; +} + +// + +auto Cartridge::loadROM(Memory& rom, Markup::Node memory) -> bool { if(!memory) return false; auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase(); rom.size = memory["size"].natural() >> 1; rom.mask = bit::round(rom.size) - 1; rom.data = new uint16[rom.mask + 1](); - if(auto fp = platform->open(pathID, name, File::Read, File::Required)) { + if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) { for(uint n : range(rom.size)) rom.data[n] = fp->readm(2); } else return false; return true; } -auto Cartridge::loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool { +auto Cartridge::loadRAM(Memory& ram, Markup::Node memory) -> bool { if(!memory) return false; auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase(); @@ -155,7 +156,7 @@ auto Cartridge::loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool { ram.mask = bit::round(ram.size) - 1; ram.data = new uint16[ram.mask + 1](); if(!(bool)memory["volatile"]) { - if(auto fp = platform->open(pathID, name, File::Read)) { + if(auto fp = platform->open(pathID(), name, File::Read)) { for(uint n : range(ram.size)) { if(ram.bits != 0xffff) ram.data[n] = fp->readm(1) * 0x0101; if(ram.bits == 0xffff) ram.data[n] = fp->readm(2); @@ -166,17 +167,12 @@ auto Cartridge::loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool { return true; } -auto Cartridge::save() -> void { - saveRAM(game.ram, game.pathID, game.document["game/board/memory(type=RAM,content=Save)"]); - saveRAM(lockOn.ram, lockOn.pathID, lockOn.document["game/board/memory(type=RAM,content=Save)"]); -} - -auto Cartridge::saveRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool { +auto Cartridge::saveRAM(Memory& ram, Markup::Node memory) -> bool { if(!memory) return false; if((bool)memory["volatile"]) return true; auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase(); - if(auto fp = platform->open(pathID, name, File::Write)) { + if(auto fp = platform->open(pathID(), name, File::Write)) { for(uint n : range(ram.size)) { if(ram.bits != 0xffff) fp->writem(ram.data[n], 1); if(ram.bits == 0xffff) fp->writem(ram.data[n], 2); @@ -186,92 +182,99 @@ auto Cartridge::saveRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool { return true; } -auto Cartridge::unload() -> void { - game.rom.reset(); - game.patch.reset(); - game.ram.reset(); - game = {}; - - lockOn.rom.reset(); - lockOn.ram.reset(); - lockOn = {}; -} - -auto Cartridge::power() -> void { - ramEnable = 1; - ramWritable = 1; - for(auto n : range(8)) bank[n] = n; -} - // auto Cartridge::readIO(uint24 address) -> uint16 { + if(slot) slot->readIO(address); return 0x0000; } auto Cartridge::writeIO(uint24 address, uint16 data) -> void { if(address == 0xa130f1) ramEnable = data.bit(0), ramWritable = data.bit(1); - if(address == 0xa130f3) bank[1] = data; - if(address == 0xa130f5) bank[2] = data; - if(address == 0xa130f7) bank[3] = data; - if(address == 0xa130f9) bank[4] = data; - if(address == 0xa130fb) bank[5] = data; - if(address == 0xa130fd) bank[6] = data; - if(address == 0xa130ff) bank[7] = data; + if(address == 0xa130f3) romBank[1] = data; + if(address == 0xa130f5) romBank[2] = data; + if(address == 0xa130f7) romBank[3] = data; + if(address == 0xa130f9) romBank[4] = data; + if(address == 0xa130fb) romBank[5] = data; + if(address == 0xa130fd) romBank[6] = data; + if(address == 0xa130ff) romBank[7] = data; + if(slot) slot->writeIO(address, data); } // -auto Cartridge::readGame(uint24 address) -> uint16 { - if(address >= 0x200000 && game.ram.size && ramEnable) { - return game.ram.data[address >> 1 & game.ram.mask]; - } else { - address = bank[address.bits(19,21)] << 19 | address.bits(0,18); - return game.rom.data[address >> 1 & game.rom.mask]; - } +auto Cartridge::readLinear(uint22 address) -> uint16 { + if(ramEnable && ram && address >= 0x200000) return ram.read(address); + return rom.read(address); } -auto Cartridge::writeGame(uint24 address, uint16 data) -> void { - //emulating RAM write protect bit breaks some commercial software - if(address >= 0x200000 && game.ram.size && ramEnable /* && ramWritable */) { - if(game.ram.bits == 0x00ff) data = data.byte(0) * 0x0101; - if(game.ram.bits == 0xff00) data = data.byte(1) * 0x0101; - game.ram.data[address >> 1 & game.ram.mask] = data; +auto Cartridge::writeLinear(uint22 address, uint16 data) -> void { + //emulating ramWritable will break commercial software: + //it does not appear that many (any?) games actually connect $a130f1.d1 to /WE; + //hence RAM ends up always being writable, and many games fail to set d1=1 + if(ramEnable && ram && address >= 0x200000) return ram.write(address, data); +} + +// + +auto Cartridge::readBanked(uint22 address) -> uint16 { + address = romBank[address.bits(19,21)] << 19 | address.bits(0,18); + return rom.read(address); +} + +auto Cartridge::writeBanked(uint22 address, uint16 data) -> void { +} + +// + +auto Cartridge::readLockOn(uint22 address) -> uint16 { + if(address < 0x200000) return rom.read(address); + if(ramEnable && address >= 0x300000) return patch.read(address); + if(slot) return slot->read(address); + return 0x0000; +} + +auto Cartridge::writeLockOn(uint22 address, uint16 data) -> void { + if(slot) return slot->write(address, data); +} + +// + +auto Cartridge::readGameGenie(uint22 address) -> uint16 { + if(gameGenie.enable) { + for(auto& code : gameGenie.codes) { + if(code.enable && code.address == address) return code.data; + } + if(slot) return slot->read(address); + } + + return rom.read(address); +} + +auto Cartridge::writeGameGenie(uint22 address, uint16 data) -> void { + if(gameGenie.enable) { + if(slot) return slot->write(address, data); + } + + if(address == 0x02 && data == 0x0001) { + gameGenie.enable = true; + } + + if(address >= 0x04 && address <= 0x20 && !address.bit(0)) { + address = address - 0x04 >> 1; + auto& code = gameGenie.codes[address / 3]; + if(address % 3 == 0) code.address.bits(16,23) = data.byte(0); + if(address % 3 == 1) code.address.bits( 0,15) = data; + if(address % 3 == 2) code.data = data, code.enable = true; } } // -auto Cartridge::readLockOn(uint24 address) -> uint16 { - if(address >= 0x200000 && lockOn.ram.size && ramEnable) { - return lockOn.ram.data[address >> 1 & lockOn.ram.mask]; - } - - if(address >= 0x300000 && lockOn.patch) { - return game.patch.data[address >> 1 & game.patch.mask]; - } - - if(address >= 0x200000 && lockOn.rom.data) { - return lockOn.rom.data[address >> 1 & lockOn.rom.mask]; - } - - if(address >= 0x200000) { - return 0x00; - } - - return game.rom.data[address >> 1 & game.rom.mask]; +Cartridge::Memory::operator bool() const { + return size; } -auto Cartridge::writeLockOn(uint24 address, uint16 data) -> void { - if(address >= 0x200000 && lockOn.ram.size && ramEnable) { - if(lockOn.ram.bits == 0x00ff) data = data.byte(0) * 0x0101; - if(lockOn.ram.bits == 0xff00) data = data.byte(1) * 0x0101; - lockOn.ram.data[address >> 1 & lockOn.ram.mask] = data; - } -} - -// - auto Cartridge::Memory::reset() -> void { delete[] data; data = nullptr; @@ -280,4 +283,16 @@ auto Cartridge::Memory::reset() -> void { bits = 0; } +auto Cartridge::Memory::read(uint24 address) -> uint16 { + if(!size) return 0x0000; + return data[address >> 1 & mask]; +} + +auto Cartridge::Memory::write(uint24 address, uint16 word) -> void { + if(!size) return; + if(bits == 0x00ff) word.byte(1) = word.byte(0); + if(bits == 0xff00) word.byte(0) = word.byte(1); + data[address >> 1 & mask] = word; +} + } diff --git a/higan/md/cartridge/cartridge.hpp b/higan/md/cartridge/cartridge.hpp index c707880b..009384f0 100644 --- a/higan/md/cartridge/cartridge.hpp +++ b/higan/md/cartridge/cartridge.hpp @@ -1,73 +1,87 @@ struct Cartridge { - auto region() const -> string; + auto pathID() const -> uint { return information.pathID; } + auto region() const -> string { return information.region; } + + Cartridge() = default; + Cartridge(uint depth) : depth(depth) {} + auto hashes() const -> vector; auto manifests() const -> vector; auto titles() const -> vector; struct Memory; auto load() -> bool; - auto loadGame() -> bool; - auto loadLockOn() -> bool; - auto loadROM(Memory& rom, uint pathID, Markup::Node memory) -> bool; - auto loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool; auto save() -> void; - auto saveRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool; auto unload() -> void; auto power() -> void; - function read; - function write; + auto loadROM(Memory& rom, Markup::Node memory) -> bool; + auto loadRAM(Memory& ram, Markup::Node memory) -> bool; + auto saveRAM(Memory& ram, Markup::Node memory) -> bool; auto readIO(uint24 address) -> uint16; auto writeIO(uint24 address, uint16 data) -> void; - auto readGame(uint24 address) -> uint16; - auto writeGame(uint24 address, uint16 data) -> void; + auto readLinear(uint22 address) -> uint16; + auto writeLinear(uint22 address, uint16 data) -> void; - auto readLockOn(uint24 address) -> uint16; - auto writeLockOn(uint24 address, uint16 data) -> void; + auto readBanked(uint22 address) -> uint16; + auto writeBanked(uint22 address, uint16 data) -> void; + + auto readLockOn(uint22 address) -> uint16; + auto writeLockOn(uint22 address, uint16 data) -> void; + + auto readGameGenie(uint22 address) -> uint16; + auto writeGameGenie(uint22 address, uint16 data) -> void; //serialization.cpp auto serialize(serializer&) -> void; - struct Memory { - auto reset() -> void; - - uint16* data = nullptr; - uint size = 0; - uint mask = 0; - uint bits = 0; - }; - - struct Game { + struct Information { uint pathID = 0; string region; string hash; string manifest; string title; - Markup::Node document; - Memory rom; - Memory patch; - Memory ram; - } game; + }; - struct LockOn { - uint pathID = 0; - string hash; - string manifest; - string title; + struct Memory { + explicit operator bool() const; + auto reset() -> void; + auto read(uint24 address) -> uint16; + auto write(uint24 address, uint16 word) -> void; - Markup::Node document; - Memory rom; - Memory ram; + uint16* data = nullptr; + uint size = 0; //16-bit word size + uint mask = 0; + uint bits = 0; + }; - bool patch = false; - } lockOn; + Information information; + + Memory rom; + Memory patch; + Memory ram; uint1 ramEnable; uint1 ramWritable; - uint6 bank[8]; + uint6 romBank[8]; + + struct GameGenie { + boolean enable; + struct Code { + boolean enable; + uint24 address; + uint16 data; + } codes[5]; + } gameGenie; + + function read; + function write; + + unique_pointer slot; + const uint depth = 0; }; extern Cartridge cartridge; diff --git a/higan/md/cartridge/serialization.cpp b/higan/md/cartridge/serialization.cpp index f983669f..2b13fd8e 100644 --- a/higan/md/cartridge/serialization.cpp +++ b/higan/md/cartridge/serialization.cpp @@ -1,4 +1,7 @@ auto Cartridge::serialize(serializer& s) -> void { - if(game.ram.size) s.array(game.ram.data, game.ram.size); - if(lockOn.ram.size) s.array(lockOn.ram.data, lockOn.ram.size); + if(ram.size) s.array(ram.data, ram.size); + s.integer(ramEnable); + s.integer(ramWritable); + s.array(romBank); + if(slot) slot->serialize(s); } diff --git a/higan/md/md.hpp b/higan/md/md.hpp index ac9094b3..d7bda91e 100644 --- a/higan/md/md.hpp +++ b/higan/md/md.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -15,8 +16,10 @@ namespace MegaDrive { #define platform Emulator::platform namespace File = Emulator::File; using Scheduler = Emulator::Scheduler; + using Random = Emulator::Random; using Cheat = Emulator::Cheat; extern Scheduler scheduler; + extern Random random; extern Cheat cheat; struct Wait { diff --git a/higan/md/system/system.cpp b/higan/md/system/system.cpp index 30d1be43..ea2696d3 100644 --- a/higan/md/system/system.cpp +++ b/higan/md/system/system.cpp @@ -4,6 +4,7 @@ namespace MegaDrive { System system; Scheduler scheduler; +Random random; Cheat cheat; #include "serialization.cpp" @@ -67,6 +68,8 @@ auto System::power(bool reset) -> void { Emulator::audio.reset(interface); + random.entropy(Random::Entropy::High); + scheduler.reset(); cartridge.power(); cpu.power(reset); diff --git a/higan/sfc/cpu/cpu.cpp b/higan/sfc/cpu/cpu.cpp index 34c5a057..bcc30f46 100644 --- a/higan/sfc/cpu/cpu.cpp +++ b/higan/sfc/cpu/cpu.cpp @@ -44,8 +44,8 @@ auto CPU::main() -> void { instruction(); } -auto CPU::load(Markup::Node node) -> bool { - version = node["cpu/version"].natural(); +auto CPU::load() -> bool { + version = configuration.system.cpu.version; if(version < 1) version = 1; if(version > 2) version = 2; return true; diff --git a/higan/sfc/cpu/cpu.hpp b/higan/sfc/cpu/cpu.hpp index 2d883653..45884573 100644 --- a/higan/sfc/cpu/cpu.hpp +++ b/higan/sfc/cpu/cpu.hpp @@ -6,7 +6,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter { //cpu.cpp static auto Enter() -> void; auto main() -> void; - auto load(Markup::Node) -> bool; + auto load() -> bool; auto power(bool reset) -> void; //dma.cpp diff --git a/higan/sfc/dsp/dsp.cpp b/higan/sfc/dsp/dsp.cpp index 0efcdc0e..4cbc3b5c 100644 --- a/higan/sfc/dsp/dsp.cpp +++ b/higan/sfc/dsp/dsp.cpp @@ -234,7 +234,7 @@ auto DSP::write(uint8 addr, uint8 data) -> void { /* initialization */ -auto DSP::load(Markup::Node node) -> bool { +auto DSP::load() -> bool { return true; } diff --git a/higan/sfc/dsp/dsp.hpp b/higan/sfc/dsp/dsp.hpp index 1c827f14..5424b9c6 100644 --- a/higan/sfc/dsp/dsp.hpp +++ b/higan/sfc/dsp/dsp.hpp @@ -13,7 +13,7 @@ struct DSP : Thread { auto write(uint8 addr, uint8 data) -> void; auto main() -> void; - auto load(Markup::Node) -> bool; + auto load() -> bool; auto power(bool reset) -> void; //serialization.cpp diff --git a/higan/sfc/interface/configuration.cpp b/higan/sfc/interface/configuration.cpp new file mode 100644 index 00000000..e6627249 --- /dev/null +++ b/higan/sfc/interface/configuration.cpp @@ -0,0 +1,92 @@ +Configuration configuration; + +auto Configuration::read() -> string { + return { + "system\n" + " cpu version=", system.cpu.version, "\n" + " ppu1 version=", system.ppu1.version, "\n" + " vram size=0x", hex(system.ppu1.vram.size), "\n" + " ppu2 version=", system.ppu2.version, "\n" + "\n" + "video\n" + " blurEmulation: ", video.blurEmulation, "\n" + " colorEmulation: ", video.colorEmulation, "\n" + "\n" + "hacks\n" + " ppuFast\n" + " enable: ", hacks.ppuFast.enable, "\n" + " noSpriteLimit: ", hacks.ppuFast.noSpriteLimit, "\n" + " hiresMode7: ", hacks.ppuFast.hiresMode7, "\n" + " dspFast\n" + " enable: ", hacks.dspFast.enable, "\n" + }; +} + +auto Configuration::read(string name) -> string { + #define bind(id) { \ + string key = {string{#id}.transform(".", "/")}; \ + if(name == key) return name; \ + } + bind(system.cpu.version); + bind(system.ppu1.version); + bind(system.ppu1.vram.size); + bind(system.ppu2.version); + + bind(video.blurEmulation); + bind(video.colorEmulation); + + bind(hacks.ppuFast.enable); + bind(hacks.ppuFast.noSpriteLimit); + bind(hacks.ppuFast.hiresMode7); + bind(hacks.dspFast.enable); + #undef bind + return {}; +} + +auto Configuration::write(string configuration) -> bool { + *this = {}; + + auto document = BML::unserialize(configuration); + if(!document) return false; + + #define bind(type, id) { \ + string key = {string{#id}.transform(".", "/")}; \ + if(auto node = document[key]) id = node.type(); \ + } + bind(natural, system.cpu.version); + bind(natural, system.ppu1.version); + bind(natural, system.ppu1.vram.size); + bind(natural, system.ppu2.version); + + bind(boolean, video.blurEmulation); + bind(boolean, video.colorEmulation); + + bind(boolean, hacks.ppuFast.enable); + bind(boolean, hacks.ppuFast.noSpriteLimit); + bind(boolean, hacks.ppuFast.hiresMode7); + bind(boolean, hacks.dspFast.enable); + #undef bind + return true; +} + +auto Configuration::write(string name, string value) -> bool { + #define bind(type, id) { \ + string key = {string{#id}.transform(".", "/")}; \ + if(name == key) return id = Markup::Node().setValue(value).type(), true; \ + } + bind(boolean, video.blurEmulation); + bind(boolean, video.colorEmulation); + + bind(boolean, hacks.ppuFast.enable); + bind(boolean, hacks.ppuFast.noSpriteLimit); + bind(boolean, hacks.ppuFast.hiresMode7); + bind(boolean, hacks.dspFast.enable); + if(SuperFamicom::system.loaded()) return false; + + bind(natural, system.cpu.version); + bind(natural, system.ppu1.version); + bind(natural, system.ppu1.vram.size); + bind(natural, system.ppu2.version); + #undef bind + return false; +} diff --git a/higan/sfc/interface/configuration.hpp b/higan/sfc/interface/configuration.hpp new file mode 100644 index 00000000..dad1cdad --- /dev/null +++ b/higan/sfc/interface/configuration.hpp @@ -0,0 +1,39 @@ +struct Configuration { + auto read() -> string; + auto read(string) -> string; + auto write(string) -> bool; + auto write(string, string) -> bool; + + struct System { + struct CPU { + uint version = 2; + } cpu; + struct PPU1 { + uint version = 1; + struct VRAM { + uint size = 0x10000; + } vram; + } ppu1; + struct PPU2 { + uint version = 3; + } ppu2; + } system; + + struct Video { + bool blurEmulation = true; + bool colorEmulation = true; + } video; + + struct Hacks { + struct PPUFast { + bool enable = false; + bool noSpriteLimit = false; + bool hiresMode7 = false; + } ppuFast; + struct DSPFast { + bool enable = false; + } dspFast; + } hacks; +}; + +extern Configuration configuration; diff --git a/higan/sfc/interface/interface.cpp b/higan/sfc/interface/interface.cpp index 34d8979f..d9cb8b1f 100644 --- a/higan/sfc/interface/interface.cpp +++ b/higan/sfc/interface/interface.cpp @@ -3,6 +3,7 @@ namespace SuperFamicom { Settings settings; +#include "configuration.cpp" auto Interface::information() -> Information { Information information; @@ -40,7 +41,7 @@ auto Interface::color(uint32 color) -> uint64 { uint64 G = L * image::normalize(g, 5, 16); uint64 B = L * image::normalize(b, 5, 16); - if(settings.colorEmulation) { + if(SuperFamicom::configuration.video.colorEmulation) { static const uint8 gammaRamp[32] = { 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, @@ -253,60 +254,20 @@ auto Interface::cheats(const vector& list) -> void { cheat.assign(list); } -auto Interface::cap(const string& name) -> bool { - if(name == "Fast PPU") return true; - if(name == "Fast PPU/No Sprite Limit") return true; - if(name == "Fast PPU/Hires Mode 7") return true; - if(name == "Fast DSP") return true; - if(name == "Mode") return true; - if(name == "Blur Emulation") return true; - if(name == "Color Emulation") return true; - if(name == "Scanline Emulation") return true; - return false; +auto Interface::configuration() -> string { + return SuperFamicom::configuration.read(); } -auto Interface::get(const string& name) -> any { - if(name == "Fast PPU") return settings.fastPPU; - if(name == "Fast PPU/No Sprite Limit") return settings.fastPPUNoSpriteLimit; - if(name == "Fast PPU/Hires Mode 7") return settings.fastPPUHiresMode7; - if(name == "Fast DSP") return settings.fastDSP; - if(name == "Blur Emulation") return settings.blurEmulation; - if(name == "Color Emulation") return settings.colorEmulation; - if(name == "Scanline Emulation") return settings.scanlineEmulation; - return {}; +auto Interface::configuration(string name) -> string { + return SuperFamicom::configuration.read(name); } -auto Interface::set(const string& name, const any& value) -> bool { - if(name == "Fast PPU" && value.is()) { - settings.fastPPU = value.get(); - return true; - } - if(name == "Fast PPU/No Sprite Limit" && value.is()) { - settings.fastPPUNoSpriteLimit = value.get(); - return true; - } - if(name == "Fast PPU/Hires Mode 7" && value.is()) { - settings.fastPPUHiresMode7 = value.get(); - return true; - } - if(name == "Fast DSP" && value.is()) { - settings.fastDSP = value.get(); - return true; - } - if(name == "Blur Emulation" && value.is()) { - settings.blurEmulation = value.get(); - return true; - } - if(name == "Color Emulation" && value.is()) { - settings.colorEmulation = value.get(); - Emulator::video.setPalette(); - return true; - } - if(name == "Scanline Emulation" && value.is()) { - settings.scanlineEmulation = value.get(); - return true; - } - return false; +auto Interface::configure(string configuration) -> bool { + return SuperFamicom::configuration.write(configuration); +} + +auto Interface::configure(string name, string value) -> bool { + return SuperFamicom::configuration.write(name, value); } } diff --git a/higan/sfc/interface/interface.hpp b/higan/sfc/interface/interface.hpp index e3aa397a..4cd5c067 100644 --- a/higan/sfc/interface/interface.hpp +++ b/higan/sfc/interface/interface.hpp @@ -64,21 +64,15 @@ struct Interface : Emulator::Interface { auto cheats(const vector&) -> void override; - auto cap(const string& name) -> bool override; - auto get(const string& name) -> any override; - auto set(const string& name, const any& value) -> bool override; + auto configuration() -> string override; + auto configuration(string name) -> string override; + auto configure(string configuration) -> bool override; + auto configure(string name, string value) -> bool override; }; +#include "configuration.hpp" + struct Settings { - bool fastPPU = false; - bool fastPPUNoSpriteLimit = false; - bool fastPPUHiresMode7 = false; - bool fastDSP = false; - - bool blurEmulation = true; - bool colorEmulation = true; - bool scanlineEmulation = true; - uint controllerPort1 = ID::Device::Gamepad; uint controllerPort2 = ID::Device::Gamepad; uint expansionPort = ID::Device::None; diff --git a/higan/sfc/ppu-fast/line.cpp b/higan/sfc/ppu-fast/line.cpp index ad5488d1..d7b114e7 100644 --- a/higan/sfc/ppu-fast/line.cpp +++ b/higan/sfc/ppu-fast/line.cpp @@ -23,7 +23,7 @@ auto PPU::Line::render() -> void { } bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6; - bool hiresMode7 = io.bgMode == 7 && settings.fastPPUHiresMode7; + bool hiresMode7 = io.bgMode == 7 && configuration.hacks.ppuFast.hiresMode7; auto aboveColor = cgram[0]; auto belowColor = hires ? cgram[0] : io.col.fixedColor; for(uint x : range(256 << hiresMode7)) { diff --git a/higan/sfc/ppu-fast/mode7.cpp b/higan/sfc/ppu-fast/mode7.cpp index 80f35794..5f9668fe 100644 --- a/higan/sfc/ppu-fast/mode7.cpp +++ b/higan/sfc/ppu-fast/mode7.cpp @@ -25,7 +25,7 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void { renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.belowEnable, windowBelow); - if(!settings.fastPPUHiresMode7) { + if(!configuration.hacks.ppuFast.hiresMode7) { for(int X : range(256)) { int x = !io.mode7.hflip ? X : 255 - X; int pixelX = originX + a * x >> 8; diff --git a/higan/sfc/ppu-fast/ppu.cpp b/higan/sfc/ppu-fast/ppu.cpp index 41f23a7f..76a270ff 100644 --- a/higan/sfc/ppu-fast/ppu.cpp +++ b/higan/sfc/ppu-fast/ppu.cpp @@ -76,7 +76,7 @@ auto PPU::scanline() -> void { if(vcounter() > 0 && vcounter() < vdisp()) { latch.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6; - latch.hires |= io.bgMode == 7 && settings.fastPPUHiresMode7; + latch.hires |= io.bgMode == 7 && configuration.hacks.ppuFast.hiresMode7; } if(vcounter() == vdisp() && !io.displayDisable) { @@ -95,16 +95,15 @@ auto PPU::refresh() -> void { auto pitch = 512 << !interlace(); auto width = 256 << hires(); auto height = 240 << interlace(); - Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, settings.blurEmulation && hires()); + Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, configuration.video.blurEmulation && hires()); Emulator::video.refresh(output, pitch * sizeof(uint32), width, height); } -auto PPU::load(Markup::Node node) -> bool { +auto PPU::load() -> bool { return true; } auto PPU::power(bool reset) -> void { -//settings.fastPPUHiresMode7=false; create(Enter, system.cpuFrequency()); PPUcounter::reset(); memory::fill(output, 512 * 480); @@ -126,8 +125,8 @@ auto PPU::power(bool reset) -> void { io = {}; updateVideoMode(); - ItemLimit = !settings.fastPPUNoSpriteLimit ? 32 : 128; - TileLimit = !settings.fastPPUNoSpriteLimit ? 34 : 128; + ItemLimit = !configuration.hacks.ppuFast.noSpriteLimit ? 32 : 128; + TileLimit = !configuration.hacks.ppuFast.noSpriteLimit ? 34 : 128; Line::start = 0; Line::count = 0; diff --git a/higan/sfc/ppu-fast/ppu.hpp b/higan/sfc/ppu-fast/ppu.hpp index b1e8da56..c98009a7 100644 --- a/higan/sfc/ppu-fast/ppu.hpp +++ b/higan/sfc/ppu-fast/ppu.hpp @@ -23,7 +23,7 @@ struct PPU : Thread, PPUcounter { auto main() -> void; auto scanline() -> void; auto refresh() -> void; - auto load(Markup::Node) -> bool; + auto load() -> bool; auto power(bool reset) -> void; //serialization.cpp diff --git a/higan/sfc/ppu/ppu.cpp b/higan/sfc/ppu/ppu.cpp index 2c69288b..946c3192 100644 --- a/higan/sfc/ppu/ppu.cpp +++ b/higan/sfc/ppu/ppu.cpp @@ -81,15 +81,15 @@ auto PPU::main() -> void { step(lineclocks() - hcounter()); } -auto PPU::load(Markup::Node node) -> bool { +auto PPU::load() -> bool { if(system.fastPPU()) { - return ppufast.load(node); + return ppufast.load(); } - ppu1.version = max(1, min(1, node["ppu1/version"].natural())); - ppu2.version = max(1, min(3, node["ppu2/version"].natural())); - ppu.vram.mask = node["ppu1/ram/size"].natural() / sizeof(uint16) - 1; - if(ppu.vram.mask != 0xffff) ppu.vram.mask = 0x7fff; + ppu1.version = max(1, min(1, configuration.system.ppu1.version)); + ppu2.version = max(1, min(3, configuration.system.ppu2.version)); + vram.mask = configuration.system.ppu1.vram.size / sizeof(uint16) - 1; + if(vram.mask != 0xffff) vram.mask = 0x7fff; return true; } @@ -243,7 +243,7 @@ auto PPU::refresh() -> void { auto pitch = 512; auto width = 512; auto height = 480; - Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, settings.blurEmulation); + Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, configuration.video.blurEmulation); Emulator::video.refresh(output, pitch * sizeof(uint32), width, height); } diff --git a/higan/sfc/ppu/ppu.hpp b/higan/sfc/ppu/ppu.hpp index e277cdd2..21e9c8ec 100644 --- a/higan/sfc/ppu/ppu.hpp +++ b/higan/sfc/ppu/ppu.hpp @@ -9,7 +9,7 @@ struct PPU : Thread, PPUcounter { static auto Enter() -> void; auto main() -> void; - auto load(Markup::Node) -> bool; + auto load() -> bool; auto power(bool reset) -> void; //io.cpp diff --git a/higan/sfc/smp/smp.cpp b/higan/sfc/smp/smp.cpp index 3facdf76..5ae391ac 100644 --- a/higan/sfc/smp/smp.cpp +++ b/higan/sfc/smp/smp.cpp @@ -18,12 +18,10 @@ auto SMP::main() -> void { instruction(); } -auto SMP::load(Markup::Node node) -> bool { - if(auto name = node["smp/rom/name"].text()) { - if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) { - fp->read(iplrom, 64); - return true; - } +auto SMP::load() -> bool { + if(auto fp = platform->open(ID::System, "ipl.rom", File::Read, File::Required)) { + fp->read(iplrom, 64); + return true; } return false; } diff --git a/higan/sfc/smp/smp.hpp b/higan/sfc/smp/smp.hpp index a5846fb3..be742749 100644 --- a/higan/sfc/smp/smp.hpp +++ b/higan/sfc/smp/smp.hpp @@ -10,7 +10,7 @@ struct SMP : Processor::SPC700, Thread { //smp.cpp static auto Enter() -> void; auto main() -> void; - auto load(Markup::Node) -> bool; + auto load() -> bool; auto power(bool reset) -> void; //serialization.cpp diff --git a/higan/sfc/system/serialization.cpp b/higan/sfc/system/serialization.cpp index af698c6a..d3d91d08 100644 --- a/higan/sfc/system/serialization.cpp +++ b/higan/sfc/system/serialization.cpp @@ -32,11 +32,9 @@ auto System::unserialize(serializer& s) -> bool { s.boolean(hacks.fastPPU); s.boolean(hacks.fastDSP); - settings.fastPPU = hacks.fastPPU; - settings.fastDSP = hacks.fastDSP; - power(/* reset = */ false); serializeAll(s); + serializeInit(); //hacks.fastPPU setting changes serializeSize return true; } diff --git a/higan/sfc/system/system.cpp b/higan/sfc/system/system.cpp index 365a308f..bd861966 100644 --- a/higan/sfc/system/system.cpp +++ b/higan/sfc/system/system.cpp @@ -23,19 +23,14 @@ auto System::runToSave() -> void { auto System::load(Emulator::Interface* interface) -> bool { information = {}; - - if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) { - information.manifest = fp->reads(); - } else return false; - - auto document = BML::unserialize(information.manifest); - auto system = document["system"]; + hacks.fastPPU = configuration.hacks.ppuFast.enable; + hacks.fastDSP = configuration.hacks.dspFast.enable; bus.reset(); - if(!cpu.load(system)) return false; - if(!smp.load(system)) return false; - if(!ppu.load(system)) return false; - if(!dsp.load(system)) return false; + if(!cpu.load()) return false; + if(!smp.load()) return false; + if(!ppu.load()) return false; + if(!dsp.load()) return false; if(!cartridge.load()) return false; if(cartridge.region() == "NTSC") { @@ -88,9 +83,6 @@ auto System::unload() -> void { } auto System::power(bool reset) -> void { - hacks.fastPPU = settings.fastPPU; - hacks.fastDSP = settings.fastDSP; - Emulator::video.reset(interface); Emulator::video.setPalette(); diff --git a/higan/sfc/system/system.hpp b/higan/sfc/system/system.hpp index eff1a558..632a24a6 100644 --- a/higan/sfc/system/system.hpp +++ b/higan/sfc/system/system.hpp @@ -25,7 +25,6 @@ private: Emulator::Interface* interface = nullptr; struct Information { - string manifest; bool loaded = false; Region region = Region::NTSC; double cpuFrequency = Emulator::Constants::Colorburst::NTSC * 6.0; diff --git a/higan/systems/Super Famicom.sys/manifest.bml b/higan/systems/Super Famicom.sys/manifest.bml deleted file mode 100644 index 67666905..00000000 --- a/higan/systems/Super Famicom.sys/manifest.bml +++ /dev/null @@ -1,12 +0,0 @@ -system name:Super Famicom - cpu version=2 - ram name=work.ram size=0x20000 volatile - smp - rom name=ipl.rom size=64 - ppu1 version=1 - ram name=video.ram size=0x10000 volatile - ram name=object.ram size=544 volatile - ppu2 version=3 - ram name=palette.ram size=512 volatile - dsp - ram name=apu.ram size=0x10000 volatile diff --git a/higan/target-bsnes/presentation/presentation.cpp b/higan/target-bsnes/presentation/presentation.cpp index dcfe5675..40e77c6f 100644 --- a/higan/target-bsnes/presentation/presentation.cpp +++ b/higan/target-bsnes/presentation/presentation.cpp @@ -53,7 +53,7 @@ Presentation::Presentation() { }); blurEmulation.setText("Blur Emulation").setChecked(settings["View/BlurEmulation"].boolean()).onToggle([&] { settings["View/BlurEmulation"].setValue(blurEmulation.checked()); - emulator->set("Blur Emulation", blurEmulation.checked()); + emulator->configure("video/blurEmulation", blurEmulation.checked()); }).doToggle(); shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader"); synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] { diff --git a/higan/target-bsnes/program/game.cpp b/higan/target-bsnes/program/game.cpp index 9af43dc6..7e2ca5d1 100644 --- a/higan/target-bsnes/program/game.cpp +++ b/higan/target-bsnes/program/game.cpp @@ -1,5 +1,10 @@ auto Program::load() -> void { unload(); + + if(auto configuration = string::read(locate("configuration.bml"))) { + emulator->configure(configuration); + settingsWindow->advanced.updateConfiguration(); + } if(!emulator->load()) return; gameQueue = {}; @@ -282,6 +287,9 @@ auto Program::unload() -> void { if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) { saveUndoState(); } + if(auto configuration = emulator->configuration()) { + file::write(locate("configuration.bml"), configuration); + } emulator->unload(); showMessage("Game unloaded"); superFamicom = {}; diff --git a/higan/target-bsnes/program/platform.cpp b/higan/target-bsnes/program/platform.cpp index ee28e40c..23045625 100644 --- a/higan/target-bsnes/program/platform.cpp +++ b/higan/target-bsnes/program/platform.cpp @@ -11,10 +11,6 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file result; if(id == 0) { //System - if(name == "manifest.bml" && mode == vfs::file::mode::read) { - result = vfs::memory::file::open(Resource::System::Manifest.data(), Resource::System::Manifest.size()); - } - if(name == "boards.bml" && mode == vfs::file::mode::read) { result = vfs::memory::file::open(Resource::System::Boards.data(), Resource::System::Boards.size()); } diff --git a/higan/target-bsnes/program/video.cpp b/higan/target-bsnes/program/video.cpp index e5c66f2b..7e76c591 100644 --- a/higan/target-bsnes/program/video.cpp +++ b/higan/target-bsnes/program/video.cpp @@ -45,7 +45,7 @@ auto Program::updateVideoShader() -> void { } auto Program::updateVideoPalette() -> void { - emulator->set("Color Emulation", false); + emulator->configure("video/colorEmulation", false); double luminance = settings["Video/Luminance"].natural() / 100.0; double saturation = settings["Video/Saturation"].natural() / 100.0; double gamma = settings["Video/Gamma"].natural() / 100.0; diff --git a/higan/target-bsnes/resource/resource.bml b/higan/target-bsnes/resource/resource.bml index 4af26fe4..118a653b 100644 --- a/higan/target-bsnes/resource/resource.bml +++ b/higan/target-bsnes/resource/resource.bml @@ -2,6 +2,5 @@ namespace name=Resource binary name=Icon file=icon.png binary name=Logo file=logo.png namespace name=System - binary name=Manifest file="../../systems/Super Famicom.sys/manifest.bml" binary name=Boards file="../../systems/Super Famicom.sys/boards.bml" binary name=IPLROM file="../../systems/Super Famicom.sys/ipl.rom" diff --git a/higan/target-bsnes/resource/resource.cpp b/higan/target-bsnes/resource/resource.cpp index ef38ee88..5407aa7e 100644 --- a/higan/target-bsnes/resource/resource.cpp +++ b/higan/target-bsnes/resource/resource.cpp @@ -850,19 +850,6 @@ const nall::vector Logo = { //size: 23467 0,0,0,73,69,78,68,174,66,96,130, }; namespace System { -const nall::vector Manifest = { //size: 334 - 115,121,115,116,101,109,32,110,97,109,101,58,83,117,112,101,114,32,70,97,109,105,99,111,109,10,32,32,99,112,117,32, - 118,101,114,115,105,111,110,61,50,10,32,32,32,32,114,97,109,32,110,97,109,101,61,119,111,114,107,46,114,97,109,32, - 115,105,122,101,61,48,120,50,48,48,48,48,32,118,111,108,97,116,105,108,101,10,32,32,115,109,112,10,32,32,32,32, - 114,111,109,32,110,97,109,101,61,105,112,108,46,114,111,109,32,115,105,122,101,61,54,52,10,32,32,112,112,117,49,32, - 118,101,114,115,105,111,110,61,49,10,32,32,32,32,114,97,109,32,110,97,109,101,61,118,105,100,101,111,46,114,97,109, - 32,115,105,122,101,61,48,120,49,48,48,48,48,32,118,111,108,97,116,105,108,101,10,32,32,32,32,114,97,109,32,110, - 97,109,101,61,111,98,106,101,99,116,46,114,97,109,32,115,105,122,101,61,53,52,52,32,118,111,108,97,116,105,108,101, - 10,32,32,112,112,117,50,32,118,101,114,115,105,111,110,61,51,10,32,32,32,32,114,97,109,32,110,97,109,101,61,112, - 97,108,101,116,116,101,46,114,97,109,32,115,105,122,101,61,53,49,50,32,118,111,108,97,116,105,108,101,10,32,32,100, - 115,112,10,32,32,32,32,114,97,109,32,110,97,109,101,61,97,112,117,46,114,97,109,32,115,105,122,101,61,48,120,49, - 48,48,48,48,32,118,111,108,97,116,105,108,101,10, -}; const nall::vector Boards = { //size: 30182 100,97,116,97,98,97,115,101,10,32,32,114,101,118,105,115,105,111,110,58,32,50,48,49,56,45,48,55,45,50,53,10, 10,47,47,66,111,97,114,100,115,32,40,80,114,111,100,117,99,116,105,111,110,41,10,10,100,97,116,97,98,97,115,101, diff --git a/higan/target-bsnes/resource/resource.hpp b/higan/target-bsnes/resource/resource.hpp index 15c7d2ae..c671bfc1 100644 --- a/higan/target-bsnes/resource/resource.hpp +++ b/higan/target-bsnes/resource/resource.hpp @@ -2,7 +2,6 @@ namespace Resource { extern const nall::vector Icon; extern const nall::vector Logo; namespace System { -extern const nall::vector Manifest; extern const nall::vector Boards; extern const nall::vector IPLROM; } diff --git a/higan/target-bsnes/settings/advanced.cpp b/higan/target-bsnes/settings/advanced.cpp index 89ac2029..511dab4b 100644 --- a/higan/target-bsnes/settings/advanced.cpp +++ b/higan/target-bsnes/settings/advanced.cpp @@ -97,7 +97,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) { }); hiresMode7.setText("Hires mode 7").setChecked(settings["Emulator/Hack/FastPPU/HiresMode7"].boolean()).onToggle([&] { settings["Emulator/Hack/FastPPU/HiresMode7"].setValue(hiresMode7.checked()); - emulator->set("Fast PPU/Hires Mode 7", hiresMode7.checked()); }); fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/Hack/FastDSP"].boolean()).onToggle([&] { settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked()); @@ -140,3 +139,10 @@ auto AdvancedSettings::updateInputDriver() -> void { if(input && input->driver() == driver) item.setSelected(); } } + +auto AdvancedSettings::updateConfiguration() -> void { + emulator->configure("hacks/ppuFast/enable", fastPPUOption.checked()); + emulator->configure("hacks/ppuFast/noSpriteLimit", noSpriteLimit.checked()); + emulator->configure("hacks/ppuFast/hiresMode7", hiresMode7.checked()); + emulator->configure("hacks/dspFast/enable", fastDSPOption.checked()); +} diff --git a/higan/target-bsnes/settings/settings.cpp b/higan/target-bsnes/settings/settings.cpp index 0157c336..bb26b5f7 100644 --- a/higan/target-bsnes/settings/settings.cpp +++ b/higan/target-bsnes/settings/settings.cpp @@ -34,7 +34,6 @@ Settings::Settings() { set("Audio/Skew", "0"); set("Audio/Volume", "100%"); set("Audio/Balance", "50%"); - set("Audio/Reverb", false); set("Input/Driver", Input::safestDriver()); set("Input/Frequency", 5); diff --git a/higan/target-bsnes/settings/settings.hpp b/higan/target-bsnes/settings/settings.hpp index 428e592c..d368cc11 100644 --- a/higan/target-bsnes/settings/settings.hpp +++ b/higan/target-bsnes/settings/settings.hpp @@ -157,6 +157,7 @@ struct AdvancedSettings : TabFrameItem { auto updateVideoDriver() -> void; auto updateAudioDriver() -> void; auto updateInputDriver() -> void; + auto updateConfiguration() -> void; public: VerticalLayout layout{this}; diff --git a/higan/target-higan/program/game.cpp b/higan/target-higan/program/game.cpp index 12510f8e..0ad55222 100644 --- a/higan/target-higan/program/game.cpp +++ b/higan/target-higan/program/game.cpp @@ -19,6 +19,14 @@ auto Program::load(Emulator::Interface& interface) -> void { gamePaths.append(locate({"systems/", information.name, ".sys/"})); inputManager->bind(emulator = &interface); + + if(auto configuration = string::read({gamePaths[0], "configuration.bml"})) { + emulator->configure(configuration); + } + if(auto configuration = emulator->configuration()) { + file::write({gamePaths[0], "configuration.bml"}, configuration); + } + presentation->updateEmulatorMenu(); if(!emulator->load()) { emulator = nullptr;