mirror of https://github.com/bsnes-emu/bsnes.git
Update to v088r12 release.
byuu says: Changelog: - all hotkeys from target-ui now exist in target-ethos - controller port menus now show up when you load a system (hidden if there are no options to choose from) - tools menu auto-hides with no game open ... not much point to it then - since we aren't using RawInput's multi-KB/MS support anyway, input and hotkey mappings remove KB0:: and turn MS0:: into Mouse::, makes it a lot easier to read - added mute audio, sync video, sync audio, mask overscan - added video settings: saturation, gamma, luminance, overscan horizontal, overscan vertical - added audio settings: frequency, latency, resampler, volume - added input settings: when focus is lost [ ] pause emulator [ ] allow input - pausing and autopausing works - status messages hooked up (show a message in status bar for a few seconds, then revert to normal status text) - sub systems (SGB, BSX, ST) sorted below primary systems list - added geometry settings cache - Emulator::Interface cleanups and simplifications - save states go into (cart foldername.extension/bsnes/state-#.bsa) now. Idea is to put emulator-specific data in their own subfolders Caveats / Missing: - SGB input does not work - Sufami Turbo second slot doesn't work yet - BS-X BIOS won't show the data pack - need XML mapping information window - need cheat editor and cheat database - need state manager - need video shaders - need driver selection - need NSS DIP switch settings - need to hide controllers that have no inputs from the input mapping list So for video settings, I used to have contrast/brightness/gamma. Contrast was just a multiplier on intensity of each channel, and brightness was an addition or subtraction against each channel. They kind of overlapped and weren't that effective. The new setup has saturation, gamma and luminance. Saturation of 100% is normal. If you lower it, color information goes away. 0% = grayscale. If you raise it, color intensity increases (and clamps.) This is wonderful for GBA games, since they are oversaturated to fucking death. Of course we'll want to normalize that inside the core, so the same sat. value works on all systems, but for now it's nice. If you raise saturation above 100%, it basically acts like contrast used to. It's just that lowering it fades to grayscale rather than black. Adding doesn't really work well for brightness, it throws off the relative distance between channels and looks like shit. So now we have luminance, which takes over the old contrast <100% role, and just fades the pixels toward black. Obviously, luminance > 100% would be the same as saturation > 100%, so that isn't allowed, it caps at 100% now. Gamma's the same old function. Gamma curve on the lower-half of the color range. Effects are applied in the order they appear in the GUI: color -> saturate -> gammify -> luminate -> output.
This commit is contained in:
parent
8703d57030
commit
5d273c5265
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const char Name[] = "bsnes";
|
static const char Name[] = "bsnes";
|
||||||
static const char Version[] = "088.11";
|
static const char Version[] = "088.12";
|
||||||
static const char Author[] = "byuu";
|
static const char Author[] = "byuu";
|
||||||
static const char License[] = "GPLv3";
|
static const char License[] = "GPLv3";
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,34 +8,31 @@ struct Interface {
|
||||||
string name;
|
string name;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
|
bool overscan;
|
||||||
double aspectRatio;
|
double aspectRatio;
|
||||||
unsigned frequency;
|
unsigned frequency;
|
||||||
bool resettable;
|
bool resettable;
|
||||||
|
|
||||||
struct Media {
|
struct Media {
|
||||||
string name;
|
string name;
|
||||||
string filter;
|
string extension;
|
||||||
};
|
};
|
||||||
vector<Media> media;
|
vector<Media> media;
|
||||||
} information;
|
} information;
|
||||||
|
|
||||||
struct Firmware {
|
|
||||||
string displayname;
|
|
||||||
string name;
|
|
||||||
unsigned id;
|
|
||||||
};
|
|
||||||
vector<Firmware> firmware;
|
|
||||||
|
|
||||||
struct Media {
|
struct Media {
|
||||||
string displayname;
|
|
||||||
string path;
|
|
||||||
string name;
|
|
||||||
string filter;
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
string name;
|
||||||
|
string extension;
|
||||||
|
string path;
|
||||||
};
|
};
|
||||||
|
vector<Media> firmware;
|
||||||
|
|
||||||
struct Schema : Media {
|
struct Schema : Media {
|
||||||
vector<Media> slot;
|
vector<Media> slot;
|
||||||
|
Schema(const Media &media) {
|
||||||
|
id = media.id, name = media.name, extension = media.extension, path = media.path;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
vector<Schema> schema;
|
vector<Schema> schema;
|
||||||
|
|
||||||
|
@ -46,19 +43,19 @@ struct Interface {
|
||||||
vector<Memory> memory;
|
vector<Memory> memory;
|
||||||
|
|
||||||
struct Port {
|
struct Port {
|
||||||
string name;
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
string name;
|
||||||
struct Device {
|
struct Device {
|
||||||
string name;
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
string name;
|
||||||
struct Input {
|
struct Input {
|
||||||
string name;
|
|
||||||
unsigned type; //0 = digital, 1 = analog
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
unsigned type; //0 = digital, 1 = analog
|
||||||
|
string name;
|
||||||
unsigned guid;
|
unsigned guid;
|
||||||
};
|
};
|
||||||
vector<Input> input;
|
vector<Input> input;
|
||||||
vector<unsigned> displayinput;
|
vector<unsigned> order;
|
||||||
};
|
};
|
||||||
vector<Device> device;
|
vector<Device> device;
|
||||||
};
|
};
|
||||||
|
@ -72,7 +69,7 @@ struct Interface {
|
||||||
function<void (Media)> mediaRequest;
|
function<void (Media)> mediaRequest;
|
||||||
} callback;
|
} callback;
|
||||||
|
|
||||||
//audio/visual bindings (provided by user interface)
|
//callback bindings (provided by user interface)
|
||||||
virtual uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
|
virtual uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
|
||||||
if(callback.videoColor) return callback.videoColor(source, red, green, blue);
|
if(callback.videoColor) return callback.videoColor(source, red, green, blue);
|
||||||
return (red >> 8) << 16 | (green >> 8) << 8 | (blue >> 8) << 0;
|
return (red >> 8) << 16 | (green >> 8) << 8 | (blue >> 8) << 0;
|
||||||
|
@ -102,10 +99,15 @@ struct Interface {
|
||||||
virtual void unload() {}
|
virtual void unload() {}
|
||||||
|
|
||||||
//system interface
|
//system interface
|
||||||
|
virtual void connect(unsigned port, unsigned device) {}
|
||||||
virtual void power() {}
|
virtual void power() {}
|
||||||
virtual void reset() {}
|
virtual void reset() {}
|
||||||
virtual void run() {}
|
virtual void run() {}
|
||||||
|
|
||||||
|
//state functions
|
||||||
|
virtual serializer serialize() = 0;
|
||||||
|
virtual bool unserialize(serializer&) = 0;
|
||||||
|
|
||||||
//utility functions
|
//utility functions
|
||||||
virtual void updatePalette() {}
|
virtual void updatePalette() {}
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,15 @@ void Interface::run() {
|
||||||
system.run();
|
system.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serializer Interface::serialize() {
|
||||||
|
system.runtosave();
|
||||||
|
return system.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interface::unserialize(serializer &s) {
|
||||||
|
return system.unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
void Interface::updatePalette() {
|
void Interface::updatePalette() {
|
||||||
video.generate_palette();
|
video.generate_palette();
|
||||||
}
|
}
|
||||||
|
@ -53,64 +62,40 @@ Interface::Interface() {
|
||||||
information.name = "Famicom";
|
information.name = "Famicom";
|
||||||
information.width = 256;
|
information.width = 256;
|
||||||
information.height = 240;
|
information.height = 240;
|
||||||
|
information.overscan = true;
|
||||||
information.aspectRatio = 8.0 / 7.0;
|
information.aspectRatio = 8.0 / 7.0;
|
||||||
information.frequency = 1789772;
|
information.frequency = 1789772;
|
||||||
information.resettable = true;
|
information.resettable = true;
|
||||||
|
|
||||||
information.media.append({"Famicom", "*.fc"});
|
information.media.append({"Famicom", "fc"});
|
||||||
|
|
||||||
|
schema.append(Media{ID::ROM, "Famicom", "fc", "program.rom"});
|
||||||
|
|
||||||
{
|
{
|
||||||
Schema schema;
|
Port port{0, "Port 1"};
|
||||||
schema.displayname = "Famicom";
|
port.device.append(controller());
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.fc";
|
|
||||||
schema.id = ID::ROM;
|
|
||||||
this->schema.append(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Port port;
|
|
||||||
port.name = "Port 1";
|
|
||||||
port.id = 0;
|
|
||||||
{
|
|
||||||
Port::Device device;
|
|
||||||
device.name = "Controller";
|
|
||||||
device.id = 0;
|
|
||||||
device.input.append({"A", 0, 0});
|
|
||||||
device.input.append({"B", 0, 1});
|
|
||||||
device.input.append({"Select", 0, 2});
|
|
||||||
device.input.append({"Start", 0, 3});
|
|
||||||
device.input.append({"Up", 0, 4});
|
|
||||||
device.input.append({"Down", 0, 5});
|
|
||||||
device.input.append({"Left", 0, 6});
|
|
||||||
device.input.append({"Right", 0, 7});
|
|
||||||
device.displayinput = {4, 5, 6, 7, 1, 0, 2, 3};
|
|
||||||
port.device.append(device);
|
|
||||||
}
|
|
||||||
this->port.append(port);
|
this->port.append(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Port port;
|
Port port{1, "Port 2"};
|
||||||
port.name = "Port 2";
|
port.device.append(controller());
|
||||||
port.id = 1;
|
|
||||||
{
|
|
||||||
Port::Device device;
|
|
||||||
device.name = "Controller";
|
|
||||||
device.id = 0;
|
|
||||||
device.input.append({"A", 0, 0});
|
|
||||||
device.input.append({"B", 0, 1});
|
|
||||||
device.input.append({"Select", 0, 2});
|
|
||||||
device.input.append({"Start", 0, 3});
|
|
||||||
device.input.append({"Up", 0, 4});
|
|
||||||
device.input.append({"Down", 0, 5});
|
|
||||||
device.input.append({"Left", 0, 6});
|
|
||||||
device.input.append({"Right", 0, 7});
|
|
||||||
device.displayinput = {4, 5, 6, 7, 1, 0, 2, 3};
|
|
||||||
port.device.append(device);
|
|
||||||
}
|
|
||||||
this->port.append(port);
|
this->port.append(port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::controller() {
|
||||||
|
Port::Device device{0, "Controller"};
|
||||||
|
device.input.append({0, 0, "A" });
|
||||||
|
device.input.append({1, 0, "B" });
|
||||||
|
device.input.append({2, 0, "Select"});
|
||||||
|
device.input.append({3, 0, "Start" });
|
||||||
|
device.input.append({4, 0, "Up" });
|
||||||
|
device.input.append({5, 0, "Down" });
|
||||||
|
device.input.append({6, 0, "Left" });
|
||||||
|
device.input.append({7, 0, "Right" });
|
||||||
|
device.order = {4, 5, 6, 7, 1, 0, 2, 3};
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,15 @@ struct Interface : Emulator::Interface {
|
||||||
void reset();
|
void reset();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
serializer serialize();
|
||||||
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Port::Device controller();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
|
@ -58,6 +58,15 @@ void Interface::run() {
|
||||||
system.run();
|
system.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serializer Interface::serialize() {
|
||||||
|
system.runtosave();
|
||||||
|
return system.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interface::unserialize(serializer &s) {
|
||||||
|
return system.unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
void Interface::updatePalette() {
|
void Interface::updatePalette() {
|
||||||
video.generate_palette();
|
video.generate_palette();
|
||||||
}
|
}
|
||||||
|
@ -68,72 +77,34 @@ Interface::Interface() {
|
||||||
information.name = "Game Boy";
|
information.name = "Game Boy";
|
||||||
information.width = 160;
|
information.width = 160;
|
||||||
information.height = 144;
|
information.height = 144;
|
||||||
|
information.overscan = false;
|
||||||
information.aspectRatio = 1.0;
|
information.aspectRatio = 1.0;
|
||||||
information.frequency = 4194304;
|
information.frequency = 4194304;
|
||||||
information.resettable = false;
|
information.resettable = false;
|
||||||
|
|
||||||
information.media.append({"Game Boy", "*.gb"});
|
information.media.append({"Game Boy", "gb" });
|
||||||
information.media.append({"Game Boy Color", "*.gbc"});
|
information.media.append({"Game Boy Color", "gbc"});
|
||||||
|
|
||||||
|
firmware.append({ID::GameBoyBootROM, "Game Boy", "sys", "boot.rom"});
|
||||||
|
firmware.append({ID::SuperGameBoyBootROM, "Super Game Boy", "sfc", "boot.rom"});
|
||||||
|
firmware.append({ID::GameBoyColorBootROM, "Game Boy Color", "sys", "boot.rom"});
|
||||||
|
|
||||||
|
schema.append(Media{ID::GameBoyROM, "Game Boy", "gb", "program.rom"});
|
||||||
|
schema.append(Media{ID::GameBoyColorROM, "Game Boy Color", "gbc", "program.rom"});
|
||||||
|
|
||||||
{
|
{
|
||||||
Firmware firmware;
|
Port port{0, "Device"};
|
||||||
firmware.displayname = "Game Boy";
|
|
||||||
firmware.name = "Game Boy.sys/boot.rom";
|
|
||||||
firmware.id = ID::GameBoyBootROM;
|
|
||||||
this->firmware.append(firmware);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Firmware firmware;
|
|
||||||
firmware.displayname = "Super Game Boy";
|
|
||||||
firmware.name = "Super Game Boy.sfc/boot.rom";
|
|
||||||
firmware.id = ID::SuperGameBoyBootROM;
|
|
||||||
this->firmware.append(firmware);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Firmware firmware;
|
|
||||||
firmware.displayname = "Game Boy Color";
|
|
||||||
firmware.name = "Game Boy Color.sys/boot.rom";
|
|
||||||
firmware.id = ID::GameBoyColorBootROM;
|
|
||||||
this->firmware.append(firmware);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Schema schema;
|
|
||||||
schema.displayname = "Game Boy";
|
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.gb";
|
|
||||||
schema.id = ID::GameBoyROM;
|
|
||||||
this->schema.append(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Schema schema;
|
|
||||||
schema.displayname = "Game Boy Color";
|
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.gbc";
|
|
||||||
schema.id = ID::GameBoyColorROM;
|
|
||||||
this->schema.append(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Port port;
|
|
||||||
port.name = "Device";
|
|
||||||
port.id = 0;
|
|
||||||
{
|
{
|
||||||
Port::Device device;
|
Port::Device device{0, "Controller"};
|
||||||
device.name = "Controller";
|
device.input.append({0, 0, "Up" });
|
||||||
device.id = 0;
|
device.input.append({1, 0, "Down" });
|
||||||
device.input.append({"Up", 0, 0});
|
device.input.append({2, 0, "Left" });
|
||||||
device.input.append({"Down", 0, 1});
|
device.input.append({3, 0, "Right" });
|
||||||
device.input.append({"Left", 0, 2});
|
device.input.append({4, 0, "B" });
|
||||||
device.input.append({"Right", 0, 3});
|
device.input.append({5, 0, "A" });
|
||||||
device.input.append({"B", 0, 4});
|
device.input.append({6, 0, "Select"});
|
||||||
device.input.append({"A", 0, 5});
|
device.input.append({7, 0, "Start" });
|
||||||
device.input.append({"Select", 0, 6});
|
device.order = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||||
device.input.append({"Start", 0, 7});
|
|
||||||
device.displayinput = {0, 1, 2, 3, 4, 5, 6, 7};
|
|
||||||
port.device.append(device);
|
port.device.append(device);
|
||||||
}
|
}
|
||||||
this->port.append(port);
|
this->port.append(port);
|
||||||
|
|
|
@ -27,6 +27,9 @@ struct Interface : Emulator::Interface {
|
||||||
void reset();
|
void reset();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
serializer serialize();
|
||||||
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
|
@ -62,6 +62,15 @@ void Interface::run() {
|
||||||
system.run();
|
system.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serializer Interface::serialize() {
|
||||||
|
system.runtosave();
|
||||||
|
return system.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interface::unserialize(serializer &s) {
|
||||||
|
return system.unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
void Interface::updatePalette() {
|
void Interface::updatePalette() {
|
||||||
video.generate_palette();
|
video.generate_palette();
|
||||||
}
|
}
|
||||||
|
@ -72,48 +81,32 @@ Interface::Interface() {
|
||||||
information.name = "Game Boy Advance";
|
information.name = "Game Boy Advance";
|
||||||
information.width = 240;
|
information.width = 240;
|
||||||
information.height = 160;
|
information.height = 160;
|
||||||
|
information.overscan = false;
|
||||||
information.aspectRatio = 1.0;
|
information.aspectRatio = 1.0;
|
||||||
information.frequency = 32768;
|
information.frequency = 32768;
|
||||||
information.resettable = false;
|
information.resettable = false;
|
||||||
|
|
||||||
information.media.append({"Game Boy Advance", "*.gba"});
|
information.media.append({"Game Boy Advance", "gba"});
|
||||||
|
|
||||||
|
firmware.append({ID::BIOS, "Game Boy Advance", "sys", "bios.rom"});
|
||||||
|
|
||||||
|
schema.append(Media{ID::ROM, "Game Boy Advance", "gba", "program.rom"});
|
||||||
|
|
||||||
{
|
{
|
||||||
Firmware firmware;
|
Port port{0, "Device"};
|
||||||
firmware.displayname = "Game Boy Advance";
|
|
||||||
firmware.name = "Game Boy Advance.sys/bios.rom";
|
|
||||||
firmware.id = ID::BIOS;
|
|
||||||
this->firmware.append(firmware);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Schema schema;
|
|
||||||
schema.displayname = "Game Boy Advance";
|
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.gba";
|
|
||||||
schema.id = ID::ROM;
|
|
||||||
this->schema.append(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Port port;
|
|
||||||
port.name = "Device";
|
|
||||||
port.id = 0;
|
|
||||||
{
|
{
|
||||||
Port::Device device;
|
Port::Device device{0, "Controller"};
|
||||||
device.name = "Controller";
|
device.input.append({0, 0, "A" });
|
||||||
device.id = 0;
|
device.input.append({1, 0, "B" });
|
||||||
device.input.append({"A", 0, 0});
|
device.input.append({2, 0, "Select"});
|
||||||
device.input.append({"B", 0, 1});
|
device.input.append({3, 0, "Start" });
|
||||||
device.input.append({"Select", 0, 2});
|
device.input.append({4, 0, "Right" });
|
||||||
device.input.append({"Start", 0, 3});
|
device.input.append({5, 0, "Left" });
|
||||||
device.input.append({"Right", 0, 4});
|
device.input.append({6, 0, "Up" });
|
||||||
device.input.append({"Left", 0, 5});
|
device.input.append({7, 0, "Down" });
|
||||||
device.input.append({"Up", 0, 6});
|
device.input.append({8, 0, "R" });
|
||||||
device.input.append({"Down", 0, 7});
|
device.input.append({9, 0, "L" });
|
||||||
device.input.append({"R", 0, 8});
|
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3};
|
||||||
device.input.append({"L", 0, 9});
|
|
||||||
device.displayinput = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3};
|
|
||||||
port.device.append(device);
|
port.device.append(device);
|
||||||
}
|
}
|
||||||
this->port.append(port);
|
this->port.append(port);
|
||||||
|
|
|
@ -22,6 +22,9 @@ struct Interface : Emulator::Interface {
|
||||||
void reset();
|
void reset();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
serializer serialize();
|
||||||
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
|
@ -110,7 +110,7 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
|
||||||
if(root.exists() == false) return;
|
if(root.exists() == false) return;
|
||||||
has_gb_slot = true;
|
has_gb_slot = true;
|
||||||
|
|
||||||
interface->mediaRequest({"Game Boy", "", "program.rom", "*.gb", 5});
|
interface->mediaRequest({ID::SuperGameBoyROM, "Game Boy", "gb", "program.rom"});
|
||||||
|
|
||||||
icd2.revision = max(1, numeral(root["revision"].data));
|
icd2.revision = max(1, numeral(root["revision"].data));
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
|
||||||
has_bs_cart = root["mmio"].exists();
|
has_bs_cart = root["mmio"].exists();
|
||||||
has_bs_slot = true;
|
has_bs_slot = true;
|
||||||
|
|
||||||
interface->mediaRequest({"BS-X Satellaview", "", "program.rom", "*.bs", 2});
|
interface->mediaRequest({ID::BsxFlashROM, "BS-X Satellaview", "bs", "program.rom"});
|
||||||
|
|
||||||
for(auto &node : root["slot"]) {
|
for(auto &node : root["slot"]) {
|
||||||
if(node.name != "map") continue;
|
if(node.name != "map") continue;
|
||||||
|
@ -400,7 +400,7 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
|
||||||
if(root.exists() == false) return;
|
if(root.exists() == false) return;
|
||||||
has_st_slot = true;
|
has_st_slot = true;
|
||||||
|
|
||||||
interface->mediaRequest({"Sufami Turbo", "", "program.rom", "*.st", 3});
|
interface->mediaRequest({ID::SufamiTurboSlotAROM, "Sufami Turbo", "st", "program.rom"});
|
||||||
|
|
||||||
for(auto &slot : root) {
|
for(auto &slot : root) {
|
||||||
if(slot.name != "slot") continue;
|
if(slot.name != "slot") continue;
|
||||||
|
|
|
@ -18,8 +18,8 @@ void Justifier::enter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(next < prev) {
|
if(next < prev) {
|
||||||
int nx1 = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::X);
|
int nx1 = interface->inputPoll(port, device, 0 + (unsigned)Input::JustifierID::X);
|
||||||
int ny1 = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::Y);
|
int ny1 = interface->inputPoll(port, device, 0 + (unsigned)Input::JustifierID::Y);
|
||||||
nx1 += player1.x;
|
nx1 += player1.x;
|
||||||
ny1 += player1.y;
|
ny1 += player1.y;
|
||||||
player1.x = max(-16, min(256 + 16, nx1));
|
player1.x = max(-16, min(256 + 16, nx1));
|
||||||
|
@ -27,8 +27,8 @@ void Justifier::enter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(next < prev && chained) {
|
if(next < prev && chained) {
|
||||||
int nx2 = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::X);
|
int nx2 = interface->inputPoll(port, device, 4 + (unsigned)Input::JustifierID::X);
|
||||||
int ny2 = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::Y);
|
int ny2 = interface->inputPoll(port, device, 4 + (unsigned)Input::JustifierID::Y);
|
||||||
nx2 += player2.x;
|
nx2 += player2.x;
|
||||||
ny2 += player2.y;
|
ny2 += player2.y;
|
||||||
player2.x = max(-16, min(256 + 16, nx2));
|
player2.x = max(-16, min(256 + 16, nx2));
|
||||||
|
@ -44,13 +44,13 @@ uint2 Justifier::data() {
|
||||||
if(counter >= 32) return 1;
|
if(counter >= 32) return 1;
|
||||||
|
|
||||||
if(counter == 0) {
|
if(counter == 0) {
|
||||||
player1.trigger = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::Trigger);
|
player1.trigger = interface->inputPoll(port, device, 0 + (unsigned)Input::JustifierID::Trigger);
|
||||||
player1.start = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::Start);
|
player1.start = interface->inputPoll(port, device, 0 + (unsigned)Input::JustifierID::Start);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(counter == 0 && chained) {
|
if(counter == 0 && chained) {
|
||||||
player2.trigger = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::Trigger);
|
player2.trigger = interface->inputPoll(port, device, 4 + (unsigned)Input::JustifierID::Trigger);
|
||||||
player2.start = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::Start);
|
player2.start = interface->inputPoll(port, device, 4 + (unsigned)Input::JustifierID::Start);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(counter++) {
|
switch(counter++) {
|
||||||
|
@ -100,7 +100,11 @@ void Justifier::latch(bool data) {
|
||||||
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
|
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
|
||||||
}
|
}
|
||||||
|
|
||||||
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
|
Justifier::Justifier(bool port, bool chained):
|
||||||
|
Controller(port),
|
||||||
|
chained(chained),
|
||||||
|
device(chained == false ? (unsigned)Input::Device::Justifier : (unsigned)Input::Device::Justifiers)
|
||||||
|
{
|
||||||
create(Controller::Enter, 21477272);
|
create(Controller::Enter, 21477272);
|
||||||
latched = 0;
|
latched = 0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
|
|
|
@ -6,6 +6,7 @@ struct Justifier : Controller {
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
const bool chained; //true if the second justifier is attached to the first
|
const bool chained; //true if the second justifier is attached to the first
|
||||||
|
const unsigned device;
|
||||||
bool latched;
|
bool latched;
|
||||||
unsigned counter;
|
unsigned counter;
|
||||||
|
|
||||||
|
|
|
@ -8,18 +8,20 @@ uint2 Multitap::data() {
|
||||||
index = counter1;
|
index = counter1;
|
||||||
if(index >= 16) return 3;
|
if(index >= 16) return 3;
|
||||||
counter1++;
|
counter1++;
|
||||||
|
if(index >= 12) return 0;
|
||||||
port1 = 0; //controller 1
|
port1 = 0; //controller 1
|
||||||
port2 = 1; //controller 2
|
port2 = 1; //controller 2
|
||||||
} else {
|
} else {
|
||||||
index = counter2;
|
index = counter2;
|
||||||
if(index >= 16) return 3;
|
if(index >= 16) return 3;
|
||||||
counter2++;
|
counter2++;
|
||||||
|
if(index >= 12) return 0;
|
||||||
port1 = 2; //controller 3
|
port1 = 2; //controller 3
|
||||||
port2 = 3; //controller 4
|
port2 = 3; //controller 4
|
||||||
}
|
}
|
||||||
|
|
||||||
bool data1 = interface->inputPoll(port, (unsigned)Input::Device::Multitap, port1 << 16 | index);
|
bool data1 = interface->inputPoll(port, (unsigned)Input::Device::Multitap, port1 * 12 + index);
|
||||||
bool data2 = interface->inputPoll(port, (unsigned)Input::Device::Multitap, port2 << 16 | index);
|
bool data2 = interface->inputPoll(port, (unsigned)Input::Device::Multitap, port2 * 12 + index);
|
||||||
return (data2 << 1) | (data1 << 0);
|
return (data2 << 1) | (data1 << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,8 @@ uint2 USART::data() {
|
||||||
//Joypad
|
//Joypad
|
||||||
if(iobit()) {
|
if(iobit()) {
|
||||||
if(counter >= 16) return 1;
|
if(counter >= 16) return 1;
|
||||||
uint2 result = interface->inputPoll(port, (unsigned)Input::Device::Joypad, counter);
|
uint2 result = 0;
|
||||||
|
if(counter < 12) result = interface->inputPoll(port, (unsigned)Input::Device::Joypad, counter);
|
||||||
if(latched == 0) counter++;
|
if(latched == 0) counter++;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,10 @@ void Interface::unload() {
|
||||||
cartridge.unload();
|
cartridge.unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Interface::connect(unsigned port, unsigned device) {
|
||||||
|
input.connect(port, (Input::Device)device);
|
||||||
|
}
|
||||||
|
|
||||||
void Interface::power() {
|
void Interface::power() {
|
||||||
system.power();
|
system.power();
|
||||||
}
|
}
|
||||||
|
@ -95,6 +99,15 @@ void Interface::run() {
|
||||||
system.run();
|
system.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serializer Interface::serialize() {
|
||||||
|
system.runtosave();
|
||||||
|
return system.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interface::unserialize(serializer &s) {
|
||||||
|
return system.unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
void Interface::updatePalette() {
|
void Interface::updatePalette() {
|
||||||
video.generate_palette();
|
video.generate_palette();
|
||||||
}
|
}
|
||||||
|
@ -105,157 +118,159 @@ Interface::Interface() {
|
||||||
information.name = "Super Famicom";
|
information.name = "Super Famicom";
|
||||||
information.width = 256;
|
information.width = 256;
|
||||||
information.height = 240;
|
information.height = 240;
|
||||||
|
information.overscan = true;
|
||||||
information.aspectRatio = 8.0 / 7.0;
|
information.aspectRatio = 8.0 / 7.0;
|
||||||
information.frequency = 32040;
|
information.frequency = 32040;
|
||||||
information.resettable = true;
|
information.resettable = true;
|
||||||
|
|
||||||
information.media.append({"Super Famicom", "*.sfc"});
|
information.media.append({"Super Famicom", "sfc"});
|
||||||
information.media.append({"BS-X Satellaview", "*.bs"});
|
information.media.append({"BS-X Satellaview", "bs" });
|
||||||
information.media.append({"Sufami Turbo", "*.st"});
|
information.media.append({"Sufami Turbo", "st" });
|
||||||
information.media.append({"Super Game Boy", "*.gb"});
|
information.media.append({"Super Game Boy", "gb" });
|
||||||
|
|
||||||
|
firmware.append({ID::IPLROM, "Super Famicom", "sys", "spc700.rom"});
|
||||||
|
|
||||||
{
|
{
|
||||||
Firmware firmware;
|
Schema schema(Media{ID::ROM, "Super Famicom", "sfc", "program.rom"});
|
||||||
firmware.displayname = "Super Famicom";
|
|
||||||
firmware.name = "Super Famicom.sys/spc700.rom";
|
|
||||||
firmware.id = ID::IPLROM;
|
|
||||||
this->firmware.append(firmware);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Schema schema;
|
|
||||||
schema.displayname = "Super Famicom";
|
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.sfc";
|
|
||||||
schema.id = ID::ROM;
|
|
||||||
this->schema.append(schema);
|
this->schema.append(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Schema schema;
|
Schema schema(Media{ID::ROM, "Super Game Boy", "sfc", "program.rom"});
|
||||||
schema.displayname = "Super Game Boy";
|
schema.slot.append({ID::SuperGameBoyROM, "Game Boy", "gb", "program.rom"});
|
||||||
schema.path = "Super Game Boy.sfc/";
|
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.sfc";
|
|
||||||
schema.id = ID::ROM;
|
|
||||||
{
|
|
||||||
Media slot;
|
|
||||||
slot.displayname = "Game Boy";
|
|
||||||
slot.name = "program.rom";
|
|
||||||
slot.filter = "*.gb";
|
|
||||||
slot.id = ID::SuperGameBoyROM;
|
|
||||||
schema.slot.append(schema);
|
|
||||||
}
|
|
||||||
this->schema.append(schema);
|
this->schema.append(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Schema schema;
|
Schema schema(Media{ID::ROM, "BS-X Satellaview", "sfc", "program.rom"});
|
||||||
schema.displayname = "BS-X Satellaview";
|
schema.slot.append({ID::BsxFlashROM, "BS-X Satellaview", "bs", "program.rom"});
|
||||||
schema.path = "BS-X Satellaview.sfc/";
|
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.sfc";
|
|
||||||
schema.id = ID::ROM;
|
|
||||||
{
|
|
||||||
Media slot;
|
|
||||||
slot.displayname = "BS-X Satellaview";
|
|
||||||
slot.name = "program.rom";
|
|
||||||
slot.filter = "*.bs";
|
|
||||||
slot.id = ID::BsxFlashROM;
|
|
||||||
schema.slot.append(slot);
|
|
||||||
}
|
|
||||||
this->schema.append(schema);
|
this->schema.append(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Schema schema;
|
Schema schema(Media{ID::ROM, "Sufami Turbo", "sfc", "program.rom"});
|
||||||
schema.displayname = "Sufami Turbo";
|
schema.slot.append({ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "st", "program.rom"});
|
||||||
schema.path = "Sufami Turbo.sfc/";
|
schema.slot.append({ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "st", "program.rom"});
|
||||||
schema.name = "program.rom";
|
|
||||||
schema.filter = "*.sfc";
|
|
||||||
schema.id = ID::ROM;
|
|
||||||
{
|
|
||||||
Media slot;
|
|
||||||
slot.displayname = "Sufami Turbo - Slot A";
|
|
||||||
slot.name = "program.rom";
|
|
||||||
slot.filter = "*.st";
|
|
||||||
slot.id = ID::SufamiTurboSlotAROM;
|
|
||||||
schema.slot.append(slot);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Media slot;
|
|
||||||
slot.displayname = "Sufami Turbo - Slot B";
|
|
||||||
slot.name = "program.rom";
|
|
||||||
slot.filter = "*.st";
|
|
||||||
slot.id = ID::SufamiTurboSlotBROM;
|
|
||||||
schema.slot.append(slot);
|
|
||||||
}
|
|
||||||
this->schema.append(schema);
|
this->schema.append(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Port port;
|
Port port{0, "Port 1"};
|
||||||
port.name = "Port 1";
|
port.device.append(none());
|
||||||
port.id = 0;
|
port.device.append(controller());
|
||||||
{
|
port.device.append(multitap());
|
||||||
Port::Device device;
|
port.device.append(mouse());
|
||||||
device.name = "None";
|
port.device.append(usart());
|
||||||
device.id = 0;
|
|
||||||
port.device.append(device);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Port::Device device;
|
|
||||||
device.name = "Controller";
|
|
||||||
device.id = 1;
|
|
||||||
device.input.append({"B", 0, 0});
|
|
||||||
device.input.append({"Y", 0, 1});
|
|
||||||
device.input.append({"Select", 0, 2});
|
|
||||||
device.input.append({"Start", 0, 3});
|
|
||||||
device.input.append({"Up", 0, 4});
|
|
||||||
device.input.append({"Down", 0, 5});
|
|
||||||
device.input.append({"Left", 0, 6});
|
|
||||||
device.input.append({"Right", 0, 7});
|
|
||||||
device.input.append({"A", 0, 8});
|
|
||||||
device.input.append({"X", 0, 9});
|
|
||||||
device.input.append({"L", 0, 10});
|
|
||||||
device.input.append({"R", 0, 11});
|
|
||||||
device.displayinput = {4, 5, 6, 7, 0, 8, 1, 9, 10, 11, 2, 3};
|
|
||||||
port.device.append(device);
|
|
||||||
}
|
|
||||||
this->port.append(port);
|
this->port.append(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Port port;
|
Port port{1, "Port 2"};
|
||||||
port.name = "Port 2";
|
port.device.append(none());
|
||||||
port.id = 1;
|
port.device.append(controller());
|
||||||
{
|
port.device.append(multitap());
|
||||||
Port::Device device;
|
port.device.append(mouse());
|
||||||
device.name = "None";
|
port.device.append(superScope());
|
||||||
device.id = 0;
|
port.device.append(justifier());
|
||||||
port.device.append(device);
|
port.device.append(justifiers());
|
||||||
}
|
|
||||||
{
|
|
||||||
Port::Device device;
|
|
||||||
device.name = "Controller";
|
|
||||||
device.id = 1;
|
|
||||||
device.input.append({"B", 0, 0});
|
|
||||||
device.input.append({"Y", 0, 1});
|
|
||||||
device.input.append({"Select", 0, 2});
|
|
||||||
device.input.append({"Start", 0, 3});
|
|
||||||
device.input.append({"Up", 0, 4});
|
|
||||||
device.input.append({"Down", 0, 5});
|
|
||||||
device.input.append({"Left", 0, 6});
|
|
||||||
device.input.append({"Right", 0, 7});
|
|
||||||
device.input.append({"A", 0, 8});
|
|
||||||
device.input.append({"X", 0, 9});
|
|
||||||
device.input.append({"L", 0, 10});
|
|
||||||
device.input.append({"R", 0, 11});
|
|
||||||
device.displayinput = {4, 5, 6, 7, 0, 8, 1, 9, 10, 11, 2, 3};
|
|
||||||
port.device.append(device);
|
|
||||||
}
|
|
||||||
this->port.append(port);
|
this->port.append(port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::none() {
|
||||||
|
Port::Device device{0, "None"};
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::controller() {
|
||||||
|
Port::Device device{1, "Controller"};
|
||||||
|
device.input.append({ 0, 0, "B" });
|
||||||
|
device.input.append({ 1, 0, "Y" });
|
||||||
|
device.input.append({ 2, 0, "Select"});
|
||||||
|
device.input.append({ 3, 0, "Start" });
|
||||||
|
device.input.append({ 4, 0, "Up" });
|
||||||
|
device.input.append({ 5, 0, "Down" });
|
||||||
|
device.input.append({ 6, 0, "Left" });
|
||||||
|
device.input.append({ 7, 0, "Right" });
|
||||||
|
device.input.append({ 8, 0, "A" });
|
||||||
|
device.input.append({ 9, 0, "X" });
|
||||||
|
device.input.append({10, 0, "L" });
|
||||||
|
device.input.append({11, 0, "R" });
|
||||||
|
device.order = {4, 5, 6, 7, 0, 8, 1, 9, 10, 11, 2, 3};
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::multitap() {
|
||||||
|
Port::Device device{2, "Multitap"};
|
||||||
|
for(unsigned p = 1, n = 0; p <= 4; p++, n += 12) {
|
||||||
|
device.input.append({n + 0, 0, {"Port ", p, " - ", "B" }});
|
||||||
|
device.input.append({n + 1, 0, {"Port ", p, " - ", "Y" }});
|
||||||
|
device.input.append({n + 2, 0, {"Port ", p, " - ", "Select"}});
|
||||||
|
device.input.append({n + 3, 0, {"Port ", p, " - ", "Start" }});
|
||||||
|
device.input.append({n + 4, 0, {"Port ", p, " - ", "Up" }});
|
||||||
|
device.input.append({n + 5, 0, {"Port ", p, " - ", "Down" }});
|
||||||
|
device.input.append({n + 6, 0, {"Port ", p, " - ", "Left" }});
|
||||||
|
device.input.append({n + 7, 0, {"Port ", p, " - ", "Right" }});
|
||||||
|
device.input.append({n + 8, 0, {"Port ", p, " - ", "A" }});
|
||||||
|
device.input.append({n + 9, 0, {"Port ", p, " - ", "X" }});
|
||||||
|
device.input.append({n + 10, 0, {"Port ", p, " - ", "L" }});
|
||||||
|
device.input.append({n + 11, 0, {"Port ", p, " - ", "R" }});
|
||||||
|
device.order.append(n + 4, n + 5, n + 6, n + 7, n + 0, n + 8);
|
||||||
|
device.order.append(n + 1, n + 9, n + 10, n + 11, n + 2, n + 3);
|
||||||
|
}
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::mouse() {
|
||||||
|
Port::Device device{3, "Mouse"};
|
||||||
|
device.input.append({0, 1, "X-axis"});
|
||||||
|
device.input.append({1, 1, "Y-axis"});
|
||||||
|
device.input.append({2, 0, "Left" });
|
||||||
|
device.input.append({3, 0, "Right" });
|
||||||
|
device.order = {0, 1, 2, 3};
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::superScope() {
|
||||||
|
Port::Device device{4, "Super Scope"};
|
||||||
|
device.input.append({0, 1, "X-axis" });
|
||||||
|
device.input.append({1, 1, "Y-axis" });
|
||||||
|
device.input.append({2, 0, "Trigger"});
|
||||||
|
device.input.append({3, 0, "Cursor" });
|
||||||
|
device.input.append({4, 0, "Turbo" });
|
||||||
|
device.input.append({5, 0, "Pause" });
|
||||||
|
device.order = {0, 1, 2, 3, 4, 5};
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::justifier() {
|
||||||
|
Port::Device device{5, "Justifier"};
|
||||||
|
device.input.append({0, 1, "X-axis" });
|
||||||
|
device.input.append({1, 1, "Y-axis" });
|
||||||
|
device.input.append({2, 0, "Trigger"});
|
||||||
|
device.input.append({3, 0, "Start" });
|
||||||
|
device.order = {0, 1, 2, 3};
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::justifiers() {
|
||||||
|
Port::Device device{6, "Justifiers"};
|
||||||
|
device.input.append({0, 1, "Port 1 - X-axis" });
|
||||||
|
device.input.append({1, 1, "Port 1 - Y-axis" });
|
||||||
|
device.input.append({2, 0, "Port 1 - Trigger"});
|
||||||
|
device.input.append({3, 0, "Port 1 - Start" });
|
||||||
|
device.order.append(0, 1, 2, 3);
|
||||||
|
device.input.append({4, 1, "Port 2 - X-axis" });
|
||||||
|
device.input.append({5, 1, "Port 2 - Y-axis" });
|
||||||
|
device.input.append({6, 0, "Port 2 - Trigger"});
|
||||||
|
device.input.append({7, 0, "Port 2 - Start" });
|
||||||
|
device.order.append(4, 5, 6, 7);
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::Interface::Port::Device Interface::usart() {
|
||||||
|
Port::Device device{7, "Serial USART"};
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,13 +27,27 @@ struct Interface : Emulator::Interface {
|
||||||
void save(unsigned id, const stream &stream);
|
void save(unsigned id, const stream &stream);
|
||||||
void unload();
|
void unload();
|
||||||
|
|
||||||
|
void connect(unsigned port, unsigned device);
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
serializer serialize();
|
||||||
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Port::Device none();
|
||||||
|
Port::Device controller();
|
||||||
|
Port::Device multitap();
|
||||||
|
Port::Device mouse();
|
||||||
|
Port::Device superScope();
|
||||||
|
Port::Device justifier();
|
||||||
|
Port::Device justifiers();
|
||||||
|
Port::Device usart();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
|
@ -7,7 +7,7 @@ include gb/Makefile
|
||||||
include gba/Makefile
|
include gba/Makefile
|
||||||
name := ethos
|
name := ethos
|
||||||
|
|
||||||
ui_objects := ui-ethos ui-configuration ui-interface ui-utility ui-input ui-general ui-settings
|
ui_objects := ui-ethos ui-configuration ui-interface ui-utility ui-input ui-window ui-general ui-settings
|
||||||
ui_objects += phoenix ruby
|
ui_objects += phoenix ruby
|
||||||
ui_objects += $(if $(call streq,$(platform),win),resource)
|
ui_objects += $(if $(call streq,$(platform),win),resource)
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ obj/ui-configuration.o: $(ui)/configuration/configuration.cpp $(call rwildcard,$
|
||||||
obj/ui-interface.o: $(ui)/interface/interface.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-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
|
||||||
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
|
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
|
||||||
|
obj/ui-window.o: $(ui)/window/window.cpp $(call rwildcard,$(ui)/)
|
||||||
obj/ui-general.o: $(ui)/general/general.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/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,9 @@ void Application::bootstrap() {
|
||||||
system->callback.audioSample = {&Interface::audioSample, interface};
|
system->callback.audioSample = {&Interface::audioSample, interface};
|
||||||
system->callback.inputPoll = {&Interface::inputPoll, interface};
|
system->callback.inputPoll = {&Interface::inputPoll, interface};
|
||||||
system->callback.mediaRequest = {&Interface::mediaRequest, interface};
|
system->callback.mediaRequest = {&Interface::mediaRequest, interface};
|
||||||
system->updatePalette();
|
|
||||||
|
|
||||||
for(auto &firmware : system->firmware) {
|
for(auto &firmware : system->firmware) {
|
||||||
filestream fs{application->path(firmware.name)};
|
filestream fs{application->path({firmware.name, ".", firmware.extension, "/", firmware.path})};
|
||||||
system->load(firmware.id, fs);
|
system->load(firmware.id, fs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,23 @@
|
||||||
Configuration *config = nullptr;
|
Configuration *config = nullptr;
|
||||||
|
|
||||||
Configuration::Configuration() {
|
Configuration::Configuration() {
|
||||||
|
append(video.synchronize = false, "Video::Synchronize");
|
||||||
append(video.scaleMode = 0, "Video::ScaleMode");
|
append(video.scaleMode = 0, "Video::ScaleMode");
|
||||||
append(video.aspectCorrection = true, "Video::AspectCorrection");
|
append(video.aspectCorrection = true, "Video::AspectCorrection");
|
||||||
|
append(video.maskOverscan = false, "Video::MaskOverscan");
|
||||||
|
append(video.maskOverscanHorizontal = 8, "Video::MaskOverscan::Horizontal");
|
||||||
|
append(video.maskOverscanVertical = 8, "Video::MaskOverscan::Vertical");
|
||||||
|
append(video.saturation = 100, "Video::Saturation");
|
||||||
|
append(video.gamma = 150, "Video::Gamma");
|
||||||
|
append(video.luminance = 100, "Video::Luminance");
|
||||||
|
append(audio.synchronize = true, "Audio::Synchronize");
|
||||||
|
append(audio.frequency = 48000, "Audio::Frequency");
|
||||||
|
append(audio.latency = 60, "Audio::Latency");
|
||||||
|
append(audio.resampler = 2, "Audio::Resampler");
|
||||||
|
append(audio.volume = 100, "Audio::Volume");
|
||||||
|
append(audio.mute = false, "Audio::Mute");
|
||||||
|
append(input.focusPause = false, "Input::Focus::Pause");
|
||||||
|
append(input.focusAllow = false, "Input::Focus::AllowInput");
|
||||||
|
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,30 @@
|
||||||
struct Configuration : configuration {
|
struct Configuration : configuration {
|
||||||
struct Video {
|
struct Video {
|
||||||
|
bool synchronize;
|
||||||
unsigned scaleMode;
|
unsigned scaleMode;
|
||||||
bool aspectCorrection;
|
bool aspectCorrection;
|
||||||
|
bool maskOverscan;
|
||||||
|
unsigned maskOverscanHorizontal;
|
||||||
|
unsigned maskOverscanVertical;
|
||||||
|
unsigned saturation;
|
||||||
|
unsigned gamma;
|
||||||
|
unsigned luminance;
|
||||||
} video;
|
} video;
|
||||||
|
|
||||||
|
struct Audio {
|
||||||
|
bool synchronize;
|
||||||
|
unsigned frequency;
|
||||||
|
unsigned latency;
|
||||||
|
unsigned resampler;
|
||||||
|
unsigned volume;
|
||||||
|
bool mute;
|
||||||
|
} audio;
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
bool focusPause;
|
||||||
|
bool focusAllow;
|
||||||
|
} input;
|
||||||
|
|
||||||
void load();
|
void load();
|
||||||
void save();
|
void save();
|
||||||
Configuration();
|
Configuration();
|
||||||
|
|
|
@ -19,8 +19,11 @@ string Application::path(const string &filename) {
|
||||||
|
|
||||||
void Application::run() {
|
void Application::run() {
|
||||||
inputManager->poll();
|
inputManager->poll();
|
||||||
|
utility->updateStatus();
|
||||||
|
autopause = config->input.focusPause && presentation->focused() == false;
|
||||||
|
|
||||||
if(active == nullptr || system().loaded() == false) {
|
if(active == nullptr || system().loaded() == false || pause || autopause) {
|
||||||
|
audio.clear();
|
||||||
usleep(20 * 1000);
|
usleep(20 * 1000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -61,9 +64,14 @@ Application::Application(int argc, char **argv) {
|
||||||
monospaceFont = "Liberation Mono, 8";
|
monospaceFont = "Liberation Mono, 8";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
video.driver("OpenGL");
|
||||||
|
audio.driver("ALSA");
|
||||||
|
input.driver("SDL");
|
||||||
|
|
||||||
config = new Configuration;
|
config = new Configuration;
|
||||||
utility = new Utility;
|
utility = new Utility;
|
||||||
inputManager = new InputManager;
|
inputManager = new InputManager;
|
||||||
|
windowManager = new WindowManager;
|
||||||
browser = new Browser;
|
browser = new Browser;
|
||||||
presentation = new Presentation;
|
presentation = new Presentation;
|
||||||
videoSettings = new VideoSettings;
|
videoSettings = new VideoSettings;
|
||||||
|
@ -71,31 +79,26 @@ Application::Application(int argc, char **argv) {
|
||||||
inputSettings = new InputSettings;
|
inputSettings = new InputSettings;
|
||||||
hotkeySettings = new HotkeySettings;
|
hotkeySettings = new HotkeySettings;
|
||||||
settings = new Settings;
|
settings = new Settings;
|
||||||
|
windowManager->loadGeometry();
|
||||||
presentation->setVisible();
|
presentation->setVisible();
|
||||||
|
|
||||||
video.driver("OpenGL");
|
|
||||||
video.set(Video::Handle, presentation->viewport.handle());
|
video.set(Video::Handle, presentation->viewport.handle());
|
||||||
video.set(Video::Synchronize, false);
|
if(!video.cap(Video::Depth) || !video.set(Video::Depth, depth = 30u)) {
|
||||||
video.set(Video::Depth, 24u);
|
video.set(Video::Depth, depth = 24u);
|
||||||
|
}
|
||||||
video.init();
|
video.init();
|
||||||
|
|
||||||
audio.driver("ALSA");
|
|
||||||
audio.set(Audio::Handle, presentation->viewport.handle());
|
audio.set(Audio::Handle, presentation->viewport.handle());
|
||||||
audio.set(Audio::Synchronize, true);
|
|
||||||
audio.set(Audio::Latency, 80u);
|
|
||||||
audio.set(Audio::Frequency, 48000u);
|
|
||||||
audio.init();
|
audio.init();
|
||||||
|
|
||||||
input.driver("SDL");
|
|
||||||
input.set(Input::Handle, presentation->viewport.handle());
|
input.set(Input::Handle, presentation->viewport.handle());
|
||||||
input.init();
|
input.init();
|
||||||
|
|
||||||
dspaudio.setPrecision(16);
|
dspaudio.setPrecision(16);
|
||||||
dspaudio.setVolume(2.0);
|
|
||||||
dspaudio.setBalance(0.0);
|
dspaudio.setBalance(0.0);
|
||||||
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
|
dspaudio.setFrequency(96000);
|
||||||
dspaudio.setResamplerFrequency(48000u);
|
|
||||||
|
utility->synchronizeRuby();
|
||||||
|
|
||||||
while(quit == false) {
|
while(quit == false) {
|
||||||
OS::processEvents();
|
OS::processEvents();
|
||||||
|
@ -106,6 +109,7 @@ Application::Application(int argc, char **argv) {
|
||||||
config->save();
|
config->save();
|
||||||
browser->saveConfiguration();
|
browser->saveConfiguration();
|
||||||
inputManager->saveConfiguration();
|
inputManager->saveConfiguration();
|
||||||
|
windowManager->saveGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
|
|
|
@ -21,6 +21,7 @@ using namespace ruby;
|
||||||
#include "interface/interface.hpp"
|
#include "interface/interface.hpp"
|
||||||
#include "utility/utility.hpp"
|
#include "utility/utility.hpp"
|
||||||
#include "input/input.hpp"
|
#include "input/input.hpp"
|
||||||
|
#include "window/window.hpp"
|
||||||
#include "general/general.hpp"
|
#include "general/general.hpp"
|
||||||
#include "settings/settings.hpp"
|
#include "settings/settings.hpp"
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ Browser *browser = nullptr;
|
||||||
Browser::Browser() {
|
Browser::Browser() {
|
||||||
bootstrap();
|
bootstrap();
|
||||||
setGeometry({128, 128, 640, 400});
|
setGeometry({128, 128, 640, 400});
|
||||||
|
windowManager->append(this, "Browser");
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
pathBrowse.setText("Browse ...");
|
pathBrowse.setText("Browse ...");
|
||||||
|
@ -50,7 +51,7 @@ void Browser::synchronize() {
|
||||||
openButton.setEnabled(fileList.selected());
|
openButton.setEnabled(fileList.selected());
|
||||||
if(fileList.selected()) {
|
if(fileList.selected()) {
|
||||||
for(auto &folder : folderList) {
|
for(auto &folder : folderList) {
|
||||||
if(folder.filter == filter) {
|
if(folder.extension == extension) {
|
||||||
folder.selection = fileList.selection();
|
folder.selection = fileList.selection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,7 @@ void Browser::bootstrap() {
|
||||||
for(auto &media : emulator->information.media) {
|
for(auto &media : emulator->information.media) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for(auto &folder : folderList) {
|
for(auto &folder : folderList) {
|
||||||
if(folder.filter == filter) {
|
if(folder.extension == media.extension) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +75,7 @@ void Browser::bootstrap() {
|
||||||
if(found == true) continue;
|
if(found == true) continue;
|
||||||
|
|
||||||
Folder folder;
|
Folder folder;
|
||||||
folder.filter = media.filter;
|
folder.extension = media.extension;
|
||||||
folder.path = application->basepath;
|
folder.path = application->basepath;
|
||||||
folder.selection = 0;
|
folder.selection = 0;
|
||||||
folderList.append(folder);
|
folderList.append(folder);
|
||||||
|
@ -82,21 +83,21 @@ void Browser::bootstrap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto &folder : folderList) {
|
for(auto &folder : folderList) {
|
||||||
config.append(folder.path, folder.filter);
|
config.append(folder.path, folder.extension);
|
||||||
config.append(folder.selection, string{folder.filter, "::selection"});
|
config.append(folder.selection, string{folder.extension, "::selection"});
|
||||||
}
|
}
|
||||||
|
|
||||||
config.load(application->path("paths.cfg"));
|
config.load(application->path("paths.cfg"));
|
||||||
config.save(application->path("paths.cfg"));
|
config.save(application->path("paths.cfg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
string Browser::select(const string &title, const string &filter) {
|
string Browser::select(const string &title, const string &extension) {
|
||||||
this->filter = filter;
|
this->extension = extension;
|
||||||
|
|
||||||
string path;
|
string path;
|
||||||
unsigned selection = 0;
|
unsigned selection = 0;
|
||||||
for(auto &folder : folderList) {
|
for(auto &folder : folderList) {
|
||||||
if(folder.filter == filter) {
|
if(folder.extension == extension) {
|
||||||
path = folder.path;
|
path = folder.path;
|
||||||
selection = folder.selection;
|
selection = folder.selection;
|
||||||
break;
|
break;
|
||||||
|
@ -105,8 +106,9 @@ string Browser::select(const string &title, const string &filter) {
|
||||||
if(path.empty()) path = application->basepath;
|
if(path.empty()) path = application->basepath;
|
||||||
setPath(path, selection);
|
setPath(path, selection);
|
||||||
|
|
||||||
filterLabel.setText({"Files of type: ", filter});
|
filterLabel.setText({"Files of type: *.", extension});
|
||||||
|
|
||||||
|
audio.clear();
|
||||||
setTitle(title);
|
setTitle(title);
|
||||||
setModal();
|
setModal();
|
||||||
setVisible();
|
setVisible();
|
||||||
|
@ -123,7 +125,7 @@ string Browser::select(const string &title, const string &filter) {
|
||||||
void Browser::setPath(const string &path, unsigned selection) {
|
void Browser::setPath(const string &path, unsigned selection) {
|
||||||
//save path for next browser selection
|
//save path for next browser selection
|
||||||
for(auto &folder : folderList) {
|
for(auto &folder : folderList) {
|
||||||
if(folder.filter == filter) folder.path = path;
|
if(folder.extension == extension) folder.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->path = path;
|
this->path = path;
|
||||||
|
@ -135,7 +137,6 @@ void Browser::setPath(const string &path, unsigned selection) {
|
||||||
lstring contents = directory::folders(path);
|
lstring contents = directory::folders(path);
|
||||||
|
|
||||||
for(auto &filename : contents) {
|
for(auto &filename : contents) {
|
||||||
string filter = {this->filter, "/"};
|
|
||||||
if(!filename.wildcard(R"(*.??/)") && !filename.wildcard(R"(*.???/)")) {
|
if(!filename.wildcard(R"(*.??/)") && !filename.wildcard(R"(*.???/)")) {
|
||||||
string name = filename;
|
string name = filename;
|
||||||
name.rtrim<1>("/");
|
name.rtrim<1>("/");
|
||||||
|
@ -146,12 +147,11 @@ void Browser::setPath(const string &path, unsigned selection) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto &filename : contents) {
|
for(auto &filename : contents) {
|
||||||
string filter = {this->filter, "/"};
|
string suffix = {".", this->extension, "/"};
|
||||||
if(filename.wildcard(R"(*.??/)") || filename.wildcard(R"(*.???/)")) {
|
if(filename.wildcard(R"(*.??/)") || filename.wildcard(R"(*.???/)")) {
|
||||||
if(filename.wildcard(filter)) {
|
if(filename.endswith(suffix)) {
|
||||||
string name = filename;
|
string name = filename;
|
||||||
filter.ltrim<1>("*");
|
name.rtrim<1>(suffix);
|
||||||
name.rtrim<1>(filter);
|
|
||||||
filenameList.append(filename);
|
filenameList.append(filename);
|
||||||
fileList.append(name);
|
fileList.append(name);
|
||||||
}
|
}
|
||||||
|
@ -166,8 +166,8 @@ void Browser::setPath(const string &path, unsigned selection) {
|
||||||
void Browser::fileListActivate() {
|
void Browser::fileListActivate() {
|
||||||
unsigned selection = fileList.selection();
|
unsigned selection = fileList.selection();
|
||||||
string filename = filenameList[selection];
|
string filename = filenameList[selection];
|
||||||
string filter = {this->filter, "/"};
|
string suffix = {this->extension, "/"};
|
||||||
if(filename.wildcard(filter) == false) return setPath({path, filename});
|
if(filename.endswith(suffix) == false) return setPath({path, filename});
|
||||||
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
dialogActive = false;
|
dialogActive = false;
|
||||||
|
|
|
@ -9,7 +9,7 @@ struct Browser : Window {
|
||||||
Label filterLabel;
|
Label filterLabel;
|
||||||
Button openButton;
|
Button openButton;
|
||||||
|
|
||||||
string select(const string &title, const string &filter);
|
string select(const string &title, const string &extension);
|
||||||
void saveConfiguration();
|
void saveConfiguration();
|
||||||
void synchronize();
|
void synchronize();
|
||||||
void bootstrap();
|
void bootstrap();
|
||||||
|
@ -18,7 +18,7 @@ struct Browser : Window {
|
||||||
private:
|
private:
|
||||||
configuration config;
|
configuration config;
|
||||||
struct Folder {
|
struct Folder {
|
||||||
string filter;
|
string extension;
|
||||||
string path;
|
string path;
|
||||||
unsigned selection;
|
unsigned selection;
|
||||||
};
|
};
|
||||||
|
@ -27,7 +27,7 @@ private:
|
||||||
bool dialogActive;
|
bool dialogActive;
|
||||||
string outputFilename;
|
string outputFilename;
|
||||||
|
|
||||||
string filter;
|
string extension;
|
||||||
string path;
|
string path;
|
||||||
lstring filenameList;
|
lstring filenameList;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
Presentation *presentation = nullptr;
|
Presentation *presentation = nullptr;
|
||||||
|
|
||||||
void Presentation::synchronize() {
|
void Presentation::synchronize() {
|
||||||
for(auto &system : emulatorList) system->menu.setVisible(false);
|
for(auto &emulator : emulatorList) emulator->menu.setVisible(false);
|
||||||
for(auto &system : emulatorList) {
|
for(auto &emulator : emulatorList) {
|
||||||
if(system->interface == application->active) {
|
if(emulator->interface == application->active) {
|
||||||
activeSystem = system;
|
active = emulator;
|
||||||
system->menu.setVisible(true);
|
emulator->menu.setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,22 +15,26 @@ void Presentation::synchronize() {
|
||||||
case 2: stretchVideo.setChecked(); break;
|
case 2: stretchVideo.setChecked(); break;
|
||||||
}
|
}
|
||||||
aspectCorrection.setChecked(config->video.aspectCorrection);
|
aspectCorrection.setChecked(config->video.aspectCorrection);
|
||||||
resizeWindow.setVisible(application->active && config->video.scaleMode != 2);
|
maskOverscan.setChecked(config->video.maskOverscan);
|
||||||
|
synchronizeVideo.setChecked(config->video.synchronize);
|
||||||
|
synchronizeAudio.setChecked(config->audio.synchronize);
|
||||||
|
muteAudio.setChecked(config->audio.mute);
|
||||||
|
toolsMenu.setVisible(application->active);
|
||||||
|
resizeWindow.setVisible(config->video.scaleMode != 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Presentation::setSystemName(const string &name) {
|
void Presentation::setSystemName(const string &name) {
|
||||||
if(activeSystem) activeSystem->menu.setText(name);
|
if(active) active->menu.setText(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Presentation::Presentation() : activeSystem(nullptr) {
|
Presentation::Presentation() : active(nullptr) {
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
||||||
setTitle({Emulator::Name, " v", Emulator::Version});
|
|
||||||
setGeometry({1024, 600, 720, 480});
|
setGeometry({1024, 600, 720, 480});
|
||||||
|
windowManager->append(this, "Presentation");
|
||||||
|
|
||||||
|
setTitle({::Emulator::Name, " v", ::Emulator::Version});
|
||||||
setBackgroundColor({0, 0, 0});
|
setBackgroundColor({0, 0, 0});
|
||||||
setMenuFont(application->normalFont);
|
|
||||||
setMenuVisible();
|
setMenuVisible();
|
||||||
setStatusFont(application->boldFont);
|
|
||||||
setStatusVisible();
|
setStatusVisible();
|
||||||
|
|
||||||
loadMenu.setText("Load");
|
loadMenu.setText("Load");
|
||||||
|
@ -40,20 +44,39 @@ Presentation::Presentation() : activeSystem(nullptr) {
|
||||||
scaleVideo.setText("Scale");
|
scaleVideo.setText("Scale");
|
||||||
stretchVideo.setText("Stretch");
|
stretchVideo.setText("Stretch");
|
||||||
RadioItem::group(centerVideo, scaleVideo, stretchVideo);
|
RadioItem::group(centerVideo, scaleVideo, stretchVideo);
|
||||||
aspectCorrection.setText("Correct Aspect Ratio");
|
aspectCorrection.setText("Aspect Correction");
|
||||||
|
maskOverscan.setText("Mask Overscan");
|
||||||
|
synchronizeVideo.setText("Synchronize Video");
|
||||||
|
synchronizeAudio.setText("Synchronize Audio");
|
||||||
|
muteAudio.setText("Mute Audio");
|
||||||
configurationSettings.setText("Configuration ...");
|
configurationSettings.setText("Configuration ...");
|
||||||
toolsMenu.setText("Tools");
|
toolsMenu.setText("Tools");
|
||||||
|
saveStateMenu.setText("Save State");
|
||||||
|
for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n});
|
||||||
|
loadStateMenu.setText("Load State");
|
||||||
|
for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n});
|
||||||
resizeWindow.setText("Resize Window");
|
resizeWindow.setText("Resize Window");
|
||||||
|
|
||||||
append(loadMenu);
|
append(loadMenu);
|
||||||
for(auto &item : loadList) loadMenu.append(*item);
|
for(auto &item : loadListDirect) loadMenu.append(*item);
|
||||||
|
if(loadListSlotted.size() > 0) {
|
||||||
|
loadMenu.append(*new Separator);
|
||||||
|
for(auto &item : loadListSlotted) loadMenu.append(*item);
|
||||||
|
}
|
||||||
for(auto &system : emulatorList) append(system->menu);
|
for(auto &system : emulatorList) append(system->menu);
|
||||||
append(settingsMenu);
|
append(settingsMenu);
|
||||||
settingsMenu.append(videoMenu);
|
settingsMenu.append(videoMenu);
|
||||||
videoMenu.append(centerVideo, scaleVideo, stretchVideo, *new Separator, aspectCorrection);
|
videoMenu.append(centerVideo, scaleVideo, stretchVideo, *new Separator, aspectCorrection, maskOverscan);
|
||||||
|
settingsMenu.append(*new Separator);
|
||||||
|
settingsMenu.append(synchronizeVideo, synchronizeAudio, muteAudio);
|
||||||
settingsMenu.append(*new Separator);
|
settingsMenu.append(*new Separator);
|
||||||
settingsMenu.append(configurationSettings);
|
settingsMenu.append(configurationSettings);
|
||||||
append(toolsMenu);
|
append(toolsMenu);
|
||||||
|
toolsMenu.append(saveStateMenu);
|
||||||
|
for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]);
|
||||||
|
toolsMenu.append(loadStateMenu);
|
||||||
|
for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]);
|
||||||
|
toolsMenu.append(stateMenuSeparator);
|
||||||
toolsMenu.append(resizeWindow);
|
toolsMenu.append(resizeWindow);
|
||||||
|
|
||||||
append(layout);
|
append(layout);
|
||||||
|
@ -66,7 +89,13 @@ Presentation::Presentation() : activeSystem(nullptr) {
|
||||||
scaleVideo.onActivate = [&] { config->video.scaleMode = 1; utility->resize(); };
|
scaleVideo.onActivate = [&] { config->video.scaleMode = 1; utility->resize(); };
|
||||||
stretchVideo.onActivate = [&] { config->video.scaleMode = 2; utility->resize(); };
|
stretchVideo.onActivate = [&] { config->video.scaleMode = 2; utility->resize(); };
|
||||||
aspectCorrection.onToggle = [&] { config->video.aspectCorrection = aspectCorrection.checked(); utility->resize(); };
|
aspectCorrection.onToggle = [&] { config->video.aspectCorrection = aspectCorrection.checked(); utility->resize(); };
|
||||||
configurationSettings.onActivate = [&] { settings->setVisible(); };
|
maskOverscan.onToggle = [&] { config->video.maskOverscan = maskOverscan.checked(); };
|
||||||
|
synchronizeVideo.onToggle = [&] { config->video.synchronize = synchronizeVideo.checked(); utility->synchronizeRuby(); };
|
||||||
|
synchronizeAudio.onToggle = [&] { config->audio.synchronize = synchronizeAudio.checked(); utility->synchronizeRuby(); };
|
||||||
|
muteAudio.onToggle = [&] { config->audio.mute = muteAudio.checked(); utility->synchronizeRuby(); };
|
||||||
|
configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); };
|
||||||
|
for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); };
|
||||||
|
for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); };
|
||||||
resizeWindow.onActivate = [&] { utility->resize(true); };
|
resizeWindow.onActivate = [&] { utility->resize(true); };
|
||||||
|
|
||||||
synchronize();
|
synchronize();
|
||||||
|
@ -74,33 +103,65 @@ Presentation::Presentation() : activeSystem(nullptr) {
|
||||||
|
|
||||||
void Presentation::bootstrap() {
|
void Presentation::bootstrap() {
|
||||||
for(auto &emulator : application->emulator) {
|
for(auto &emulator : application->emulator) {
|
||||||
System *system = new System;
|
auto iEmulator = new Emulator;
|
||||||
system->interface = emulator;
|
iEmulator->interface = emulator;
|
||||||
|
|
||||||
for(auto &schema : emulator->schema) {
|
for(auto &schema : emulator->schema) {
|
||||||
Item *item = new Item;
|
Item *item = new Item;
|
||||||
item->setText({schema.displayname, " ..."});
|
item->setText({schema.name, " ..."});
|
||||||
item->onActivate = [=, &schema] {
|
item->onActivate = [=, &schema] {
|
||||||
utility->loadSchema(system->interface, schema);
|
utility->loadSchema(iEmulator->interface, schema);
|
||||||
};
|
};
|
||||||
loadList.append(item);
|
if(schema.slot.size() == 0) loadListDirect.append(item);
|
||||||
|
if(schema.slot.size() >= 1) loadListSlotted.append(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
system->menu.setText(emulator->information.name);
|
iEmulator->menu.setText(emulator->information.name);
|
||||||
system->power.setText("Power");
|
iEmulator->power.setText("Power");
|
||||||
system->reset.setText("Reset");
|
iEmulator->reset.setText("Reset");
|
||||||
system->unload.setText("Unload");
|
iEmulator->unload.setText("Unload");
|
||||||
|
|
||||||
system->menu.append(system->power);
|
unsigned portNumber = 0;
|
||||||
|
for(auto &port : emulator->port) {
|
||||||
|
auto iPort = new Emulator::Port;
|
||||||
|
iPort->menu.setText(port.name);
|
||||||
|
iEmulator->port.append(iPort);
|
||||||
|
|
||||||
|
unsigned deviceNumber = 0;
|
||||||
|
for(auto &device : port.device) {
|
||||||
|
auto iDevice = new RadioItem;
|
||||||
|
iDevice->setText(device.name);
|
||||||
|
iDevice->onActivate = [=] { utility->connect(portNumber, deviceNumber); };
|
||||||
|
iPort->group.append(*iDevice);
|
||||||
|
iPort->device.append(iDevice);
|
||||||
|
deviceNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
RadioItem::group(iPort->group);
|
||||||
|
portNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
iEmulator->menu.append(iEmulator->power);
|
||||||
if(emulator->information.resettable)
|
if(emulator->information.resettable)
|
||||||
system->menu.append(system->reset);
|
iEmulator->menu.append(iEmulator->reset);
|
||||||
system->menu.append(system->separator);
|
iEmulator->menu.append(*new Separator);
|
||||||
system->menu.append(system->unload);
|
unsigned visiblePorts = 0;
|
||||||
|
for(auto &iPort : iEmulator->port) {
|
||||||
|
iEmulator->menu.append(iPort->menu);
|
||||||
|
if(iPort->device.size() <= 1) iPort->menu.setVisible(false);
|
||||||
|
else visiblePorts++;
|
||||||
|
for(auto &iDevice : iPort->device) {
|
||||||
|
iPort->menu.append(*iDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iEmulator->menu.append(iEmulator->controllerSeparator);
|
||||||
|
if(visiblePorts == 0) iEmulator->controllerSeparator.setVisible(false);
|
||||||
|
iEmulator->menu.append(iEmulator->unload);
|
||||||
|
|
||||||
system->power.onActivate = {&Utility::power, utility};
|
iEmulator->power.onActivate = {&Utility::power, utility};
|
||||||
system->reset.onActivate = {&Utility::reset, utility};
|
iEmulator->reset.onActivate = {&Utility::reset, utility};
|
||||||
system->unload.onActivate = {&Utility::unload, utility};
|
iEmulator->unload.onActivate = {&Utility::unload, utility};
|
||||||
|
|
||||||
emulatorList.append(system);
|
emulatorList.append(iEmulator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,44 @@ struct Presentation : Window {
|
||||||
FixedLayout layout;
|
FixedLayout layout;
|
||||||
Viewport viewport;
|
Viewport viewport;
|
||||||
|
|
||||||
struct System {
|
struct Emulator {
|
||||||
Emulator::Interface *interface;
|
::Emulator::Interface *interface;
|
||||||
|
|
||||||
Menu menu;
|
Menu menu;
|
||||||
Item power;
|
Item power;
|
||||||
Item reset;
|
Item reset;
|
||||||
Separator separator;
|
|
||||||
Item unload;
|
Item unload;
|
||||||
|
Separator controllerSeparator;
|
||||||
|
struct Port {
|
||||||
|
Menu menu;
|
||||||
|
set<RadioItem&> group;
|
||||||
|
vector<RadioItem*> device;
|
||||||
|
};
|
||||||
|
vector<Port*> port;
|
||||||
function<void (string)> callback;
|
function<void (string)> callback;
|
||||||
};
|
};
|
||||||
vector<System*> emulatorList;
|
vector<Emulator*> emulatorList;
|
||||||
|
|
||||||
Menu loadMenu;
|
Menu loadMenu;
|
||||||
vector<Action*> loadList;
|
vector<Item*> loadListDirect;
|
||||||
|
vector<Item*> loadListSlotted;
|
||||||
Menu settingsMenu;
|
Menu settingsMenu;
|
||||||
Menu videoMenu;
|
Menu videoMenu;
|
||||||
RadioItem centerVideo;
|
RadioItem centerVideo;
|
||||||
RadioItem scaleVideo;
|
RadioItem scaleVideo;
|
||||||
RadioItem stretchVideo;
|
RadioItem stretchVideo;
|
||||||
CheckItem aspectCorrection;
|
CheckItem aspectCorrection;
|
||||||
|
CheckItem maskOverscan;
|
||||||
|
CheckItem synchronizeVideo;
|
||||||
|
CheckItem synchronizeAudio;
|
||||||
|
CheckItem muteAudio;
|
||||||
Item configurationSettings;
|
Item configurationSettings;
|
||||||
Menu toolsMenu;
|
Menu toolsMenu;
|
||||||
|
Menu saveStateMenu;
|
||||||
|
Item saveStateItem[5];
|
||||||
|
Menu loadStateMenu;
|
||||||
|
Item loadStateItem[5];
|
||||||
|
Separator stateMenuSeparator;
|
||||||
Item resizeWindow;
|
Item resizeWindow;
|
||||||
|
|
||||||
void synchronize();
|
void synchronize();
|
||||||
|
@ -32,7 +48,7 @@ struct Presentation : Window {
|
||||||
Presentation();
|
Presentation();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
System *activeSystem;
|
Emulator *active;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Presentation *presentation;
|
extern Presentation *presentation;
|
||||||
|
|
|
@ -2,21 +2,37 @@ void InputManager::appendHotkeys() {
|
||||||
{
|
{
|
||||||
auto hotkey = new HotkeyInput;
|
auto hotkey = new HotkeyInput;
|
||||||
hotkey->name = "Toggle Fullscreen Mode";
|
hotkey->name = "Toggle Fullscreen Mode";
|
||||||
hotkey->mapping = "KB0::Alt,KB0::Return";
|
hotkey->mapping = "KB0::F11";
|
||||||
hotkey->logic = 1;
|
|
||||||
hotkeyMap.append(hotkey);
|
|
||||||
|
|
||||||
hotkey->press = [] {
|
hotkey->press = [] {
|
||||||
utility->toggleFullScreen();
|
utility->toggleFullScreen();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hotkey = new HotkeyInput;
|
||||||
|
hotkey->name = "Toggle Mouse Capture";
|
||||||
|
hotkey->mapping = "KB0::F12";
|
||||||
|
|
||||||
|
hotkey->press = [] {
|
||||||
|
input.acquired() ? input.unacquire() : input.acquire();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hotkey = new HotkeyInput;
|
||||||
|
hotkey->name = "Pause Emulation";
|
||||||
|
hotkey->mapping = "KB0::P";
|
||||||
|
|
||||||
|
hotkey->press = [] {
|
||||||
|
application->pause = !application->pause;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto hotkey = new HotkeyInput;
|
auto hotkey = new HotkeyInput;
|
||||||
hotkey->name = "Fast Forward";
|
hotkey->name = "Fast Forward";
|
||||||
hotkey->mapping = "KB0::Tilde";
|
hotkey->mapping = "KB0::Tilde";
|
||||||
hotkey->logic = 1;
|
|
||||||
hotkeyMap.append(hotkey);
|
|
||||||
|
|
||||||
hotkey->press = [] {
|
hotkey->press = [] {
|
||||||
video.set(Video::Synchronize, false);
|
video.set(Video::Synchronize, false);
|
||||||
|
@ -24,8 +40,8 @@ void InputManager::appendHotkeys() {
|
||||||
};
|
};
|
||||||
|
|
||||||
hotkey->release = [] {
|
hotkey->release = [] {
|
||||||
video.set(Video::Synchronize, false);
|
video.set(Video::Synchronize, ::config->video.synchronize);
|
||||||
audio.set(Audio::Synchronize, true);
|
audio.set(Audio::Synchronize, ::config->audio.synchronize);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,8 +49,6 @@ void InputManager::appendHotkeys() {
|
||||||
auto hotkey = new HotkeyInput;
|
auto hotkey = new HotkeyInput;
|
||||||
hotkey->name = "Power Cycle";
|
hotkey->name = "Power Cycle";
|
||||||
hotkey->mapping = "None";
|
hotkey->mapping = "None";
|
||||||
hotkey->logic = 1;
|
|
||||||
hotkeyMap.append(hotkey);
|
|
||||||
|
|
||||||
hotkey->press = [] {
|
hotkey->press = [] {
|
||||||
utility->power();
|
utility->power();
|
||||||
|
@ -45,16 +59,70 @@ void InputManager::appendHotkeys() {
|
||||||
auto hotkey = new HotkeyInput;
|
auto hotkey = new HotkeyInput;
|
||||||
hotkey->name = "Soft Reset";
|
hotkey->name = "Soft Reset";
|
||||||
hotkey->mapping = "None";
|
hotkey->mapping = "None";
|
||||||
hotkey->logic = 1;
|
|
||||||
hotkeyMap.append(hotkey);
|
|
||||||
|
|
||||||
hotkey->press = [] {
|
hotkey->press = [] {
|
||||||
utility->reset();
|
utility->reset();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned activeSlot = 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hotkey = new HotkeyInput;
|
||||||
|
hotkey->name = "Save State";
|
||||||
|
hotkey->mapping = "KB0::F5";
|
||||||
|
|
||||||
|
hotkey->press = [&] {
|
||||||
|
utility->saveState(activeSlot);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hotkey = new HotkeyInput;
|
||||||
|
hotkey->name = "Load State";
|
||||||
|
hotkey->mapping = "KB0::F7";
|
||||||
|
|
||||||
|
hotkey->press = [&] {
|
||||||
|
utility->loadState(activeSlot);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hotkey = new HotkeyInput;
|
||||||
|
hotkey->name = "Decrement Slot";
|
||||||
|
hotkey->mapping = "KB0::F6";
|
||||||
|
|
||||||
|
hotkey->press = [&] {
|
||||||
|
if(--activeSlot == 0) activeSlot = 5;
|
||||||
|
utility->showMessage({"Selected slot ", activeSlot});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hotkey = new HotkeyInput;
|
||||||
|
hotkey->name = "Increment Slot";
|
||||||
|
hotkey->mapping = "KB0::F8";
|
||||||
|
|
||||||
|
hotkey->press = [&] {
|
||||||
|
if(++activeSlot == 6) activeSlot = 1;
|
||||||
|
utility->showMessage({"Selected slot ", activeSlot});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hotkey = new HotkeyInput;
|
||||||
|
hotkey->name = "Close Emulator";
|
||||||
|
hotkey->mapping = "None";
|
||||||
|
|
||||||
|
hotkey->press = [] {
|
||||||
|
application->quit = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for(auto &hotkey : hotkeyMap) {
|
for(auto &hotkey : hotkeyMap) {
|
||||||
config.append(hotkey->mapping, hotkey->name);
|
string name = {"Hotkey::", hotkey->name};
|
||||||
|
name.replace(" ", "");
|
||||||
|
config.append(hotkey->mapping, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,13 @@ int16_t AnalogInput::poll() {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
HotkeyInput::HotkeyInput() {
|
||||||
|
logic = 1; //AND
|
||||||
|
inputManager->hotkeyMap.append(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
void InputManager::bind() {
|
void InputManager::bind() {
|
||||||
for(auto &input : inputMap) input->bind();
|
for(auto &input : inputMap) input->bind();
|
||||||
for(auto &input : hotkeyMap) input->bind();
|
for(auto &input : hotkeyMap) input->bind();
|
||||||
|
@ -174,6 +181,7 @@ void InputManager::saveConfiguration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
InputManager::InputManager() {
|
InputManager::InputManager() {
|
||||||
|
inputManager = this;
|
||||||
bootstrap();
|
bootstrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +190,7 @@ void InputManager::bootstrap() {
|
||||||
for(auto &emulator : application->emulator) {
|
for(auto &emulator : application->emulator) {
|
||||||
for(auto &port : emulator->port) {
|
for(auto &port : emulator->port) {
|
||||||
for(auto &device : port.device) {
|
for(auto &device : port.device) {
|
||||||
for(auto &number : device.displayinput) {
|
for(auto &number : device.order) {
|
||||||
auto &input = device.input[number];
|
auto &input = device.input[number];
|
||||||
|
|
||||||
AbstractInput *abstract = nullptr;
|
AbstractInput *abstract = nullptr;
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct AnalogInput : AbstractInput {
|
||||||
struct HotkeyInput : DigitalInput {
|
struct HotkeyInput : DigitalInput {
|
||||||
function<void ()> press;
|
function<void ()> press;
|
||||||
function<void ()> release;
|
function<void ()> release;
|
||||||
|
HotkeyInput();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputManager {
|
struct InputManager {
|
||||||
|
|
|
@ -1,9 +1,42 @@
|
||||||
#include "../ethos.hpp"
|
#include "../ethos.hpp"
|
||||||
Interface *interface = nullptr;
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
uint32_t Interface::videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
|
uint32_t Interface::videoColor(unsigned source, uint16_t r, uint16_t g, uint16_t b) {
|
||||||
red >>= 8, green >>= 8, blue >>= 8;
|
if(config->video.saturation != 100) {
|
||||||
return red << 16 | green << 8 | blue << 0;
|
uint16_t grayscale = uclamp<16>((r + g + b) / 3);
|
||||||
|
double saturation = config->video.saturation * 0.01;
|
||||||
|
double inverse = max(0.0, 1.0 - saturation);
|
||||||
|
r = uclamp<16>(r * saturation + grayscale * inverse);
|
||||||
|
g = uclamp<16>(g * saturation + grayscale * inverse);
|
||||||
|
b = uclamp<16>(b * saturation + grayscale * inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config->video.gamma != 100) {
|
||||||
|
double exponent = config->video.gamma * 0.01;
|
||||||
|
double reciprocal = 1.0 / 32767.0;
|
||||||
|
r = r > 32767 ? r : 32767 * pow(r * reciprocal, exponent);
|
||||||
|
g = g > 32767 ? g : 32767 * pow(g * reciprocal, exponent);
|
||||||
|
b = b > 32767 ? b : 32767 * pow(b * reciprocal, exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config->video.luminance != 100) {
|
||||||
|
double luminance = config->video.luminance * 0.01;
|
||||||
|
r = r * luminance;
|
||||||
|
g = g * luminance;
|
||||||
|
b = b * luminance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(application->depth == 30) {
|
||||||
|
r >>= 6, g >>= 6, b >>= 6;
|
||||||
|
return r << 20 | g << 10 | b << 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(application->depth == 24) {
|
||||||
|
r >>= 8, g >>= 8, b >>= 8;
|
||||||
|
return r << 16 | g << 8 | b << 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
|
void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
|
||||||
|
@ -17,6 +50,21 @@ void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned widt
|
||||||
memcpy(output + y * outputPitch, data + y * pitch, 4 * width);
|
memcpy(output + y * outputPitch, data + y * pitch, 4 * width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(system().information.overscan && config->video.maskOverscan) {
|
||||||
|
unsigned h = config->video.maskOverscanHorizontal;
|
||||||
|
unsigned v = config->video.maskOverscanVertical;
|
||||||
|
|
||||||
|
if(h) for(unsigned y = 0; y < height; y++) {
|
||||||
|
memset(output + y * outputPitch, 0, 4 * h);
|
||||||
|
memset(output + y * outputPitch + (width - h), 0, 4 * h);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(v) for(unsigned y = 0; y < v; y++) {
|
||||||
|
memset(output + y * outputPitch, 0, 4 * width);
|
||||||
|
memset(output + (height - 1 - y) * outputPitch, 0, 4 * width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
video.unlock();
|
video.unlock();
|
||||||
video.refresh();
|
video.refresh();
|
||||||
}
|
}
|
||||||
|
@ -43,16 +91,17 @@ void Interface::audioSample(int16_t lsample, int16_t rsample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
|
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
|
||||||
|
if(config->input.focusAllow == false && presentation->focused() == false) return 0;
|
||||||
unsigned guid = system().port[port].device[device].input[input].guid;
|
unsigned guid = system().port[port].device[device].input[input].guid;
|
||||||
return inputManager->inputMap[guid]->poll();
|
return inputManager->inputMap[guid]->poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::mediaRequest(Emulator::Interface::Media media) {
|
void Interface::mediaRequest(Emulator::Interface::Media media) {
|
||||||
string pathname = browser->select({"Load ", media.displayname}, media.filter);
|
string pathname = browser->select({"Load ", media.name}, media.extension);
|
||||||
if(pathname.empty()) return;
|
if(pathname.empty()) return;
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile({pathname, "manifest.xml"});
|
markup.readfile({pathname, "manifest.xml"});
|
||||||
mmapstream stream({pathname, media.name});
|
mmapstream stream({pathname, media.path});
|
||||||
system().load(media.id, stream, markup);
|
system().load(media.id, stream, markup);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,73 @@
|
||||||
AudioSettings *audioSettings = nullptr;
|
AudioSettings *audioSettings = nullptr;
|
||||||
|
|
||||||
|
AudioSlider::AudioSlider() {
|
||||||
|
append(name, {75, 0});
|
||||||
|
append(value, {75, 0});
|
||||||
|
append(slider, {~0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
AudioSettings::AudioSettings() {
|
AudioSettings::AudioSettings() {
|
||||||
title.setFont(application->titleFont);
|
title.setFont(application->titleFont);
|
||||||
title.setText("Audio Settings");
|
title.setText("Audio Settings");
|
||||||
|
frequencyLabel.setText("Frequency:");
|
||||||
|
frequency.append("32000hz", "44100hz", "48000hz", "96000hz");
|
||||||
|
latencyLabel.setText("Latency:");
|
||||||
|
latency.append("20ms", "40ms", "60ms", "80ms", "100ms");
|
||||||
|
resamplerLabel.setText("Resampler:");
|
||||||
|
resampler.append("Linear", "Hermite", "Sinc");
|
||||||
|
volume.name.setText("Volume:");
|
||||||
|
volume.slider.setLength(201);
|
||||||
|
|
||||||
append(title, {~0, 0}, 5);
|
append(title, {~0, 0}, 5);
|
||||||
|
append(controlLayout, {~0, 0}, 5);
|
||||||
|
controlLayout.append(frequencyLabel, {0, 0}, 5);
|
||||||
|
controlLayout.append(frequency, {~0, 0}, 5);
|
||||||
|
controlLayout.append(latencyLabel, {0, 0}, 5);
|
||||||
|
controlLayout.append(latency, {~0, 0}, 5);
|
||||||
|
controlLayout.append(resamplerLabel, {0, 0}, 5);
|
||||||
|
controlLayout.append(resampler, {~0, 0});
|
||||||
|
append(volume, {~0, 0});
|
||||||
|
|
||||||
|
switch(config->audio.frequency) { default:
|
||||||
|
case 32000: frequency.setSelection(0); break;
|
||||||
|
case 44100: frequency.setSelection(1); break;
|
||||||
|
case 48000: frequency.setSelection(2); break;
|
||||||
|
case 96000: frequency.setSelection(3); break;
|
||||||
|
}
|
||||||
|
switch(config->audio.latency) { default:
|
||||||
|
case 20: latency.setSelection(0); break;
|
||||||
|
case 40: latency.setSelection(1); break;
|
||||||
|
case 60: latency.setSelection(2); break;
|
||||||
|
case 80: latency.setSelection(3); break;
|
||||||
|
case 100: latency.setSelection(4); break;
|
||||||
|
}
|
||||||
|
resampler.setSelection(config->audio.resampler);
|
||||||
|
volume.slider.setPosition(config->audio.volume);
|
||||||
|
|
||||||
|
frequency.onChange = latency.onChange = resampler.onChange = volume.slider.onChange =
|
||||||
|
{&AudioSettings::synchronize, this};
|
||||||
|
|
||||||
|
synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioSettings::synchronize() {
|
||||||
|
switch(frequency.selection()) {
|
||||||
|
case 0: config->audio.frequency = 32000; break;
|
||||||
|
case 1: config->audio.frequency = 44100; break;
|
||||||
|
case 2: config->audio.frequency = 48000; break;
|
||||||
|
case 3: config->audio.frequency = 96000; break;
|
||||||
|
}
|
||||||
|
switch(latency.selection()) {
|
||||||
|
case 0: config->audio.latency = 20; break;
|
||||||
|
case 1: config->audio.latency = 40; break;
|
||||||
|
case 2: config->audio.latency = 60; break;
|
||||||
|
case 3: config->audio.latency = 80; break;
|
||||||
|
case 4: config->audio.latency = 100; break;
|
||||||
|
}
|
||||||
|
config->audio.resampler = resampler.selection();
|
||||||
|
config->audio.volume = volume.slider.position();
|
||||||
|
|
||||||
|
volume.value.setText({config->audio.volume, "%"});
|
||||||
|
|
||||||
|
utility->synchronizeRuby();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,23 @@
|
||||||
|
struct AudioSlider : HorizontalLayout {
|
||||||
|
Label name;
|
||||||
|
Label value;
|
||||||
|
HorizontalSlider slider;
|
||||||
|
|
||||||
|
AudioSlider();
|
||||||
|
};
|
||||||
|
|
||||||
struct AudioSettings : SettingsLayout {
|
struct AudioSettings : SettingsLayout {
|
||||||
Label title;
|
Label title;
|
||||||
|
HorizontalLayout controlLayout;
|
||||||
|
Label frequencyLabel;
|
||||||
|
ComboBox frequency;
|
||||||
|
Label latencyLabel;
|
||||||
|
ComboBox latency;
|
||||||
|
Label resamplerLabel;
|
||||||
|
ComboBox resampler;
|
||||||
|
AudioSlider volume;
|
||||||
|
|
||||||
|
void synchronize();
|
||||||
AudioSettings();
|
AudioSettings();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ void HotkeySettings::refresh() {
|
||||||
inputList.reset();
|
inputList.reset();
|
||||||
for(auto &hotkey : inputManager->hotkeyMap) {
|
for(auto &hotkey : inputManager->hotkeyMap) {
|
||||||
string mapping = hotkey->mapping;
|
string mapping = hotkey->mapping;
|
||||||
|
mapping.replace("KB0::", "");
|
||||||
|
mapping.replace("MS0::", "Mouse::");
|
||||||
mapping.replace(",", " and ");
|
mapping.replace(",", " and ");
|
||||||
inputList.append(hotkey->name, mapping);
|
inputList.append(hotkey->name, mapping);
|
||||||
}
|
}
|
||||||
|
@ -44,23 +46,22 @@ void HotkeySettings::assignInput() {
|
||||||
activeInput = inputManager->hotkeyMap[inputList.selection()];
|
activeInput = inputManager->hotkeyMap[inputList.selection()];
|
||||||
|
|
||||||
settings->setStatusText({"Set assignment for [", activeInput->name, "] ..."});
|
settings->setStatusText({"Set assignment for [", activeInput->name, "] ..."});
|
||||||
settings->panelList.setEnabled(false);
|
settings->layout.setEnabled(false);
|
||||||
inputList.setEnabled(false);
|
setEnabled(false);
|
||||||
clearButton.setEnabled(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HotkeySettings::inputEvent(unsigned scancode, int16_t value) {
|
void HotkeySettings::inputEvent(unsigned scancode, int16_t value) {
|
||||||
using nall::Mouse;
|
using nall::Mouse;
|
||||||
|
|
||||||
if(activeInput == nullptr) return;
|
if(activeInput == nullptr) return;
|
||||||
|
if(value != 1) return;
|
||||||
if(Mouse::isAnyButton(scancode) || Mouse::isAnyAxis(scancode)) return;
|
if(Mouse::isAnyButton(scancode) || Mouse::isAnyAxis(scancode)) return;
|
||||||
if(Joypad::isAnyAxis(scancode)) return;
|
if(Joypad::isAnyAxis(scancode)) return;
|
||||||
if(activeInput->bind(scancode, value) == false) return;
|
if(activeInput->bind(scancode, value) == false) return;
|
||||||
|
|
||||||
activeInput = nullptr;
|
activeInput = nullptr;
|
||||||
settings->setStatusText("");
|
settings->setStatusText("");
|
||||||
settings->panelList.setEnabled(true);
|
settings->layout.setEnabled(true);
|
||||||
inputList.setEnabled(true);
|
setEnabled(true);
|
||||||
clearButton.setEnabled(true);
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,18 @@ InputSettings *inputSettings = nullptr;
|
||||||
InputSettings::InputSettings() : activeInput(nullptr) {
|
InputSettings::InputSettings() : activeInput(nullptr) {
|
||||||
title.setFont(application->titleFont);
|
title.setFont(application->titleFont);
|
||||||
title.setText("Input Settings");
|
title.setText("Input Settings");
|
||||||
|
focusLabel.setText("When Focus is Lost:");
|
||||||
|
focusPause.setText("Pause Emulation");
|
||||||
|
focusAllow.setText("Allow Input");
|
||||||
inputList.setHeaderText("Name", "Mapping");
|
inputList.setHeaderText("Name", "Mapping");
|
||||||
inputList.setHeaderVisible();
|
inputList.setHeaderVisible();
|
||||||
clearButton.setText("Clear");
|
clearButton.setText("Clear");
|
||||||
|
|
||||||
append(title, {~0, 0}, 5);
|
append(title, {~0, 0}, 5);
|
||||||
|
append(focusLayout, {~0, 0}, 5);
|
||||||
|
focusLayout.append(focusLabel, {0, 0}, 5);
|
||||||
|
focusLayout.append(focusPause, {0, 0}, 5);
|
||||||
|
focusLayout.append(focusAllow, {0, 0});
|
||||||
append(selectionLayout, {~0, 0}, 5);
|
append(selectionLayout, {~0, 0}, 5);
|
||||||
selectionLayout.append(systemList, {~0, 0}, 5);
|
selectionLayout.append(systemList, {~0, 0}, 5);
|
||||||
selectionLayout.append(portList, {~0, 0}, 5);
|
selectionLayout.append(portList, {~0, 0}, 5);
|
||||||
|
@ -24,6 +31,12 @@ InputSettings::InputSettings() : activeInput(nullptr) {
|
||||||
systemList.append(emulator->information.name);
|
systemList.append(emulator->information.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focusPause.setChecked(config->input.focusPause);
|
||||||
|
focusAllow.setChecked(config->input.focusAllow);
|
||||||
|
focusAllow.setEnabled(!config->input.focusPause);
|
||||||
|
|
||||||
|
focusPause.onToggle = [&] { config->input.focusPause = focusPause.checked(); focusAllow.setEnabled(!focusPause.checked()); };
|
||||||
|
focusAllow.onToggle = [&] { config->input.focusAllow = focusAllow.checked(); };
|
||||||
systemList.onChange = {&InputSettings::systemChanged, this};
|
systemList.onChange = {&InputSettings::systemChanged, this};
|
||||||
portList.onChange = {&InputSettings::portChanged, this};
|
portList.onChange = {&InputSettings::portChanged, this};
|
||||||
deviceList.onChange = {&InputSettings::deviceChanged, this};
|
deviceList.onChange = {&InputSettings::deviceChanged, this};
|
||||||
|
@ -43,11 +56,11 @@ void InputSettings::synchronize() {
|
||||||
assign[1].setVisible(false);
|
assign[1].setVisible(false);
|
||||||
assign[2].setVisible(false);
|
assign[2].setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
unsigned number = activeDevice().displayinput[inputList.selection()];
|
unsigned number = activeDevice().order[inputList.selection()];
|
||||||
auto &input = activeDevice().input[number];
|
auto &input = activeDevice().input[number];
|
||||||
activeInput = inputManager->inputMap[input.guid];
|
auto selectedInput = inputManager->inputMap[input.guid];
|
||||||
|
|
||||||
if(dynamic_cast<DigitalInput*>(activeInput)) {
|
if(dynamic_cast<DigitalInput*>(selectedInput)) {
|
||||||
assign[0].setText("Mouse Left");
|
assign[0].setText("Mouse Left");
|
||||||
assign[1].setText("Mouse Middle");
|
assign[1].setText("Mouse Middle");
|
||||||
assign[2].setText("Mouse Right");
|
assign[2].setText("Mouse Right");
|
||||||
|
@ -56,7 +69,7 @@ void InputSettings::synchronize() {
|
||||||
assign[2].setVisible(true);
|
assign[2].setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dynamic_cast<AnalogInput*>(activeInput)) {
|
if(dynamic_cast<AnalogInput*>(selectedInput)) {
|
||||||
assign[0].setText("Mouse X-axis");
|
assign[0].setText("Mouse X-axis");
|
||||||
assign[1].setText("Mouse Y-axis");
|
assign[1].setText("Mouse Y-axis");
|
||||||
assign[0].setVisible(true);
|
assign[0].setVisible(true);
|
||||||
|
@ -98,10 +111,12 @@ void InputSettings::portChanged() {
|
||||||
|
|
||||||
void InputSettings::deviceChanged() {
|
void InputSettings::deviceChanged() {
|
||||||
inputList.reset();
|
inputList.reset();
|
||||||
for(unsigned number : activeDevice().displayinput) {
|
for(unsigned number : activeDevice().order) {
|
||||||
auto &input = activeDevice().input[number];
|
auto &input = activeDevice().input[number];
|
||||||
auto abstract = inputManager->inputMap(input.guid);
|
auto abstract = inputManager->inputMap(input.guid);
|
||||||
string mapping = abstract->mapping;
|
string mapping = abstract->mapping;
|
||||||
|
mapping.replace("KB0::", "");
|
||||||
|
mapping.replace("MS0::", "Mouse::");
|
||||||
mapping.replace(",", " or ");
|
mapping.replace(",", " or ");
|
||||||
inputList.append(input.name, mapping);
|
inputList.append(input.name, mapping);
|
||||||
}
|
}
|
||||||
|
@ -109,31 +124,24 @@ void InputSettings::deviceChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSettings::clearInput() {
|
void InputSettings::clearInput() {
|
||||||
unsigned number = activeDevice().displayinput[inputList.selection()];
|
unsigned number = activeDevice().order[inputList.selection()];
|
||||||
auto &input = activeDevice().input[number];
|
auto &input = activeDevice().input[number];
|
||||||
activeInput = inputManager->inputMap[input.guid];
|
activeInput = inputManager->inputMap[input.guid];
|
||||||
inputEvent(Scancode::None, 1);
|
inputEvent(Scancode::None, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSettings::assignInput() {
|
void InputSettings::assignInput() {
|
||||||
unsigned number = activeDevice().displayinput[inputList.selection()];
|
unsigned number = activeDevice().order[inputList.selection()];
|
||||||
auto &input = activeDevice().input[number];
|
auto &input = activeDevice().input[number];
|
||||||
activeInput = inputManager->inputMap[input.guid];
|
activeInput = inputManager->inputMap[input.guid];
|
||||||
|
|
||||||
settings->setStatusText({"Set assignment for [", activeDevice().name, "::", input.name, "] ..."});
|
settings->setStatusText({"Set assignment for [", activeDevice().name, "::", input.name, "] ..."});
|
||||||
settings->panelList.setEnabled(false);
|
settings->layout.setEnabled(false);
|
||||||
systemList.setEnabled(false);
|
setEnabled(false);
|
||||||
portList.setEnabled(false);
|
|
||||||
deviceList.setEnabled(false);
|
|
||||||
inputList.setEnabled(false);
|
|
||||||
assign[0].setEnabled(false);
|
|
||||||
assign[1].setEnabled(false);
|
|
||||||
assign[2].setEnabled(false);
|
|
||||||
clearButton.setEnabled(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSettings::assignMouseInput(unsigned n) {
|
void InputSettings::assignMouseInput(unsigned n) {
|
||||||
unsigned number = activeDevice().displayinput[inputList.selection()];
|
unsigned number = activeDevice().order[inputList.selection()];
|
||||||
auto &input = activeDevice().input[number];
|
auto &input = activeDevice().input[number];
|
||||||
activeInput = inputManager->inputMap[input.guid];
|
activeInput = inputManager->inputMap[input.guid];
|
||||||
|
|
||||||
|
@ -155,14 +163,7 @@ void InputSettings::inputEvent(unsigned scancode, int16_t value, bool allowMouse
|
||||||
activeInput = nullptr;
|
activeInput = nullptr;
|
||||||
deviceChanged();
|
deviceChanged();
|
||||||
settings->setStatusText("");
|
settings->setStatusText("");
|
||||||
settings->panelList.setEnabled(true);
|
settings->layout.setEnabled(true);
|
||||||
systemList.setEnabled(true);
|
setEnabled(true);
|
||||||
portList.setEnabled(true);
|
|
||||||
deviceList.setEnabled(true);
|
|
||||||
inputList.setEnabled(true);
|
|
||||||
assign[0].setEnabled(true);
|
|
||||||
assign[1].setEnabled(true);
|
|
||||||
assign[2].setEnabled(true);
|
|
||||||
clearButton.setEnabled(true);
|
|
||||||
synchronize();
|
synchronize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
struct InputSettings : SettingsLayout {
|
struct InputSettings : SettingsLayout {
|
||||||
Label title;
|
Label title;
|
||||||
|
HorizontalLayout focusLayout;
|
||||||
|
Label focusLabel;
|
||||||
|
CheckBox focusPause;
|
||||||
|
CheckBox focusAllow;
|
||||||
HorizontalLayout selectionLayout;
|
HorizontalLayout selectionLayout;
|
||||||
ComboBox systemList;
|
ComboBox systemList;
|
||||||
ComboBox portList;
|
ComboBox portList;
|
||||||
|
|
|
@ -16,9 +16,10 @@ SettingsLayout::SettingsLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::Settings() {
|
Settings::Settings() {
|
||||||
setTitle("Configuration Settings");
|
|
||||||
setGeometry({128, 128, 640, 360});
|
setGeometry({128, 128, 640, 360});
|
||||||
setStatusFont(application->boldFont);
|
windowManager->append(this, "Settings");
|
||||||
|
|
||||||
|
setTitle("Configuration Settings");
|
||||||
setStatusVisible();
|
setStatusVisible();
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
|
|
|
@ -1,8 +1,63 @@
|
||||||
VideoSettings *videoSettings = nullptr;
|
VideoSettings *videoSettings = nullptr;
|
||||||
|
|
||||||
|
VideoSlider::VideoSlider() {
|
||||||
|
append(name, {75, 0});
|
||||||
|
append(value, {75, 0});
|
||||||
|
append(slider, {~0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
VideoSettings::VideoSettings() {
|
VideoSettings::VideoSettings() {
|
||||||
title.setFont(application->titleFont);
|
title.setFont(application->titleFont);
|
||||||
title.setText("Video Settings");
|
title.setText("Video Settings");
|
||||||
|
colorAdjustment.setFont(application->boldFont);
|
||||||
|
colorAdjustment.setText("Color adjustment:");
|
||||||
|
saturation.name.setText("Saturation:");
|
||||||
|
saturation.slider.setLength(201);
|
||||||
|
gamma.name.setText("Gamma:");
|
||||||
|
gamma.slider.setLength(101);
|
||||||
|
luminance.name.setText("Luminance:");
|
||||||
|
luminance.slider.setLength(101);
|
||||||
|
overscanAdjustment.setFont(application->boldFont);
|
||||||
|
overscanAdjustment.setText("Overscan mask:");
|
||||||
|
overscanHorizontal.name.setText("Horizontal:");
|
||||||
|
overscanHorizontal.slider.setLength(17);
|
||||||
|
overscanVertical.name.setText("Vertical:");
|
||||||
|
overscanVertical.slider.setLength(17);
|
||||||
|
|
||||||
append(title, {~0, 0}, 5);
|
append(title, {~0, 0}, 5);
|
||||||
|
append(colorAdjustment, {~0, 0});
|
||||||
|
append(saturation, {~0, 0});
|
||||||
|
append(gamma, {~0, 0});
|
||||||
|
append(luminance, {~0, 0}, 5);
|
||||||
|
append(overscanAdjustment, {~0, 0});
|
||||||
|
append(overscanHorizontal, {~0, 0});
|
||||||
|
append(overscanVertical, {~0, 0}, 5);
|
||||||
|
|
||||||
|
saturation.slider.setPosition(config->video.saturation);
|
||||||
|
gamma.slider.setPosition(config->video.gamma - 100);
|
||||||
|
luminance.slider.setPosition(config->video.luminance);
|
||||||
|
overscanHorizontal.slider.setPosition(config->video.maskOverscanHorizontal);
|
||||||
|
overscanVertical.slider.setPosition(config->video.maskOverscanVertical);
|
||||||
|
|
||||||
|
synchronize();
|
||||||
|
|
||||||
|
saturation.slider.onChange = gamma.slider.onChange = luminance.slider.onChange =
|
||||||
|
overscanHorizontal.slider.onChange = overscanVertical.slider.onChange =
|
||||||
|
{&VideoSettings::synchronize, this};
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSettings::synchronize() {
|
||||||
|
config->video.saturation = saturation.slider.position();
|
||||||
|
config->video.gamma = 100 + gamma.slider.position();
|
||||||
|
config->video.luminance = luminance.slider.position();
|
||||||
|
config->video.maskOverscanHorizontal = overscanHorizontal.slider.position();
|
||||||
|
config->video.maskOverscanVertical = overscanVertical.slider.position();
|
||||||
|
|
||||||
|
saturation.value.setText({config->video.saturation, "%"});
|
||||||
|
gamma.value.setText({config->video.gamma, "%"});
|
||||||
|
luminance.value.setText({config->video.luminance, "%"});
|
||||||
|
overscanHorizontal.value.setText({config->video.maskOverscanHorizontal, "px"});
|
||||||
|
overscanVertical.value.setText({config->video.maskOverscanVertical, "px"});
|
||||||
|
|
||||||
|
if(application->active) system().updatePalette();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
|
struct VideoSlider : HorizontalLayout {
|
||||||
|
Label name;
|
||||||
|
Label value;
|
||||||
|
HorizontalSlider slider;
|
||||||
|
|
||||||
|
VideoSlider();
|
||||||
|
};
|
||||||
|
|
||||||
struct VideoSettings : SettingsLayout {
|
struct VideoSettings : SettingsLayout {
|
||||||
Label title;
|
Label title;
|
||||||
|
Label colorAdjustment;
|
||||||
|
VideoSlider saturation;
|
||||||
|
VideoSlider gamma;
|
||||||
|
VideoSlider luminance;
|
||||||
|
Label overscanAdjustment;
|
||||||
|
VideoSlider overscanHorizontal;
|
||||||
|
VideoSlider overscanVertical;
|
||||||
|
|
||||||
|
void synchronize();
|
||||||
VideoSettings();
|
VideoSettings();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,8 @@ void Utility::setInterface(Emulator::Interface *emulator) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utility::loadSchema(Emulator::Interface *emulator, Emulator::Interface::Schema &schema) {
|
void Utility::loadSchema(Emulator::Interface *emulator, Emulator::Interface::Schema &schema) {
|
||||||
string pathname;
|
string pathname = application->path({schema.name, ".", schema.extension, "/"});
|
||||||
if(!schema.path.empty()) pathname = application->path(schema.path);
|
if(!directory::exists(pathname)) pathname = browser->select({"Load ", schema.name}, schema.extension);
|
||||||
if(!directory::exists(pathname)) pathname = browser->select(schema.displayname, schema.filter);
|
|
||||||
if(!directory::exists(pathname)) return;
|
if(!directory::exists(pathname)) return;
|
||||||
|
|
||||||
loadMedia(emulator, schema, pathname);
|
loadMedia(emulator, schema, pathname);
|
||||||
|
@ -23,7 +22,7 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
|
||||||
|
|
||||||
string manifest;
|
string manifest;
|
||||||
manifest.readfile({pathname, "manifest.xml"});
|
manifest.readfile({pathname, "manifest.xml"});
|
||||||
auto memory = file::read({pathname, media.name});
|
auto memory = file::read({pathname, media.path});
|
||||||
system().load(media.id, vectorstream{memory}, manifest);
|
system().load(media.id, vectorstream{memory}, manifest);
|
||||||
|
|
||||||
for(auto &memory : system().memory) {
|
for(auto &memory : system().memory) {
|
||||||
|
@ -37,7 +36,7 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
|
||||||
string displayname = pathname;
|
string displayname = pathname;
|
||||||
displayname.rtrim<1>("/");
|
displayname.rtrim<1>("/");
|
||||||
presentation->setTitle(notdir(nall::basename(displayname)));
|
presentation->setTitle(notdir(nall::basename(displayname)));
|
||||||
presentation->setSystemName(media.displayname);
|
presentation->setSystemName(media.name);
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +47,11 @@ void Utility::saveMedia() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Utility::connect(unsigned port, unsigned device) {
|
||||||
|
if(application->active == nullptr) return;
|
||||||
|
system().connect(port, device);
|
||||||
|
}
|
||||||
|
|
||||||
void Utility::power() {
|
void Utility::power() {
|
||||||
if(application->active == nullptr) return;
|
if(application->active == nullptr) return;
|
||||||
system().power();
|
system().power();
|
||||||
|
@ -68,6 +72,39 @@ void Utility::unload() {
|
||||||
video.clear();
|
video.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Utility::saveState(unsigned slot) {
|
||||||
|
if(application->active == nullptr) return;
|
||||||
|
serializer s = system().serialize();
|
||||||
|
if(s.size() == 0) return;
|
||||||
|
mkdir(string{pathname, "bsnes/"}, 0755);
|
||||||
|
if(file::write({pathname, "bsnes/state-", slot, ".bsa"}, s.data(), s.size()) == false);
|
||||||
|
showMessage({"Save to slot ", slot});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utility::loadState(unsigned slot) {
|
||||||
|
if(application->active == nullptr) return;
|
||||||
|
auto memory = file::read({pathname, "bsnes/state-", slot, ".bsa"});
|
||||||
|
if(memory.size() == 0) return;
|
||||||
|
serializer s(memory.data(), memory.size());
|
||||||
|
if(system().unserialize(s) == false) return;
|
||||||
|
showMessage({"Loaded from slot ", slot});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utility::synchronizeRuby() {
|
||||||
|
video.set(Video::Synchronize, config->video.synchronize);
|
||||||
|
audio.set(Audio::Synchronize, config->audio.synchronize);
|
||||||
|
audio.set(Audio::Frequency, config->audio.frequency);
|
||||||
|
audio.set(Audio::Latency, config->audio.latency);
|
||||||
|
|
||||||
|
switch(config->audio.resampler) {
|
||||||
|
case 0: dspaudio.setResampler(DSP::ResampleEngine::Linear); break;
|
||||||
|
case 1: dspaudio.setResampler(DSP::ResampleEngine::Hermite); break;
|
||||||
|
case 2: dspaudio.setResampler(DSP::ResampleEngine::Sinc); break;
|
||||||
|
}
|
||||||
|
dspaudio.setResamplerFrequency(config->audio.frequency);
|
||||||
|
dspaudio.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
void Utility::resize(bool resizeWindow) {
|
void Utility::resize(bool resizeWindow) {
|
||||||
if(application->active == nullptr) return;
|
if(application->active == nullptr) return;
|
||||||
Geometry geometry = presentation->geometry();
|
Geometry geometry = presentation->geometry();
|
||||||
|
@ -132,6 +169,32 @@ void Utility::toggleFullScreen() {
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utility::setStatusText(const string &text) {
|
void Utility::updateStatus() {
|
||||||
presentation->setStatusText(text);
|
time_t currentTime = time(0);
|
||||||
|
string text;
|
||||||
|
if((currentTime - statusTime) <= 2) {
|
||||||
|
text = statusMessage;
|
||||||
|
} else if(application->active == nullptr) {
|
||||||
|
text = "No cartridge loaded";
|
||||||
|
} else if(application->pause || application->autopause) {
|
||||||
|
text = "Paused";
|
||||||
|
} else {
|
||||||
|
text = statusText;
|
||||||
|
}
|
||||||
|
if(text != presentation->statusText()) {
|
||||||
|
presentation->setStatusText(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utility::setStatusText(const string &text) {
|
||||||
|
statusText = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utility::showMessage(const string &message) {
|
||||||
|
statusTime = time(0);
|
||||||
|
statusMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utility::Utility() {
|
||||||
|
statusTime = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,28 @@ struct Utility {
|
||||||
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
|
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
|
||||||
void saveMedia();
|
void saveMedia();
|
||||||
|
|
||||||
|
void connect(unsigned port, unsigned device);
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
void unload();
|
void unload();
|
||||||
|
|
||||||
|
void saveState(unsigned slot);
|
||||||
|
void loadState(unsigned slot);
|
||||||
|
|
||||||
|
void synchronizeRuby();
|
||||||
void resize(bool resizeWindow = false);
|
void resize(bool resizeWindow = false);
|
||||||
void toggleFullScreen();
|
void toggleFullScreen();
|
||||||
|
|
||||||
|
void updateStatus();
|
||||||
void setStatusText(const string &text);
|
void setStatusText(const string &text);
|
||||||
|
void showMessage(const string &message);
|
||||||
|
|
||||||
|
Utility();
|
||||||
|
|
||||||
|
private:
|
||||||
|
string statusText;
|
||||||
|
string statusMessage;
|
||||||
|
time_t statusTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Utility *utility;
|
extern Utility *utility;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "../ethos.hpp"
|
||||||
|
WindowManager *windowManager = nullptr;
|
||||||
|
|
||||||
|
void WindowManager::append(Window *window, const string &name) {
|
||||||
|
window->setMenuFont(application->normalFont);
|
||||||
|
window->setWidgetFont(application->normalFont);
|
||||||
|
window->setStatusFont(application->boldFont);
|
||||||
|
windowList.append({window, name, window->geometry().text()});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::loadGeometry() {
|
||||||
|
for(auto &window : windowList) {
|
||||||
|
config.append(window.geometry, window.name);
|
||||||
|
}
|
||||||
|
config.load(application->path("geometry.cfg"));
|
||||||
|
config.save(application->path("geometry.cfg"));
|
||||||
|
for(auto &window : windowList) {
|
||||||
|
window.window->setGeometry(window.geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::saveGeometry() {
|
||||||
|
for(auto &window : windowList) {
|
||||||
|
window.geometry = window.window->geometry().text();
|
||||||
|
}
|
||||||
|
config.save(application->path("geometry.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::hideAll() {
|
||||||
|
for(auto &window : windowList) {
|
||||||
|
window.window->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
struct WindowManager {
|
||||||
|
struct WindowList {
|
||||||
|
Window *window;
|
||||||
|
string name;
|
||||||
|
string geometry;
|
||||||
|
};
|
||||||
|
vector<WindowList> windowList;
|
||||||
|
|
||||||
|
void append(Window *window, const string &name);
|
||||||
|
void loadGeometry();
|
||||||
|
void saveGeometry();
|
||||||
|
void hideAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
configuration config;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern WindowManager *windowManager;
|
Loading…
Reference in New Issue