Update to v088r09 release.

byuu says:

Lots of work on ethos, nothing more.
Settings window is in, InputManager pulls all the inputs from all cores
and binds them to ruby inputs, main window adds menu and dynamically
maps in all systems and cartridge slots and options and such, file
browser's back in, RAM is loaded and saved, etc. It's barely usable, but
you have to set up your inputs from the config file by hand for now.
This commit is contained in:
Tim Allen 2012-04-30 09:58:41 +10:00
parent 4fd20f0ae0
commit 76553756a2
31 changed files with 808 additions and 110 deletions

View File

@ -1,7 +1,7 @@
#ifndef EMULATOR_HPP
#define EMULATOR_HPP
static const char Version[] = "088.08";
static const char Version[] = "088.09";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

View File

@ -10,26 +10,52 @@ struct Interface {
unsigned height;
unsigned frequency;
unsigned ports;
bool resettable;
} information;
struct Media {
struct Firmware {
string name;
unsigned id;
};
vector<Firmware> firmware;
struct MediaObject {
string displayname;
string name;
string filter;
unsigned id;
};
struct Media : MediaObject {
vector<MediaObject> slot;
};
vector<Media> media;
struct Controller {
struct Memory {
string name;
string port;
string device;
struct Input {
unsigned id;
uint8_t *data;
unsigned size;
};
vector<Memory> memory;
struct Port {
string name;
unsigned id;
struct Device {
string name;
unsigned id;
struct Input {
string name;
unsigned id;
unsigned guid;
};
vector<Input> input;
vector<unsigned> displayinput;
};
vector<Input> inputs;
vector<Device> device;
};
vector<Controller> controllers;
vector<Port> port;
struct Callback {
function<uint32_t (unsigned, uint16_t, uint16_t, uint16_t)> videoColor;
@ -52,8 +78,8 @@ struct Interface {
if(callback.audioSample) return callback.audioSample(lsample, rsample);
}
virtual int16_t inputPoll(unsigned port, unsigned device, unsigned id) {
if(callback.inputPoll) return callback.inputPoll(port, device, id);
virtual int16_t inputPoll(unsigned port, unsigned device, unsigned input) {
if(callback.inputPoll) return callback.inputPoll(port, device, input);
return 0;
}

View File

@ -29,6 +29,8 @@ bool Cartridge::load(const string &markup, const stream &memory) {
ram.size = numeral(info["size"].data);
ram.mask = ram.size - 1;
for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff;
interface->memory.append({"save.ram", 2, ram.data, ram.size});
}
if(info["type"].data == "EEPROM") {
@ -39,6 +41,8 @@ bool Cartridge::load(const string &markup, const stream &memory) {
eeprom.mask = size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000;
eeprom.test = size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff;
interface->memory.append({"save.ram", 3, eeprom.data, eeprom.size});
}
if(info["type"].data == "FlashROM") {
@ -46,6 +50,8 @@ bool Cartridge::load(const string &markup, const stream &memory) {
flashrom.id = numeral(info["id"].data);
flashrom.size = numeral(info["size"].data);
for(unsigned n = 0; n < flashrom.size; n++) flashrom.data[n] = 0xff;
interface->memory.append({"save.ram", 4, flashrom.data, flashrom.size});
}
}

View File

@ -8,9 +8,24 @@ bool Interface::loaded() {
return cartridge.loaded();
}
void Interface::load(unsigned id, const stream &memory, const string &markup) {
if(id == 0) cartridge.load(markup, memory);
if(id == 1) memory.read(bios.data, min(bios.size, memory.size()));
void Interface::load(unsigned id, const stream &stream, const string &markup) {
if(id == 0) {
memory.reset();
cartridge.load(markup, stream);
system.power();
}
if(id == 1) {
stream.read(bios.data, min(bios.size, stream.size()));
}
if(id == 2) {
stream.read(cartridge.ram.data, min(cartridge.ram.size, stream.size()));
}
if(id == 3) {
stream.read(cartridge.eeprom.data, min(cartridge.eeprom.size, stream.size()));
}
if(id == 4) {
stream.read(cartridge.flashrom.data, min(cartridge.flashrom.size, stream.size()));
}
}
void Interface::unload() {
@ -36,36 +51,51 @@ void Interface::updatePalette() {
Interface::Interface() {
interface = this;
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.frequency = 32768;
information.ports = 1;
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.frequency = 32768;
information.ports = 1;
information.resettable = false;
{
Media medium;
medium.name = "Game Boy Advance";
medium.filter = "*.sfc";
medium.id = 0;
media.append(medium);
Firmware firmware;
firmware.name = "BIOS";
firmware.id = 1;
this->firmware.append(firmware);
}
{
Controller controller;
controller.name = "Controller";
controller.port = 0;
controller.device = 0;
controller.inputs.append({"Up", 6});
controller.inputs.append({"Down", 7});
controller.inputs.append({"Left", 5});
controller.inputs.append({"Right", 4});
controller.inputs.append({"B", 1});
controller.inputs.append({"A", 0});
controller.inputs.append({"L", 9});
controller.inputs.append({"R", 8});
controller.inputs.append({"Select", 2});
controller.inputs.append({"Start", 3});
controllers.append(controller);
Media media;
media.displayname = "Game Boy Advance";
media.name = "program.rom";
media.filter = "*.gba";
media.id = 0;
this->media.append(media);
}
{
Port port;
port.name = "Device";
port.id = 0;
{
Port::Device device;
device.name = "Controller";
device.id = 0;
device.input.append({"A", 0});
device.input.append({"B", 1});
device.input.append({"Select", 2});
device.input.append({"Start", 3});
device.input.append({"Right", 4});
device.input.append({"Left", 5});
device.input.append({"Up", 6});
device.input.append({"Down", 7});
device.input.append({"R", 8});
device.input.append({"L", 9});
device.displayinput = { 6, 7, 5, 4, 1, 0, 9, 8, 2, 3 };
port.device.append(device);
}
this->port.append(port);
}
}

View File

@ -68,6 +68,7 @@ struct map {
}
inline RHS& operator()(const LHS &name) {
if(auto position = find(name)) return list[position()].data;
return insert(name, RHS());
}

View File

@ -7,7 +7,7 @@ include gb/Makefile
include gba/Makefile
name := ethos
ui_objects := ui-ethos ui-interface ui-general
ui_objects := ui-ethos ui-interface ui-utility ui-input ui-general ui-settings
ui_objects += phoenix ruby
ui_objects += $(if $(call streq,$(platform),win),resource)
@ -40,7 +40,10 @@ objects := $(patsubst %,obj/%.o,$(objects))
obj/ui-ethos.o: $(ui)/ethos.cpp $(call rwildcard,$(ui)/)
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/)
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
$(call compile,$(rubyflags))

View File

@ -0,0 +1,34 @@
#include <gba/interface/interface.hpp>
void Application::bootstrap() {
interface = new Interface;
emulator.append(new GameBoyAdvance::Interface);
for(auto &system : emulator) {
system->callback.videoColor = {&Interface::videoColor, interface};
system->callback.videoRefresh = {&Interface::videoRefresh, interface};
system->callback.audioSample = {&Interface::audioSample, interface};
system->callback.inputPoll = {&Interface::inputPoll, interface};
system->updatePalette();
string basepath = path({system->information.name, ".sys/"});
string manifest;
manifest.readfile({basepath, "manifest.xml"});
XML::Document document(manifest);
for(auto &firmware : system->firmware) {
string path = firmware.name;
path.lower();
for(auto &root : document) {
for(auto &node : root) {
if(node.name == path) {
filestream fs({basepath, node["firmware"].data});
system->load(firmware.id, fs);
}
}
}
}
}
}

View File

@ -1,14 +1,30 @@
#include "ethos.hpp"
#include "bootstrap.cpp"
Application *application = nullptr;
Emulator::Interface& system() {
struct application_interface_null{};
if(application->active == nullptr) throw application_interface_null();
return *application->active;
}
string Application::path(const string &filename) {
string path = {basepath, filename};
if(file::exists(path)) return path;
if(directory::exists(path)) return path;
return {userpath, filename};
}
void Application::run() {
inputManager->poll();
if(active == nullptr || system().loaded() == false) {
usleep(20 * 1000);
return;
}
system().run();
}
Application::Application(int argc, char **argv) {
@ -23,21 +39,14 @@ Application::Application(int argc, char **argv) {
unused = ::userpath(path);
userpath = path;
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
userpath.append("bsnes/");
userpath.append("ethos/");
} else {
userpath.append(".config/bsnes/");
userpath.append(".config/ethos/");
}
mkdir(userpath, 0755);
interface = new Interface;
auto gba = new GameBoyAdvance::Interface;
gba->callback.videoColor = {&Interface::videoColor, interface};
gba->callback.videoRefresh = {&Interface::videoRefresh, interface};
gba->callback.audioSample = {&Interface::audioSample, interface};
gba->callback.inputPoll = {&Interface::inputPoll, interface};
gba->updatePalette();
emulators.append(gba);
bootstrap();
active = nullptr;
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
normalFont = "Tahoma, 8";
@ -51,39 +60,37 @@ Application::Application(int argc, char **argv) {
monospaceFont = "Liberation Mono, 8";
}
filestream bios{"/home/byuu/.config/bsnes/Game Boy Advance.sys/bios.rom"};
gba->load(1, bios);
utility = new Utility;
inputManager = new InputManager;
browser = new Browser;
presentation = new Presentation;
videoSettings = new VideoSettings;
audioSettings = new AudioSettings;
inputSettings = new InputSettings;
settings = new Settings;
string manifest;
manifest.readfile("/media/sdb1/root/cartridges/Game Boy Advance/Super Mario Advance (US).gba/manifest.xml");
filestream fs{"/media/sdb1/root/cartridges/Game Boy Advance/Super Mario Advance (US).gba/program.rom"};
gba->load(0, fs, manifest);
gba->power();
videoWindow = new VideoWindow;
videoWindow->setVisible();
presentation->setVisible();
video.driver("OpenGL");
video.set(Video::Handle, videoWindow->viewport.handle());
video.set(Video::Handle, presentation->viewport.handle());
video.set(Video::Synchronize, false);
video.set(Video::Depth, 24u);
video.init();
audio.driver("ALSA");
audio.set(Audio::Handle, videoWindow->viewport.handle());
audio.set(Audio::Synchronize, true);
audio.set(Audio::Handle, presentation->viewport.handle());
audio.set(Audio::Synchronize, false);
audio.set(Audio::Latency, 80u);
audio.set(Audio::Frequency, 32768u);
audio.init();
input.driver("SDL");
input.set(Input::Handle, videoWindow->viewport.handle());
input.set(Input::Handle, presentation->viewport.handle());
input.init();
while(quit == false) {
OS::processEvents();
gba->run();
run();
}
}

View File

@ -1,8 +1,9 @@
#include <emulator/emulator.hpp>
#include <gba/interface/interface.hpp>
#include <nall/platform.hpp>
#include <nall/config.hpp>
#include <nall/directory.hpp>
#include <nall/map.hpp>
#include <nall/stream/file.hpp>
#include <nall/stream/memory.hpp>
#include <nall/stream/vector.hpp>
@ -15,10 +16,17 @@ using namespace phoenix;
using namespace ruby;
#include "interface/interface.hpp"
#include "utility/utility.hpp"
#include "input/input.hpp"
#include "general/general.hpp"
#include "settings/settings.hpp"
Emulator::Interface& system();
struct Application {
vector<Emulator::Interface*> emulators;
vector<Emulator::Interface*> emulator;
Emulator::Interface *active;
bool quit;
bool pause;
bool autopause;
@ -34,6 +42,7 @@ struct Application {
string path(const string &filename);
void run();
void bootstrap();
Application(int argc, char **argv);
~Application();
};

View File

@ -0,0 +1,106 @@
Browser *browser = nullptr;
Browser::Browser() {
setGeometry({128, 128, 640, 400});
layout.setMargin(5);
pathBrowse.setText("Browse ...");
pathUp.setText("..");
openButton.setText("Open");
append(layout);
layout.append(pathLayout, {~0, 0}, 5);
pathLayout.append(pathEdit, {~0, 0}, 5);
pathLayout.append(pathBrowse, {0, 0}, 5);
pathLayout.append(pathUp, {0, 0});
layout.append(fileList, {~0, ~0}, 5);
layout.append(controlLayout, {~0, 0});
controlLayout.append(filterLabel, {~0, 0}, 5);
controlLayout.append(openButton, {80, 0});
pathEdit.onActivate = [&] {
string path = pathEdit.text();
path.transform("\\", "/");
if(path.endswith("/") == false) path.append("/");
setPath(path);
};
pathBrowse.onActivate = [&] {
string path = DialogWindow::folderSelect(*this, this->path);
if(!path.empty()) setPath(path);
};
pathUp.onActivate = [&] {
if(this->path == "/") return;
string path = this->path;
path.rtrim<1>("/");
path = dir(path);
setPath(path);
};
fileList.onChange = {&Browser::synchronize, this};
fileList.onActivate = openButton.onActivate = {&Browser::fileListActivate, this};
synchronize();
}
void Browser::open(Emulator::Interface::Media &media, function<void (string)> callback) {
this->media = media;
this->callback = callback;
setTitle({"Load ", media.displayname});
setPath("/media/sdb1/root/cartridges/Game Boy Advance/");
filterLabel.setText({"Files of type: ", media.filter});
setVisible();
}
void Browser::synchronize() {
openButton.setEnabled(fileList.selected());
}
void Browser::setPath(const string &path) {
this->path = path;
pathEdit.setText(path);
fileList.reset();
filenameList.reset();
lstring contents = directory::contents(path);
for(auto &filename : contents) {
if(filename.endswith("/")) {
filenameList.append(filename);
} else if(filename.wildcard(media.filter)) {
filenameList.append(filename);
}
}
for(auto &filename : filenameList) fileList.append(filename);
fileList.setSelection(0);
fileList.setFocused();
synchronize();
}
void Browser::fileListActivate() {
unsigned selection = fileList.selection();
string filename = filenameList[selection];
if(filename.endswith("/")) {
if(loadFolder({path, filename})) return;
return setPath({path, filename});
}
loadFile({path, filename});
}
bool Browser::loadFolder(const string &path) {
string requested = path;
requested.rtrim<1>("/");
if(requested.wildcard(media.filter) == false) return false;
loadFile(path);
return true;
}
void Browser::loadFile(const string &filename) {
setVisible(false);
if(callback) callback(filename);
}

View File

@ -0,0 +1,28 @@
struct Browser : Window {
VerticalLayout layout;
HorizontalLayout pathLayout;
LineEdit pathEdit;
Button pathBrowse;
Button pathUp;
ListView fileList;
HorizontalLayout controlLayout;
Label filterLabel;
Button openButton;
void open(Emulator::Interface::Media &media, function<void (string)> callback);
Browser();
public:
Emulator::Interface::Media media;
function<void (string)> callback;
string path;
lstring filenameList;
void synchronize();
void setPath(const string &path);
void fileListActivate();
bool loadFolder(const string &path);
void loadFile(const string &filename);
};
extern Browser *browser;

View File

@ -1,2 +1,3 @@
#include "../ethos.hpp"
#include "video-window.cpp"
#include "browser.cpp"
#include "presentation.cpp"

View File

@ -1 +1,2 @@
#include "video-window.hpp"
#include "browser.hpp"
#include "presentation.hpp"

View File

@ -0,0 +1,78 @@
Presentation *presentation = nullptr;
void Presentation::synchronize() {
for(auto &system : emulatorList) {
system->menu.setVisible(system->interface == application->active);
}
}
Presentation::Presentation() {
bootstrap();
setTitle("ethos");
setGeometry({1024, 600, 720, 480});
setBackgroundColor({0, 0, 0});
setMenuFont(application->normalFont);
setMenuVisible();
setStatusFont(application->boldFont);
setStatusVisible();
loadMenu.setText("Load");
settingsMenu.setText("Settings");
configurationSettings.setText("Configuration ...");
toolsMenu.setText("Tools");
for(auto &system : emulatorList) {
loadMenu.append(system->load);
}
append(loadMenu);
for(auto &system : emulatorList) {
append(system->menu);
}
append(settingsMenu);
settingsMenu.append(configurationSettings);
append(toolsMenu);
append(layout);
layout.append(viewport, {0, 0, 720, 480});
onClose = [&] { application->quit = true; };
configurationSettings.onActivate = [&] { settings->setVisible(); };
synchronize();
}
void Presentation::bootstrap() {
for(auto &emulator : application->emulator) {
System *system = new System;
system->interface = emulator;
system->name = emulator->information.name;
system->filter = "*.gba";
system->load.setText(system->name);
system->load.onActivate = [=] {
browser->open(system->interface->media[0], [=](string filename) {
utility->loadMedia(system->interface, system->interface->media[0], filename);
});
};
system->menu.setText(system->name);
system->power.setText("Power");
system->reset.setText("Reset");
system->unload.setText("Unload");
system->menu.append(system->power);
if(emulator->information.resettable)
system->menu.append(system->reset);
system->menu.append(system->separator);
system->menu.append(system->unload);
system->power.onActivate = {&Utility::power, utility};
system->reset.onActivate = {&Utility::reset, utility};
system->unload.onActivate = {&Utility::unload, utility};
emulatorList.append(system);
}
}

View File

@ -0,0 +1,30 @@
struct Presentation : Window {
FixedLayout layout;
Viewport viewport;
struct System {
Emulator::Interface *interface;
string name;
string filter;
Item load;
Menu menu;
Item power;
Item reset;
Separator separator;
Item unload;
function<void (string)> callback;
};
vector<System*> emulatorList;
Menu loadMenu;
Menu settingsMenu;
Item configurationSettings;
Menu toolsMenu;
void synchronize();
void bootstrap();
Presentation();
};
extern Presentation *presentation;

View File

@ -1,12 +0,0 @@
VideoWindow *videoWindow = nullptr;
VideoWindow::VideoWindow() {
setTitle("ethos");
setGeometry({1024, 600, 720, 480});
setBackgroundColor({0, 0, 0});
append(layout);
layout.append(viewport, {0, 0, 720, 480});
onClose = [&] { application->quit = true; };
}

View File

@ -1,8 +0,0 @@
struct VideoWindow : Window {
FixedLayout layout;
Viewport viewport;
VideoWindow();
};
extern VideoWindow *videoWindow;

View File

@ -0,0 +1,69 @@
#include "../ethos.hpp"
InputManager *inputManager = nullptr;
void AbstractInput::bind() {
if(mapping.empty()) type = Type::Button, mapping = "None";
if(mapping.endswith(".Up")) type = Type::HatUp;
else if(mapping.endswith(".Down")) type = Type::HatDown;
else if(mapping.endswith(".Left")) type = Type::HatLeft;
else if(mapping.endswith(".Right")) type = Type::HatRight;
else if(mapping.endswith(".Lo")) type = Type::AxisLo;
else if(mapping.endswith(".Hi")) type = Type::AxisHi;
else if(mapping.beginswith("JP") && mapping.position("Axis")) type = Type::Axis;
else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Type::MouseAxis;
else if(mapping.beginswith("MS")) type = Type::MouseButton;
else type = Type::Button;
string decode = mapping;
if(auto position = decode.position(".")) decode[position()] = 0;
scancode = Scancode::decode(decode);
}
void InputManager::bind() {
for(auto &input : inputMap) input.data.bind();
}
void InputManager::poll() {
input.poll(table);
}
int16_t InputManager::poll(unsigned guid) {
return table[inputMap[guid].scancode];
}
InputManager::InputManager() {
bootstrap();
}
void InputManager::bootstrap() {
unsigned guid = 0;
for(auto &emulator : application->emulator) {
for(auto &port : emulator->port) {
for(auto &device : port.device) {
for(auto &number : device.displayinput) {
auto &input = device.input[number];
AbstractInput abstract;
abstract.type = AbstractInput::Type::Button;
abstract.name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name};
abstract.mapping = "None";
abstract.scancode = 0;
abstract.name.replace(" ", "");
input.guid = guid++;
inputMap(input.guid) = abstract;
}
}
}
}
for(auto &input : inputMap) {
config.append(input.data.mapping, input.data.name);
}
config.load(application->path("input.cfg"));
config.save(application->path("input.cfg"));
bind();
}

View File

@ -0,0 +1,25 @@
struct AbstractInput {
enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi } type;
string name;
string mapping;
unsigned scancode;
void bind();
};
struct InputManager {
int16_t table[Scancode::Limit];
map<unsigned, AbstractInput> inputMap;
void bind();
void poll();
int16_t poll(unsigned guid);
void bootstrap();
InputManager();
private:
configuration config;
};
extern InputManager *inputManager;

View File

@ -20,29 +20,24 @@ void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned widt
video.unlock();
video.refresh();
}
static unsigned frameCounter = 0;
static time_t previous, current;
frameCounter++;
time(&current);
if(current != previous) {
previous = current;
utility->setStatusText({"FPS: ", frameCounter});
frameCounter = 0;
}
}
void Interface::audioSample(int16_t lsample, int16_t rsample) {
audio.sample(lsample, rsample);
}
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned id) {
using nall::Keyboard;
static int16_t table[Scancode::Limit];
if(id == 0) input.poll(table);
switch(id) {
case 0: return table[keyboard(0)[Keyboard::X]]; //A
case 1: return table[keyboard(0)[Keyboard::Z]]; //B
case 2: return table[keyboard(0)[Keyboard::Apostrophe]]; //Select
case 3: return table[keyboard(0)[Keyboard::Return]]; //Start
case 4: return table[keyboard(0)[Keyboard::Right]]; //Right
case 5: return table[keyboard(0)[Keyboard::Left]]; //Left
case 6: return table[keyboard(0)[Keyboard::Up]]; //Up
case 7: return table[keyboard(0)[Keyboard::Down]]; //Down
case 8: return table[keyboard(0)[Keyboard::R]]; //R
case 9: return table[keyboard(0)[Keyboard::L]]; //L
}
return 0;
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
unsigned guid = system().port[port].device[device].input[input].guid;
return inputManager->poll(guid);
}

View File

@ -2,7 +2,7 @@ struct Interface {
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
void audioSample(int16_t lsample, int16_t rsample);
int16_t inputPoll(unsigned port, unsigned device, unsigned id);
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
};
extern Interface *interface;

View File

@ -0,0 +1,8 @@
AudioSettings *audioSettings = nullptr;
AudioSettings::AudioSettings() {
title.setFont(application->titleFont);
title.setText("Audio Settings");
append(title, {~0, 0}, 5);
}

View File

@ -0,0 +1,7 @@
struct AudioSettings : SettingsLayout {
Label title;
AudioSettings();
};
extern AudioSettings *audioSettings;

View File

@ -0,0 +1,74 @@
InputSettings *inputSettings = nullptr;
InputSettings::InputSettings() {
title.setFont(application->titleFont);
title.setText("Input Settings");
inputList.setHeaderText("Name", "Mapping");
inputList.setHeaderVisible();
clearButton.setText("Clear");
append(title, {~0, 0}, 5);
append(selectionLayout, {~0, 0}, 5);
selectionLayout.append(systemList, {~0, 0}, 5);
selectionLayout.append(portList, {~0, 0}, 5);
selectionLayout.append(deviceList, {~0, 0});
append(inputList, {~0, ~0}, 5);
append(controlLayout, {~0, 0});
controlLayout.append(assign[0], {100, 0}, 5);
controlLayout.append(assign[1], {100, 0}, 5);
controlLayout.append(assign[2], {100, 0}, 5);
controlLayout.append(spacer, {~0, 0});
controlLayout.append(clearButton, {80, 0});
for(auto &emulator : application->emulator) {
systemList.append(emulator->information.name);
}
systemList.onChange = {&InputSettings::systemChanged, this};
portList.onChange = {&InputSettings::portChanged, this};
deviceList.onChange = {&InputSettings::deviceChanged, this};
inputList.onChange = {&InputSettings::synchronize, this};
systemChanged();
}
void InputSettings::synchronize() {
clearButton.setEnabled(inputList.selected());
}
Emulator::Interface& InputSettings::activeSystem() {
return *application->emulator[systemList.selection()];
}
Emulator::Interface::Port& InputSettings::activePort() {
return activeSystem().port[portList.selection()];
}
Emulator::Interface::Port::Device& InputSettings::activeDevice() {
return activePort().device[deviceList.selection()];
}
void InputSettings::systemChanged() {
portList.reset();
for(auto &port : activeSystem().port) {
portList.append(port.name);
}
portChanged();
}
void InputSettings::portChanged() {
deviceList.reset();
for(auto &device : activePort().device) {
deviceList.append(device.name);
}
deviceChanged();
}
void InputSettings::deviceChanged() {
inputList.reset();
for(unsigned number : activeDevice().displayinput) {
auto &input = activeDevice().input[number];
inputList.append(input.name, inputManager->inputMap(input.guid).mapping);
}
synchronize();
}

View File

@ -0,0 +1,25 @@
struct InputSettings : SettingsLayout {
Label title;
HorizontalLayout selectionLayout;
ComboBox systemList;
ComboBox portList;
ComboBox deviceList;
ListView inputList;
HorizontalLayout controlLayout;
Button assign[3];
Widget spacer;
Button clearButton;
void synchronize();
Emulator::Interface& activeSystem();
Emulator::Interface::Port& activePort();
Emulator::Interface::Port::Device& activeDevice();
void systemChanged();
void portChanged();
void deviceChanged();
InputSettings();
};
extern InputSettings *inputSettings;

View File

@ -0,0 +1,52 @@
#include "../ethos.hpp"
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp"
Settings *settings = nullptr;
void SettingsLayout::append(Sizable &sizable, const Size &size, unsigned spacing) {
layout.append(sizable, size, spacing);
}
SettingsLayout::SettingsLayout() {
setMargin(5);
HorizontalLayout::append(spacer, {120, ~0}, 5);
HorizontalLayout::append(layout, { ~0, ~0});
}
Settings::Settings() {
setTitle("Configuration Settings");
setGeometry({128, 128, 640, 360});
setStatusFont(application->boldFont);
setStatusVisible();
layout.setMargin(5);
panelList.setFont(application->boldFont);
panelList.append("Video");
panelList.append("Audio");
panelList.append("Input");
append(layout);
layout.append(panelList, {120, ~0}, 5);
append(*videoSettings);
append(*audioSettings);
append(*inputSettings);
panelList.onChange = {&Settings::panelChanged, this};
panelList.setSelection(2);
panelChanged();
}
void Settings::panelChanged() {
videoSettings->setVisible(false);
audioSettings->setVisible(false);
inputSettings->setVisible(false);
if(panelList.selected() == false) return;
switch(panelList.selection()) {
case 0: return videoSettings->setVisible();
case 1: return audioSettings->setVisible();
case 2: return inputSettings->setVisible();
}
}

View File

@ -0,0 +1,22 @@
struct SettingsLayout : HorizontalLayout {
Widget spacer;
VerticalLayout layout;
void append(Sizable &widget, const Size &size, unsigned spacing = 0);
SettingsLayout();
};
#include "video.hpp"
#include "audio.hpp"
#include "input.hpp"
struct Settings : Window {
HorizontalLayout layout;
ListView panelList;
void panelChanged();
Settings();
};
extern Settings *settings;

View File

@ -0,0 +1,8 @@
VideoSettings *videoSettings = nullptr;
VideoSettings::VideoSettings() {
title.setFont(application->titleFont);
title.setText("Video Settings");
append(title, {~0, 0}, 5);
}

View File

@ -0,0 +1,7 @@
struct VideoSettings : SettingsLayout {
Label title;
VideoSettings();
};
extern VideoSettings *videoSettings;

View File

@ -0,0 +1,51 @@
#include "../ethos.hpp"
Utility *utility = nullptr;
void Utility::setInterface(Emulator::Interface *emulator) {
application->active = emulator;
presentation->synchronize();
}
void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname) {
unload();
setInterface(emulator);
this->pathname = pathname;
string manifest;
manifest.readfile({pathname, "manifest.xml"});
auto memory = file::read({pathname, media.name});
system().load(media.id, vectorstream{memory}, manifest);
for(auto &memory : system().memory) {
filestream fs({pathname, memory.name});
system().load(memory.id, fs);
}
}
void Utility::saveMedia() {
for(auto &memory : system().memory) {
file::write({pathname, memory.name}, memory.data, memory.size);
}
}
void Utility::power() {
system().power();
}
void Utility::reset() {
system().reset();
}
void Utility::unload() {
if(application->active) {
saveMedia();
system().unload();
setInterface(nullptr);
}
video.clear();
}
void Utility::setStatusText(const string &text) {
presentation->setStatusText(text);
}

View File

@ -0,0 +1,15 @@
struct Utility {
string pathname;
void setInterface(Emulator::Interface *emulator);
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
void saveMedia();
void power();
void reset();
void unload();
void setStatusText(const string &text);
};
extern Utility *utility;