mirror of https://github.com/bsnes-emu/bsnes.git
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
This commit is contained in:
parent
4a069761f9
commit
a1b2fb0124
|
@ -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/";
|
||||
|
|
|
@ -183,6 +183,9 @@ auto pWindow::destruct() -> void {
|
|||
}
|
||||
|
||||
auto pWindow::append(shared_pointer<mMenuBar> menuBar) -> void {
|
||||
_setMenuEnabled(menuBar->enabled(true));
|
||||
_setMenuFont(menuBar->font(true));
|
||||
_setMenuVisible(menuBar->visible(true));
|
||||
}
|
||||
|
||||
auto pWindow::append(shared_pointer<mStatusBar> 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <Carbon/Carbon.h>
|
||||
#undef decimal
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -23,11 +23,11 @@ struct RawInput {
|
|||
};
|
||||
vector<Device> devices;
|
||||
|
||||
optional<Device&> find(uint16_t vendorID, uint16_t productID) {
|
||||
maybe<Device&> 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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -22,7 +22,6 @@ struct Coprocessor : Thread {
|
|||
#include <sfc/chip/sdd1/sdd1.hpp>
|
||||
#include <sfc/chip/obc1/obc1.hpp>
|
||||
|
||||
#include <sfc/chip/hsu1/hsu1.hpp>
|
||||
#include <sfc/chip/msu1/msu1.hpp>
|
||||
|
||||
void Coprocessor::step(unsigned clocks) {
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
#include <sfc/sfc.hpp>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<uint8> txbuffer;
|
||||
vector<uint8> rxbuffer;
|
||||
};
|
||||
|
||||
extern HSU1 hsu1;
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)/)
|
||||
|
|
|
@ -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"});
|
||||
}
|
|
@ -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&;
|
|
@ -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"});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include "../tomoko.hpp"
|
||||
#include "input.cpp"
|
||||
#include "advanced.cpp"
|
||||
SettingsManager* settingsManager = nullptr;
|
||||
|
||||
SettingsManager::SettingsManager() {
|
||||
|
|
|
@ -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};
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue