Update to v106r43 release.

byuu says:

Changelog:

  - bsnes: added video settings panel
  - bsnes: added audio settings panel
  - bsnes: disable assign/clear buttons at startup for hotkeys panel
  - bsnes: program initialization restructured: drivers initialize last
      - this lets me reinitialize the settings panel values on driver
        changes
      - so eg things like input/hotkey remappings should work after
        input driver changes now
      - ... but I had to disable the window icon for this ... it takes
        too long to show up this way
  - bsnes: added synchronize video/audio options to settings menu
  - bsnes: added audio skew slider for video/audio synchronization
  - bsnes: state manager edit/remove works on game ROM .bsz archives now
  - bsnes: removed View→Color Emulation; default to 150% gamma instead
    (it's a touch brighter but similar)

At this point, I'm pretty much ready to make an initial beta release for
wider testing.

Please use this WIP to indicate any must-fix issues before I do so.
This commit is contained in:
Tim Allen 2018-06-27 11:56:27 +10:00
parent 5b97fa2415
commit b14c6bf155
22 changed files with 529 additions and 209 deletions

View File

@ -13,7 +13,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.42";
static const string Version = "106.43";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -176,6 +176,14 @@ auto InputMapping::displayName() -> string {
InputManager::InputManager() {
inputManager = this;
}
auto InputManager::initialize() -> void {
devices.reset();
ports.reset();
hotkeys.reset();
if(!input) return;
input->onChange({&InputManager::onChange, this});
frequency = max(1u, settings["Input/Frequency"].natural());
@ -197,6 +205,7 @@ InputManager::InputManager() {
}
bindHotkeys();
poll();
}
auto InputManager::bind() -> void {

View File

@ -54,6 +54,7 @@ struct InputPort {
struct InputManager {
InputManager();
auto initialize() -> void;
auto bind() -> void;
auto poll() -> void;
auto onChange(shared_pointer<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void;

View File

@ -85,14 +85,19 @@ Presentation::Presentation() {
settings["View/BlurEmulation"].setValue(blurEmulation.checked());
emulator->set("Blur Emulation", blurEmulation.checked());
}).doToggle();
colorEmulation.setText("Color Emulation").setChecked(settings["View/ColorEmulation"].boolean()).onToggle([&] {
settings["View/ColorEmulation"].setValue(colorEmulation.checked());
emulator->set("Color Emulation", colorEmulation.checked());
}).doToggle();
shaderMenu.setText("Shader");
updateShaders();
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] {
settings["Video/Blocking"].setValue(synchronizeVideo.checked());
program->updateVideoBlocking();
});
synchronizeAudio.setText("Synchronize Audio").setChecked(settings["Audio/Blocking"].boolean()).onToggle([&] {
settings["Audio/Blocking"].setValue(synchronizeAudio.checked());
program->updateAudioBlocking();
});
muteAudio.setText("Mute Audio").setChecked(settings["Audio/Mute"].boolean()).onToggle([&] {
settings["Audio/Mute"].setValue(muteAudio.checked());
program->updateAudioEffects();
});
showStatusBar.setText("Show Status Bar").setChecked(settings["UserInterface/ShowStatusBar"].boolean()).onToggle([&] {
settings["UserInterface/ShowStatusBar"].setValue(showStatusBar.checked());
@ -196,6 +201,8 @@ Presentation::Presentation() {
}
auto Presentation::drawIcon(uint32_t* output, uint length, uint width, uint height) -> void {
return;
int ox = width - 144;
int oy = height - 128;
if(ox >= 0 && oy >= 0) {

View File

@ -46,11 +46,13 @@ struct Presentation : Window {
MenuCheckItem overscanCropping{&viewMenu};
MenuCheckItem integralScaling{&viewMenu};
MenuCheckItem blurEmulation{&viewMenu};
MenuCheckItem colorEmulation{&viewMenu};
Menu shaderMenu{&settingsMenu};
MenuSeparator settingsSeparatorA{&settingsMenu};
MenuCheckItem synchronizeVideo{&settingsMenu};
MenuCheckItem synchronizeAudio{&settingsMenu};
MenuCheckItem muteAudio{&settingsMenu};
MenuCheckItem showStatusBar{&settingsMenu};
MenuSeparator settingsSeparator{&settingsMenu};
MenuSeparator settingsSeparatorB{&settingsMenu};
MenuItem videoSettings{&settingsMenu};
MenuItem audioSettings{&settingsMenu};
MenuItem inputSettings{&settingsMenu};

View File

@ -0,0 +1,74 @@
auto Program::updateAudioDriver() -> void {
audio = Audio::create(settings["Audio/Driver"].text());
audio->setContext(presentation->viewport.handle());
audio->setChannels(2);
updateAudioExclusive();
updateAudioDevice();
updateAudioBlocking();
settingsWindow->advanced.updateAudioDriver();
if(!audio->ready()) {
MessageDialog({
"Error: failed to initialize [", settings["Audio/Driver"].text(), "] audio driver."
}).error();
settings["Audio/Driver"].setValue("None");
return updateAudioDriver();
}
}
auto Program::updateAudioExclusive() -> void {
if(!audio) return;
audio->clear();
audio->setExclusive(settings["Audio/Exclusive"].boolean());
updateAudioFrequency();
updateAudioLatency();
}
auto Program::updateAudioDevice() -> void {
if(!audio) return;
audio->clear();
if(!audio->availableDevices().find(settings["Audio/Device"].text())) {
settings["Audio/Device"].setValue(audio->availableDevices()(0));
}
audio->setDevice(settings["Audio/Device"].text());
updateAudioFrequency();
updateAudioLatency();
settingsWindow->audio.updateDevice();
}
auto Program::updateAudioBlocking() -> void {
audio->setBlocking(settings["Audio/Blocking"].boolean());
}
auto Program::updateAudioFrequency() -> void {
if(!audio) return;
audio->clear();
if(!audio->availableFrequencies().find(settings["Audio/Frequency"].real())) {
settings["Audio/Frequency"].setValue(audio->availableFrequencies()(0));
}
audio->setFrequency(settings["Audio/Frequency"].real());
Emulator::audio.setFrequency(settings["Audio/Frequency"].real() + settings["Audio/Skew"].integer());
settingsWindow->audio.updateFrequency();
}
auto Program::updateAudioLatency() -> void {
if(!audio) return;
audio->clear();
if(!audio->availableLatencies().find(settings["Audio/Latency"].natural())) {
settings["Audio/Latency"].setValue(audio->availableLatencies()(0));
}
audio->setLatency(settings["Audio/Latency"].natural());
settingsWindow->audio.updateLatency();
}
auto Program::updateAudioEffects() -> void {
double volume = settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01;
Emulator::audio.setVolume(volume);
double balance = max(-1.0, min(+1.0, (settings["Audio/Balance"].integer() - 50) / 50.0));
Emulator::audio.setBalance(balance);
bool reverb = settings["Audio/Reverb"].boolean();
Emulator::audio.setReverb(reverb);
}

View File

@ -7,9 +7,11 @@ auto Program::load() -> void {
Emulator::audio.reset(2, audio->frequency());
if(emulator->load(media.id)) {
gameQueue = {};
connectDevices();
updateInputDevices();
applyHacks();
emulator->power();
updateVideoPalette();
updateAudioEffects();
showMessage(!appliedPatch() ? "Game loaded" : "Game loaded and patch applied");
presentation->setTitle(emulator->title());
presentation->resetSystem.setEnabled(true);

View File

@ -0,0 +1,30 @@
auto Program::updateInputDriver() -> void {
input = Input::create(settings["Input/Driver"].text());
input->setContext(presentation->viewport.handle());
inputManager->initialize();
settingsWindow->input.reloadPorts();
settingsWindow->hotkeys.reloadMappings();
settingsWindow->advanced.updateInputDriver();
if(!input->ready()) {
MessageDialog({
"Error: failed to initialize [", settings["Input/Driver"].text(), "] input driver."
}).error();
settings["Input/Driver"].setValue("None");
return updateInputDriver();
}
}
auto Program::updateInputDevices() -> void {
for(auto& port : emulator->ports) {
auto path = string{"Emulator/", port.name}.replace(" ", "");
auto name = settings(path).text();
for(auto& device : port.devices) {
if(device.name == name) {
emulator->connect(port.id, device.id);
break;
}
}
}
}

View File

@ -200,12 +200,7 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
}
auto Program::audioSample(const double* samples, uint channels) -> void {
if(presentation->muteAudio.checked()) {
static const double mutedSamples[] = {0.0, 0.0};
audio->output(mutedSamples);
} else {
audio->output(samples);
}
audio->output(samples);
}
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {

View File

@ -5,6 +5,9 @@
#include "game-rom.cpp"
#include "paths.cpp"
#include "states.cpp"
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp"
#include "utility.cpp"
#include "patch.cpp"
#include "hacks.cpp"
@ -17,6 +20,14 @@ Program::Program(string_vector arguments) {
new Presentation;
presentation->setVisible();
new InputManager;
new SettingsWindow;
new CheatDatabase;
new CheatWindow;
new StateWindow;
new ToolsWindow;
new AboutWindow;
if(settings["Crashed"].boolean()) {
MessageDialog(
"Driver crash detected. Hardware drivers have been disabled.\n"
@ -29,43 +40,12 @@ Program::Program(string_vector arguments) {
settings["Crashed"].setValue(true);
settings.save();
initializeVideoDriver();
if(!video->ready()) {
MessageDialog({
"Error: failed to initialize ", settings["Video/Driver"].text(), " video driver."
}).setParent(*presentation).error();
settings["Video/Driver"].setValue("None");
initializeVideoDriver();
}
initializeAudioDriver();
if(!audio->ready()) {
MessageDialog({
"Error: failed to initialize ", settings["Audio/Driver"].text(), " audio driver."
}).setParent(*presentation).error();
settings["Audio/Driver"].setValue("None");
initializeAudioDriver();
}
initializeInputDriver();
if(!input->ready()) {
MessageDialog({
"Error: failed to initialize ", settings["Input/Driver"].text(), " input driver."
}).setParent(*presentation).error();
settings["Input/Driver"].setValue("None");
initializeInputDriver();
}
updateVideoDriver();
updateAudioDriver();
updateInputDriver();
settings["Crashed"].setValue(false);
settings.save();
new InputManager;
new SettingsWindow;
new CheatDatabase;
new CheatWindow;
new StateWindow;
new ToolsWindow;
new AboutWindow;
arguments.takeLeft(); //ignore program location in argument parsing
for(auto& argument : arguments) {
if(argument == "--fullscreen") {

View File

@ -45,16 +45,33 @@ struct Program : Emulator::Platform {
auto statePath() -> string;
//states.cpp
auto managedStates() -> string_vector;
auto loadState(string filename) -> bool;
auto saveState(string filename) -> bool;
auto saveRecoveryState() -> bool;
auto removeState(string filename) -> bool;
auto renameState(string from, string to) -> bool;
//video.cpp
auto updateVideoDriver() -> void;
auto updateVideoBlocking() -> void;
auto updateVideoShader() -> void;
auto updateVideoPalette() -> void;
//audio.cpp
auto updateAudioDriver() -> void;
auto updateAudioExclusive() -> void;
auto updateAudioDevice() -> void;
auto updateAudioBlocking() -> void;
auto updateAudioFrequency() -> void;
auto updateAudioLatency() -> void;
auto updateAudioEffects() -> void;
//input.cpp
auto updateInputDriver() -> void;
auto updateInputDevices() -> void;
//utility.cpp
auto initializeVideoDriver() -> void;
auto initializeAudioDriver() -> void;
auto initializeInputDriver() -> void;
auto updateVideoShader() -> void;
auto connectDevices() -> void;
auto showMessage(string text) -> void;
auto showFrameRate(string text) -> void;
auto updateStatus() -> void;

View File

@ -1,3 +1,23 @@
auto Program::managedStates() -> string_vector {
if(!emulator->loaded()) return {};
if(gamePath().endsWith("/")) {
return directory::ifiles({statePath(), "managed/"}, "*.bst");
} else {
Decode::ZIP input;
if(input.open(statePath())) {
string_vector filenames;
for(auto& file : input.file) {
if(file.name.match("managed/*.bst")) filenames.append(file.name);
}
filenames.isort();
return filenames;
}
}
return {};
}
auto Program::loadState(string filename) -> bool {
if(!emulator->loaded()) return false;
@ -44,10 +64,7 @@ auto Program::saveState(string filename) -> bool {
} else {
string location = {filename, ".bst"};
struct State {
string name;
vector<uint8_t> memory;
};
struct State { string name; vector<uint8_t> memory; };
vector<State> states;
Decode::ZIP input;
@ -76,3 +93,70 @@ auto Program::saveRecoveryState() -> bool {
this->statusTime = statusTime;
this->statusMessage = statusMessage;
}
auto Program::removeState(string filename) -> bool {
if(!emulator->loaded()) return false;
if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"};
return file::remove(location);
} else {
bool found = false;
string location = {filename, ".bst"};
struct State { string name; vector<uint8_t> memory; };
vector<State> states;
Decode::ZIP input;
if(input.open(statePath())) {
for(auto& file : input.file) {
if(file.name == location) { found = true; continue; }
states.append({file.name, input.extract(file)});
}
}
if(states) {
Encode::ZIP output{statePath()};
for(auto& state : states) {
output.append(state.name, state.memory.data(), state.memory.size());
}
} else {
//remove .bsz file if there are no states left in the archive
file::remove(statePath());
}
return found;
}
}
auto Program::renameState(string from, string to) -> bool {
if(!emulator->loaded()) return false;
if(gamePath().endsWith("/")) {
from = {statePath(), from, ".bst"};
to = {statePath(), to, ".bst."};
return file::rename(from, to);
} else {
bool found = false;
from = {from, ".bst"};
to = {to, ".bst"};
struct State { string name; vector<uint8_t> memory; };
vector<State> states;
Decode::ZIP input;
if(input.open(statePath())) {
for(auto& file : input.file) {
if(file.name == from) { found = true; file.name = to; }
states.append({file.name, input.extract(file)});
}
}
Encode::ZIP output{statePath()};
for(auto& state : states) {
output.append(state.name, state.memory.data(), state.memory.size());
}
return found;
}
}

View File

@ -1,70 +1,3 @@
auto Program::initializeVideoDriver() -> void {
video = Video::create(settings["Video/Driver"].text());
video->setContext(presentation->viewport.handle());
video->setExclusive(false);
video->setBlocking(settings["Video/Blocking"].boolean());
if(video->ready()) {
presentation->clearViewport();
updateVideoShader();
}
video->onUpdate([&](uint width, uint height) {
if(!emulator->loaded()) presentation->clearViewport();
});
}
auto Program::initializeAudioDriver() -> void {
audio = Audio::create(settings["Audio/Driver"].text());
audio->setContext(presentation->viewport.handle());
audio->setExclusive(false);
audio->setBlocking(settings["Audio/Blocking"].boolean());
if(!audio->availableDevices().find(settings["Audio/Device"].text())) {
settings["Audio/Device"].setValue(audio->availableDevices()(0));
}
audio->setDevice(settings["Audio/Device"].text());
if(!audio->availableFrequencies().find(settings["Audio/Frequency"].real())) {
settings["Audio/Frequency"].setValue(audio->availableFrequencies()(0));
}
audio->setFrequency(settings["Audio/Frequency"].real());
if(!audio->availableLatencies().find(settings["Audio/Latency"].natural())) {
settings["Audio/Latency"].setValue(audio->availableLatencies()(0));
}
audio->setLatency(settings["Audio/Latency"].natural());
audio->setChannels(2);
}
auto Program::initializeInputDriver() -> void {
input = Input::create(settings["Input/Driver"].text());
input->setContext(presentation->viewport.handle());
}
auto Program::updateVideoShader() -> void {
if(settings["Video/Driver"].text() == "OpenGL"
&& settings["Video/Shader"].text() != "None"
&& settings["Video/Shader"].text() != "Blur"
) {
video->setSmooth(false);
video->setShader(settings["Video/Shader"].text());
} else {
video->setSmooth(settings["Video/Shader"].text() == "Blur");
video->setShader("");
}
}
auto Program::connectDevices() -> void {
for(auto& port : emulator->ports) {
auto path = string{"Emulator/", port.name}.replace(" ", "");
auto name = settings(path).text();
for(auto& device : port.devices) {
if(device.name == name) {
emulator->connect(port.id, device.id);
break;
}
}
}
}
auto Program::showMessage(string text) -> void {
statusTime = chrono::timestamp();
statusMessage = text;

View File

@ -0,0 +1,54 @@
auto Program::updateVideoDriver() -> void {
video = Video::create(settings["Video/Driver"].text());
video->setContext(presentation->viewport.handle());
video->setExclusive(false);
updateVideoBlocking();
updateVideoShader();
if(video->ready()) {
presentation->clearViewport();
updateVideoShader();
}
video->onUpdate([&](uint width, uint height) {
if(!emulator->loaded()) presentation->clearViewport();
});
settingsWindow->advanced.updateVideoDriver();
if(!video->ready()) {
MessageDialog({
"Error: failed to initialize [", settings["Video/Driver"].text(), "] video driver."
}).error();
settings["Video/Driver"].setValue("None");
return updateVideoDriver();
}
}
auto Program::updateVideoBlocking() -> void {
video->setBlocking(settings["Video/Blocking"].boolean());
}
auto Program::updateVideoShader() -> void {
if(settings["Video/Driver"].text() == "OpenGL"
&& settings["Video/Shader"].text() != "None"
&& settings["Video/Shader"].text() != "Blur"
) {
video->setSmooth(false);
video->setShader(settings["Video/Shader"].text());
} else {
video->setSmooth(settings["Video/Shader"].text() == "Blur");
video->setShader("");
}
}
auto Program::updateVideoPalette() -> void {
emulator->set("Color Emulation", false);
double luminance = settings["Video/Luminance"].natural() / 100.0;
double saturation = settings["Video/Saturation"].natural() / 100.0;
double gamma = settings["Video/Gamma"].natural() / 100.0;
Emulator::video.setLuminance(luminance);
Emulator::video.setSaturation(saturation);
Emulator::video.setGamma(gamma);
Emulator::video.setPalette();
}

View File

@ -6,12 +6,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
driversLabel.setText("Drivers").setFont(Font().setBold());
videoDriverLabel.setText("Video:");
for(auto& driver : Video::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
videoDriverOption.append(item);
if(video->driver() == driver) item.setSelected();
}
videoDriverOption.onChange([&] {
auto item = videoDriverOption.selected();
settings["Video/Driver"].setValue(item.text());
@ -24,29 +18,13 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
program->saveRecoveryState();
settings["Crashed"].setValue(true);
settings.save();
program->initializeVideoDriver();
if(!video->ready()) {
MessageDialog({
"Error: failed to initialize ", item.text(), " video driver."
}).setParent(*settingsWindow).error();
settings["Video/Driver"].setValue("None");
program->initializeVideoDriver();
for(auto item : videoDriverOption.items()) {
if(video->driver() == item.text()) item.setSelected();
}
}
program->updateVideoDriver();
settings["Crashed"].setValue(false);
settings.save();
}
});
audioDriverLabel.setText("Audio:");
for(auto& driver : Audio::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
audioDriverOption.append(item);
if(audio->driver() == driver) item.setSelected();
}
audioDriverOption.onChange([&] {
auto item = audioDriverOption.selected();
settings["Audio/Driver"].setValue(item.text());
@ -59,29 +37,13 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
program->saveRecoveryState();
settings["Crashed"].setValue(true);
settings.save();
program->initializeAudioDriver();
if(!audio->ready()) {
MessageDialog({
"Error: failed to initialize ", item.text(), " audio driver."
}).setParent(*settingsWindow).error();
settings["Audio/Driver"].setValue("None");
program->initializeAudioDriver();
for(auto item : audioDriverOption.items()) {
if(audio->driver() == item.text()) item.setSelected();
}
}
program->updateAudioDriver();
settings["Crashed"].setValue(false);
settings.save();
}
});
inputDriverLabel.setText("Input:");
for(auto& driver : Input::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
inputDriverOption.append(item);
if(input->driver() == driver) item.setSelected();
}
inputDriverOption.onChange([&] {
auto item = inputDriverOption.selected();
settings["Input/Driver"].setValue(item.text());
@ -94,17 +56,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
program->saveRecoveryState();
settings["Crashed"].setValue(true);
settings.save();
program->initializeInputDriver();
if(!input->ready()) {
MessageDialog({
"Error: failed to initialize ", item.text(), " input driver."
}).setParent(*settingsWindow).error();
settings["Input/Driver"].setValue("None");
program->initializeInputDriver();
for(auto item : inputDriverOption.items()) {
if(input->driver() == item.text()) item.setSelected();
}
}
program->updateInputDriver();
settings["Crashed"].setValue(false);
settings.save();
}
@ -123,5 +75,35 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
settings["Emulator/Hack/FastSuperFX"].setValue({superFXClock.position() * 10 + 100, "%"});
superFXValue.setText(settings["Emulator/Hack/FastSuperFX"].text());
}).doChange();
hacksNote.setFont(Font().setItalic()).setText("Note: hack setting changes do not take effect until after reloading games.");
hacksNote.setForegroundColor({224, 0, 0}).setText("Note: hack setting changes do not take effect until after reloading games.");
}
auto AdvancedSettings::updateVideoDriver() -> void {
videoDriverOption.reset();
for(auto& driver : Video::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
videoDriverOption.append(item);
if(video && video->driver() == driver) item.setSelected();
}
}
auto AdvancedSettings::updateAudioDriver() -> void {
audioDriverOption.reset();
for(auto& driver : Audio::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
audioDriverOption.append(item);
if(audio && audio->driver() == driver) item.setSelected();
}
}
auto AdvancedSettings::updateInputDriver() -> void {
inputDriverOption.reset();
for(auto& driver : Input::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
inputDriverOption.append(item);
if(input && input->driver() == driver) item.setSelected();
}
}

View File

@ -1,4 +1,87 @@
AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Device::Speaker);
setText("Audio");
layout.setMargin(5);
driverLabel.setFont(Font().setBold()).setText("Driver");
deviceLabel.setText("Device:");
deviceList.onChange([&] {
settings["Audio/Device"].setValue(deviceList.selected().text());
program->updateAudioDevice();
});
frequencyLabel.setText("Frequency:");
frequencyList.onChange([&] {
settings["Audio/Frequency"].setValue(frequencyList.selected().text());
program->updateAudioFrequency();
});
latencyLabel.setText("Latency:");
latencyList.onChange([&] {
settings["Audio/Latency"].setValue(latencyList.selected().text());
program->updateAudioLatency();
});
exclusiveMode.setText("Exclusive mode").setChecked(settings["Audio/Exclusive"].boolean()).onToggle([&] {
settings["Audio/Exclusive"].setValue(exclusiveMode.checked());
program->updateAudioExclusive();
});
effectsLabel.setFont(Font().setBold()).setText("Effects");
skewLabel.setText("Skew:");
skewValue.setAlignment(0.5);
skewSlider.setLength(10001).setPosition(settings["Audio/Skew"].integer() + 5000).onChange([&] {
string value = {skewSlider.position() > 5000 ? "+" : "", (int)skewSlider.position() - 5000};
settings["Audio/Skew"].setValue(value);
skewValue.setText(value);
program->updateAudioFrequency();
}).doChange();
volumeLabel.setText("Volume:");
volumeValue.setAlignment(0.5);
volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] {
string value = {volumeSlider.position(), "%"};
settings["Audio/Volume"].setValue(value);
volumeValue.setText(value);
program->updateAudioEffects();
}).doChange();
balanceLabel.setText("Balance:");
balanceValue.setAlignment(0.5);
balanceSlider.setLength(101).setPosition(settings["Audio/Balance"].natural()).onChange([&] {
string value = {balanceSlider.position(), "%"};
settings["Audio/Balance"].setValue(value);
balanceValue.setText(value);
program->updateAudioEffects();
}).doChange();
reverb.setText("Reverb").setChecked(settings["Audio/Reverb"].boolean()).onToggle([&] {
settings["Audio/Reverb"].setValue(reverb.checked());
program->updateAudioEffects();
});
}
auto AudioSettings::updateDevice() -> void {
deviceList.reset();
for(auto& device : audio->availableDevices()) {
deviceList.append(ComboButtonItem().setText(device));
if(device == settings["Audio/Device"].text()) {
deviceList.items().right().setSelected();
}
}
}
auto AudioSettings::updateFrequency() -> void {
frequencyList.reset();
for(auto& frequency : audio->availableFrequencies()) {
frequencyList.append(ComboButtonItem().setText((uint)frequency));
if(frequency == settings["Audio/Frequency"].real()) {
frequencyList.items().right().setSelected();
}
}
}
auto AudioSettings::updateLatency() -> void {
latencyList.reset();
for(auto& latency : audio->availableLatencies()) {
latencyList.append(ComboButtonItem().setText(latency));
if(latency == settings["Audio/Latency"].natural()) {
latencyList.items().right().setSelected();
}
}
}

View File

@ -21,9 +21,6 @@ HotkeySettings::HotkeySettings(TabFrame* parent) : TabFrameItem(parent) {
}
refreshMappings();
});
reloadMappings();
refreshMappings();
}
auto HotkeySettings::reloadMappings() -> void {
@ -38,7 +35,8 @@ auto HotkeySettings::reloadMappings() -> void {
.append(TableViewCell())
);
}
mappingList.resizeColumns();
refreshMappings();
mappingList.doChange();
}
auto HotkeySettings::refreshMappings() -> void {

View File

@ -35,8 +35,6 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
}
refreshMappings();
});
reloadPorts();
}
auto InputSettings::updateControls() -> void {

View File

@ -20,6 +20,9 @@ Settings::Settings() {
set("Video/Exclusive", false);
set("Video/Blocking", false);
set("Video/Shader", "Blur");
set("Video/Luminance", "100%");
set("Video/Saturation", "100%");
set("Video/Gamma", "150%");
set("Audio/Driver", Audio::safestDriver());
set("Audio/Exclusive", false);
@ -28,6 +31,10 @@ Settings::Settings() {
set("Audio/Frequency", 48000.0);
set("Audio/Latency", 0);
set("Audio/Mute", false);
set("Audio/Skew", "0");
set("Audio/Volume", "100%");
set("Audio/Balance", "50%");
set("Audio/Reverb", false);
set("Input/Driver", Input::safestDriver());
set("Input/Frequency", 5);
@ -38,7 +45,6 @@ Settings::Settings() {
set("View/OverscanCropping", true);
set("View/IntegralScaling", true);
set("View/BlurEmulation", true);
set("View/ColorEmulation", true);
set("Path/Games", "");
set("Path/Patches", "");

View File

@ -5,10 +5,57 @@ struct Settings : Markup::Node {
struct VideoSettings : TabFrameItem {
VideoSettings(TabFrame*);
public:
VerticalLayout layout{this};
Label colorAdjustmentLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout luminanceLayout{&layout, Size{~0, 0}};
Label luminanceLabel{&luminanceLayout, Size{65, 0}};
Label luminanceValue{&luminanceLayout, Size{50, 0}};
HorizontalSlider luminanceSlider{&luminanceLayout, Size{~0, 0}};
HorizontalLayout saturationLayout{&layout, Size{~0, 0}};
Label saturationLabel{&saturationLayout, Size{65, 0}};
Label saturationValue{&saturationLayout, Size{50, 0}};
HorizontalSlider saturationSlider{&saturationLayout, Size{~0, 0}};
HorizontalLayout gammaLayout{&layout, Size{~0, 0}};
Label gammaLabel{&gammaLayout, Size{65, 0}};
Label gammaValue{&gammaLayout, Size{50, 0}};
HorizontalSlider gammaSlider{&gammaLayout, Size{~0, 0}};
Label fullscreenLabel{&layout, Size{~0, 0}, 2};
CheckLabel exclusiveMode{&layout, Size{~0, 0}};
};
struct AudioSettings : TabFrameItem {
AudioSettings(TabFrame*);
auto updateDevice() -> void;
auto updateFrequency() -> void;
auto updateLatency() -> void;
public:
VerticalLayout layout{this};
Label driverLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout driverLayout{&layout, Size{~0, 0}};
Label deviceLabel{&driverLayout, Size{0, 0}};
ComboButton deviceList{&driverLayout, Size{~0, 0}};
Label frequencyLabel{&driverLayout, Size{0, 0}};
ComboButton frequencyList{&driverLayout, Size{80, 0}};
Label latencyLabel{&driverLayout, Size{0, 0}};
ComboButton latencyList{&driverLayout, Size{80, 0}};
CheckLabel exclusiveMode{&layout, Size{~0, 0}};
Label effectsLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout skewLayout{&layout, Size{~0, 0}};
Label skewLabel{&skewLayout, Size{65, 0}};
Label skewValue{&skewLayout, Size{50, 0}};
HorizontalSlider skewSlider{&skewLayout, Size{~0, 0}};
HorizontalLayout volumeLayout{&layout, Size{~0, 0}};
Label volumeLabel{&volumeLayout, Size{65, 0}};
Label volumeValue{&volumeLayout, Size{50, 0}};
HorizontalSlider volumeSlider{&volumeLayout, Size{~0, 0}};
HorizontalLayout balanceLayout{&layout, Size{~0, 0}};
Label balanceLabel{&balanceLayout, Size{65, 0}};
Label balanceValue{&balanceLayout, Size{50, 0}};
HorizontalSlider balanceSlider{&balanceLayout, Size{~0, 0}};
CheckLabel reverb{&layout, Size{~0, 0}};
};
struct InputSettings : TabFrameItem {
@ -104,6 +151,9 @@ public:
struct AdvancedSettings : TabFrameItem {
AdvancedSettings(TabFrame*);
auto updateVideoDriver() -> void;
auto updateAudioDriver() -> void;
auto updateInputDriver() -> void;
public:
VerticalLayout layout{this};

View File

@ -1,4 +1,37 @@
VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Device::Display);
setText("Video");
layout.setMargin(5);
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
luminanceLabel.setText("Luminance:");
luminanceValue.setAlignment(0.5);
luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] {
string value = {luminanceSlider.position(), "%"};
settings["Video/Luminance"].setValue(value);
luminanceValue.setText(value);
program->updateVideoPalette();
}).doChange();
saturationLabel.setText("Saturation:");
saturationValue.setAlignment(0.5);
saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] {
string value = {saturationSlider.position(), "%"};
settings["Video/Saturation"].setValue(value);
saturationValue.setText(value);
program->updateVideoPalette();
}).doChange();
gammaLabel.setText("Gamma:");
gammaValue.setAlignment(0.5);
gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] {
string value = {100 + gammaSlider.position(), "%"};
settings["Video/Gamma"].setValue(value);
gammaValue.setText(value);
program->updateVideoPalette();
}).doChange();
fullscreenLabel.setFont(Font().setBold()).setText("Fullscreen");
exclusiveMode.setText("Exclusive mode").setChecked(settings["Video/Exclusive"].boolean()).onToggle([&] {
settings["Video/Exclusive"].setValue(exclusiveMode.checked());
});
}

View File

@ -107,27 +107,10 @@ auto StateManager::loadStates() -> void {
stateList.append(TableViewHeader().setVisible(false)
.append(TableViewColumn().setExpandable())
);
if(program->gamePath().endsWith("/")) {
for(auto filename : directory::ifiles({program->statePath(), "managed/"}, "*.bst")) {
stateList.append(TableViewItem()
.append(TableViewCell().setText(filename.trimRight(".bst", 1L)))
);
}
} else {
Decode::ZIP input;
if(input.open(program->statePath())) {
string_vector states;
for(auto& file : input.file) {
if(!file.name.match("managed/*.bst")) continue;
states.append(Location::prefix(file.name));
}
states.isort();
for(auto& state : states) {
stateList.append(TableViewItem()
.append(TableViewCell().setText(state))
);
}
}
for(auto filename : program->managedStates()) {
stateList.append(TableViewItem()
.append(TableViewCell().setText(filename.trimRight(".bst", 1L)))
);
}
stateList.resizeColumns().doChange();
}
@ -143,10 +126,10 @@ auto StateManager::createState(string name) -> void {
auto StateManager::modifyState(string name) -> void {
if(auto item = stateList.selected()) {
string from = {program->statePath(), "managed/", item.cell(0).text(), ".bst"};
string to = {program->statePath(), "managed/", name, ".bst"};
string from = {"managed/", item.cell(0).text()};
string to = {"managed/", name};
if(from != to) {
file::rename(from, to);
program->renameState(from, to);
loadStates();
for(auto item : stateList.items()) {
if(item.cell(0).text() == name) item.setSelected();
@ -161,8 +144,7 @@ auto StateManager::removeStates() -> void {
if(MessageDialog("Are you sure you want to permanently remove the selected state(s)?")
.setParent(*toolsWindow).question() == "Yes") {
for(auto item : batched) {
string location = {program->statePath(), "managed/", item.cell(0).text(), ".bst"};
file::remove(location);
program->removeState({"managed/", item.cell(0).text()});
}
loadStates();
}