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