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 {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
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 Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
|
|
@ -4,7 +4,7 @@ system name:Super Famicom
|
||||||
smp
|
smp
|
||||||
rom name=ipl.rom size=64
|
rom name=ipl.rom size=64
|
||||||
ppu1 version=1
|
ppu1 version=1
|
||||||
ram name=video.ram size=0x8000 volatile
|
ram name=video.ram size=0x10000 volatile
|
||||||
ram name=object.ram size=544 volatile
|
ram name=object.ram size=544 volatile
|
||||||
ppu2 version=3
|
ppu2 version=3
|
||||||
ram name=palette.ram size=512 volatile
|
ram name=palette.ram size=512 volatile
|
||||||
|
|
|
@ -5,7 +5,7 @@ include sfc/GNUmakefile
|
||||||
include gb/GNUmakefile
|
include gb/GNUmakefile
|
||||||
include processor/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 += ruby hiro
|
||||||
ui_objects += $(if $(call streq,$(platform),windows),ui-resource)
|
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-bsnes.o: $(ui)/bsnes.cpp $(call rwildcard,$(ui)/)
|
||||||
obj/ui-program.o: $(ui)/program/program.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-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
|
||||||
|
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
|
||||||
|
|
||||||
obj/ui-resource.o:
|
obj/ui-resource.o:
|
||||||
$(windres) data/bsnes.rc obj/ui-resource.o
|
$(windres) data/bsnes.rc obj/ui-resource.o
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#include "bsnes.hpp"
|
#include "bsnes.hpp"
|
||||||
|
#include <sfc/interface/interface.hpp>
|
||||||
unique_pointer<Video> video;
|
unique_pointer<Video> video;
|
||||||
unique_pointer<Audio> audio;
|
unique_pointer<Audio> audio;
|
||||||
unique_pointer<Input> input;
|
unique_pointer<Input> input;
|
||||||
Emulator::Interface* interface = nullptr;
|
unique_pointer<Emulator::Interface> emulator;
|
||||||
|
|
||||||
auto locate(string name) -> string {
|
auto locate(string name) -> string {
|
||||||
string location = {Path::program(), name};
|
string location = {Path::program(), name};
|
||||||
|
@ -18,6 +19,7 @@ auto locate(string name) -> string {
|
||||||
#include <nall/main.hpp>
|
#include <nall/main.hpp>
|
||||||
auto nall::main(string_vector args) -> void {
|
auto nall::main(string_vector args) -> void {
|
||||||
Application::setName("bsnes");
|
Application::setName("bsnes");
|
||||||
|
emulator = new SuperFamicom::Interface;
|
||||||
new Program(args);
|
new Program(args);
|
||||||
Application::run();
|
Application::run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,11 @@ extern unique_pointer<Audio> audio;
|
||||||
extern unique_pointer<Input> input;
|
extern unique_pointer<Input> input;
|
||||||
|
|
||||||
#include <emulator/emulator.hpp>
|
#include <emulator/emulator.hpp>
|
||||||
extern Emulator::Interface* emulator;
|
extern unique_pointer<Emulator::Interface> emulator;
|
||||||
|
|
||||||
#include "program/program.hpp"
|
#include "program/program.hpp"
|
||||||
|
#include "input/input.hpp"
|
||||||
#include "presentation/presentation.hpp"
|
#include "presentation/presentation.hpp"
|
||||||
|
#include "settings/settings.hpp"
|
||||||
|
|
||||||
auto locate(string name) -> string;
|
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::Presentation() {
|
||||||
presentation = this;
|
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(); });
|
quit.setText("Quit").onActivate([&] { program->quit(); });
|
||||||
|
|
||||||
settingsMenu.setText("Settings");
|
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");
|
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");
|
helpMenu.setText("Help");
|
||||||
about.setText("About ...").onActivate([&] {
|
about.setText("About ...").onActivate([&] {
|
||||||
|
@ -24,7 +69,7 @@ Presentation::Presentation() {
|
||||||
|
|
||||||
setTitle({"bsnes v", Emulator::Version});
|
setTitle({"bsnes v", Emulator::Version});
|
||||||
setBackgroundColor({0, 0, 0});
|
setBackgroundColor({0, 0, 0});
|
||||||
setSize({640, 480});
|
setSize({512, 480});
|
||||||
setCentered();
|
setCentered();
|
||||||
|
|
||||||
#if defined(PLATFORM_MACOS)
|
#if defined(PLATFORM_MACOS)
|
||||||
|
@ -33,3 +78,18 @@ Presentation::Presentation() {
|
||||||
Application::Cocoa::onQuit([&] { doClose(); });
|
Application::Cocoa::onQuit([&] { doClose(); });
|
||||||
#endif
|
#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 {
|
struct Presentation : Window {
|
||||||
Presentation();
|
Presentation();
|
||||||
|
auto clearViewport() -> void;
|
||||||
|
|
||||||
MenuBar menuBar{this};
|
MenuBar menuBar{this};
|
||||||
Menu fileMenu{&menuBar};
|
Menu systemMenu{&menuBar};
|
||||||
MenuItem quit{&fileMenu};
|
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 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 toolsMenu{&menuBar};
|
||||||
|
Menu saveState{&toolsMenu};
|
||||||
|
Menu loadState{&toolsMenu};
|
||||||
|
MenuCheckItem pauseEmulation{&toolsMenu};
|
||||||
Menu helpMenu{&menuBar};
|
Menu helpMenu{&menuBar};
|
||||||
MenuItem about{&helpMenu};
|
MenuItem about{&helpMenu};
|
||||||
|
|
||||||
FixedLayout layout{this};
|
FixedLayout layout{this};
|
||||||
Viewport viewport{&layout, Geometry{0, 0, 1, 1}};
|
Viewport viewport{&layout, Geometry{0, 0, 512, 480}};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern unique_pointer<AboutWindow> aboutWindow;
|
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 "../bsnes.hpp"
|
||||||
#include <sfc/interface/interface.hpp>
|
#include "data.cpp"
|
||||||
#include <gb/interface/interface.hpp>
|
#include "interface.cpp"
|
||||||
|
#include "utility.cpp"
|
||||||
unique_pointer<Program> program;
|
unique_pointer<Program> program;
|
||||||
|
|
||||||
Program::Program(string_vector args) {
|
Program::Program(string_vector args) {
|
||||||
program = this;
|
program = this;
|
||||||
|
|
||||||
Emulator::platform = this;
|
Emulator::platform = this;
|
||||||
|
|
||||||
new Presentation;
|
new Presentation;
|
||||||
presentation->setVisible();
|
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;
|
new AboutWindow;
|
||||||
|
|
||||||
Application::onMain({&Program::main, this});
|
Application::onMain({&Program::main, this});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::main() -> void {
|
auto Program::main() -> void {
|
||||||
|
inputManager->poll();
|
||||||
|
|
||||||
|
if(emulator->loaded() && !presentation->pauseEmulation.checked()) {
|
||||||
|
emulator->run();
|
||||||
|
} else {
|
||||||
|
usleep(20 * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::quit() -> void {
|
auto Program::quit() -> void {
|
||||||
|
unload();
|
||||||
|
settings.save();
|
||||||
|
video.reset();
|
||||||
|
audio.reset();
|
||||||
|
input.reset();
|
||||||
Application::quit();
|
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);
|
Program(string_vector args);
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto quit() -> 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;
|
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::InputManager() {
|
||||||
inputManager = this;
|
inputManager = this;
|
||||||
|
input->onChange({&InputManager::onChange, this});
|
||||||
frequency = max(1u, settings["Input/Frequency"].natural());
|
frequency = max(1u, settings["Input/Frequency"].natural());
|
||||||
|
|
||||||
for(auto& emulator : program->emulators) {
|
for(auto& emulator : program->emulators) {
|
||||||
|
|
|
@ -56,8 +56,6 @@ auto Program::initializeInputDriver() -> void {
|
||||||
input = Input::create(settings["Input/Driver"].text());
|
input = Input::create(settings["Input/Driver"].text());
|
||||||
input->setContext(presentation->viewport.handle());
|
input->setContext(presentation->viewport.handle());
|
||||||
|
|
||||||
input->onChange({&InputManager::onChange, &inputManager()});
|
|
||||||
|
|
||||||
if(!input->ready()) {
|
if(!input->ready()) {
|
||||||
MessageDialog().setText("Failed to initialize input driver").warning();
|
MessageDialog().setText("Failed to initialize input driver").warning();
|
||||||
input = Input::create("None");
|
input = Input::create("None");
|
||||||
|
|
|
@ -61,7 +61,7 @@ auto SuperFamicom::manifest() const -> string {
|
||||||
string output;
|
string output;
|
||||||
output.append("game\n");
|
output.append("game\n");
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\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(" name: ", Location::prefix(location), "\n");
|
||||||
output.append(" region: ", region(), "\n");
|
output.append(" region: ", region(), "\n");
|
||||||
output.append(" revision: ", revision(), "\n");
|
output.append(" revision: ", revision(), "\n");
|
||||||
|
|
Loading…
Reference in New Issue