mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r23 release.
byuu says: Changelog: - bsnes: work on the new GUI; can load games now, but no input support yet - icarus: heuristics game/label uses filename instead of internal header name
This commit is contained in:
parent
f5b96e9e9e
commit
ea11c6d098
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "106.22";
|
||||
static const string Version = "106.23";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
|
|
@ -4,7 +4,7 @@ system name:Super Famicom
|
|||
smp
|
||||
rom name=ipl.rom size=64
|
||||
ppu1 version=1
|
||||
ram name=video.ram size=0x8000 volatile
|
||||
ram name=video.ram size=0x10000 volatile
|
||||
ram name=object.ram size=544 volatile
|
||||
ppu2 version=3
|
||||
ram name=palette.ram size=512 volatile
|
||||
|
|
|
@ -5,7 +5,7 @@ include sfc/GNUmakefile
|
|||
include gb/GNUmakefile
|
||||
include processor/GNUmakefile
|
||||
|
||||
ui_objects := ui-bsnes ui-program ui-presentation
|
||||
ui_objects := ui-bsnes ui-program ui-input ui-presentation ui-settings
|
||||
ui_objects += ruby hiro
|
||||
ui_objects += $(if $(call streq,$(platform),windows),ui-resource)
|
||||
|
||||
|
@ -39,7 +39,9 @@ obj/hiro.o: ../hiro/hiro.cpp $(call rwildcard,../hiro/)
|
|||
|
||||
obj/ui-bsnes.o: $(ui)/bsnes.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-program.o: $(ui)/program/program.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
|
||||
|
||||
obj/ui-resource.o:
|
||||
$(windres) data/bsnes.rc obj/ui-resource.o
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "bsnes.hpp"
|
||||
#include <sfc/interface/interface.hpp>
|
||||
unique_pointer<Video> video;
|
||||
unique_pointer<Audio> audio;
|
||||
unique_pointer<Input> input;
|
||||
Emulator::Interface* interface = nullptr;
|
||||
unique_pointer<Emulator::Interface> emulator;
|
||||
|
||||
auto locate(string name) -> string {
|
||||
string location = {Path::program(), name};
|
||||
|
@ -18,6 +19,7 @@ auto locate(string name) -> string {
|
|||
#include <nall/main.hpp>
|
||||
auto nall::main(string_vector args) -> void {
|
||||
Application::setName("bsnes");
|
||||
emulator = new SuperFamicom::Interface;
|
||||
new Program(args);
|
||||
Application::run();
|
||||
}
|
||||
|
|
|
@ -9,9 +9,11 @@ extern unique_pointer<Audio> audio;
|
|||
extern unique_pointer<Input> input;
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
extern Emulator::Interface* emulator;
|
||||
extern unique_pointer<Emulator::Interface> emulator;
|
||||
|
||||
#include "program/program.hpp"
|
||||
#include "input/input.hpp"
|
||||
#include "presentation/presentation.hpp"
|
||||
#include "settings/settings.hpp"
|
||||
|
||||
auto locate(string name) -> string;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
#include "../bsnes.hpp"
|
||||
unique_pointer<InputManager> inputManager;
|
||||
|
||||
auto InputMapping::poll() -> int16 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
InputManager::InputManager() {
|
||||
inputManager = this;
|
||||
frequency = 5;
|
||||
|
||||
for(auto& port : emulator->ports) {
|
||||
InputPort inputPort{port.id, port.name};
|
||||
for(auto& device : port.devices) {
|
||||
InputDevice inputDevice{device.id, device.name};
|
||||
for(auto& input : device.inputs) {
|
||||
InputMapping inputMapping;
|
||||
inputMapping.name = input.name;
|
||||
inputMapping.type = input.type;
|
||||
inputMapping.path = string{inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
|
||||
inputDevice.mappings.append(inputMapping);
|
||||
}
|
||||
inputPort.devices.append(inputDevice);
|
||||
}
|
||||
ports.append(inputPort);
|
||||
}
|
||||
|
||||
input->onChange({&InputManager::onChange, this});
|
||||
}
|
||||
|
||||
auto InputManager::poll() -> void {
|
||||
//polling actual hardware devices is time-consuming; skip if poll was called too recently
|
||||
auto thisPoll = chrono::millisecond();
|
||||
if(thisPoll - lastPoll < frequency) return;
|
||||
lastPoll = thisPoll;
|
||||
|
||||
devices = input->poll();
|
||||
}
|
||||
|
||||
auto InputManager::onChange(shared_pointer<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void {
|
||||
}
|
||||
|
||||
auto InputManager::mapping(uint port, uint device, uint input) -> maybe<InputMapping&> {
|
||||
if(!emulator) return nothing;
|
||||
for(auto& inputPort : ports) {
|
||||
if(inputPort.id != port) continue;
|
||||
for(auto& inputDevice : inputPort.devices) {
|
||||
if(inputDevice.id != device) continue;
|
||||
if(input >= inputDevice.mappings.size()) continue;
|
||||
return inputDevice.mappings[input];
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
struct InputMapping {
|
||||
auto poll() -> int16;
|
||||
|
||||
string path; //configuration file key path
|
||||
string name; //input name (human readable)
|
||||
uint type = 0;
|
||||
string assignment = "None";
|
||||
};
|
||||
|
||||
struct InputDevice {
|
||||
uint id;
|
||||
string name;
|
||||
vector<InputMapping> mappings;
|
||||
};
|
||||
|
||||
struct InputPort {
|
||||
uint id;
|
||||
string name;
|
||||
vector<InputDevice> devices;
|
||||
};
|
||||
|
||||
struct InputManager {
|
||||
InputManager();
|
||||
auto poll() -> void;
|
||||
auto onChange(shared_pointer<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void;
|
||||
auto mapping(uint port, uint device, uint input) -> maybe<InputMapping&>;
|
||||
|
||||
private:
|
||||
vector<shared_pointer<HID::Device>> devices;
|
||||
vector<InputPort> ports;
|
||||
|
||||
uint64 lastPoll; //time in milliseconds since last call to poll()
|
||||
uint64 frequency; //minimum time in milliseconds before poll() can be called again
|
||||
};
|
||||
|
||||
extern unique_pointer<InputManager> inputManager;
|
|
@ -6,12 +6,57 @@ unique_pointer<Presentation> presentation;
|
|||
Presentation::Presentation() {
|
||||
presentation = this;
|
||||
|
||||
fileMenu.setText("File");
|
||||
systemMenu.setText("System");
|
||||
load.setText("Load Game ...").onActivate([&] {
|
||||
BrowserDialog dialog;
|
||||
dialog.setTitle("Load Game");
|
||||
dialog.setPath({Path::user(), "bsnes/"});
|
||||
dialog.setFilters({string{"SNES Games|*.sfc:*.smc"}});
|
||||
if(auto location = dialog.openFile()) {
|
||||
program->load(location);
|
||||
}
|
||||
});
|
||||
reset.setText("Reset").setEnabled(false).onActivate([&] {
|
||||
if(emulator->loaded()) emulator->reset();
|
||||
});
|
||||
controllerPort1.setText("Controller Port 1");
|
||||
controllerPort2.setText("Controller Port 2");
|
||||
for(auto& port : emulator->ports) {
|
||||
Menu* menu = nullptr;
|
||||
if(port.name == "Controller Port 1") menu = &controllerPort1;
|
||||
if(port.name == "Controller Port 2") menu = &controllerPort2;
|
||||
if(!menu) continue;
|
||||
|
||||
Group devices;
|
||||
for(auto& device : port.devices) {
|
||||
if(device.name == "None") continue;
|
||||
MenuRadioItem item{menu};
|
||||
item.setText(device.name).onActivate([=] {
|
||||
emulator->connect(port.id, device.id);
|
||||
});
|
||||
devices.append(item);
|
||||
}
|
||||
}
|
||||
quit.setText("Quit").onActivate([&] { program->quit(); });
|
||||
|
||||
settingsMenu.setText("Settings");
|
||||
viewMenu.setText("View");
|
||||
smallView.setText("Small").onActivate([&] {});
|
||||
mediumView.setText("Medium").onActivate([&] {});
|
||||
largeView.setText("Large").onActivate([&] {});
|
||||
aspectCorrection.setText("Aspect Correction").onToggle([&] {});
|
||||
integralScaling.setText("Integral Scaling").onToggle([&] {});
|
||||
muteAudio.setText("Mute Audio").setChecked(settings["Audio/Mute"].boolean()).onToggle([&] {
|
||||
settings["Audio/Mute"].setValue(muteAudio.checked());
|
||||
});
|
||||
configuration.setText("Configuration ...").onActivate([&] { settingsWindow->setVisible().setFocused(); });
|
||||
|
||||
toolsMenu.setText("Tools");
|
||||
saveState.setText("Save State").setEnabled(false);
|
||||
loadState.setText("Load State").setEnabled(false);
|
||||
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
||||
if(pauseEmulation.checked()) audio->clear();
|
||||
});
|
||||
|
||||
helpMenu.setText("Help");
|
||||
about.setText("About ...").onActivate([&] {
|
||||
|
@ -24,7 +69,7 @@ Presentation::Presentation() {
|
|||
|
||||
setTitle({"bsnes v", Emulator::Version});
|
||||
setBackgroundColor({0, 0, 0});
|
||||
setSize({640, 480});
|
||||
setSize({512, 480});
|
||||
setCentered();
|
||||
|
||||
#if defined(PLATFORM_MACOS)
|
||||
|
@ -33,3 +78,18 @@ Presentation::Presentation() {
|
|||
Application::Cocoa::onQuit([&] { doClose(); });
|
||||
#endif
|
||||
}
|
||||
|
||||
auto Presentation::clearViewport() -> void {
|
||||
uint32_t* output;
|
||||
uint length;
|
||||
uint width = viewport.geometry().width();
|
||||
uint height = viewport.geometry().height();
|
||||
if(video->lock(output, length, width, height)) {
|
||||
for(uint y : range(height)) {
|
||||
auto line = output + y * (length >> 2);
|
||||
for(uint x : range(width)) *line++ = 0xff000000;
|
||||
}
|
||||
video->unlock();
|
||||
video->output();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,17 +10,37 @@ struct AboutWindow : Window {
|
|||
|
||||
struct Presentation : Window {
|
||||
Presentation();
|
||||
auto clearViewport() -> void;
|
||||
|
||||
MenuBar menuBar{this};
|
||||
Menu fileMenu{&menuBar};
|
||||
MenuItem quit{&fileMenu};
|
||||
Menu systemMenu{&menuBar};
|
||||
MenuItem load{&systemMenu};
|
||||
MenuItem reset{&systemMenu};
|
||||
MenuSeparator portSeparator{&systemMenu};
|
||||
Menu controllerPort1{&systemMenu};
|
||||
Menu controllerPort2{&systemMenu};
|
||||
MenuSeparator quitSeparator{&systemMenu};
|
||||
MenuItem quit{&systemMenu};
|
||||
Menu settingsMenu{&menuBar};
|
||||
Menu viewMenu{&settingsMenu};
|
||||
MenuItem smallView{&viewMenu};
|
||||
MenuItem mediumView{&viewMenu};
|
||||
MenuItem largeView{&viewMenu};
|
||||
MenuSeparator viewSeparator{&viewMenu};
|
||||
MenuCheckItem aspectCorrection{&viewMenu};
|
||||
MenuCheckItem integralScaling{&viewMenu};
|
||||
MenuCheckItem muteAudio{&settingsMenu};
|
||||
MenuSeparator muteSeparator{&settingsMenu};
|
||||
MenuItem configuration{&settingsMenu};
|
||||
Menu toolsMenu{&menuBar};
|
||||
Menu saveState{&toolsMenu};
|
||||
Menu loadState{&toolsMenu};
|
||||
MenuCheckItem pauseEmulation{&toolsMenu};
|
||||
Menu helpMenu{&menuBar};
|
||||
MenuItem about{&helpMenu};
|
||||
|
||||
FixedLayout layout{this};
|
||||
Viewport viewport{&layout, Geometry{0, 0, 1, 1}};
|
||||
Viewport viewport{&layout, Geometry{0, 0, 512, 480}};
|
||||
};
|
||||
|
||||
extern unique_pointer<AboutWindow> aboutWindow;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
static const string systemManifest = R"(
|
||||
system name:Super Famicom
|
||||
cpu version=2
|
||||
ram name=work.ram size=0x20000 volatile
|
||||
smp
|
||||
rom name=ipl.rom size=64
|
||||
ppu1 version=1
|
||||
ram name=video.ram size=0x10000 volatile
|
||||
ram name=object.ram size=544 volatile
|
||||
ppu2 version=3
|
||||
ram name=palette.ram size=512 volatile
|
||||
dsp
|
||||
ram name=apu.ram size=0x10000 volatile
|
||||
)";
|
||||
|
||||
static const uint8_t systemIPLROM[64] = {
|
||||
0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa, 0xf4, 0x8f, 0xbb, 0xf5, 0x78,
|
||||
0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19, 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5,
|
||||
0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab, 0x01, 0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba,
|
||||
0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff,
|
||||
};
|
|
@ -0,0 +1,84 @@
|
|||
#include <icarus/heuristics/heuristics.hpp>
|
||||
#include <icarus/heuristics/heuristics.cpp>
|
||||
#include <icarus/heuristics/super-famicom.cpp>
|
||||
#include <icarus/heuristics/game-boy.cpp>
|
||||
#include <icarus/heuristics/bs-memory.cpp>
|
||||
#include <icarus/heuristics/sufami-turbo.cpp>
|
||||
|
||||
auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file {
|
||||
//print("Open ", id, ": ", name, "\n");
|
||||
|
||||
if(id == 0 && name == "manifest.bml") {
|
||||
return vfs::memory::file::open(systemManifest.data<uint8_t>(), systemManifest.size());
|
||||
}
|
||||
|
||||
if(id == 0 && name == "boards.bml") {
|
||||
string location = {Path::local(), "higan/systems/Super Famicom.sys/boards.bml"};
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
|
||||
if(id == 0 && name == "ipl.rom") {
|
||||
return vfs::memory::file::open(systemIPLROM, 64);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "manifest.bml") {
|
||||
auto data = file::read(context.gameROM);
|
||||
Heuristics::SuperFamicom heuristics{data, context.gameROM};
|
||||
auto manifest = heuristics.manifest();
|
||||
return vfs::memory::file::open(manifest.data<uint8_t>(), manifest.size());
|
||||
}
|
||||
|
||||
if(id == 1 && name == "program.rom") {
|
||||
return vfs::fs::file::open(context.gameROM, mode);
|
||||
}
|
||||
|
||||
if(id == 1 && (name == "save.ram" || name == "upd96050.data.ram")) {
|
||||
return vfs::fs::file::open(context.gameRAM, mode);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Program::load(uint id, string name, string type, string_vector options) -> Emulator::Platform::Load {
|
||||
//print("Load ", id, ": ", name, ", ", type, ", ", options.merge(";"), "\n");
|
||||
|
||||
if(name == "Super Famicom" && type == "sfc") {
|
||||
return {1, "Auto"};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void {
|
||||
uint32_t* output;
|
||||
uint length;
|
||||
|
||||
if(video->lock(output, length, width, height)) {
|
||||
pitch >>= 2;
|
||||
length >>= 2;
|
||||
|
||||
for(auto y : range(height)) {
|
||||
memory::copy(output + y * length, data + y * pitch, width * sizeof(uint32));
|
||||
}
|
||||
|
||||
video->unlock();
|
||||
video->output();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
|
||||
inputManager->poll();
|
||||
if(auto mapping = inputManager->mapping(port, device, input)) {
|
||||
return mapping->poll();
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,24 +1,95 @@
|
|||
#include "../bsnes.hpp"
|
||||
#include <sfc/interface/interface.hpp>
|
||||
#include <gb/interface/interface.hpp>
|
||||
#include "data.cpp"
|
||||
#include "interface.cpp"
|
||||
#include "utility.cpp"
|
||||
unique_pointer<Program> program;
|
||||
|
||||
Program::Program(string_vector args) {
|
||||
program = this;
|
||||
|
||||
Emulator::platform = this;
|
||||
|
||||
new Presentation;
|
||||
presentation->setVisible();
|
||||
|
||||
if(settings["Crashed"].boolean()) {
|
||||
MessageDialog().setText("Driver crash detected. Hardware drivers have been disabled.").information();
|
||||
settings["Video/Driver"].setValue("None");
|
||||
settings["Audio/Driver"].setValue("None");
|
||||
settings["Input/Driver"].setValue("None");
|
||||
}
|
||||
|
||||
settings["Crashed"].setValue(true);
|
||||
settings.save();
|
||||
|
||||
initializeVideoDriver();
|
||||
initializeAudioDriver();
|
||||
initializeInputDriver();
|
||||
|
||||
settings["Crashed"].setValue(false);
|
||||
settings.save();
|
||||
|
||||
new InputManager;
|
||||
new SettingsWindow;
|
||||
new AboutWindow;
|
||||
|
||||
Application::onMain({&Program::main, this});
|
||||
}
|
||||
|
||||
auto Program::main() -> void {
|
||||
inputManager->poll();
|
||||
|
||||
if(emulator->loaded() && !presentation->pauseEmulation.checked()) {
|
||||
emulator->run();
|
||||
} else {
|
||||
usleep(20 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::quit() -> void {
|
||||
unload();
|
||||
settings.save();
|
||||
video.reset();
|
||||
audio.reset();
|
||||
input.reset();
|
||||
Application::quit();
|
||||
}
|
||||
|
||||
auto Program::load(string location) -> void {
|
||||
if(!file::exists(location)) return;
|
||||
unload();
|
||||
|
||||
context.gameROM = location;
|
||||
context.gameRAM = {Location::dir(location), Location::prefix(location), ".srm"};
|
||||
|
||||
auto type = Location::suffix(location).trimLeft(".", 1L);
|
||||
for(auto& media : emulator->media) {
|
||||
if(media.type != type) continue;
|
||||
|
||||
Emulator::audio.reset(2, audio->frequency());
|
||||
if(emulator->load(media.id)) {
|
||||
emulator->power();
|
||||
presentation->setTitle(emulator->title());
|
||||
presentation->reset.setEnabled(true);
|
||||
presentation->saveState.setEnabled(true);
|
||||
presentation->loadState.setEnabled(true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::save() -> void {
|
||||
if(!emulator->loaded()) return;
|
||||
emulator->save();
|
||||
}
|
||||
|
||||
auto Program::unload() -> void {
|
||||
if(!emulator->loaded()) return;
|
||||
emulator->unload();
|
||||
context = {};
|
||||
presentation->setTitle({"bsnes v", Emulator::Version});
|
||||
presentation->reset.setEnabled(false);
|
||||
presentation->saveState.setEnabled(false);
|
||||
presentation->loadState.setEnabled(false);
|
||||
presentation->clearViewport();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,28 @@ struct Program : Emulator::Platform {
|
|||
Program(string_vector args);
|
||||
auto main() -> void;
|
||||
auto quit() -> void;
|
||||
|
||||
auto load(string location) -> void;
|
||||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
//interface.cpp
|
||||
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
|
||||
auto load(uint id, string name, string type, string_vector options = {}) -> Emulator::Platform::Load override;
|
||||
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void override;
|
||||
auto audioSample(const double* samples, uint channels) -> void override;
|
||||
auto inputPoll(uint port, uint device, uint input) -> int16 override;
|
||||
|
||||
//utility.cpp
|
||||
auto initializeVideoDriver() -> void;
|
||||
auto initializeAudioDriver() -> void;
|
||||
auto initializeInputDriver() -> void;
|
||||
|
||||
private:
|
||||
struct Context {
|
||||
string gameROM; //program.rom
|
||||
string gameRAM; //save.ram or upd96050.data.ram
|
||||
} context;
|
||||
};
|
||||
|
||||
extern unique_pointer<Program> program;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
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()) {
|
||||
MessageDialog().setText("Failed to initialize video driver").warning();
|
||||
video = Video::create("None");
|
||||
}
|
||||
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());
|
||||
audio->setChannels(2);
|
||||
audio->setFrequency(48000.0);
|
||||
audio->setLatency(0);
|
||||
if(!audio->ready()) {
|
||||
MessageDialog().setText("Failed to initialize audio driver").warning();
|
||||
audio = Audio::create("None");
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::initializeInputDriver() -> void {
|
||||
input = Input::create(settings["Input/Driver"].boolean());
|
||||
input->setContext(presentation->viewport.handle());
|
||||
if(!input->ready()) {
|
||||
MessageDialog().setText("Failed to initialize input driver").warning();
|
||||
input = Input::create("None");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#include "../bsnes.hpp"
|
||||
Settings settings;
|
||||
unique_pointer<SettingsWindow> settingsWindow;
|
||||
|
||||
Settings::Settings() {
|
||||
Markup::Node::operator=(BML::unserialize(string::read(locate("settings.bml"))));
|
||||
|
||||
auto set = [&](string name, string value) {
|
||||
//create node and set to default value only if it does not already exist
|
||||
if(!operator[](name)) operator()(name).setValue(value);
|
||||
};
|
||||
|
||||
set("Video/Driver", Video::safestDriver());
|
||||
set("Video/Blocking", false);
|
||||
|
||||
set("Audio/Driver", Audio::safestDriver());
|
||||
set("Audio/Blocking", true);
|
||||
set("Audio/Mute", false);
|
||||
|
||||
set("Input/Driver", Input::safestDriver());
|
||||
|
||||
set("Crashed", false);
|
||||
}
|
||||
|
||||
auto Settings::save() -> void {
|
||||
file::write(locate("settings.bml"), BML::serialize(*this));
|
||||
}
|
||||
|
||||
SettingsWindow::SettingsWindow() {
|
||||
settingsWindow = this;
|
||||
|
||||
layout.setMargin(5);
|
||||
|
||||
setTitle("Settings");
|
||||
setSize({600, 400});
|
||||
setAlignment({0.0, 1.0});
|
||||
setDismissable();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
struct Settings : Markup::Node {
|
||||
Settings();
|
||||
auto save() -> void;
|
||||
};
|
||||
|
||||
struct SettingsWindow : Window {
|
||||
SettingsWindow();
|
||||
|
||||
VerticalLayout layout{this};
|
||||
TabFrame panel{&layout, Size{~0, ~0}};
|
||||
TabFrameItem placeholder{&panel};
|
||||
};
|
||||
|
||||
extern Settings settings;
|
||||
extern unique_pointer<SettingsWindow> settingsWindow;
|
|
@ -189,6 +189,7 @@ auto InputMapping::displayName() -> string {
|
|||
|
||||
InputManager::InputManager() {
|
||||
inputManager = this;
|
||||
input->onChange({&InputManager::onChange, this});
|
||||
frequency = max(1u, settings["Input/Frequency"].natural());
|
||||
|
||||
for(auto& emulator : program->emulators) {
|
||||
|
|
|
@ -56,8 +56,6 @@ auto Program::initializeInputDriver() -> void {
|
|||
input = Input::create(settings["Input/Driver"].text());
|
||||
input->setContext(presentation->viewport.handle());
|
||||
|
||||
input->onChange({&InputManager::onChange, &inputManager()});
|
||||
|
||||
if(!input->ready()) {
|
||||
MessageDialog().setText("Failed to initialize input driver").warning();
|
||||
input = Input::create("None");
|
||||
|
|
|
@ -61,7 +61,7 @@ auto SuperFamicom::manifest() const -> string {
|
|||
string output;
|
||||
output.append("game\n");
|
||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
||||
output.append(" label: ", label(), "\n");
|
||||
output.append(" label: ", Location::prefix(location), "\n");
|
||||
output.append(" name: ", Location::prefix(location), "\n");
|
||||
output.append(" region: ", region(), "\n");
|
||||
output.append(" revision: ", revision(), "\n");
|
||||
|
|
Loading…
Reference in New Issue