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
This commit is contained in:
byuu 2019-09-03 12:01:45 +09:00
parent 08e5e81609
commit 2bb1606552
21 changed files with 446 additions and 326 deletions

View File

@ -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";

View File

@ -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

View File

@ -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)) {

View File

@ -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

View File

@ -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 );

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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};

View File

@ -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();

View File

@ -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;

View File

@ -29,6 +29,15 @@ auto Program::applyPatchIPS(vector<uint8_t>& 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<uint8_t>& 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;

View File

@ -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();

View File

@ -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.");
}

View File

@ -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.");
}

View File

@ -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.");
}

View File

@ -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& 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();

View File

@ -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> settingsWindow; }
extern SettingsWindow& settingsWindow;

View File

@ -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();
});
}

View File

@ -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();

View File

@ -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