From 876b4be1d2cbf8bac125d67baf852fc2d35a1c5e Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 26 Jul 2018 20:36:43 +1000 Subject: [PATCH] Update to 20180726 release. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit byuu says: Once again, I wasn't able to complete a full WIP revision. This WIP-WIP adds very sophisticated emulation of the Sega Genesis Lock-On and Game Genie cartridges ... essentially, through recursion and a linked list, higan supports an infinite nesting of cartridges. Of course, on real hardware, after you stack more than three or four cartridges, the power draw gets too high and things start glitching out more and more as you keep stacking. I've heard that someone chained up to ten Sonic & Knuckles cartridges before it finally became completely unplayable. And so of course, higan emulates this limitation as well ^-^. On the fourth cartridge and beyond, it will become more and more likely that address and/or data lines "glitch" out randomly, causing various glitches. It's a completely silly easter egg that requires no speed impact whatsoever beyond the impact of the new linked list cartridge system. I also designed the successor to Emulator::Interface::cap,get,set. Those were holdovers from the older, since-removed ruby-style accessors. In its place is the new Emulator::Interface::configuration,configure API. There's the usual per-property access, and there's also access to read and write all configurable options at once. In essence, this enables introspection into core-specific features. So far, you can control processor version#s, PPU VRAM size, video settings, and hacks. As such, the .sys/manifest.bml files are no longer necessary. Instead, it all goes into .sys/configuration.bml, which is generated by the emulator if it's missing. higan is going to take this even further and allow each option under "Systems" to have its own editable configuration file. So if you wanted, you could have a 1/1/1 SNES menu option, and a 2/1/3 SNES menu option. Or a Model 1 Genesis option, and a Model 2 Genesis option. Or the various Game Boy model revisions. Or an "SNES-Fast" and "SNES-Accurate" option. I've not fully settled on the syntax of the new configuration API. I feel it might be useful to provide type information, but I really quite passionately hate any container objects. For now it's all string-based, because strings can hold anything in nall. I might also change the access rules. Right now it's like: emulator→configure("video/blurEmulation", true); but it might be nicer as "Video::Blur Emulation", or "Video.BlurEmulation", or something like that. --- higan/emulator/emulator.hpp | 2 +- higan/emulator/interface.hpp | 6 + higan/md/cartridge/cartridge.cpp | 351 +++++++++--------- higan/md/cartridge/cartridge.hpp | 90 +++-- higan/md/cartridge/serialization.cpp | 7 +- higan/md/md.hpp | 3 + higan/md/system/system.cpp | 3 + higan/sfc/cpu/cpu.cpp | 4 +- higan/sfc/cpu/cpu.hpp | 2 +- higan/sfc/dsp/dsp.cpp | 2 +- higan/sfc/dsp/dsp.hpp | 2 +- higan/sfc/interface/configuration.cpp | 92 +++++ higan/sfc/interface/configuration.hpp | 39 ++ higan/sfc/interface/interface.cpp | 63 +--- higan/sfc/interface/interface.hpp | 18 +- higan/sfc/ppu-fast/line.cpp | 2 +- higan/sfc/ppu-fast/mode7.cpp | 2 +- higan/sfc/ppu-fast/ppu.cpp | 11 +- higan/sfc/ppu-fast/ppu.hpp | 2 +- higan/sfc/ppu/ppu.cpp | 14 +- higan/sfc/ppu/ppu.hpp | 2 +- higan/sfc/smp/smp.cpp | 10 +- higan/sfc/smp/smp.hpp | 2 +- higan/sfc/system/serialization.cpp | 4 +- higan/sfc/system/system.cpp | 20 +- higan/sfc/system/system.hpp | 1 - higan/systems/Super Famicom.sys/manifest.bml | 12 - .../presentation/presentation.cpp | 2 +- higan/target-bsnes/program/game.cpp | 8 + higan/target-bsnes/program/platform.cpp | 4 - higan/target-bsnes/program/video.cpp | 2 +- higan/target-bsnes/resource/resource.bml | 1 - higan/target-bsnes/resource/resource.cpp | 13 - higan/target-bsnes/resource/resource.hpp | 1 - higan/target-bsnes/settings/advanced.cpp | 8 +- higan/target-bsnes/settings/settings.cpp | 1 - higan/target-bsnes/settings/settings.hpp | 1 + higan/target-higan/program/game.cpp | 8 + 38 files changed, 461 insertions(+), 354 deletions(-) create mode 100644 higan/sfc/interface/configuration.cpp create mode 100644 higan/sfc/interface/configuration.hpp delete mode 100644 higan/systems/Super Famicom.sys/manifest.bml 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;