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:
Tim Allen 2018-05-19 12:51:34 +10:00
parent f5b96e9e9e
commit ea11c6d098
19 changed files with 478 additions and 16 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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