From 2bb1606552ef6b3e5f4b5dd575c2cf0e9100aa11 Mon Sep 17 00:00:00 2001 From: byuu <2107894+byuu@users.noreply.github.com> Date: Tue, 3 Sep 2019 12:01:45 +0900 Subject: [PATCH] v108.16 Added compatibility option to disable accurate CPU ALU emulation Refactored settings panels Added dialog to choose whether IPS patches are for headered ROMs Disabled extended SNES header decoding thanks to ROM hacks ignoring them --- bsnes/emulator/emulator.hpp | 2 +- bsnes/heuristics/super-famicom.cpp | 5 + bsnes/sfc/cpu/cpu.cpp | 12 +- bsnes/sfc/cpu/io.cpp | 22 +- bsnes/sfc/dsp/SPC_DSP.cpp | 2 +- bsnes/sfc/interface/configuration.cpp | 2 + bsnes/sfc/interface/configuration.hpp | 2 + .../presentation/presentation.cpp | 31 +-- .../presentation/presentation.hpp | 5 +- bsnes/target-bsnes/program/game.cpp | 7 +- bsnes/target-bsnes/program/hacks.cpp | 8 +- bsnes/target-bsnes/program/patch.cpp | 13 +- bsnes/target-bsnes/program/program.cpp | 3 +- bsnes/target-bsnes/settings/compatibility.cpp | 60 ++++++ bsnes/target-bsnes/settings/emulator.cpp | 195 +++++++++--------- bsnes/target-bsnes/settings/enhancements.cpp | 117 +++++++++++ bsnes/target-bsnes/settings/settings.cpp | 26 ++- bsnes/target-bsnes/settings/settings.hpp | 123 +++++++---- bsnes/target-bsnes/settings/speed.cpp | 133 ------------ bsnes/target-bsnes/tools/tools.cpp | 2 +- ruby/GNUmakefile | 2 +- 21 files changed, 446 insertions(+), 326 deletions(-) create mode 100644 bsnes/target-bsnes/settings/compatibility.cpp create mode 100644 bsnes/target-bsnes/settings/enhancements.cpp delete mode 100644 bsnes/target-bsnes/settings/speed.cpp diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index c14f255d..d38282c1 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -29,7 +29,7 @@ using namespace nall; namespace Emulator { static const string Name = "bsnes"; - static const string Version = "108.15"; + static const string Version = "108.16"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; diff --git a/bsnes/heuristics/super-famicom.cpp b/bsnes/heuristics/super-famicom.cpp index 375edf54..1bc29cb1 100644 --- a/bsnes/heuristics/super-famicom.cpp +++ b/bsnes/heuristics/super-famicom.cpp @@ -140,6 +140,11 @@ auto SuperFamicom::manifest() const -> string { } auto SuperFamicom::region() const -> string { + //Unlicensed software (homebrew, ROM hacks, etc) often change the standard region code, + //and then neglect to change the extended header region code. Thanks to that, we can't + //decode and display the full game serial + region code. + return videoRegion(); + string region; char A = data[headerAddress + 0x02]; //game type diff --git a/bsnes/sfc/cpu/cpu.cpp b/bsnes/sfc/cpu/cpu.cpp index af9871e6..782c1d57 100644 --- a/bsnes/sfc/cpu/cpu.cpp +++ b/bsnes/sfc/cpu/cpu.cpp @@ -96,11 +96,13 @@ auto CPU::power(bool reset) -> void { if(!reset) random.array(wram, sizeof(wram)); - //Dirt Racer (Europe) relies on uninitialized memory containing certain values to boot without freezing. - //the game itself is broken and will fail to run sometimes on real hardware, but for the sake of expedience, - //WRAM is initialized to a constant value that will allow this game to always boot in successfully. - if(cartridge.headerTitle() == "DIRT RACER") { - for(auto& byte : wram) byte = 0xff; + if(configuration.hacks.hotfixes) { + //Dirt Racer (Europe) relies on uninitialized memory containing certain values to boot without freezing. + //the game itself is broken and will fail to run sometimes on real hardware, but for the sake of expedience, + //WRAM is initialized to a constant value that will allow this game to always boot in successfully. + if(cartridge.headerTitle() == "DIRT RACER") { + for(auto& byte : wram) byte = 0xff; + } } for(uint n : range(8)) { diff --git a/bsnes/sfc/cpu/io.cpp b/bsnes/sfc/cpu/io.cpp index ce16bbfb..25c32489 100644 --- a/bsnes/sfc/cpu/io.cpp +++ b/bsnes/sfc/cpu/io.cpp @@ -151,8 +151,12 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void { io.wrmpyb = data; io.rddiv = io.wrmpyb << 8 | io.wrmpya; - alu.mpyctr = 8; //perform multiplication over the next eight cycles - alu.shift = io.wrmpyb; + if(!configuration.hacks.cpu.fastMath) { + alu.mpyctr = 8; //perform multiplication over the next eight cycles + alu.shift = io.wrmpyb; + } else { + io.rdmpy = io.wrmpya * io.wrmpyb; + } return; case 0x4204: //WRDIVL @@ -169,8 +173,18 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void { io.wrdivb = data; - alu.divctr = 16; //perform division over the next sixteen cycles - alu.shift = io.wrdivb << 16; + if(!configuration.hacks.cpu.fastMath) { + alu.divctr = 16; //perform division over the next sixteen cycles + alu.shift = io.wrdivb << 16; + } else { + if(io.wrdivb) { + io.rddiv = io.wrdiva / io.wrdivb; + io.rdmpy = io.wrdiva % io.wrdivb; + } else { + io.rddiv = 0xffff; + io.rdmpy = io.wrdiva; + } + } return; case 0x4207: //HTIMEL diff --git a/bsnes/sfc/dsp/SPC_DSP.cpp b/bsnes/sfc/dsp/SPC_DSP.cpp index ab6dc826..a89d491f 100644 --- a/bsnes/sfc/dsp/SPC_DSP.cpp +++ b/bsnes/sfc/dsp/SPC_DSP.cpp @@ -838,7 +838,7 @@ void SPC_DSP::run( int clocks_remain ) void SPC_DSP::init( void* ram_64k, void* echo_64k ) { m.ram = (uint8_t*) ram_64k; - m.echo = (uint8_t*) echo_64k; + m.echo = (uint8_t*) echo_64k; mute_voices( 0 ); disable_surround( false ); set_output( 0, 0 ); diff --git a/bsnes/sfc/interface/configuration.cpp b/bsnes/sfc/interface/configuration.cpp index c68d865c..dbbe8056 100644 --- a/bsnes/sfc/interface/configuration.cpp +++ b/bsnes/sfc/interface/configuration.cpp @@ -16,8 +16,10 @@ auto Configuration::process(Markup::Node document, bool load) -> void { bind(boolean, "Video/BlurEmulation", video.blurEmulation); bind(boolean, "Video/ColorEmulation", video.colorEmulation); + bind(boolean, "Hacks/Hotfixes", hacks.hotfixes); bind(text, "Hacks/Entropy", hacks.entropy); bind(natural, "Hacks/CPU/Overclock", hacks.cpu.overclock); + bind(boolean, "Hacks/CPU/FastMath", hacks.cpu.fastMath); bind(boolean, "Hacks/PPU/Fast", hacks.ppu.fast); bind(boolean, "Hacks/PPU/Deinterlace", hacks.ppu.deinterlace); bind(natural, "Hacks/PPU/RenderCycle", hacks.ppu.renderCycle); diff --git a/bsnes/sfc/interface/configuration.hpp b/bsnes/sfc/interface/configuration.hpp index 577a7040..4092d1a0 100644 --- a/bsnes/sfc/interface/configuration.hpp +++ b/bsnes/sfc/interface/configuration.hpp @@ -25,9 +25,11 @@ struct Configuration { } video; struct Hacks { + bool hotfixes = true; string entropy = "Low"; struct CPU { uint overclock = 100; + bool fastMath = false; } cpu; struct PPU { bool fast = true; diff --git a/bsnes/target-bsnes/presentation/presentation.cpp b/bsnes/target-bsnes/presentation/presentation.cpp index 0996b143..4fcd6d41 100644 --- a/bsnes/target-bsnes/presentation/presentation.cpp +++ b/bsnes/target-bsnes/presentation/presentation.cpp @@ -106,9 +106,10 @@ auto Presentation::create() -> void { inputSettings.setIcon(Icon::Device::Joypad).setText("Input ...").onActivate([&] { settingsWindow.show(2); }); hotkeySettings.setIcon(Icon::Device::Keyboard).setText("Hotkeys ...").onActivate([&] { settingsWindow.show(3); }); pathSettings.setIcon(Icon::Emblem::Folder).setText("Paths ...").onActivate([&] { settingsWindow.show(4); }); - speedSettings.setIcon(Icon::Device::Clock).setText("Speed ...").onActivate([&] { settingsWindow.show(5); }); - emulatorSettings.setIcon(Icon::Action::Settings).setText("Emulator ...").onActivate([&] { settingsWindow.show(6); }); - driverSettings.setIcon(Icon::Place::Settings).setText("Drivers ...").onActivate([&] { settingsWindow.show(7); }); + emulatorSettings.setIcon(Icon::Action::Settings).setText("Emulator ...").onActivate([&] { settingsWindow.show(5); }); + enhancementSettings.setIcon(Icon::Action::Add).setText("Enhancements ...").onActivate([&] { settingsWindow.show(6); }); + compatibilitySettings.setIcon(Icon::Action::Remove).setText("Compatibility ...").onActivate([&] { settingsWindow.show(7); }); + driverSettings.setIcon(Icon::Place::Settings).setText("Drivers ...").onActivate([&] { settingsWindow.show(8); }); toolsMenu.setText(tr("Tools")).setVisible(false); saveState.setIcon(Icon::Media::Record).setText("Save State"); @@ -180,18 +181,6 @@ auto Presentation::create() -> void { documentation.setIcon(Icon::Application::Browser).setText({tr("Documentation"), " ..."}).onActivate([&] { invoke("https://doc.byuu.org/bsnes"); }); - about.setIcon(Icon::Prompt::Question).setText({tr("About bsnes"), " ..."}).onActivate([&] { - AboutDialog() - .setName(Emulator::Name) - .setLogo(Resource::Logo) - .setDescription("Super Nintendo emulator") - .setVersion(Emulator::Version) - .setAuthor("byuu") - .setLicense("GPLv3") - .setWebsite("https://byuu.org") - .setAlignment(*this) - .show(); - }); aboutSameBoy.setIcon(Icon::Prompt::Question).setText({tr("About SameBoy"), " ..."}).onActivate([&] { AboutDialog() .setName("SameBoy") @@ -204,6 +193,18 @@ auto Presentation::create() -> void { .setAlignment(*this) .show(); }); + about.setIcon(Icon::Prompt::Question).setText({tr("About bsnes"), " ..."}).onActivate([&] { + AboutDialog() + .setName(Emulator::Name) + .setLogo(Resource::Logo) + .setDescription("Super Nintendo emulator") + .setVersion(Emulator::Version) + .setAuthor("byuu") + .setLicense("GPLv3") + .setWebsite("https://byuu.org") + .setAlignment(*this) + .show(); + }); viewport.setFocusable(); viewport.setDroppable(); diff --git a/bsnes/target-bsnes/presentation/presentation.hpp b/bsnes/target-bsnes/presentation/presentation.hpp index 8c5e888c..0210af16 100644 --- a/bsnes/target-bsnes/presentation/presentation.hpp +++ b/bsnes/target-bsnes/presentation/presentation.hpp @@ -85,8 +85,9 @@ struct Presentation : Window { MenuItem inputSettings{&settingsMenu}; MenuItem hotkeySettings{&settingsMenu}; MenuItem pathSettings{&settingsMenu}; - MenuItem speedSettings{&settingsMenu}; MenuItem emulatorSettings{&settingsMenu}; + MenuItem enhancementSettings{&settingsMenu}; + MenuItem compatibilitySettings{&settingsMenu}; MenuItem driverSettings{&settingsMenu}; Menu toolsMenu{&menuBar}; Menu saveState{&toolsMenu}; @@ -118,8 +119,8 @@ struct Presentation : Window { Menu helpMenu{&menuBar}; MenuItem documentation{&helpMenu}; MenuSeparator helpSeparator{&helpMenu}; - MenuItem about{&helpMenu}; MenuItem aboutSameBoy{&helpMenu}; + MenuItem about{&helpMenu}; VerticalLayout layout{this}; HorizontalLayout viewportLayout{&layout, Size{~0, ~0}, 0}; diff --git a/bsnes/target-bsnes/program/game.cpp b/bsnes/target-bsnes/program/game.cpp index 0ca34a7c..928d412a 100644 --- a/bsnes/target-bsnes/program/game.cpp +++ b/bsnes/target-bsnes/program/game.cpp @@ -1,8 +1,10 @@ auto Program::load() -> void { unload(); + emulator->configure("Hacks/Hotfixes", settings.emulator.hack.hotfixes); emulator->configure("Hacks/Entropy", settings.emulator.hack.entropy); emulator->configure("Hacks/CPU/Overclock", settings.emulator.hack.cpu.overclock); + emulator->configure("Hacks/CPU/FastMath", settings.emulator.hack.cpu.fastMath); emulator->configure("Hacks/PPU/Fast", settings.emulator.hack.ppu.fast); emulator->configure("Hacks/PPU/Deinterlace", settings.emulator.hack.ppu.deinterlace); emulator->configure("Hacks/PPU/NoSpriteLimit", settings.emulator.hack.ppu.noSpriteLimit); @@ -115,14 +117,13 @@ auto Program::loadSuperFamicom(string location) -> bool { } if(rom.size() < 0x8000) return false; - //assume ROM and IPS agree on whether a copier header is present - superFamicom.patched = applyPatchIPS(rom, location); if((rom.size() & 0x7fff) == 512) { //remove copier header memory::move(&rom[0], &rom[512], rom.size() - 512); rom.resize(rom.size() - 512); } - //assume BPS is made against a ROM without a copier header + + if(!superFamicom.patched) superFamicom.patched = applyPatchIPS(rom, location); if(!superFamicom.patched) superFamicom.patched = applyPatchBPS(rom, location); auto heuristics = Heuristics::SuperFamicom(rom, location); auto sha256 = Hash::SHA256(rom).digest(); diff --git a/bsnes/target-bsnes/program/hacks.cpp b/bsnes/target-bsnes/program/hacks.cpp index 5defba93..4c5f4e7c 100644 --- a/bsnes/target-bsnes/program/hacks.cpp +++ b/bsnes/target-bsnes/program/hacks.cpp @@ -1,8 +1,8 @@ auto Program::hackCompatibility() -> void { - bool fastPPU = emulatorSettings.fastPPU.checked(); - bool fastPPUNoSpriteLimit = emulatorSettings.noSpriteLimit.checked(); - bool fastDSP = emulatorSettings.fastDSP.checked(); - bool coprocessorDelayedSync = emulatorSettings.coprocessorDelayedSyncOption.checked(); + bool fastPPU = settings.emulator.hack.ppu.fast; + bool fastPPUNoSpriteLimit = settings.emulator.hack.ppu.noSpriteLimit; + bool fastDSP = settings.emulator.hack.dsp.fast; + bool coprocessorDelayedSync = settings.emulator.hack.coprocessor.delayedSync; uint renderCycle = 512; auto title = superFamicom.title; diff --git a/bsnes/target-bsnes/program/patch.cpp b/bsnes/target-bsnes/program/patch.cpp index 9695e0df..493e30bf 100644 --- a/bsnes/target-bsnes/program/patch.cpp +++ b/bsnes/target-bsnes/program/patch.cpp @@ -29,6 +29,15 @@ auto Program::applyPatchIPS(vector& data, string location) -> bool { } if(!patch) return false; + bool headered = false; + if(MessageDialog().setAlignment(*presentation).setTitle({Location::prefix(location), ".ips"}).setText({ + "(You're seeing this prompt because IPS is a terrible patch file format,\n" + " and nobody can agree on whether SNES ROMs should be headered or not.\n" + " Please consider asking the patch author to use BPS patches instead.)\n\n" + "Does this IPS patch expect to be applied to a headered ROM?\n" + "If you're not sure, try 'No', and if it fails to work, try again with 'Yes'." + }).question() == "Yes") headered = true; + //sanity checks if(patch.size() < 8) return false; if(patch[0] != 'P') return false; @@ -57,10 +66,12 @@ auto Program::applyPatchIPS(vector& data, string location) -> bool { if(index >= patch.size()) break; - uint32_t offset = 0; + int32_t offset = 0; offset |= patch(index++, 0) << 16; offset |= patch(index++, 0) << 8; offset |= patch(index++, 0) << 0; + if(headered) offset -= 512; + if(offset < 0 || offset > 16_MiB) return false; //sanity check uint16_t length = 0; length |= patch(index++, 0) << 8; diff --git a/bsnes/target-bsnes/program/program.cpp b/bsnes/target-bsnes/program/program.cpp index 64e9307c..9bf9220b 100644 --- a/bsnes/target-bsnes/program/program.cpp +++ b/bsnes/target-bsnes/program/program.cpp @@ -30,8 +30,9 @@ auto Program::create() -> void { inputSettings.create(); hotkeySettings.create(); pathSettings.create(); - speedSettings.create(); emulatorSettings.create(); + enhancementSettings.create(); + compatibilitySettings.create(); driverSettings.create(); toolsWindow.create(); diff --git a/bsnes/target-bsnes/settings/compatibility.cpp b/bsnes/target-bsnes/settings/compatibility.cpp new file mode 100644 index 00000000..6fdb0d33 --- /dev/null +++ b/bsnes/target-bsnes/settings/compatibility.cpp @@ -0,0 +1,60 @@ +auto CompatibilitySettings::create() -> void { + setCollapsible(); + setVisible(false); + + entropyLabel.setText("Entropy (randomization)").setFont(Font().setBold()); + entropyNone.setText("None").setToolTip( + "All memory and registers are initialized to constant values at startup.\n" + "Use this for compatibility with very old demoscene homebrew games." + ).onActivate([&] { + settings.emulator.hack.entropy = "None"; + }); + entropyLow.setText("Low").setToolTip( + "All memory is randomized with repeating patterns, all registers are randomized at startup.\n" + "Use this for the most accurate representation of a real SNES." + ).onActivate([&] { + settings.emulator.hack.entropy = "Low"; + }); + entropyHigh.setText("High").setToolTip( + "All memory and registers are randomized as much as possible.\n" + "Use this when developing new SNES software to ensure maximum compatibility with real hardware." + ).onActivate([&] { + settings.emulator.hack.entropy = "High"; + }); + if(settings.emulator.hack.entropy == "None") entropyNone.setChecked(); + if(settings.emulator.hack.entropy == "Low" ) entropyLow.setChecked(); + if(settings.emulator.hack.entropy == "High") entropyHigh.setChecked(); + + cpuLabel.setFont(Font().setBold()).setText("CPU (processor)"); + fastMath.setText("Fast math").setToolTip( + "CPU multiplication and division take time to complete on a real SNES.\n" + "Older emulators did not simulate these delays, and provided results immediately.\n" + "Some older ROM hacks do not wait for math operations to complete and need this hack." + ).setChecked(settings.emulator.hack.cpu.fastMath).onToggle([&] { + settings.emulator.hack.cpu.fastMath = fastMath.checked(); + emulator->configure("Hacks/CPU/FastMath", settings.emulator.hack.cpu.fastMath); + }); + + ppuLabel.setFont(Font().setBold()).setText("PPU (video)"); + noVRAMBlocking.setText("No VRAM blocking").setToolTip( + "This option emulates a bug in older releases of ZSNES and Snes9X where VRAM blocking was not emulated.\n" + "A few older ROM hacks relied on this behavior, and will render graphics incorrectly if not enabled.\n" + "Not only is this extremely inaccurate to real hardware, it also hurts the speed of the fast PPU.\n" + "Do not enable this option unless you need to play a game that is incompatible with bsnes otherwise." + ).setChecked(settings.emulator.hack.ppu.noVRAMBlocking).onToggle([&] { + settings.emulator.hack.ppu.noVRAMBlocking = noVRAMBlocking.checked(); + emulator->configure("Hacks/PPU/NoVRAMBlocking", settings.emulator.hack.ppu.noVRAMBlocking); + }); + + dspLabel.setFont(Font().setBold()).setText("DSP (audio)"); + echoShadow.setText("Echo shadow RAM").setToolTip( + "This option emulates a bug in ZSNES where echo RAM was treated as separate from APU RAM.\n" + "Many older ROM hacks for Super Mario World relied on this behavior, and will crash without enabling this.\n" + "It is, however, extremely inaccurate to real hardware and should not be enabled unless required." + ).setChecked(settings.emulator.hack.dsp.echoShadow).onToggle([&] { + settings.emulator.hack.dsp.echoShadow = echoShadow.checked(); + //not a run-time setting: do not call emulator->configure() here. + }); + + note.setText("Note: some settings do not take effect until after reloading games."); +} diff --git a/bsnes/target-bsnes/settings/emulator.cpp b/bsnes/target-bsnes/settings/emulator.cpp index 7c1bd69d..f1630c3d 100644 --- a/bsnes/target-bsnes/settings/emulator.cpp +++ b/bsnes/target-bsnes/settings/emulator.cpp @@ -22,114 +22,107 @@ auto EmulatorSettings::create() -> void { }); optionsSpacer.setColor({192, 192, 192}); - entropyLabel.setText("Entropy (randomness)").setFont(Font().setBold()); - entropyNone.setText("None").setToolTip( - "All memory and registers are initialized to constant values at startup.\n" - "Use this for compatibility with very old demoscene homebrew games." - ).onActivate([&] { - settings.emulator.hack.entropy = "None"; - }); - entropyLow.setText("Low").setToolTip( - "All memory is randomized with repeating patterns, all registers are randomized at startup.\n" - "Use this for the most accurate representation of a real SNES." - ).onActivate([&] { - settings.emulator.hack.entropy = "Low"; - }); - entropyHigh.setText("High").setToolTip( - "All memory and registers are randomized as much as possible.\n" - "Use this when developing new SNES software to ensure maximum compatibility with real hardware." - ).onActivate([&] { - settings.emulator.hack.entropy = "High"; - }); - if(settings.emulator.hack.entropy == "None") entropyNone.setChecked(); - if(settings.emulator.hack.entropy == "Low") entropyLow.setChecked(); - if(settings.emulator.hack.entropy == "High") entropyHigh.setChecked(); + fastForwardLabel.setText("Fast Forward").setFont(Font().setBold()); - ppuLabel.setText("PPU (video)").setFont(Font().setBold()); - fastPPU.setText("Fast mode").setChecked(settings.emulator.hack.ppu.fast).onToggle([&] { - settings.emulator.hack.ppu.fast = fastPPU.checked(); - if(!fastPPU.checked()) { - noSpriteLimit.setEnabled(false); - deinterlace.setEnabled(false); - mode7Layout.setEnabled(false); - } else { - noSpriteLimit.setEnabled(true); - deinterlace.setEnabled(true); - mode7Layout.setEnabled(true); - } - }).doToggle(); - deinterlace.setText("Deinterlace").setChecked(settings.emulator.hack.ppu.deinterlace).onToggle([&] { - settings.emulator.hack.ppu.deinterlace = deinterlace.checked(); - emulator->configure("Hacks/PPU/Deinterlace", settings.emulator.hack.ppu.deinterlace); + frameSkipLabel.setText("Frame skip:").setToolTip({ + "Set how many frames to skip while fast forwarding.\n" + "Frame skipping allows a higher maximum fast forwarding frame rate." }); - noSpriteLimit.setText("No sprite limit").setChecked(settings.emulator.hack.ppu.noSpriteLimit).onToggle([&] { - settings.emulator.hack.ppu.noSpriteLimit = noSpriteLimit.checked(); + + frameSkipAmount.append(ComboButtonItem().setText("None")); + frameSkipAmount.append(ComboButtonItem().setText("1 frame")); + frameSkipAmount.append(ComboButtonItem().setText("2 frames")); + frameSkipAmount.append(ComboButtonItem().setText("3 frames")); + frameSkipAmount.append(ComboButtonItem().setText("4 frames")); + frameSkipAmount.append(ComboButtonItem().setText("5 frames")); + frameSkipAmount.append(ComboButtonItem().setText("6 frames")); + frameSkipAmount.append(ComboButtonItem().setText("7 frames")); + frameSkipAmount.append(ComboButtonItem().setText("8 frames")); + frameSkipAmount.append(ComboButtonItem().setText("9 frames")); + frameSkipAmount.item(settings.fastForward.frameSkip).setSelected(); + frameSkipAmount.onChange([&] { + settings.fastForward.frameSkip = frameSkipAmount.selected().offset(); }); - noVRAMBlocking.setText("No VRAM blocking").setToolTip( - "This option emulates a bug in older releases of ZSNES and Snes9X where VRAM blocking was not emulated.\n" - "A few older ROM hacks relied on this behavior, and will render graphics incorrectly if not enabled.\n" - "Not only is this extremely inaccurate to real hardware, it also hurts the speed of the fast PPU.\n" - "Do not enable this option unless you need to play a game that is incompatible with bsnes otherwise." - ).setChecked(settings.emulator.hack.ppu.noVRAMBlocking).onToggle([&] { - settings.emulator.hack.ppu.noVRAMBlocking = noVRAMBlocking.checked(); - emulator->configure("Hacks/PPU/NoVRAMBlocking", settings.emulator.hack.ppu.noVRAMBlocking); + + limiterLabel.setText("Limiter:").setToolTip({ + "Set the maximum speed when fast forwarding." }); - mode7Label.setText("HD Mode 7 (fast PPU only)").setFont(Font().setBold()); - mode7ScaleLabel.setText("Scale:"); - mode7Scale.append(ComboButtonItem().setText( "240p").setProperty("multiplier", 1)); - mode7Scale.append(ComboButtonItem().setText( "480p").setProperty("multiplier", 2)); - mode7Scale.append(ComboButtonItem().setText( "720p").setProperty("multiplier", 3)); - mode7Scale.append(ComboButtonItem().setText( "960p").setProperty("multiplier", 4)); - mode7Scale.append(ComboButtonItem().setText("1200p").setProperty("multiplier", 5)); - mode7Scale.append(ComboButtonItem().setText("1440p").setProperty("multiplier", 6)); - mode7Scale.append(ComboButtonItem().setText("1680p").setProperty("multiplier", 7)); - mode7Scale.append(ComboButtonItem().setText("1920p").setProperty("multiplier", 8)); - mode7Scale.append(ComboButtonItem().setText("2160p").setProperty("multiplier", 9)); - for(uint n = 1; n <= 9; n++) { - if(settings.emulator.hack.ppu.mode7.scale == n) mode7Scale.item(n - 1).setSelected(); - } - mode7Scale.onChange([&] { - settings.emulator.hack.ppu.mode7.scale = mode7Scale.selected().property("multiplier").natural(); - emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale); + + limiterAmount.append(ComboButtonItem().setText("None")); + limiterAmount.append(ComboButtonItem().setText("200%")); + limiterAmount.append(ComboButtonItem().setText("300%")); + limiterAmount.append(ComboButtonItem().setText("400%")); + limiterAmount.append(ComboButtonItem().setText("500%")); + limiterAmount.append(ComboButtonItem().setText("600%")); + limiterAmount.append(ComboButtonItem().setText("700%")); + limiterAmount.append(ComboButtonItem().setText("800%")); + if(settings.fastForward.limiter == 0) limiterAmount.item(0).setSelected(); + if(settings.fastForward.limiter == 2) limiterAmount.item(1).setSelected(); + if(settings.fastForward.limiter == 3) limiterAmount.item(2).setSelected(); + if(settings.fastForward.limiter == 4) limiterAmount.item(3).setSelected(); + if(settings.fastForward.limiter == 5) limiterAmount.item(4).setSelected(); + if(settings.fastForward.limiter == 6) limiterAmount.item(5).setSelected(); + if(settings.fastForward.limiter == 7) limiterAmount.item(6).setSelected(); + if(settings.fastForward.limiter == 8) limiterAmount.item(7).setSelected(); + limiterAmount.onChange([&] { + auto index = limiterAmount.selected().offset(); + if(index == 0) settings.fastForward.limiter = 0; + if(index == 1) settings.fastForward.limiter = 2; + if(index == 2) settings.fastForward.limiter = 3; + if(index == 3) settings.fastForward.limiter = 4; + if(index == 4) settings.fastForward.limiter = 5; + if(index == 5) settings.fastForward.limiter = 6; + if(index == 6) settings.fastForward.limiter = 7; + if(index == 7) settings.fastForward.limiter = 8; }); - mode7Perspective.setText("Perspective correction").setChecked(settings.emulator.hack.ppu.mode7.perspective).onToggle([&] { - settings.emulator.hack.ppu.mode7.perspective = mode7Perspective.checked(); - emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective); + + fastForwardMute.setText("Mute while fast forwarding").setChecked(settings.fastForward.mute).onToggle([&] { + settings.fastForward.mute = fastForwardMute.checked(); }); - mode7Supersample.setText("Supersampling").setChecked(settings.emulator.hack.ppu.mode7.supersample).onToggle([&] { - settings.emulator.hack.ppu.mode7.supersample = mode7Supersample.checked(); - emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample); + + fastForwardSpacer.setColor({192, 192, 192}); + + rewindLabel.setText("Rewind").setFont(Font().setBold()); + + rewindFrequencyLabel.setText("Frequency:"); + rewindFrequencyOption.append(ComboButtonItem().setText("Disabled")); + rewindFrequencyOption.append(ComboButtonItem().setText("Every 10 frames")); + rewindFrequencyOption.append(ComboButtonItem().setText("Every 20 frames")); + rewindFrequencyOption.append(ComboButtonItem().setText("Every 30 frames")); + rewindFrequencyOption.append(ComboButtonItem().setText("Every 40 frames")); + rewindFrequencyOption.append(ComboButtonItem().setText("Every 50 frames")); + rewindFrequencyOption.append(ComboButtonItem().setText("Every 60 frames")); + if(settings.rewind.frequency == 0) rewindFrequencyOption.item(0).setSelected(); + if(settings.rewind.frequency == 10) rewindFrequencyOption.item(1).setSelected(); + if(settings.rewind.frequency == 20) rewindFrequencyOption.item(2).setSelected(); + if(settings.rewind.frequency == 30) rewindFrequencyOption.item(3).setSelected(); + if(settings.rewind.frequency == 40) rewindFrequencyOption.item(4).setSelected(); + if(settings.rewind.frequency == 50) rewindFrequencyOption.item(5).setSelected(); + if(settings.rewind.frequency == 60) rewindFrequencyOption.item(6).setSelected(); + rewindFrequencyOption.onChange([&] { + settings.rewind.frequency = rewindFrequencyOption.selected().offset() * 10; + program.rewindReset(); }); - mode7Mosaic.setText("HD->SD Mosaic").setChecked(settings.emulator.hack.ppu.mode7.mosaic).onToggle([&] { - settings.emulator.hack.ppu.mode7.mosaic = mode7Mosaic.checked(); - emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic); + + rewindLengthLabel.setText("Length:"); + rewindLengthOption.append(ComboButtonItem().setText( "10 states")); + rewindLengthOption.append(ComboButtonItem().setText( "20 states")); + rewindLengthOption.append(ComboButtonItem().setText( "40 states")); + rewindLengthOption.append(ComboButtonItem().setText( "80 states")); + rewindLengthOption.append(ComboButtonItem().setText("160 states")); + rewindLengthOption.append(ComboButtonItem().setText("320 states")); + if(settings.rewind.length == 10) rewindLengthOption.item(0).setSelected(); + if(settings.rewind.length == 20) rewindLengthOption.item(1).setSelected(); + if(settings.rewind.length == 40) rewindLengthOption.item(2).setSelected(); + if(settings.rewind.length == 80) rewindLengthOption.item(3).setSelected(); + if(settings.rewind.length == 160) rewindLengthOption.item(4).setSelected(); + if(settings.rewind.length == 320) rewindLengthOption.item(5).setSelected(); + rewindLengthOption.onChange([&] { + settings.rewind.length = 10 << rewindLengthOption.selected().offset(); + program.rewindReset(); }); - dspLabel.setText("DSP (audio)").setFont(Font().setBold()); - fastDSP.setText("Fast mode").setChecked(settings.emulator.hack.dsp.fast).onToggle([&] { - settings.emulator.hack.dsp.fast = fastDSP.checked(); - emulator->configure("Hacks/DSP/Fast", settings.emulator.hack.dsp.fast); + + rewindMute.setText("Mute while rewinding").setChecked(settings.rewind.mute).onToggle([&] { + settings.rewind.mute = rewindMute.checked(); }); - cubicInterpolation.setText("Cubic interpolation").setChecked(settings.emulator.hack.dsp.cubic).onToggle([&] { - settings.emulator.hack.dsp.cubic = cubicInterpolation.checked(); - emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic); - }); - echoShadow.setText("Echo shadow RAM").setToolTip( - "This option emulates a bug in ZSNES where echo RAM was treated as separate from APU RAM.\n" - "Many older ROM hacks for Super Mario World relied on this behavior, and will crash without enabling this.\n" - "It is, however, extremely inaccurate to real hardware and should not be enabled unless required." - ).setChecked(settings.emulator.hack.dsp.echoShadow).onToggle([&] { - settings.emulator.hack.dsp.echoShadow = echoShadow.checked(); - //not a run-time setting: do not call emulator->configure() here. - }); - coprocessorLabel.setText("Coprocessors").setFont(Font().setBold()); - coprocessorDelayedSyncOption.setText("Fast mode").setChecked(settings.emulator.hack.coprocessor.delayedSync).onToggle([&] { - settings.emulator.hack.coprocessor.delayedSync = coprocessorDelayedSyncOption.checked(); - }); - coprocessorPreferHLEOption.setText("Prefer HLE").setChecked(settings.emulator.hack.coprocessor.preferHLE).setToolTip( - "When checked, less accurate HLE emulation will always be used when available.\n" - "When unchecked, HLE will only be used when LLE firmware is missing." - ).onToggle([&] { - settings.emulator.hack.coprocessor.preferHLE = coprocessorPreferHLEOption.checked(); - }); - hacksNote.setText("Note: some hack setting changes do not take effect until after reloading games."); } diff --git a/bsnes/target-bsnes/settings/enhancements.cpp b/bsnes/target-bsnes/settings/enhancements.cpp new file mode 100644 index 00000000..e34016b0 --- /dev/null +++ b/bsnes/target-bsnes/settings/enhancements.cpp @@ -0,0 +1,117 @@ +auto EnhancementSettings::create() -> void { + setCollapsible(); + setVisible(false); + + overclockingLabel.setText("Overclocking").setFont(Font().setBold()); + + overclockingLayout.setSize({3, 3}); + overclockingLayout.column(0).setAlignment(1.0); + overclockingLayout.column(1).setAlignment(0.5); + + cpuLabel.setText("CPU:"); + cpuClock.setLength(301).setPosition((settings.emulator.hack.cpu.overclock - 100)).onChange([&] { + settings.emulator.hack.cpu.overclock = cpuClock.position() + 100; + emulator->configure("Hacks/CPU/Overclock", settings.emulator.hack.cpu.overclock); + cpuValue.setText({settings.emulator.hack.cpu.overclock, "%"}); + }).doChange(); + + sa1Label.setText("SA-1:"); + sa1Clock.setLength(301).setPosition((settings.emulator.hack.sa1.overclock - 100)).onChange([&] { + settings.emulator.hack.sa1.overclock = sa1Clock.position() + 100; + emulator->configure("Hacks/SA1/Overclock", settings.emulator.hack.sa1.overclock); + sa1Value.setText({settings.emulator.hack.sa1.overclock, "%"}); + }).doChange(); + + sfxLabel.setText("SuperFX:"); + sfxClock.setLength(141).setPosition((settings.emulator.hack.superfx.overclock - 100) / 5).onChange([&] { + settings.emulator.hack.superfx.overclock = sfxClock.position() * 5 + 100; + emulator->configure("Hacks/SuperFX/Overclock", settings.emulator.hack.superfx.overclock); + sfxValue.setText({settings.emulator.hack.superfx.overclock, "%"}); + }).doChange(); + + overclockingSpacer.setColor({192, 192, 192}); + + ppuLabel.setText("PPU (video)").setFont(Font().setBold()); + fastPPU.setText("Fast mode").setChecked(settings.emulator.hack.ppu.fast).onToggle([&] { + settings.emulator.hack.ppu.fast = fastPPU.checked(); + if(!fastPPU.checked()) { + noSpriteLimit.setEnabled(false); + deinterlace.setEnabled(false); + mode7Layout.setEnabled(false); + } else { + noSpriteLimit.setEnabled(true); + deinterlace.setEnabled(true); + mode7Layout.setEnabled(true); + } + }).doToggle(); + deinterlace.setText("Deinterlace").setChecked(settings.emulator.hack.ppu.deinterlace).onToggle([&] { + settings.emulator.hack.ppu.deinterlace = deinterlace.checked(); + emulator->configure("Hacks/PPU/Deinterlace", settings.emulator.hack.ppu.deinterlace); + }); + noSpriteLimit.setText("No sprite limit").setChecked(settings.emulator.hack.ppu.noSpriteLimit).onToggle([&] { + settings.emulator.hack.ppu.noSpriteLimit = noSpriteLimit.checked(); + }); + + mode7Label.setText("HD Mode 7 (fast PPU only)").setFont(Font().setBold()); + mode7ScaleLabel.setText("Scale:"); + mode7Scale.append(ComboButtonItem().setText( "240p").setProperty("multiplier", 1)); + mode7Scale.append(ComboButtonItem().setText( "480p").setProperty("multiplier", 2)); + mode7Scale.append(ComboButtonItem().setText( "720p").setProperty("multiplier", 3)); + mode7Scale.append(ComboButtonItem().setText( "960p").setProperty("multiplier", 4)); + mode7Scale.append(ComboButtonItem().setText("1200p").setProperty("multiplier", 5)); + mode7Scale.append(ComboButtonItem().setText("1440p").setProperty("multiplier", 6)); + mode7Scale.append(ComboButtonItem().setText("1680p").setProperty("multiplier", 7)); + mode7Scale.append(ComboButtonItem().setText("1920p").setProperty("multiplier", 8)); + mode7Scale.append(ComboButtonItem().setText("2160p").setProperty("multiplier", 9)); + for(uint n = 1; n <= 9; n++) { + if(settings.emulator.hack.ppu.mode7.scale == n) mode7Scale.item(n - 1).setSelected(); + } + mode7Scale.onChange([&] { + settings.emulator.hack.ppu.mode7.scale = mode7Scale.selected().property("multiplier").natural(); + emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale); + }); + mode7Perspective.setText("Perspective correction").setChecked(settings.emulator.hack.ppu.mode7.perspective).onToggle([&] { + settings.emulator.hack.ppu.mode7.perspective = mode7Perspective.checked(); + emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective); + }); + mode7Supersample.setText("Supersampling").setChecked(settings.emulator.hack.ppu.mode7.supersample).onToggle([&] { + settings.emulator.hack.ppu.mode7.supersample = mode7Supersample.checked(); + emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample); + }); + mode7Mosaic.setText("HD->SD Mosaic").setChecked(settings.emulator.hack.ppu.mode7.mosaic).onToggle([&] { + settings.emulator.hack.ppu.mode7.mosaic = mode7Mosaic.checked(); + emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic); + }); + + dspLabel.setText("DSP (audio)").setFont(Font().setBold()); + fastDSP.setText("Fast mode").setChecked(settings.emulator.hack.dsp.fast).onToggle([&] { + settings.emulator.hack.dsp.fast = fastDSP.checked(); + emulator->configure("Hacks/DSP/Fast", settings.emulator.hack.dsp.fast); + }); + cubicInterpolation.setText("Cubic interpolation").setChecked(settings.emulator.hack.dsp.cubic).onToggle([&] { + settings.emulator.hack.dsp.cubic = cubicInterpolation.checked(); + emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic); + }); + + coprocessorLabel.setText("Coprocessors").setFont(Font().setBold()); + coprocessorDelayedSyncOption.setText("Fast mode").setChecked(settings.emulator.hack.coprocessor.delayedSync).onToggle([&] { + settings.emulator.hack.coprocessor.delayedSync = coprocessorDelayedSyncOption.checked(); + }); + coprocessorPreferHLEOption.setText("Prefer HLE").setChecked(settings.emulator.hack.coprocessor.preferHLE).setToolTip( + "When checked, less accurate HLE emulation will always be used when available.\n" + "When unchecked, HLE will only be used when LLE firmware is missing." + ).onToggle([&] { + settings.emulator.hack.coprocessor.preferHLE = coprocessorPreferHLEOption.checked(); + }); + coprocessorSpacer.setColor({192, 192, 192}); + + gameLabel.setText("Game Enhancements").setFont(Font().setBold()); + hotfixes.setText("Hotfixes").setToolTip({ + "Even commercially licensed and officially released software sometimes shipped with bugs.\n" + "This option will correct certain issues that occurred even on real hardware." + }).setChecked(settings.emulator.hack.hotfixes).onToggle([&] { + settings.emulator.hack.hotfixes = hotfixes.checked(); + }); + + note.setText("Note: some settings do not take effect until after reloading games."); +} diff --git a/bsnes/target-bsnes/settings/settings.cpp b/bsnes/target-bsnes/settings/settings.cpp index 4fe50e17..47c21802 100644 --- a/bsnes/target-bsnes/settings/settings.cpp +++ b/bsnes/target-bsnes/settings/settings.cpp @@ -4,8 +4,9 @@ #include "input.cpp" #include "hotkeys.cpp" #include "paths.cpp" -#include "speed.cpp" #include "emulator.cpp" +#include "enhancements.cpp" +#include "compatibility.cpp" #include "drivers.cpp" Settings settings; VideoSettings videoSettings; @@ -13,8 +14,9 @@ AudioSettings audioSettings; InputSettings inputSettings; HotkeySettings hotkeySettings; PathSettings pathSettings; -SpeedSettings speedSettings; EmulatorSettings emulatorSettings; +EnhancementSettings enhancementSettings; +CompatibilitySettings compatibilitySettings; DriverSettings driverSettings; namespace Instances { Instance settingsWindow; } SettingsWindow& settingsWindow = Instances::settingsWindow(); @@ -112,8 +114,10 @@ auto Settings::process(bool load) -> void { bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval); bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload); bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad); + bind(boolean, "Emulator/Hack/Hotfixes", emulator.hack.hotfixes); bind(text, "Emulator/Hack/Entropy", emulator.hack.entropy); bind(natural, "Emulator/Hack/CPU/Overclock", emulator.hack.cpu.overclock); + bind(boolean, "Emulator/Hack/CPU/FastMath", emulator.hack.cpu.fastMath); bind(boolean, "Emulator/Hack/PPU/Fast", emulator.hack.ppu.fast); bind(boolean, "Emulator/Hack/PPU/Deinterlace", emulator.hack.ppu.deinterlace); bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit); @@ -172,8 +176,9 @@ auto SettingsWindow::create() -> void { panelList.append(ListViewItem().setText("Input").setIcon(Icon::Device::Joypad)); panelList.append(ListViewItem().setText("Hotkeys").setIcon(Icon::Device::Keyboard)); panelList.append(ListViewItem().setText("Paths").setIcon(Icon::Emblem::Folder)); - panelList.append(ListViewItem().setText("Speed").setIcon(Icon::Device::Clock)); panelList.append(ListViewItem().setText("Emulator").setIcon(Icon::Action::Settings)); + panelList.append(ListViewItem().setText("Enhancements").setIcon(Icon::Action::Add)); + panelList.append(ListViewItem().setText("Compatibility").setIcon(Icon::Action::Remove)); panelList.append(ListViewItem().setText("Drivers").setIcon(Icon::Place::Settings)); panelList.onChange([&] { if(auto item = panelList.selected()) { @@ -188,13 +193,14 @@ auto SettingsWindow::create() -> void { panelContainer.append(inputSettings, Size{~0, ~0}); panelContainer.append(hotkeySettings, Size{~0, ~0}); panelContainer.append(pathSettings, Size{~0, ~0}); - panelContainer.append(speedSettings, Size{~0, ~0}); panelContainer.append(emulatorSettings, Size{~0, ~0}); + panelContainer.append(enhancementSettings, Size{~0, ~0}); + panelContainer.append(compatibilitySettings, Size{~0, ~0}); panelContainer.append(driverSettings, Size{~0, ~0}); statusBar.setFont(Font().setBold()); setTitle("Settings"); - setSize({680_sx, 400_sx}); + setSize({680_sx, 400_sy}); setAlignment({0.0, 1.0}); setDismissable(); @@ -221,8 +227,9 @@ auto SettingsWindow::show(int index) -> void { inputSettings.setVisible(false); hotkeySettings.setVisible(false); pathSettings.setVisible(false); - speedSettings.setVisible(false); emulatorSettings.setVisible(false); + enhancementSettings.setVisible(false); + compatibilitySettings.setVisible(false); driverSettings.setVisible(false); panelList.item(index).setSelected(); if(index ==-1) settingsHome.setVisible(true); @@ -231,9 +238,10 @@ auto SettingsWindow::show(int index) -> void { if(index == 2) inputSettings.setVisible(true); if(index == 3) hotkeySettings.setVisible(true); if(index == 4) pathSettings.setVisible(true); - if(index == 5) speedSettings.setVisible(true); - if(index == 6) emulatorSettings.setVisible(true); - if(index == 7) driverSettings.setVisible(true); + if(index == 5) emulatorSettings.setVisible(true); + if(index == 6) enhancementSettings.setVisible(true); + if(index == 7) compatibilitySettings.setVisible(true); + if(index == 8) driverSettings.setVisible(true); panelContainer.resize(); setVisible(); setFocused(); diff --git a/bsnes/target-bsnes/settings/settings.hpp b/bsnes/target-bsnes/settings/settings.hpp index a9cf7995..05fddc5f 100644 --- a/bsnes/target-bsnes/settings/settings.hpp +++ b/bsnes/target-bsnes/settings/settings.hpp @@ -95,9 +95,11 @@ struct Settings : Markup::Node { bool autoSaveStateOnUnload = false; bool autoLoadStateOnLoad = false; struct Hack { + bool hotfixes = true; string entropy = "Low"; struct CPU { uint overclock = 100; + bool fastMath = false; } cpu; struct PPU { bool fast = true; @@ -290,39 +292,6 @@ public: Button screenshotsReset{&layout, Size{80_sx, 0}}; }; -struct SpeedSettings : VerticalLayout { - auto create() -> void; - -public: - Label overclockingLabel{this, Size{~0, 0}, 2}; - TableLayout overclockingLayout{this, Size{~0, 0}}; - Label cpuLabel{&overclockingLayout, Size{0, 0}}; - Label cpuValue{&overclockingLayout, Size{50_sx, 0}}; - HorizontalSlider cpuClock{&overclockingLayout, Size{~0, 0}}; - // - Label sa1Label{&overclockingLayout, Size{0, 0}}; - Label sa1Value{&overclockingLayout, Size{50_sx, 0}}; - HorizontalSlider sa1Clock{&overclockingLayout, Size{~0, 0}}; - // - Label sfxLabel{&overclockingLayout, Size{0, 0}}; - Label sfxValue{&overclockingLayout, Size{50_sx, 0}}; - HorizontalSlider sfxClock{&overclockingLayout, Size{~0, 0}}; - Label fastForwardLabel{this, Size{~0, 0}, 2}; - HorizontalLayout fastForwardLayout{this, Size{~0, 0}}; - Label frameSkipLabel{&fastForwardLayout, Size{0, 0}}; - ComboButton frameSkipAmount{&fastForwardLayout, Size{0, 0}}; - Label limiterLabel{&fastForwardLayout, Size{0, 0}}; - ComboButton limiterAmount{&fastForwardLayout, Size{0, 0}}; - CheckLabel fastForwardMute{this, Size{0, 0}}; - Label rewindLabel{this, Size{~0, 0}, 2}; - HorizontalLayout rewindLayout{this, Size{~0, 0}}; - Label rewindFrequencyLabel{&rewindLayout, Size{0, 0}}; - ComboButton rewindFrequencyOption{&rewindLayout, Size{0, 0}}; - Label rewindLengthLabel{&rewindLayout, Size{0, 0}}; - ComboButton rewindLengthOption{&rewindLayout, Size{0, 0}}; - CheckLabel rewindMute{this, Size{0, 0}}; -}; - struct EmulatorSettings : VerticalLayout { auto create() -> void; @@ -334,18 +303,50 @@ public: CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}}; CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}}; Canvas optionsSpacer{this, Size{~0, 1}}; - Label entropyLabel{this, Size{~0, 0}, 2}; - HorizontalLayout entropyLayout{this, Size{~0, 0}}; - RadioLabel entropyNone{&entropyLayout, Size{0, 0}}; - RadioLabel entropyLow{&entropyLayout, Size{0, 0}}; - RadioLabel entropyHigh{&entropyLayout, Size{0, 0}}; - Group entropyGroup{&entropyNone, &entropyLow, &entropyHigh}; + // + Label fastForwardLabel{this, Size{~0, 0}, 2}; + HorizontalLayout fastForwardLayout{this, Size{~0, 0}}; + Label frameSkipLabel{&fastForwardLayout, Size{0, 0}}; + ComboButton frameSkipAmount{&fastForwardLayout, Size{0, 0}}; + Label limiterLabel{&fastForwardLayout, Size{0, 0}}; + ComboButton limiterAmount{&fastForwardLayout, Size{0, 0}}; + CheckLabel fastForwardMute{this, Size{0, 0}}; + Canvas fastForwardSpacer{this, Size{~0, 1}}; + // + Label rewindLabel{this, Size{~0, 0}, 2}; + HorizontalLayout rewindLayout{this, Size{~0, 0}}; + Label rewindFrequencyLabel{&rewindLayout, Size{0, 0}}; + ComboButton rewindFrequencyOption{&rewindLayout, Size{0, 0}}; + Label rewindLengthLabel{&rewindLayout, Size{0, 0}}; + ComboButton rewindLengthOption{&rewindLayout, Size{0, 0}}; + CheckLabel rewindMute{this, Size{0, 0}}; +}; + +struct EnhancementSettings : VerticalLayout { + auto create() -> void; + +private: + Label overclockingLabel{this, Size{~0, 0}, 2}; + TableLayout overclockingLayout{this, Size{~0, 0}}; + Label cpuLabel{&overclockingLayout, Size{0, 0}}; + Label cpuValue{&overclockingLayout, Size{50_sx, 0}}; + HorizontalSlider cpuClock{&overclockingLayout, Size{~0, 0}}; + Canvas overclockingSpacer{this, Size{~0, 1}}; + // + Label sa1Label{&overclockingLayout, Size{0, 0}}; + Label sa1Value{&overclockingLayout, Size{50_sx, 0}}; + HorizontalSlider sa1Clock{&overclockingLayout, Size{~0, 0}}; + // + Label sfxLabel{&overclockingLayout, Size{0, 0}}; + Label sfxValue{&overclockingLayout, Size{50_sx, 0}}; + HorizontalSlider sfxClock{&overclockingLayout, Size{~0, 0}}; + // Label ppuLabel{this, Size{~0, 0}, 2}; HorizontalLayout ppuLayout{this, Size{~0, 0}}; CheckLabel fastPPU{&ppuLayout, Size{0, 0}}; CheckLabel deinterlace{&ppuLayout, Size{0, 0}}; CheckLabel noSpriteLimit{&ppuLayout, Size{0, 0}}; - CheckLabel noVRAMBlocking{&ppuLayout, Size{0, 0}}; + // Label mode7Label{this, Size{~0, 0}, 2}; HorizontalLayout mode7Layout{this, Size{~0, 0}}; Label mode7ScaleLabel{&mode7Layout, Size{0, 0}}; @@ -353,16 +354,47 @@ public: CheckLabel mode7Perspective{&mode7Layout, Size{0, 0}}; CheckLabel mode7Supersample{&mode7Layout, Size{0, 0}}; CheckLabel mode7Mosaic{&mode7Layout, Size{0, 0}}; + // Label dspLabel{this, Size{~0, 0}, 2}; HorizontalLayout dspLayout{this, Size{~0, 0}}; CheckLabel fastDSP{&dspLayout, Size{0, 0}}; CheckLabel cubicInterpolation{&dspLayout, Size{0, 0}}; - CheckLabel echoShadow{&dspLayout, Size{0, 0}}; + // Label coprocessorLabel{this, Size{~0, 0}, 2}; HorizontalLayout coprocessorLayout{this, Size{~0, 0}}; CheckLabel coprocessorDelayedSyncOption{&coprocessorLayout, Size{0, 0}}; CheckLabel coprocessorPreferHLEOption{&coprocessorLayout, Size{0, 0}}; - Label hacksNote{this, Size{~0, 0}}; + Canvas coprocessorSpacer{this, Size{~0, 1}}; + // + Label gameLabel{this, Size{~0, 0}, 2}; + CheckLabel hotfixes{this, Size{0, 0}}; + // + Widget spacer{this, Size{~0, ~0}}; + Label note{this, Size{~0, 0}}; +}; + +struct CompatibilitySettings : VerticalLayout { + auto create() -> void; + +private: + Label entropyLabel{this, Size{~0, 0}, 2}; + HorizontalLayout entropyLayout{this, Size{~0, 0}}; + RadioLabel entropyNone{&entropyLayout, Size{0, 0}}; + RadioLabel entropyLow{&entropyLayout, Size{0, 0}}; + RadioLabel entropyHigh{&entropyLayout, Size{0, 0}}; + Group entropyGroup{&entropyNone, &entropyLow, &entropyHigh}; + // + Label cpuLabel{this, Size{~0, 0}, 2}; + CheckLabel fastMath{this, Size{0, 0}}; + // + Label ppuLabel{this, Size{~0, 0}, 2}; + CheckLabel noVRAMBlocking{this, Size{0, 0}}; + // + Label dspLabel{this, Size{~0, 0}, 2}; + CheckLabel echoShadow{this, Size{0, 0}}; + // + Widget spacer{this, Size{~0, ~0}}; + Label note{this, Size{~0, 0}}; }; struct DriverSettings : VerticalLayout { @@ -402,6 +434,7 @@ public: CheckLabel videoBlockingToggle{&videoToggleLayout, Size{0, 0}}; CheckLabel videoFlushToggle{&videoToggleLayout, Size{0, 0}}; Canvas videoSpacer{this, Size{~0, 1}}; + // Label audioLabel{this, Size{~0, 0}, 2}; VerticalLayout audioLayout{this, Size{~0, 0}}; HorizontalLayout audioDriverLayout{&audioLayout, Size{~0, 0}}; @@ -421,6 +454,7 @@ public: CheckLabel audioBlockingToggle{&audioToggleLayout, Size{0, 0}}; CheckLabel audioDynamicToggle{&audioToggleLayout, Size{0, 0}}; Canvas audioSpacer{this, Size{~0, 1}}; + // Label inputLabel{this, Size{~0, 0}, 2}; VerticalLayout inputLayout{this, Size{~0, 0}}; HorizontalLayout inputDriverLayout{&inputLayout, Size{~0, 0}}; @@ -438,7 +472,7 @@ struct SettingsWindow : Window, Lock { public: VerticalLayout layout{this}; HorizontalLayout panelLayout{&layout, Size{~0, ~0}}; - ListView panelList{&panelLayout, Size{120_sx, ~0}}; + ListView panelList{&panelLayout, Size{125_sx, ~0}}; VerticalLayout panelContainer{&panelLayout, Size{~0, ~0}}; StatusBar statusBar{this}; }; @@ -449,8 +483,9 @@ extern AudioSettings audioSettings; extern InputSettings inputSettings; extern HotkeySettings hotkeySettings; extern PathSettings pathSettings; -extern SpeedSettings speedSettings; extern EmulatorSettings emulatorSettings; +extern EnhancementSettings enhancementSettings; +extern CompatibilitySettings compatibilitySettings; extern DriverSettings driverSettings; namespace Instances { extern Instance settingsWindow; } extern SettingsWindow& settingsWindow; diff --git a/bsnes/target-bsnes/settings/speed.cpp b/bsnes/target-bsnes/settings/speed.cpp deleted file mode 100644 index c1f1183c..00000000 --- a/bsnes/target-bsnes/settings/speed.cpp +++ /dev/null @@ -1,133 +0,0 @@ -auto SpeedSettings::create() -> void { - setCollapsible(); - setVisible(false); - - overclockingLabel.setText("Overclocking").setFont(Font().setBold()); - - overclockingLayout.setSize({3, 3}); - overclockingLayout.column(0).setAlignment(1.0); - overclockingLayout.column(1).setAlignment(0.5); - - cpuLabel.setText("CPU:"); - cpuClock.setLength(301).setPosition((settings.emulator.hack.cpu.overclock - 100)).onChange([&] { - settings.emulator.hack.cpu.overclock = cpuClock.position() + 100; - emulator->configure("Hacks/CPU/Overclock", settings.emulator.hack.cpu.overclock); - cpuValue.setText({settings.emulator.hack.cpu.overclock, "%"}); - }).doChange(); - - sa1Label.setText("SA-1:"); - sa1Clock.setLength(301).setPosition((settings.emulator.hack.sa1.overclock - 100)).onChange([&] { - settings.emulator.hack.sa1.overclock = sa1Clock.position() + 100; - emulator->configure("Hacks/SA1/Overclock", settings.emulator.hack.sa1.overclock); - sa1Value.setText({settings.emulator.hack.sa1.overclock, "%"}); - }).doChange(); - - sfxLabel.setText("SuperFX:"); - sfxClock.setLength(141).setPosition((settings.emulator.hack.superfx.overclock - 100) / 5).onChange([&] { - settings.emulator.hack.superfx.overclock = sfxClock.position() * 5 + 100; - emulator->configure("Hacks/SuperFX/Overclock", settings.emulator.hack.superfx.overclock); - sfxValue.setText({settings.emulator.hack.superfx.overclock, "%"}); - }).doChange(); - - fastForwardLabel.setText("Fast Forward").setFont(Font().setBold()); - - frameSkipLabel.setText("Frame skip:").setToolTip({ - "Set how many frames to skip while fast forwarding.\n" - "Frame skipping allows a higher maximum fast forwarding frame rate." - }); - - frameSkipAmount.append(ComboButtonItem().setText("None")); - frameSkipAmount.append(ComboButtonItem().setText("1 frame")); - frameSkipAmount.append(ComboButtonItem().setText("2 frames")); - frameSkipAmount.append(ComboButtonItem().setText("3 frames")); - frameSkipAmount.append(ComboButtonItem().setText("4 frames")); - frameSkipAmount.append(ComboButtonItem().setText("5 frames")); - frameSkipAmount.append(ComboButtonItem().setText("6 frames")); - frameSkipAmount.append(ComboButtonItem().setText("7 frames")); - frameSkipAmount.append(ComboButtonItem().setText("8 frames")); - frameSkipAmount.append(ComboButtonItem().setText("9 frames")); - frameSkipAmount.item(settings.fastForward.frameSkip).setSelected(); - frameSkipAmount.onChange([&] { - settings.fastForward.frameSkip = frameSkipAmount.selected().offset(); - }); - - limiterLabel.setText("Limiter:").setToolTip({ - "Set the maximum speed when fast forwarding." - }); - - limiterAmount.append(ComboButtonItem().setText("None")); - limiterAmount.append(ComboButtonItem().setText("200%")); - limiterAmount.append(ComboButtonItem().setText("300%")); - limiterAmount.append(ComboButtonItem().setText("400%")); - limiterAmount.append(ComboButtonItem().setText("500%")); - limiterAmount.append(ComboButtonItem().setText("600%")); - limiterAmount.append(ComboButtonItem().setText("700%")); - limiterAmount.append(ComboButtonItem().setText("800%")); - if(settings.fastForward.limiter == 0) limiterAmount.item(0).setSelected(); - if(settings.fastForward.limiter == 2) limiterAmount.item(1).setSelected(); - if(settings.fastForward.limiter == 3) limiterAmount.item(2).setSelected(); - if(settings.fastForward.limiter == 4) limiterAmount.item(3).setSelected(); - if(settings.fastForward.limiter == 5) limiterAmount.item(4).setSelected(); - if(settings.fastForward.limiter == 6) limiterAmount.item(5).setSelected(); - if(settings.fastForward.limiter == 7) limiterAmount.item(6).setSelected(); - if(settings.fastForward.limiter == 8) limiterAmount.item(7).setSelected(); - limiterAmount.onChange([&] { - auto index = limiterAmount.selected().offset(); - if(index == 0) settings.fastForward.limiter = 0; - if(index == 1) settings.fastForward.limiter = 2; - if(index == 2) settings.fastForward.limiter = 3; - if(index == 3) settings.fastForward.limiter = 4; - if(index == 4) settings.fastForward.limiter = 5; - if(index == 5) settings.fastForward.limiter = 6; - if(index == 6) settings.fastForward.limiter = 7; - if(index == 7) settings.fastForward.limiter = 8; - }); - - fastForwardMute.setText("Mute while fast forwarding").setChecked(settings.fastForward.mute).onToggle([&] { - settings.fastForward.mute = fastForwardMute.checked(); - }); - - rewindLabel.setText("Rewind").setFont(Font().setBold()); - - rewindFrequencyLabel.setText("Frequency:"); - rewindFrequencyOption.append(ComboButtonItem().setText("Disabled")); - rewindFrequencyOption.append(ComboButtonItem().setText("Every 10 frames")); - rewindFrequencyOption.append(ComboButtonItem().setText("Every 20 frames")); - rewindFrequencyOption.append(ComboButtonItem().setText("Every 30 frames")); - rewindFrequencyOption.append(ComboButtonItem().setText("Every 40 frames")); - rewindFrequencyOption.append(ComboButtonItem().setText("Every 50 frames")); - rewindFrequencyOption.append(ComboButtonItem().setText("Every 60 frames")); - if(settings.rewind.frequency == 0) rewindFrequencyOption.item(0).setSelected(); - if(settings.rewind.frequency == 10) rewindFrequencyOption.item(1).setSelected(); - if(settings.rewind.frequency == 20) rewindFrequencyOption.item(2).setSelected(); - if(settings.rewind.frequency == 30) rewindFrequencyOption.item(3).setSelected(); - if(settings.rewind.frequency == 40) rewindFrequencyOption.item(4).setSelected(); - if(settings.rewind.frequency == 50) rewindFrequencyOption.item(5).setSelected(); - if(settings.rewind.frequency == 60) rewindFrequencyOption.item(6).setSelected(); - rewindFrequencyOption.onChange([&] { - settings.rewind.frequency = rewindFrequencyOption.selected().offset() * 10; - program.rewindReset(); - }); - - rewindLengthLabel.setText("Length:"); - rewindLengthOption.append(ComboButtonItem().setText( "10 states")); - rewindLengthOption.append(ComboButtonItem().setText( "20 states")); - rewindLengthOption.append(ComboButtonItem().setText( "40 states")); - rewindLengthOption.append(ComboButtonItem().setText( "80 states")); - rewindLengthOption.append(ComboButtonItem().setText("160 states")); - rewindLengthOption.append(ComboButtonItem().setText("320 states")); - if(settings.rewind.length == 10) rewindLengthOption.item(0).setSelected(); - if(settings.rewind.length == 20) rewindLengthOption.item(1).setSelected(); - if(settings.rewind.length == 40) rewindLengthOption.item(2).setSelected(); - if(settings.rewind.length == 80) rewindLengthOption.item(3).setSelected(); - if(settings.rewind.length == 160) rewindLengthOption.item(4).setSelected(); - if(settings.rewind.length == 320) rewindLengthOption.item(5).setSelected(); - rewindLengthOption.onChange([&] { - settings.rewind.length = 10 << rewindLengthOption.selected().offset(); - program.rewindReset(); - }); - - rewindMute.setText("Mute while rewinding").setChecked(settings.rewind.mute).onToggle([&] { - settings.rewind.mute = rewindMute.checked(); - }); -} diff --git a/bsnes/target-bsnes/tools/tools.cpp b/bsnes/target-bsnes/tools/tools.cpp index c654a81f..7f74ae89 100644 --- a/bsnes/target-bsnes/tools/tools.cpp +++ b/bsnes/target-bsnes/tools/tools.cpp @@ -61,7 +61,7 @@ auto ToolsWindow::create() -> void { panelContainer.append(manifestViewer, Size{~0, ~0}); setTitle("Tools"); - setSize({720_sx, 400_sx}); + setSize({720_sx, 400_sy}); setAlignment({1.0, 1.0}); setDismissable(); diff --git a/ruby/GNUmakefile b/ruby/GNUmakefile index 9700e14e..eaafbdac 100755 --- a/ruby/GNUmakefile +++ b/ruby/GNUmakefile @@ -13,7 +13,7 @@ ifeq ($(ruby),) ruby += input.sdl input.xlib input.udev else ifeq ($(platform),bsd) ruby += video.glx video.glx2 video.xvideo video.xshm - ruby += audio.oss #audio.openal + ruby += audio.oss #audio.pulseaudio ruby += input.sdl input.xlib endif endif