From a1b2fb012476f3161597769117ef61a452bfcd89 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 3 Mar 2015 21:14:49 +1100 Subject: [PATCH] Update to v094r12 release. byuu says: Changelog: * added driver selection * added video scale + aspect correction settings * added A/V sync + audio mute settings * added configuration file * fixed compilation bugs under Windows and Linux * fixed window sizing * removed HSU1 * the system menu stays as "System", because "Game Boy Advance" was too long a string for the smallest scale size * some more stuff You guys probably won't be ecstatic about the video sizing options, but it's basically your choice of 1x, 2x or 4x scale with optional aspect correction. 3x was intentionally skipped because it looks horrible on hires SNES games. The window is resized and recentered upon loading games. The window doesn't resize otherwise. I never really liked the way v094 always left you with black screen areas and left you with off-centered window positions. I might go ahead and add the pseudo-fullscreen toggle that will jump into 4x mode (respecting your aspect setting.) Short-term: * add input port changing support * add other input types (mouse-based, etc) * add save states * add cheat codes * add timing configuration (video/audio sync) * add hotkeys (single state) We can probably do a new release once the short-term items are completed. Long-term: * add slotted cart loader (SGB, BSX, ST) * add DIP switch selection window (NSS) * add cheat code database * add state manager * add overscan masking Not planned: * video color adjustments (will allow emulated color vs raw color; but no more sliders) * pixel shaders * ananke integration (will need to make a command-line version to get my games in) * fancy audio adjustment controls (resampler, latency, volume) * input focus settings * relocating game library (not hard, just don't feel like it) * localization support (not enough users) * window geometry memory * anything else not in higan v094 --- emulator/emulator.hpp | 2 +- hiro/gtk/window.cpp | 12 +- nall/stream/stream.hpp | 2 +- ruby/implementation.cpp | 1 - ruby/input/joypad/directinput.cpp | 2 +- ruby/input/joypad/udev.cpp | 2 +- ruby/input/shared/rawinput.cpp | 6 +- ruby/video/direct3d.cpp | 2 +- sfc/GNUmakefile | 17 ++- sfc/cartridge/cartridge.hpp | 1 - sfc/cartridge/markup.cpp | 16 --- sfc/chip/chip.hpp | 1 - sfc/chip/hsu1/hsu1.cpp | 112 ------------------ sfc/chip/hsu1/hsu1.hpp | 21 ---- sfc/chip/hsu1/serialization.cpp | 21 ---- sfc/system/serialization.cpp | 1 - sfc/system/system.cpp | 5 - target-tomoko/GNUmakefile | 3 +- target-tomoko/configuration/configuration.cpp | 31 +++++ target-tomoko/configuration/configuration.hpp | 24 ++++ target-tomoko/input/input.cpp | 6 +- target-tomoko/presentation/presentation.cpp | 76 +++++++++++- target-tomoko/presentation/presentation.hpp | 13 ++ target-tomoko/program/interface.cpp | 8 +- target-tomoko/program/media.cpp | 5 +- target-tomoko/program/program.cpp | 46 ++----- target-tomoko/program/program.hpp | 1 - target-tomoko/settings/advanced.cpp | 31 +++++ target-tomoko/settings/settings.cpp | 1 + target-tomoko/settings/settings.hpp | 15 +++ target-tomoko/tomoko.hpp | 1 + 31 files changed, 239 insertions(+), 246 deletions(-) delete mode 100644 sfc/chip/hsu1/hsu1.cpp delete mode 100644 sfc/chip/hsu1/hsu1.hpp delete mode 100644 sfc/chip/hsu1/serialization.cpp create mode 100644 target-tomoko/configuration/configuration.cpp create mode 100644 target-tomoko/configuration/configuration.hpp create mode 100644 target-tomoko/settings/advanced.cpp diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 96a9a47d..85460f30 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "094.11"; + static const char Version[] = "094.12"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp index 443be7da..e82ad5f1 100644 --- a/hiro/gtk/window.cpp +++ b/hiro/gtk/window.cpp @@ -183,6 +183,9 @@ auto pWindow::destruct() -> void { } auto pWindow::append(shared_pointer menuBar) -> void { + _setMenuEnabled(menuBar->enabled(true)); + _setMenuFont(menuBar->font(true)); + _setMenuVisible(menuBar->visible(true)); } auto pWindow::append(shared_pointer statusBar) -> void { @@ -298,10 +301,17 @@ auto pWindow::setVisible(bool visible) -> void { if(menuBar->self()) menuBar->self()->setVisible(menuBar->visible(true)); } + if(auto& layout = state().layout) { + if(layout->self()) layout->self()->setVisible(layout->visible(true)); + } + if(auto& statusBar = state().statusBar) { if(statusBar->self()) statusBar->self()->setVisible(statusBar->visible(true)); } + //GTK+ returns invalid widget sizes below without this call + Application::processEvents(); + if(visible) { if(gtk_widget_get_visible(gtkMenu)) { GtkAllocation allocation; @@ -317,8 +327,6 @@ auto pWindow::setVisible(bool visible) -> void { } if(auto& layout = state().layout) { - if(layout->self()) layout->self()->setVisible(layout->visible(true)); - Application::processEvents(); //todo: this should not be necessary layout->setGeometry(self().geometry().setPosition(0, 0)); } } diff --git a/nall/stream/stream.hpp b/nall/stream/stream.hpp index 2b10bf17..7353a7a1 100644 --- a/nall/stream/stream.hpp +++ b/nall/stream/stream.hpp @@ -50,7 +50,7 @@ struct stream { string text() const { string buffer; - buffer.resize(size() + 1); + buffer.resize(size()); seek(0); read((uint8_t*)buffer.data(), size()); return buffer; diff --git a/ruby/implementation.cpp b/ruby/implementation.cpp index 4bc78a00..4672b936 100644 --- a/ruby/implementation.cpp +++ b/ruby/implementation.cpp @@ -10,7 +10,6 @@ #include #undef decimal #elif defined(PLATFORM_WINDOWS) - #define _WIN32_WINNT 0x0501 #include #endif diff --git a/ruby/input/joypad/directinput.cpp b/ruby/input/joypad/directinput.cpp index 1ffbc555..e61b0359 100644 --- a/ruby/input/joypad/directinput.cpp +++ b/ruby/input/joypad/directinput.cpp @@ -137,7 +137,7 @@ struct InputJoypadDirectInput { property.diph.dwHow = DIPH_DEVICE; device->GetProperty(DIPROP_GUIDANDPATH, &property.diph); string devicePath = (const char*)utf8_t(property.wszPath); - jp.pathID = crc32_calculate((const uint8_t*)devicePath.data(), devicePath.size()); + jp.pathID = Hash::CRC32(devicePath.data(), devicePath.size()).value(); jp.hid.id = (uint64_t)jp.pathID << 32 | jp.vendorID << 16 | jp.productID << 0; if(jp.hid.rumble) { diff --git a/ruby/input/joypad/udev.cpp b/ruby/input/joypad/udev.cpp index a0674d8e..8693ec17 100644 --- a/ruby/input/joypad/udev.cpp +++ b/ruby/input/joypad/udev.cpp @@ -255,7 +255,7 @@ private: } void createJoypadHID(Joypad& jp) { - uint64_t pathID = crc32_calculate((const uint8_t*)jp.deviceName.data(), jp.deviceName.size()); + uint64_t pathID = Hash::CRC32(jp.deviceName.data(), jp.deviceName.size()).value(); jp.hid.id = pathID << 32 | hex(jp.vendorID) << 16 | hex(jp.productID) << 0; for(unsigned n = 0; n < jp.axes.size(); n++) jp.hid.axis().append({n}); diff --git a/ruby/input/shared/rawinput.cpp b/ruby/input/shared/rawinput.cpp index 8e7a9cd8..063384ef 100644 --- a/ruby/input/shared/rawinput.cpp +++ b/ruby/input/shared/rawinput.cpp @@ -23,11 +23,11 @@ struct RawInput { }; vector devices; - optional find(uint16_t vendorID, uint16_t productID) { + maybe find(uint16_t vendorID, uint16_t productID) { for(auto& device : devices) { - if(device.vendorID == vendorID && device.productID == productID) return {true, device}; + if(device.vendorID == vendorID && device.productID == productID) return device; } - return false; + return nothing; } void scanDevices() { diff --git a/ruby/video/direct3d.cpp b/ruby/video/direct3d.cpp index 33368da6..3af018d8 100644 --- a/ruby/video/direct3d.cpp +++ b/ruby/video/direct3d.cpp @@ -366,7 +366,7 @@ public: TextureProc textureProc = (TextureProc)GetProcAddress(d3dx, "D3DXCreateTextureFromFileA"); LPD3DXBUFFER pBufferErrors = NULL; - effectProc(device, shader_source, lstrlenA(shader_source), NULL, NULL, 0, NULL, &effect, &pBufferErrors); + effectProc(device, (const void*)shader_source.data(), lstrlenA(shader_source), NULL, NULL, 0, NULL, &effect, &pBufferErrors); D3DXHANDLE hTech; effect->FindNextValidTechnique(NULL, &hTech); diff --git a/sfc/GNUmakefile b/sfc/GNUmakefile index f18b65ab..e418158b 100644 --- a/sfc/GNUmakefile +++ b/sfc/GNUmakefile @@ -7,7 +7,7 @@ sfc_objects += sfc-sa1 sfc-superfx sfc_objects += sfc-armdsp sfc-hitachidsp sfc-necdsp sfc_objects += sfc-epsonrtc sfc-sharprtc sfc_objects += sfc-spc7110 sfc-sdd1 sfc-obc1 -sfc_objects += sfc-hsu1 sfc-msu1 +sfc_objects += sfc-msu1 sfc_objects += sfc-satellaviewcart sfc-sufamiturbo objects += $(sfc_objects) @@ -30,14 +30,14 @@ else ifeq ($(profile),performance) sfcdsp := $(sfc)/alt/dsp sfcppu := $(sfc)/alt/ppu-performance else - $(error unknown profile.) + $(error unknown Super Famicom profile) endif obj/sfc-interface.o: $(sfc)/interface/interface.cpp $(call rwildcard,$(sfc)/interface) obj/sfc-system.o: $(sfc)/system/system.cpp $(call rwildcard,$(sfc)/system/) obj/sfc-controller.o: $(sfc)/controller/controller.cpp $(call rwildcard,$(sfc)/controller/) -obj/sfc-cartridge.o: $(sfc)/cartridge/cartridge.cpp $(sfc)/cartridge/* -obj/sfc-cheat.o: $(sfc)/cheat/cheat.cpp $(sfc)/cheat/* +obj/sfc-cartridge.o: $(sfc)/cartridge/cartridge.cpp $(call rwildcard,$(sfc)/cartridge/) +obj/sfc-cheat.o: $(sfc)/cheat/cheat.cpp $(call rwildcard,$(sfc)/cheat/) obj/sfc-memory.o: $(sfc)/memory/memory.cpp $(call rwildcard,$(sfc)/memory/) obj/sfc-cpu.o: $(sfccpu)/cpu.cpp $(call rwildcard,$(sfccpu)/) obj/sfc-smp.o: $(sfcsmp)/smp.cpp $(call rwildcard,$(sfcsmp)/) @@ -61,12 +61,11 @@ obj/sfc-necdsp.o: $(sfc)/chip/necdsp/necdsp.cpp $(call rwildcard,$(sfc) obj/sfc-epsonrtc.o: $(sfc)/chip/epsonrtc/epsonrtc.cpp $(call rwildcard,$(sfc)/chip/epsonrtc/) obj/sfc-sharprtc.o: $(sfc)/chip/sharprtc/sharprtc.cpp $(call rwildcard,$(sfc)/chip/sharprtc/) -obj/sfc-spc7110.o: $(sfc)/chip/spc7110/spc7110.cpp $(sfc)/chip/spc7110/* -obj/sfc-sdd1.o: $(sfc)/chip/sdd1/sdd1.cpp $(sfc)/chip/sdd1/* -obj/sfc-obc1.o: $(sfc)/chip/obc1/obc1.cpp $(sfc)/chip/obc1/* +obj/sfc-spc7110.o: $(sfc)/chip/spc7110/spc7110.cpp $(call rwildcard,$(sfc)/chip/spc7110/) +obj/sfc-sdd1.o: $(sfc)/chip/sdd1/sdd1.cpp $(call rwildcard,$(sfc)/chip/sdd1/) +obj/sfc-obc1.o: $(sfc)/chip/obc1/obc1.cpp $(call rwildcard,$(sfc)/chip/obc1/) -obj/sfc-hsu1.o: $(sfc)/chip/hsu1/hsu1.cpp $(sfc)/chip/hsu1/* -obj/sfc-msu1.o: $(sfc)/chip/msu1/msu1.cpp $(sfc)/chip/msu1/* +obj/sfc-msu1.o: $(sfc)/chip/msu1/msu1.cpp $(call rwildcard,$(sfc)/chip/msu1/) obj/sfc-satellaviewcart.o: $(sfc)/slot/satellaview/satellaview.cpp $(call rwildcard,$(sfc)/slot/satellaview/) obj/sfc-sufamiturbo.o: $(sfc)/slot/sufamiturbo/sufamiturbo.cpp $(call rwildcard,$(sfc)/slot/sufamiturbo/) diff --git a/sfc/cartridge/cartridge.hpp b/sfc/cartridge/cartridge.hpp index 6a63482d..9160ea0d 100644 --- a/sfc/cartridge/cartridge.hpp +++ b/sfc/cartridge/cartridge.hpp @@ -114,7 +114,6 @@ private: void parse_markup_spc7110(Markup::Node); void parse_markup_sdd1(Markup::Node); void parse_markup_obc1(Markup::Node); - void parse_markup_hsu1(Markup::Node); void parse_markup_msu1(Markup::Node); friend class Interface; diff --git a/sfc/cartridge/markup.cpp b/sfc/cartridge/markup.cpp index 165f2aa4..9e6495b6 100644 --- a/sfc/cartridge/markup.cpp +++ b/sfc/cartridge/markup.cpp @@ -26,7 +26,6 @@ void Cartridge::parse_markup(const char* markup) { parse_markup_spc7110(cartridge["spc7110"]); parse_markup_sdd1(cartridge["sdd1"]); parse_markup_obc1(cartridge["obc1"]); - parse_markup_hsu1(cartridge["hsu1"]); parse_markup_msu1(cartridge["msu1"]); } @@ -556,21 +555,6 @@ void Cartridge::parse_markup_obc1(Markup::Node root) { } } -void Cartridge::parse_markup_hsu1(Markup::Node root) { - if(root.exists() == false) return; - has_hsu1 = true; - - for(auto& node : root) { - if(node.name != "map") continue; - - if(node["id"].data == "io") { - Mapping m({&HSU1::read, &hsu1}, {&HSU1::write, &hsu1}); - parse_markup_map(m, node); - mapping.append(m); - } - } -} - void Cartridge::parse_markup_msu1(Markup::Node root) { if(root.exists() == false) return; has_msu1 = true; diff --git a/sfc/chip/chip.hpp b/sfc/chip/chip.hpp index de691961..9e4b13f7 100644 --- a/sfc/chip/chip.hpp +++ b/sfc/chip/chip.hpp @@ -22,7 +22,6 @@ struct Coprocessor : Thread { #include #include -#include #include void Coprocessor::step(unsigned clocks) { diff --git a/sfc/chip/hsu1/hsu1.cpp b/sfc/chip/hsu1/hsu1.cpp deleted file mode 100644 index 6cf06d63..00000000 --- a/sfc/chip/hsu1/hsu1.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include - -#define HSU1_CPP -namespace SuperFamicom { - -#include "serialization.cpp" -HSU1 hsu1; - -void HSU1::init() { -} - -void HSU1::load() { -} - -void HSU1::unload() { -} - -void HSU1::power() { -} - -void HSU1::reset() { - txbusy = 0; - rxbusy = 1; - txlatch = 0; - txbuffer.reset(); - rxbuffer.reset(); -} - -uint8 HSU1::read(unsigned addr) { - addr &= 1; - - if(addr == 0) { - return (txbusy << 7) | (rxbusy << 6) | (1 << 0); - } - - if(addr == 1) { - if(rxbusy) return 0x00; - uint8 data = rxbuffer.take(0); - if(rxbuffer.size() == 0) rxbusy = 1; - return data; - } - - //unreachable - return cpu.regs.mdr; -} - -void HSU1::write(unsigned addr, uint8 data) { - addr &= 1; - - if(addr == 0) { - if(txbusy) return; - bool latch = data & 0x01; - if(txlatch == 1 && latch == 0) { - //username:password@http://server:port/path - lstring side = interface->server().split<1>("@"); - string username = side(0).split<1>(":")(0); - string password = side(0).split<1>(":")(1); - side(1).ltrim("http://"); - string hostname = side(1).split<1>("/")(0); - string hostpath = side(1).split<1>("/")(1); - side = hostname.split<1>(":"); - hostname = side(0); - string hostport = side(1); - if(hostport.empty()) hostport = "80"; - - /*http server; - if(server.connect(hostname, decimal(hostport))) { - string header { - "username:", username, "\n", - "password:", password, "\n", - "emulator:bsnes\n", - "sha256:", interface->sha256(), "\n", - "\n" - }; - - string packet { - "POST /", hostpath, " HTTP/1.0\r\n", - "Host: ", hostname, "\r\n", - "Connection: close\r\n", - "Content-Type: application/octet-stream\r\n", - "Content-Length: ", header.length() + txbuffer.size(), "\r\n", - "\r\n", - }; - - server.send(packet); - server.send(header); - server.send(txbuffer.data(), txbuffer.size()); - txbuffer.reset(); - - server.header = server.downloadHeader(); - uint8_t* data = nullptr; - unsigned size = 0; - server.downloadContent(data, size); - rxbuffer.resize(size); - memcpy(rxbuffer.data(), data, size); - rxbusy = rxbuffer.size() == 0; - free(data); - - server.disconnect(); - }*/ - } - txlatch = latch; - } - - if(addr == 1) { - if(txbusy) return; - if(txlatch == 0) return; - txbuffer.append(data); - } -} - -} diff --git a/sfc/chip/hsu1/hsu1.hpp b/sfc/chip/hsu1/hsu1.hpp deleted file mode 100644 index f0e2e676..00000000 --- a/sfc/chip/hsu1/hsu1.hpp +++ /dev/null @@ -1,21 +0,0 @@ -struct HSU1 { - void init(); - void load(); - void unload(); - void power(); - void reset(); - - uint8 read(unsigned addr); - void write(unsigned addr, uint8 data); - - void serialize(serializer&); - -private: - bool txbusy; - bool rxbusy; - bool txlatch; - vector txbuffer; - vector rxbuffer; -}; - -extern HSU1 hsu1; diff --git a/sfc/chip/hsu1/serialization.cpp b/sfc/chip/hsu1/serialization.cpp deleted file mode 100644 index 9f359679..00000000 --- a/sfc/chip/hsu1/serialization.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifdef HSU1_CPP - -void HSU1::serialize(serializer& s) { - s.integer(txbusy); - s.integer(rxbusy); - s.integer(txlatch); - - unsigned size; - - size = txbuffer.size(); - s.integer(size); - txbuffer.resize(size); - s.array(txbuffer.data(), txbuffer.size()); - - size = rxbuffer.size(); - s.integer(size); - rxbuffer.resize(size); - s.array(rxbuffer.data(), rxbuffer.size()); -} - -#endif diff --git a/sfc/system/serialization.cpp b/sfc/system/serialization.cpp index 3c68e902..fe11e04a 100644 --- a/sfc/system/serialization.cpp +++ b/sfc/system/serialization.cpp @@ -70,7 +70,6 @@ void System::serialize_all(serializer& s) { if(cartridge.has_spc7110()) spc7110.serialize(s); if(cartridge.has_sdd1()) sdd1.serialize(s); if(cartridge.has_obc1()) obc1.serialize(s); - if(cartridge.has_hsu1()) hsu1.serialize(s); if(cartridge.has_msu1()) msu1.serialize(s); if(cartridge.has_st_slots()) sufamiturboA.serialize(s), sufamiturboB.serialize(s); } diff --git a/sfc/system/system.cpp b/sfc/system/system.cpp index 5b5c624e..0ea81ec2 100644 --- a/sfc/system/system.cpp +++ b/sfc/system/system.cpp @@ -79,7 +79,6 @@ void System::init() { spc7110.init(); sdd1.init(); obc1.init(); - hsu1.init(); msu1.init(); satellaviewcartridge.init(); @@ -134,7 +133,6 @@ void System::load() { if(cartridge.has_spc7110()) spc7110.load(); if(cartridge.has_sdd1()) sdd1.load(); if(cartridge.has_obc1()) obc1.load(); - if(cartridge.has_hsu1()) hsu1.load(); if(cartridge.has_msu1()) msu1.load(); if(cartridge.has_bs_slot()) satellaviewcartridge.load(); if(cartridge.has_st_slots()) sufamiturboA.load(), sufamiturboB.load(); @@ -158,7 +156,6 @@ void System::unload() { if(cartridge.has_spc7110()) spc7110.unload(); if(cartridge.has_sdd1()) sdd1.unload(); if(cartridge.has_obc1()) obc1.unload(); - if(cartridge.has_hsu1()) hsu1.unload(); if(cartridge.has_msu1()) msu1.unload(); if(cartridge.has_bs_slot()) satellaviewcartridge.unload(); if(cartridge.has_st_slots()) sufamiturboA.unload(), sufamiturboB.unload(); @@ -187,7 +184,6 @@ void System::power() { if(cartridge.has_spc7110()) spc7110.power(); if(cartridge.has_sdd1()) sdd1.power(); if(cartridge.has_obc1()) obc1.power(); - if(cartridge.has_hsu1()) hsu1.power(); if(cartridge.has_msu1()) msu1.power(); if(cartridge.has_bs_slot()) satellaviewcartridge.power(); @@ -215,7 +211,6 @@ void System::reset() { if(cartridge.has_spc7110()) spc7110.reset(); if(cartridge.has_sdd1()) sdd1.reset(); if(cartridge.has_obc1()) obc1.reset(); - if(cartridge.has_hsu1()) hsu1.reset(); if(cartridge.has_msu1()) msu1.reset(); if(cartridge.has_bs_slot()) satellaviewcartridge.reset(); diff --git a/target-tomoko/GNUmakefile b/target-tomoko/GNUmakefile index 4348863b..1fdb523a 100644 --- a/target-tomoko/GNUmakefile +++ b/target-tomoko/GNUmakefile @@ -8,7 +8,7 @@ include sfc/GNUmakefile include gb/GNUmakefile include gba/GNUmakefile -ui_objects := ui-tomoko ui-program ui-input +ui_objects := ui-tomoko ui-program ui-configuration ui-input ui_objects += ui-library ui-settings ui-presentation ui_objects += ruby hiro @@ -51,6 +51,7 @@ obj/hiro.o: hiro/hiro.cpp $(call rwildcard,hiro/) obj/ui-tomoko.o: $(ui)/tomoko.cpp $(call rwildcard,$(ui)/) obj/ui-program.o: $(ui)/program/program.cpp $(call rwildcard,$(ui)/) +obj/ui-configuration.o: $(ui)/configuration/configuration.cpp $(call rwildcard,$(ui)/) obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/) obj/ui-library.o: $(ui)/library/library.cpp $(call rwildcard,$(ui)/) obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/) diff --git a/target-tomoko/configuration/configuration.cpp b/target-tomoko/configuration/configuration.cpp new file mode 100644 index 00000000..63e1f1af --- /dev/null +++ b/target-tomoko/configuration/configuration.cpp @@ -0,0 +1,31 @@ +#include "../tomoko.hpp" +ConfigurationManager* configurationManager = nullptr; +auto config() -> ConfigurationManager& { return *configurationManager; } + +ConfigurationManager::ConfigurationManager() { + configurationManager = this; + + video.append(video.driver, "Driver"); + video.append(video.synchronize, "Synchronize"); + video.append(video.scale, "Scale"); + video.append(video.aspectCorrection, "AspectCorrection"); + append(video, "Video"); + + audio.append(audio.driver, "Driver"); + audio.append(audio.synchronize, "Synchronize"); + audio.append(audio.mute, "Mute"); + append(audio, "Audio"); + + input.append(input.driver, "Driver"); + append(input, "Input"); + + load({configpath(), "tomoko/settings.bml"}); + if(!video.driver) video.driver = ruby::video.safestDriver(); + if(!audio.driver) audio.driver = ruby::audio.safestDriver(); + if(!input.driver) input.driver = ruby::input.safestDriver(); + save({configpath(), "tomoko/settings.bml"}); +} + +auto ConfigurationManager::quit() -> void { + save({configpath(), "tomoko/settings.bml"}); +} diff --git a/target-tomoko/configuration/configuration.hpp b/target-tomoko/configuration/configuration.hpp new file mode 100644 index 00000000..31be1576 --- /dev/null +++ b/target-tomoko/configuration/configuration.hpp @@ -0,0 +1,24 @@ +struct ConfigurationManager : Configuration::Document { + ConfigurationManager(); + auto quit() -> void; + + struct Video : Configuration::Node { + string driver; + bool synchronize = false; + string scale = "Normal"; + bool aspectCorrection = true; + } video; + + struct Audio : Configuration::Node { + string driver; + bool synchronize = true; + bool mute = false; + } audio; + + struct Input : Configuration::Node { + string driver; + } input; +}; + +extern ConfigurationManager* configurationManager; +auto config() -> ConfigurationManager&; diff --git a/target-tomoko/input/input.cpp b/target-tomoko/input/input.cpp index 37ae7580..1020455b 100644 --- a/target-tomoko/input/input.cpp +++ b/target-tomoko/input/input.cpp @@ -76,8 +76,8 @@ InputManager::InputManager() { config.append(nodeEmulator, string{inputEmulator.name}.replace(" ", "")); } - config.load({configpath(), "tomoko/settings.bml"}); - config.save({configpath(), "tomoko/settings.bml"}); + config.load({configpath(), "tomoko/input.bml"}); + config.save({configpath(), "tomoko/input.bml"}); poll(); //will call bind(); } @@ -115,5 +115,5 @@ auto InputManager::onChange(HID::Device& device, unsigned group, unsigned input, } auto InputManager::quit() -> void { - config.save({configpath(), "tomoko/settings.bml"}); + config.save({configpath(), "tomoko/input.bml"}); } diff --git a/target-tomoko/presentation/presentation.cpp b/target-tomoko/presentation/presentation.cpp index 9e67f1b2..1f95b30e 100644 --- a/target-tomoko/presentation/presentation.cpp +++ b/target-tomoko/presentation/presentation.cpp @@ -16,12 +16,45 @@ Presentation::Presentation() { } } - systemMenu.setVisible(false); + systemMenu.setText("System").setVisible(false); powerSystem.setText("Power"); resetSystem.setText("Reset"); - unloadSystem.setText("Unload").onActivate([&] { program->unloadMedia(); }); + unloadSystem.setText("Unload").onActivate([&] { program->unloadMedia(); drawSplashScreen(); }); settingsMenu.setText("Settings"); + videoScaleMenu.setText("Video Scale"); + MenuRadioItem::group({videoScaleSmall, videoScaleNormal, videoScaleLarge}); + if(config().video.scale == "Small") videoScaleSmall.setChecked(); + if(config().video.scale == "Normal") videoScaleNormal.setChecked(); + if(config().video.scale == "Large") videoScaleLarge.setChecked(); + videoScaleSmall.setText("Small").onActivate([&] { + config().video.scale = "Small"; + resizeViewport(); + }); + videoScaleNormal.setText("Normal").onActivate([&] { + config().video.scale = "Normal"; + resizeViewport(); + }); + videoScaleLarge.setText("Large").onActivate([&] { + config().video.scale = "Large"; + resizeViewport(); + }); + aspectCorrection.setText("Aspect Correction").setChecked(config().video.aspectCorrection).onToggle([&] { + config().video.aspectCorrection = aspectCorrection.checked(); + resizeViewport(); + }); + synchronizeVideo.setText("Synchronize Video").setChecked(config().video.synchronize).onToggle([&] { + config().video.synchronize = synchronizeVideo.checked(); + video.set(Video::Synchronize, config().video.synchronize); + }); + synchronizeAudio.setText("Synchronize Audio").setChecked(config().audio.synchronize).onToggle([&] { + config().audio.synchronize = synchronizeAudio.checked(); + audio.set(Audio::Synchronize, config().audio.synchronize); + }); + muteAudio.setText("Mute Audio").setChecked(config().audio.mute).onToggle([&] { + config().audio.mute = muteAudio.checked(); + program->dsp.setVolume(config().audio.mute ? 0.0 : 1.0); + }); showConfiguration.setText("Configuration ...").onActivate([&] { settingsManager->setVisible(); settingsManager->setFocused(); @@ -34,7 +67,40 @@ Presentation::Presentation() { onClose([&] { program->quit(); }); setTitle({"tomoko v", Emulator::Version}); -//setResizable(false); - setSize({512, 480}); - setCentered(); + setResizable(false); + resizeViewport(); +} + +auto Presentation::resizeViewport() -> void { + signed width = 256; + signed height = 240; + + if(program->activeEmulator) { + width = program->emulator().information.width; + height = program->emulator().information.height; + } + + if(config().video.scale == "Small" ) width *= 1, height *= 1; + if(config().video.scale == "Normal") width *= 2, height *= 2; + if(config().video.scale == "Large" ) width *= 4, height *= 4; + if(config().video.aspectCorrection) { + if(!program->activeEmulator || program->emulator().information.aspectRatio != 1.0) width = width * 5 / 4; + } + + setSize({width, height}); + setCentered(); + if(!program->activeEmulator) drawSplashScreen(); +} + +auto Presentation::drawSplashScreen() -> void { + uint32* output; + unsigned length; + if(video.lock(output, length, 256, 240)) { + for(auto y : range(240)) { + uint32* dp = output + y * (length >> 2); + for(auto x : range(256)) *dp++ = 0xff000000; + } + video.unlock(); + video.refresh(); + } } diff --git a/target-tomoko/presentation/presentation.hpp b/target-tomoko/presentation/presentation.hpp index 98063196..a27fbe16 100644 --- a/target-tomoko/presentation/presentation.hpp +++ b/target-tomoko/presentation/presentation.hpp @@ -1,5 +1,7 @@ struct Presentation : Window { Presentation(); + auto resizeViewport() -> void; + auto drawSplashScreen() -> void; MenuBar menuBar{this}; Menu libraryMenu{&menuBar}; @@ -10,6 +12,17 @@ struct Presentation : Window { MenuSeparator systemMenuSeparator{&systemMenu}; MenuItem unloadSystem{&systemMenu}; Menu settingsMenu{&menuBar}; + Menu videoScaleMenu{&settingsMenu}; + MenuRadioItem videoScaleSmall{&videoScaleMenu}; + MenuRadioItem videoScaleNormal{&videoScaleMenu}; + MenuRadioItem videoScaleLarge{&videoScaleMenu}; + MenuSeparator videoScaleSeparator{&videoScaleMenu}; + MenuCheckItem aspectCorrection{&videoScaleMenu}; + MenuSeparator settingsMenuSeparator1{&settingsMenu}; + MenuCheckItem synchronizeVideo{&settingsMenu}; + MenuCheckItem synchronizeAudio{&settingsMenu}; + MenuCheckItem muteAudio{&settingsMenu}; + MenuSeparator settingsMenuSeparator2{&settingsMenu}; MenuItem showConfiguration{&settingsMenu}; Menu toolsMenu{&menuBar}; diff --git a/target-tomoko/program/interface.cpp b/target-tomoko/program/interface.cpp index cf839e4d..60775cd6 100644 --- a/target-tomoko/program/interface.cpp +++ b/target-tomoko/program/interface.cpp @@ -66,9 +66,11 @@ auto Program::audioSample(int16 lsample, int16 rsample) -> void { } auto Program::inputPoll(unsigned port, unsigned device, unsigned input) -> int16 { - auto guid = emulator().port[port].device[device].input[input].guid; - auto mapping = (InputMapping*)guid; - if(mapping) return mapping->poll(); + if(presentation->focused()) { + auto guid = emulator().port[port].device[device].input[input].guid; + auto mapping = (InputMapping*)guid; + if(mapping) return mapping->poll(); + } return 0; } diff --git a/target-tomoko/program/media.cpp b/target-tomoko/program/media.cpp index 9fa9f575..f03390ca 100644 --- a/target-tomoko/program/media.cpp +++ b/target-tomoko/program/media.cpp @@ -1,5 +1,6 @@ auto Program::loadMedia(string location) -> void { location.transform("\\", "/"); + if(!location.endsWith("/")) location.append("/"); if(!directory::exists(location)) return; string type = suffixname(location).ltrim("."); @@ -23,8 +24,9 @@ auto Program::loadMedia(Emulator::Interface& _emulator, Emulator::Interface::Med emulator().load(media.id); emulator().power(); + presentation->resizeViewport(); presentation->setTitle(emulator().title()); - presentation->systemMenu.setText(emulator().information.name).setVisible(true); + presentation->systemMenu.setVisible(true); } auto Program::unloadMedia() -> void { @@ -35,5 +37,4 @@ auto Program::unloadMedia() -> void { presentation->setTitle({"tomoko v", Emulator::Version}); presentation->systemMenu.setVisible(false); - drawSplashScreen(); } diff --git a/target-tomoko/program/program.cpp b/target-tomoko/program/program.cpp index 8543bb9c..456516d3 100644 --- a/target-tomoko/program/program.cpp +++ b/target-tomoko/program/program.cpp @@ -18,6 +18,7 @@ Program::Program() { emulators.append(new GameBoyAdvance::Interface); for(auto& emulator : emulators) emulator->bind = this; + new ConfigurationManager; new InputManager; new LibraryManager; new SettingsManager; @@ -25,30 +26,30 @@ Program::Program() { presentation->setVisible(); - video.driver("XShm"); + video.driver(config().video.driver); video.set(Video::Handle, presentation->viewport.handle()); - video.set(Video::Synchronize, false); + video.set(Video::Synchronize, config().video.synchronize); if(!video.init()) { video.driver("None"); video.init(); } - audio.driver("OpenAL"); + audio.driver(config().audio.driver); audio.set(Audio::Handle, presentation->viewport.handle()); - audio.set(Audio::Synchronize, false); + audio.set(Audio::Synchronize, config().audio.synchronize); audio.set(Audio::Frequency, 96000u); audio.set(Audio::Latency, 80u); if(!audio.init()) { audio.driver("None"); audio.init(); } - input.driver("Xlib"); + input.driver(config().input.driver); input.set(Input::Handle, presentation->viewport.handle()); if(!input.init()) { input.driver("None"); input.init(); } dsp.setPrecision(16); dsp.setBalance(0.0); - dsp.setVolume(1.0); + dsp.setVolume(config().audio.mute ? 0.0 : 1.0); dsp.setFrequency(32040); dsp.setResampler(DSP::ResampleEngine::Sinc); dsp.setResamplerFrequency(96000); - drawSplashScreen(); + presentation->drawSplashScreen(); } auto Program::emulator() -> Emulator::Interface& { @@ -70,35 +71,14 @@ auto Program::main() -> void { auto Program::quit() -> void { unloadMedia(); + configurationManager->quit(); inputManager->quit(); - exit(0); + video.term(); + audio.term(); + input.term(); + Application::quit(); } auto Program::setEmulator(Emulator::Interface* emulator) -> void { activeEmulator = emulator; } - -auto Program::drawSplashScreen() -> void { - image emblem{string{userpath(), ".local/share/icons/tomoko.png"}}; - emblem.alphaBlend(0x000000); - emblem.scale(128, 128); - - uint32* output; - unsigned length; - if(video.lock(output, length, 512, 480)) { - for(auto y : range(480)) { - uint32* dp = output + y * (length >> 2); - for(auto x : range(640)) *dp++ = 0xff000000; - } - unsigned z = 0; - for(auto y : range(emblem.height)) { - uint32* dp = output + (480 - emblem.height + y - 8) * (length >> 2) + (512 - emblem.width - 8); - for(auto x : range(emblem.width)) { - *dp++ = emblem.read(emblem.data + z); - z += 4; - } - } - video.unlock(); - video.refresh(); - } -} diff --git a/target-tomoko/program/program.hpp b/target-tomoko/program/program.hpp index 04cfd465..0c3cb313 100644 --- a/target-tomoko/program/program.hpp +++ b/target-tomoko/program/program.hpp @@ -5,7 +5,6 @@ struct Program : Emulator::Interface::Bind { auto main() -> void; auto quit() -> void; auto setEmulator(Emulator::Interface*) -> void; - auto drawSplashScreen() -> void; //interface.cpp auto loadRequest(unsigned id, string name, string type) -> void override; diff --git a/target-tomoko/settings/advanced.cpp b/target-tomoko/settings/advanced.cpp new file mode 100644 index 00000000..f0565164 --- /dev/null +++ b/target-tomoko/settings/advanced.cpp @@ -0,0 +1,31 @@ +AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) { + setIcon(Icon::Action::Settings); + setText("Advanced"); + + layout.setMargin(5); + driverLabel.setText("Driver Selection").setFont(Font::sans(8, "Bold")); + videoLabel.setText("Video:"); + videoDriver.onChange([&] { config().video.driver = videoDriver.selected()->text(); }); + for(auto& driver : string{video.availableDrivers()}.split(";")) { + ComboButtonItem item; + item.setText(driver); + videoDriver.append(item); + if(config().video.driver == driver) item.setSelected(); + } + audioLabel.setText("Audio:"); + audioDriver.onChange([&] { config().audio.driver = audioDriver.selected()->text(); }); + for(auto& driver : string{audio.availableDrivers()}.split(";")) { + ComboButtonItem item; + item.setText(driver); + audioDriver.append(item); + if(config().audio.driver == driver) item.setSelected(); + } + inputLabel.setText("Input:"); + inputDriver.onChange([&] { config().input.driver = inputDriver.selected()->text(); }); + for(auto& driver : string{input.availableDrivers()}.split(";")) { + ComboButtonItem item; + item.setText(driver); + inputDriver.append(item); + if(config().input.driver == driver) item.setSelected(); + } +} diff --git a/target-tomoko/settings/settings.cpp b/target-tomoko/settings/settings.cpp index 8541e638..3edb4f1c 100644 --- a/target-tomoko/settings/settings.cpp +++ b/target-tomoko/settings/settings.cpp @@ -1,5 +1,6 @@ #include "../tomoko.hpp" #include "input.cpp" +#include "advanced.cpp" SettingsManager* settingsManager = nullptr; SettingsManager::SettingsManager() { diff --git a/target-tomoko/settings/settings.hpp b/target-tomoko/settings/settings.hpp index 7bcc4fd2..947b41be 100644 --- a/target-tomoko/settings/settings.hpp +++ b/target-tomoko/settings/settings.hpp @@ -24,12 +24,27 @@ struct InputSettings : TabFrameItem { Button eraseButton{&controlLayout, Size{80, 0}}; }; +struct AdvancedSettings : TabFrameItem { + AdvancedSettings(TabFrame*); + + VerticalLayout layout{this}; + Label driverLabel{&layout, Size{~0, 0}, 0}; + HorizontalLayout driverLayout{&layout, Size{~0, 0}}; + Label videoLabel{&driverLayout, Size{0, 0}}; + ComboButton videoDriver{&driverLayout, Size{~0, 0}}; + Label audioLabel{&driverLayout, Size{0, 0}}; + ComboButton audioDriver{&driverLayout, Size{~0, 0}}; + Label inputLabel{&driverLayout, Size{0, 0}}; + ComboButton inputDriver{&driverLayout, Size{~0, 0}}; +}; + struct SettingsManager : Window { SettingsManager(); VerticalLayout layout{this}; TabFrame panelLayout{&layout, Size{~0, ~0}}; InputSettings input{&panelLayout}; + AdvancedSettings advanced{&panelLayout}; StatusBar statusBar{this}; }; diff --git a/target-tomoko/tomoko.hpp b/target-tomoko/tomoko.hpp index e681a276..4d1afd04 100644 --- a/target-tomoko/tomoko.hpp +++ b/target-tomoko/tomoko.hpp @@ -8,6 +8,7 @@ using namespace ruby; using namespace hiro; #include "program/program.hpp" +#include "configuration/configuration.hpp" #include "input/input.hpp" #include "library/library.hpp" #include "settings/settings.hpp"