mirror of https://github.com/bsnes-emu/bsnes.git
Update to v088r11 release.
byuu says: Changelog: - phoenix has added Window::setModal(bool modal = true); - file dialog is now modal. This allows emulation cores to request data and get it immediately before continuing the loading process - save data is hooked up for most systems, still need to handle subsystem slot saves (Sufami Turbo, basically.) - toggle fullscreen key binding added (Alt+Enter for now. I think F11 is probably better though, Enter is often mapped to game start button.) - video scaling is in (center, scale, stretch), works the same in windowed and fullscreen mode (stretch hides resize window option), all in the settings menu now - enough structure to map all saved paths for the browser and to load BS-X slotted carts, BS-X carts, single Sufami Turbo carts Caveats / Missing: - Super Game Boy input doesn't work yet (due to change in callback binding) - doesn't load secondary Sufami Turbo slot yet - BS-X BIOS isn't show the data pack games to load for some reason (ugh, I hate the shit out of debugging BS-X stuff ...) - need mute audio, sync audio+video toggle, save/load state menu and quick keys, XML mapping information window - need cheat editor and cheat database - need state manager - need to sort subsystems below main systems in load menu (basically just see if media.slot.size() > 0) - need video shaders (will probably leave off filters for the time being ... due to that 24/30-bit thing) - need video adjustments (contrast etc, overscan masks) - need audio adjustments (frequency, latency, resampler, volume, per-system frequency) - need driver selection and input focus policy (driver crash detection would be nice too) - need NSS DIP switch settings (that one will be really fun) - need to save and load window geometry settings - need to hook up controller selection (won't be fun), create a map to hide controllers with no inputs to reassign
This commit is contained in:
parent
9ad8b7eaac
commit
8703d57030
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "088.10";
|
||||
static const char Version[] = "088.11";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
}
|
||||
|
|
|
@ -8,9 +8,15 @@ struct Interface {
|
|||
string name;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
double aspectRatio;
|
||||
unsigned frequency;
|
||||
unsigned ports;
|
||||
bool resettable;
|
||||
|
||||
struct Media {
|
||||
string name;
|
||||
string filter;
|
||||
};
|
||||
vector<Media> media;
|
||||
} information;
|
||||
|
||||
struct Firmware {
|
||||
|
@ -20,23 +26,22 @@ struct Interface {
|
|||
};
|
||||
vector<Firmware> firmware;
|
||||
|
||||
struct MediaObject {
|
||||
struct Media {
|
||||
string displayname;
|
||||
string path;
|
||||
string name;
|
||||
string filter;
|
||||
unsigned id;
|
||||
};
|
||||
|
||||
struct Media : MediaObject {
|
||||
vector<MediaObject> slot;
|
||||
struct Schema : Media {
|
||||
vector<Media> slot;
|
||||
};
|
||||
vector<Media> media;
|
||||
vector<Schema> schema;
|
||||
|
||||
struct Memory {
|
||||
string name;
|
||||
unsigned id;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
string name;
|
||||
};
|
||||
vector<Memory> memory;
|
||||
|
||||
|
@ -64,6 +69,7 @@ struct Interface {
|
|||
function<void (const uint32_t*, unsigned, unsigned, unsigned)> videoRefresh;
|
||||
function<void (int16_t, int16_t)> audioSample;
|
||||
function<int16_t (unsigned, unsigned, unsigned)> inputPoll;
|
||||
function<void (Media)> mediaRequest;
|
||||
} callback;
|
||||
|
||||
//audio/visual bindings (provided by user interface)
|
||||
|
@ -85,9 +91,14 @@ struct Interface {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//cartridge interface
|
||||
virtual void mediaRequest(Media media) {
|
||||
if(callback.mediaRequest) return callback.mediaRequest(media);
|
||||
}
|
||||
|
||||
//media interface
|
||||
virtual bool loaded() { return false; }
|
||||
virtual void load(unsigned id, const stream &memory, const string &markup = "") {}
|
||||
virtual void save(unsigned id, const stream &memory) {}
|
||||
virtual void unload() {}
|
||||
|
||||
//system interface
|
||||
|
|
|
@ -20,6 +20,8 @@ void Cartridge::load(const string &markup, const stream &memory) {
|
|||
board = Board::load(markup, memory);
|
||||
if(board == nullptr) return;
|
||||
|
||||
interface->memory.append({ID::RAM, "save.ram"});
|
||||
|
||||
sha256_ctx sha;
|
||||
uint8_t hash[32];
|
||||
sha256_init(&sha);
|
||||
|
|
|
@ -8,13 +8,23 @@ bool Interface::loaded() {
|
|||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id, const stream &memory, const string &markup) {
|
||||
if(id == 0) {
|
||||
cartridge.load(markup, memory);
|
||||
void Interface::load(unsigned id, const stream &stream, const string &markup) {
|
||||
if(id == ID::ROM) {
|
||||
cartridge.load(markup, stream);
|
||||
system.power();
|
||||
input.connect(0, Input::Device::Joypad);
|
||||
input.connect(1, Input::Device::Joypad);
|
||||
}
|
||||
|
||||
if(id == ID::RAM) {
|
||||
stream.read(cartridge.ram_data(), min(stream.size(), cartridge.ram_size()));
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::save(unsigned id, const stream &stream) {
|
||||
if(id == ID::RAM) {
|
||||
stream.write(cartridge.ram_data(), cartridge.ram_size());
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::unload() {
|
||||
|
@ -43,17 +53,19 @@ Interface::Interface() {
|
|||
information.name = "Famicom";
|
||||
information.width = 256;
|
||||
information.height = 240;
|
||||
information.aspectRatio = 8.0 / 7.0;
|
||||
information.frequency = 1789772;
|
||||
information.ports = 2;
|
||||
information.resettable = true;
|
||||
|
||||
information.media.append({"Famicom", "*.fc"});
|
||||
|
||||
{
|
||||
Media media;
|
||||
media.displayname = "Famicom";
|
||||
media.name = "program.rom";
|
||||
media.filter = "*.fc";
|
||||
media.id = 0;
|
||||
this->media.append(media);
|
||||
Schema schema;
|
||||
schema.displayname = "Famicom";
|
||||
schema.name = "program.rom";
|
||||
schema.filter = "*.fc";
|
||||
schema.id = ID::ROM;
|
||||
this->schema.append(schema);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -2,9 +2,17 @@
|
|||
namespace Famicom {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
ROM,
|
||||
RAM,
|
||||
};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
bool loaded();
|
||||
void load(unsigned id, const stream &memory, const string &markup = "");
|
||||
void load(unsigned id, const stream &stream, const string &markup = "");
|
||||
void save(unsigned id, const stream &stream);
|
||||
void unload();
|
||||
|
||||
void power();
|
||||
|
|
|
@ -64,6 +64,7 @@ void Cartridge::load(System::Revision revision, const string &markup, const stre
|
|||
|
||||
loaded = true;
|
||||
sha256 = nall::sha256(romdata, romsize);
|
||||
if(ramsize) interface->memory.append({ID::RAM, "save.ram"});
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
|
|
|
@ -9,21 +9,37 @@ bool Interface::loaded() {
|
|||
}
|
||||
|
||||
void Interface::load(unsigned id, const stream &stream, const string &markup) {
|
||||
if(id == 0) stream.read(system.bootROM.dmg, min( 256u, stream.size()));
|
||||
if(id == 1) stream.read(system.bootROM.sgb, min( 256u, stream.size()));
|
||||
if(id == 2) stream.read(system.bootROM.cgb, min(2048u, stream.size()));
|
||||
if(id == 3) {
|
||||
if(id == ID::GameBoyBootROM) {
|
||||
stream.read(system.bootROM.dmg, min( 256u, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::SuperGameBoyBootROM) {
|
||||
stream.read(system.bootROM.sgb, min( 256u, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::GameBoyColorBootROM) {
|
||||
stream.read(system.bootROM.cgb, min(2048u, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::GameBoyROM) {
|
||||
cartridge.load(System::Revision::GameBoy, markup, stream);
|
||||
system.power();
|
||||
}
|
||||
if(id == 4) {
|
||||
cartridge.load(System::Revision::SuperGameBoy, markup, stream);
|
||||
system.power();
|
||||
}
|
||||
if(id == 5) {
|
||||
|
||||
if(id == ID::GameBoyColorROM) {
|
||||
cartridge.load(System::Revision::GameBoyColor, markup, stream);
|
||||
system.power();
|
||||
}
|
||||
|
||||
if(id == ID::RAM) {
|
||||
stream.read(cartridge.ramdata, min(stream.size(), cartridge.ramsize));
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::save(unsigned id, const stream &stream) {
|
||||
if(id == ID::RAM) {
|
||||
stream.write(cartridge.ramdata, cartridge.ramsize);
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::unload() {
|
||||
|
@ -52,15 +68,18 @@ Interface::Interface() {
|
|||
information.name = "Game Boy";
|
||||
information.width = 160;
|
||||
information.height = 144;
|
||||
information.aspectRatio = 1.0;
|
||||
information.frequency = 4194304;
|
||||
information.ports = 1;
|
||||
information.resettable = false;
|
||||
|
||||
information.media.append({"Game Boy", "*.gb"});
|
||||
information.media.append({"Game Boy Color", "*.gbc"});
|
||||
|
||||
{
|
||||
Firmware firmware;
|
||||
firmware.displayname = "Game Boy";
|
||||
firmware.name = "Game Boy.sys/boot.rom";
|
||||
firmware.id = 0;
|
||||
firmware.id = ID::GameBoyBootROM;
|
||||
this->firmware.append(firmware);
|
||||
}
|
||||
|
||||
|
@ -68,7 +87,7 @@ Interface::Interface() {
|
|||
Firmware firmware;
|
||||
firmware.displayname = "Super Game Boy";
|
||||
firmware.name = "Super Game Boy.sfc/boot.rom";
|
||||
firmware.id = 1;
|
||||
firmware.id = ID::SuperGameBoyBootROM;
|
||||
this->firmware.append(firmware);
|
||||
}
|
||||
|
||||
|
@ -76,26 +95,26 @@ Interface::Interface() {
|
|||
Firmware firmware;
|
||||
firmware.displayname = "Game Boy Color";
|
||||
firmware.name = "Game Boy Color.sys/boot.rom";
|
||||
firmware.id = 2;
|
||||
firmware.id = ID::GameBoyColorBootROM;
|
||||
this->firmware.append(firmware);
|
||||
}
|
||||
|
||||
{
|
||||
Media media;
|
||||
media.displayname = "Game Boy";
|
||||
media.name = "program.rom";
|
||||
media.filter = "*.gb";
|
||||
media.id = 3;
|
||||
this->media.append(media);
|
||||
Schema schema;
|
||||
schema.displayname = "Game Boy";
|
||||
schema.name = "program.rom";
|
||||
schema.filter = "*.gb";
|
||||
schema.id = ID::GameBoyROM;
|
||||
this->schema.append(schema);
|
||||
}
|
||||
|
||||
{
|
||||
Media media;
|
||||
media.displayname = "Game Boy Color";
|
||||
media.name = "program.rom";
|
||||
media.filter = "*.gbc";
|
||||
media.id = 5;
|
||||
this->media.append(media);
|
||||
Schema schema;
|
||||
schema.displayname = "Game Boy Color";
|
||||
schema.name = "program.rom";
|
||||
schema.filter = "*.gbc";
|
||||
schema.id = ID::GameBoyColorROM;
|
||||
this->schema.append(schema);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -2,13 +2,25 @@
|
|||
namespace GameBoy {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
GameBoyBootROM,
|
||||
SuperGameBoyBootROM,
|
||||
GameBoyColorBootROM,
|
||||
GameBoyROM,
|
||||
GameBoyColorROM,
|
||||
RAM,
|
||||
};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
//Super Game Boy bindings
|
||||
virtual void lcdScanline() {}
|
||||
virtual void joypWrite(bool p15, bool p14) {}
|
||||
|
||||
bool loaded();
|
||||
void load(unsigned id, const stream &memory, const string &markup = "");
|
||||
void load(unsigned id, const stream &stream, const string &markup = "");
|
||||
void save(unsigned id, const stream &stream);
|
||||
void unload();
|
||||
|
||||
void power();
|
||||
|
|
|
@ -30,7 +30,7 @@ bool Cartridge::load(const string &markup, const stream &memory) {
|
|||
ram.mask = ram.size - 1;
|
||||
for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff;
|
||||
|
||||
interface->memory.append({"save.ram", 2, ram.data, ram.size});
|
||||
interface->memory.append({2, "save.ram"});
|
||||
}
|
||||
|
||||
if(info["type"].data == "EEPROM") {
|
||||
|
@ -42,7 +42,7 @@ bool Cartridge::load(const string &markup, const stream &memory) {
|
|||
eeprom.test = size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
|
||||
for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff;
|
||||
|
||||
interface->memory.append({"save.ram", 3, eeprom.data, eeprom.size});
|
||||
interface->memory.append({3, "save.ram"});
|
||||
}
|
||||
|
||||
if(info["type"].data == "FlashROM") {
|
||||
|
@ -51,7 +51,7 @@ bool Cartridge::load(const string &markup, const stream &memory) {
|
|||
flashrom.size = numeral(info["size"].data);
|
||||
for(unsigned n = 0; n < flashrom.size; n++) flashrom.data[n] = 0xff;
|
||||
|
||||
interface->memory.append({"save.ram", 4, flashrom.data, flashrom.size});
|
||||
interface->memory.append({4, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,25 +9,43 @@ bool Interface::loaded() {
|
|||
}
|
||||
|
||||
void Interface::load(unsigned id, const stream &stream, const string &markup) {
|
||||
if(id == 0) {
|
||||
if(id == ID::BIOS) {
|
||||
stream.read(bios.data, min(bios.size, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::ROM) {
|
||||
memory.reset();
|
||||
cartridge.load(markup, stream);
|
||||
system.power();
|
||||
}
|
||||
if(id == 1) {
|
||||
stream.read(bios.data, min(bios.size, stream.size()));
|
||||
}
|
||||
if(id == 2) {
|
||||
|
||||
if(id == ID::RAM) {
|
||||
stream.read(cartridge.ram.data, min(cartridge.ram.size, stream.size()));
|
||||
}
|
||||
if(id == 3) {
|
||||
|
||||
if(id == ID::EEPROM) {
|
||||
stream.read(cartridge.eeprom.data, min(cartridge.eeprom.size, stream.size()));
|
||||
}
|
||||
if(id == 4) {
|
||||
|
||||
if(id == ID::FlashROM) {
|
||||
stream.read(cartridge.flashrom.data, min(cartridge.flashrom.size, stream.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::save(unsigned id, const stream &stream) {
|
||||
if(id == ID::RAM) {
|
||||
stream.write(cartridge.ram.data, cartridge.ram.size);
|
||||
}
|
||||
|
||||
if(id == ID::EEPROM) {
|
||||
stream.write(cartridge.eeprom.data, cartridge.eeprom.size);
|
||||
}
|
||||
|
||||
if(id == ID::FlashROM) {
|
||||
stream.write(cartridge.flashrom.data, cartridge.flashrom.size);
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::unload() {
|
||||
cartridge.unload();
|
||||
}
|
||||
|
@ -54,25 +72,27 @@ Interface::Interface() {
|
|||
information.name = "Game Boy Advance";
|
||||
information.width = 240;
|
||||
information.height = 160;
|
||||
information.aspectRatio = 1.0;
|
||||
information.frequency = 32768;
|
||||
information.ports = 1;
|
||||
information.resettable = false;
|
||||
|
||||
information.media.append({"Game Boy Advance", "*.gba"});
|
||||
|
||||
{
|
||||
Firmware firmware;
|
||||
firmware.displayname = "Game Boy Advance";
|
||||
firmware.name = "Game Boy Advance.sys/bios.rom";
|
||||
firmware.id = 1;
|
||||
firmware.id = ID::BIOS;
|
||||
this->firmware.append(firmware);
|
||||
}
|
||||
|
||||
{
|
||||
Media media;
|
||||
media.displayname = "Game Boy Advance";
|
||||
media.name = "program.rom";
|
||||
media.filter = "*.gba";
|
||||
media.id = 0;
|
||||
this->media.append(media);
|
||||
Schema schema;
|
||||
schema.displayname = "Game Boy Advance";
|
||||
schema.name = "program.rom";
|
||||
schema.filter = "*.gba";
|
||||
schema.id = ID::ROM;
|
||||
this->schema.append(schema);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -2,9 +2,20 @@
|
|||
namespace GameBoyAdvance {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
BIOS,
|
||||
ROM,
|
||||
RAM,
|
||||
EEPROM,
|
||||
FlashROM,
|
||||
};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
bool loaded();
|
||||
void load(unsigned id, const stream &memory, const string &markup = "");
|
||||
void load(unsigned id, const stream &stream, const string &markup = "");
|
||||
void save(unsigned id, const stream &stream);
|
||||
void unload();
|
||||
|
||||
void power();
|
||||
|
|
|
@ -27,6 +27,11 @@ struct filestream : stream {
|
|||
if(!pwritable) pfile.open(filename, file::mode::read);
|
||||
}
|
||||
|
||||
filestream(const string &filename, file::mode mode) {
|
||||
pfile.open(filename, mode);
|
||||
pwritable = mode == file::mode::write || mode == file::mode::readwrite;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable file pfile;
|
||||
bool pwritable;
|
||||
|
|
|
@ -12,7 +12,7 @@ struct mmapstream : stream {
|
|||
bool seekable() const { return true; }
|
||||
bool readable() const { return true; }
|
||||
bool writable() const { return pwritable; }
|
||||
bool randomaccess() const { return false; }
|
||||
bool randomaccess() const { return true; }
|
||||
|
||||
unsigned size() const { return pmmap.size(); }
|
||||
unsigned offset() const { return poffset; }
|
||||
|
|
|
@ -326,6 +326,11 @@ void Window::setMenuVisible(bool visible) {
|
|||
return p.setMenuVisible(visible);
|
||||
}
|
||||
|
||||
void Window::setModal(bool modal) {
|
||||
state.modal = modal;
|
||||
return p.setModal(modal);
|
||||
}
|
||||
|
||||
void Window::setResizable(bool resizable) {
|
||||
state.resizable = resizable;
|
||||
return p.setResizable(resizable);
|
||||
|
|
|
@ -199,6 +199,7 @@ struct Window : private nall::base_from_member<pWindow&>, Object {
|
|||
void setGeometry(const Geometry &geometry);
|
||||
void setMenuFont(const nall::string &font);
|
||||
void setMenuVisible(bool visible = true);
|
||||
void setModal(bool modal = true);
|
||||
void setResizable(bool resizable = true);
|
||||
void setStatusFont(const nall::string &font);
|
||||
void setStatusText(const nall::string &text);
|
||||
|
|
|
@ -18,6 +18,7 @@ struct Window::State {
|
|||
set<Menu&> menu;
|
||||
string menuFont;
|
||||
bool menuVisible;
|
||||
bool modal;
|
||||
bool resizable;
|
||||
string statusFont;
|
||||
string statusText;
|
||||
|
@ -34,6 +35,7 @@ struct Window::State {
|
|||
geometry = {128, 128, 256, 256};
|
||||
ignore = false;
|
||||
menuVisible = false;
|
||||
modal = false;
|
||||
resizable = true;
|
||||
statusVisible = false;
|
||||
visible = false;
|
||||
|
|
|
@ -119,6 +119,7 @@ struct pWindow : public pObject {
|
|||
void setGeometry(const Geometry &geometry);
|
||||
void setMenuFont(const string &font);
|
||||
void setMenuVisible(bool visible);
|
||||
void setModal(bool modal);
|
||||
void setResizable(bool resizable);
|
||||
void setStatusFont(const string &font);
|
||||
void setStatusText(const string &text);
|
||||
|
|
|
@ -233,6 +233,10 @@ void pWindow::setMenuVisible(bool visible) {
|
|||
gtk_widget_set_visible(menu, visible);
|
||||
}
|
||||
|
||||
void pWindow::setModal(bool modal) {
|
||||
gtk_window_set_modal(GTK_WINDOW(widget), modal);
|
||||
}
|
||||
|
||||
void pWindow::setResizable(bool resizable) {
|
||||
gtk_window_set_resizable(GTK_WINDOW(widget), resizable);
|
||||
gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), resizable);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
** Meta object code from reading C++ file 'platform.moc.hpp'
|
||||
**
|
||||
** Created: Thu Apr 26 04:47:17 2012
|
||||
** Created: Tue May 1 09:36:37 2012
|
||||
** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
static QApplication *qtApplication = 0;
|
||||
static QApplication *qtApplication = nullptr;
|
||||
|
||||
struct Settings : public configuration {
|
||||
bidirectional_map<Keyboard::Scancode, unsigned> keymap;
|
||||
|
@ -133,6 +133,7 @@ public:
|
|||
void setGeometry(const Geometry &geometry);
|
||||
void setMenuFont(const string &font);
|
||||
void setMenuVisible(bool visible);
|
||||
void setModal(bool modal);
|
||||
void setResizable(bool resizable);
|
||||
void setStatusFont(const string &font);
|
||||
void setStatusText(const string &text);
|
||||
|
|
|
@ -121,6 +121,10 @@ void pWindow::setMenuVisible(bool visible) {
|
|||
setGeometry(window.state.geometry);
|
||||
}
|
||||
|
||||
void pWindow::setModal(bool modal) {
|
||||
qtWindow->setWindowModality(modal ? Qt::ApplicationModal : Qt::NonModal);
|
||||
}
|
||||
|
||||
void pWindow::setResizable(bool resizable) {
|
||||
if(resizable) {
|
||||
qtLayout->setSizeConstraint(QLayout::SetDefaultConstraint);
|
||||
|
|
|
@ -85,6 +85,7 @@ struct pWindow : public pObject {
|
|||
void setGeometry(const Geometry &geometry);
|
||||
void setMenuFont(const string &font);
|
||||
void setMenuVisible(bool visible);
|
||||
void setModal(bool modal);
|
||||
void setResizable(bool resizable);
|
||||
void setStatusFont(const string &font);
|
||||
void setStatusText(const string &text);
|
||||
|
|
|
@ -50,6 +50,9 @@ void pWindow::setMenuFont(const string &font) {
|
|||
void pWindow::setMenuVisible(bool visible) {
|
||||
}
|
||||
|
||||
void pWindow::setModal(bool modal) {
|
||||
}
|
||||
|
||||
void pWindow::setResizable(bool resizable) {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
array<pObject*> pObject::objects;
|
||||
vector<pObject*> pObject::objects;
|
||||
|
||||
pObject::pObject(Object &object) : object(object) {
|
||||
static unsigned uniqueId = 100;
|
||||
|
|
|
@ -152,6 +152,7 @@ static bool OS_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
|
||||
if(dynamic_cast<Window*>(object)) {
|
||||
Window &window = (Window&)*object;
|
||||
if(pWindow::modal.size() > 0 && !pWindow::modal.find(&window.p)) return false;
|
||||
Keyboard::Keycode keysym = Keysym(wparam, lparam);
|
||||
if(keysym != Keyboard::Keycode::None) {
|
||||
if((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && window.onKeyPress) window.onKeyPress(keysym);
|
||||
|
@ -220,11 +221,18 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
|
|||
if(!object || !dynamic_cast<Window*>(object)) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
Window &window = (Window&)*object;
|
||||
|
||||
if(!osQuit) switch(msg) {
|
||||
bool process = true;
|
||||
if(pWindow::modal.size() > 0 && !pWindow::modal.find(&window.p)) process = false;
|
||||
if(osQuit) process = false;
|
||||
|
||||
if(process) switch(msg) {
|
||||
case WM_CLOSE: {
|
||||
window.state.ignore = false;
|
||||
if(window.onClose) window.onClose();
|
||||
if(window.state.ignore == false) window.setVisible(false);
|
||||
if(window.state.ignore == false) {
|
||||
window.setVisible(false);
|
||||
window.setModal(false);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ struct Settings {
|
|||
};
|
||||
|
||||
struct pFont;
|
||||
struct pObject;
|
||||
struct pWindow;
|
||||
struct pMenu;
|
||||
struct pLayout;
|
||||
|
@ -25,7 +26,7 @@ struct pDesktop {
|
|||
|
||||
struct pKeyboard {
|
||||
static bool pressed(Keyboard::Scancode scancode);
|
||||
static array<bool> state();
|
||||
static vector<bool> state();
|
||||
|
||||
static void initialize();
|
||||
};
|
||||
|
@ -49,10 +50,11 @@ struct pMessageWindow {
|
|||
};
|
||||
|
||||
struct pObject {
|
||||
static vector<pObject*> objects;
|
||||
|
||||
Object &object;
|
||||
uintptr_t id;
|
||||
bool locked;
|
||||
static array<pObject*> objects;
|
||||
|
||||
pObject(Object &object);
|
||||
static pObject* find(unsigned id);
|
||||
|
@ -83,6 +85,9 @@ struct pTimer : public pObject {
|
|||
};
|
||||
|
||||
struct pWindow : public pObject {
|
||||
static vector<pWindow*> modal;
|
||||
static void updateModality();
|
||||
|
||||
Window &window;
|
||||
HWND hwnd;
|
||||
HMENU hmenu;
|
||||
|
@ -107,6 +112,7 @@ struct pWindow : public pObject {
|
|||
void setGeometry(const Geometry &geometry);
|
||||
void setMenuFont(const string &font);
|
||||
void setMenuVisible(bool visible);
|
||||
void setModal(bool modal);
|
||||
void setResizable(bool resizable);
|
||||
void setStatusFont(const string &font);
|
||||
void setStatusText(const string &text);
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
vector<pWindow*> pWindow::modal;
|
||||
|
||||
void pWindow::updateModality() {
|
||||
for(auto &object : pObject::objects) {
|
||||
if(dynamic_cast<pWindow*>(object) == nullptr) continue;
|
||||
pWindow *p = (pWindow*)object;
|
||||
if(modal.size() == 0) EnableWindow(p->hwnd, true);
|
||||
else EnableWindow(p->hwnd, modal.find(p));
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER;
|
||||
static const unsigned ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
|
||||
|
||||
|
@ -128,6 +139,15 @@ void pWindow::setMenuVisible(bool visible) {
|
|||
locked = false;
|
||||
}
|
||||
|
||||
void pWindow::setModal(bool modality) {
|
||||
if(modality == false) {
|
||||
if(auto position = modal.find(this)) modal.remove(position());
|
||||
} else {
|
||||
modal.appendonce(this);
|
||||
}
|
||||
updateModality();
|
||||
}
|
||||
|
||||
void pWindow::setResizable(bool resizable) {
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, window.state.resizable ? ResizableStyle : FixedStyle);
|
||||
setGeometry(window.state.geometry);
|
||||
|
|
|
@ -8,14 +8,18 @@ namespace SuperFamicom {
|
|||
|
||||
Cartridge cartridge;
|
||||
|
||||
void Cartridge::load(Mode cartridge_mode, const string &markup) {
|
||||
mode = cartridge_mode;
|
||||
void Cartridge::load(const string &markup, const stream &stream) {
|
||||
information.markup = markup;
|
||||
rom.copy(stream);
|
||||
sha256 = nall::sha256(rom.data(), rom.size()); //TODO: special case SGB, BSX, ST mode SHA256 sums
|
||||
|
||||
region = Region::NTSC;
|
||||
ram_size = 0;
|
||||
|
||||
has_bsx_slot = false;
|
||||
has_gb_slot = false;
|
||||
has_bs_cart = false;
|
||||
has_bs_slot = false;
|
||||
has_st_slot = false;
|
||||
has_nss_dip = false;
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
|
@ -37,32 +41,12 @@ void Cartridge::load(Mode cartridge_mode, const string &markup) {
|
|||
|
||||
if(ram_size > 0) {
|
||||
ram.map(allocate<uint8>(ram_size, 0xff), ram_size);
|
||||
nvram.append({ "save.ram", ram.data(), ram.size() });
|
||||
interface->memory.append({ID::RAM, "save.ram"});
|
||||
}
|
||||
|
||||
rom.write_protect(true);
|
||||
ram.write_protect(false);
|
||||
|
||||
switch((Mode)mode) {
|
||||
case Mode::Normal:
|
||||
case Mode::BsxSlotted:
|
||||
sha256 = nall::sha256(rom.data(), rom.size());
|
||||
break;
|
||||
case Mode::Bsx:
|
||||
sha256 = nall::sha256(bsxflash.memory.data(), bsxflash.memory.size());
|
||||
break;
|
||||
case Mode::SufamiTurbo:
|
||||
sha256 = nall::sha256(sufamiturbo.slotA.rom.data(), sufamiturbo.slotA.rom.size());
|
||||
break;
|
||||
case Mode::SuperGameBoy:
|
||||
#if defined(GAMEBOY)
|
||||
sha256 = GameBoy::cartridge.sha256();
|
||||
#else
|
||||
throw "Game Boy support not present";
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
system.load();
|
||||
loaded = true;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
struct Cartridge : property<Cartridge> {
|
||||
enum class Mode : unsigned {
|
||||
Normal,
|
||||
BsxSlotted,
|
||||
Bsx,
|
||||
SufamiTurbo,
|
||||
SuperGameBoy,
|
||||
};
|
||||
|
||||
enum class Region : unsigned {
|
||||
NTSC,
|
||||
PAL,
|
||||
|
@ -27,11 +19,13 @@ struct Cartridge : property<Cartridge> {
|
|||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
readonly<Mode> mode;
|
||||
readonly<Region> region;
|
||||
readonly<unsigned> ram_size;
|
||||
|
||||
readonly<bool> has_bsx_slot;
|
||||
readonly<bool> has_gb_slot;
|
||||
readonly<bool> has_bs_cart;
|
||||
readonly<bool> has_bs_slot;
|
||||
readonly<bool> has_st_slot;
|
||||
readonly<bool> has_nss_dip;
|
||||
readonly<bool> has_superfx;
|
||||
readonly<bool> has_sa1;
|
||||
|
@ -82,7 +76,7 @@ struct Cartridge : property<Cartridge> {
|
|||
} nss;
|
||||
} information;
|
||||
|
||||
void load(Mode, const string&);
|
||||
void load(const string &markup, const stream &stream);
|
||||
void unload();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
|
|
@ -108,7 +108,9 @@ void Cartridge::parse_markup_nss(XML::Node &root) {
|
|||
void Cartridge::parse_markup_icd2(XML::Node &root) {
|
||||
#if defined(GAMEBOY)
|
||||
if(root.exists() == false) return;
|
||||
if(mode != Mode::SuperGameBoy) return;
|
||||
has_gb_slot = true;
|
||||
|
||||
interface->mediaRequest({"Game Boy", "", "program.rom", "*.gb", 5});
|
||||
|
||||
icd2.revision = max(1, numeral(root["revision"].data));
|
||||
|
||||
|
@ -367,8 +369,10 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) {
|
|||
|
||||
void Cartridge::parse_markup_bsx(XML::Node &root) {
|
||||
if(root.exists() == false) return;
|
||||
if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return;
|
||||
has_bsx_slot = true;
|
||||
has_bs_cart = root["mmio"].exists();
|
||||
has_bs_slot = true;
|
||||
|
||||
interface->mediaRequest({"BS-X Satellaview", "", "program.rom", "*.bs", 2});
|
||||
|
||||
for(auto &node : root["slot"]) {
|
||||
if(node.name != "map") continue;
|
||||
|
@ -394,7 +398,9 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
|
|||
|
||||
void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
|
||||
if(root.exists() == false) return;
|
||||
if(mode != Mode::SufamiTurbo) return;
|
||||
has_st_slot = true;
|
||||
|
||||
interface->mediaRequest({"Sufami Turbo", "", "program.rom", "*.st", 3});
|
||||
|
||||
for(auto &slot : root) {
|
||||
if(slot.name != "slot") continue;
|
||||
|
|
|
@ -8,11 +8,11 @@ void BSXCartridge::init() {
|
|||
void BSXCartridge::load() {
|
||||
sram.map(allocate<uint8>(32 * 1024, 0xff), 32 * 1024);
|
||||
sram.write_protect(false);
|
||||
cartridge.nvram.append({ "bsx.ram", sram.data(), sram.size() });
|
||||
interface->memory.append({ID::BsxRAM, "bsx.ram"});
|
||||
|
||||
psram.map(allocate<uint8>(512 * 1024, 0xff), 512 * 1024);
|
||||
psram.write_protect(false);
|
||||
cartridge.nvram.append({ "bsx.psram", psram.data(), psram.size() });
|
||||
interface->memory.append({ID::BsxPSRAM, "bsx.psram"});
|
||||
}
|
||||
|
||||
void BSXCartridge::unload() {
|
||||
|
|
|
@ -15,7 +15,7 @@ void SPC7110::init() {
|
|||
|
||||
void SPC7110::load() {
|
||||
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
|
||||
if(cartridge.has_spc7110rtc()) cartridge.nvram.append({ "rtc.ram", rtc, 20 });
|
||||
if(cartridge.has_spc7110rtc()) interface->memory.append({ID::SPC7110RTC, "rtc.ram"});
|
||||
}
|
||||
|
||||
void SPC7110::unload() {
|
||||
|
|
|
@ -14,7 +14,7 @@ void SRTC::init() {
|
|||
|
||||
void SRTC::load() {
|
||||
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
|
||||
cartridge.nvram.append({ "rtc.ram", rtc, 20 });
|
||||
interface->memory.append({ID::RTC, "rtc.ram"});
|
||||
}
|
||||
|
||||
void SRTC::unload() {
|
||||
|
|
|
@ -9,17 +9,74 @@ bool Interface::loaded() {
|
|||
}
|
||||
|
||||
void Interface::load(unsigned id, const stream &stream, const string &markup) {
|
||||
if(id == 0) {
|
||||
if(id == ID::IPLROM) {
|
||||
stream.read(smp.iplrom, min(64u, stream.size()));
|
||||
}
|
||||
|
||||
if(id == 1) {
|
||||
cartridge.rom.copy(stream);
|
||||
cartridge.load(Cartridge::Mode::Normal, markup);
|
||||
if(id == ID::ROM) {
|
||||
cartridge.load(markup, stream);
|
||||
system.power();
|
||||
input.connect(0, Input::Device::Joypad);
|
||||
input.connect(1, Input::Device::Joypad);
|
||||
}
|
||||
|
||||
if(id == ID::SuperGameBoyROM) {
|
||||
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, markup, stream);
|
||||
}
|
||||
|
||||
if(id == ID::BsxFlashROM) {
|
||||
bsxflash.memory.copy(stream);
|
||||
}
|
||||
|
||||
if(id == ID::SufamiTurboSlotAROM) {
|
||||
sufamiturbo.slotA.rom.copy(stream);
|
||||
}
|
||||
|
||||
if(id == ID::SufamiTurboSlotBROM) {
|
||||
sufamiturbo.slotB.rom.copy(stream);
|
||||
}
|
||||
|
||||
if(id == ID::RAM) {
|
||||
stream.read(cartridge.ram.data(), min(cartridge.ram.size(), stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::RTC) {
|
||||
stream.read(srtc.rtc, min(stream.size(), sizeof srtc.rtc));
|
||||
}
|
||||
|
||||
if(id == ID::SPC7110RTC) {
|
||||
stream.read(spc7110.rtc, min(stream.size(), sizeof srtc.rtc));
|
||||
}
|
||||
|
||||
if(id == ID::BsxRAM) {
|
||||
stream.read(bsxcartridge.sram.data(), min(stream.size(), bsxcartridge.sram.size()));
|
||||
}
|
||||
|
||||
if(id == ID::BsxPSRAM) {
|
||||
stream.read(bsxcartridge.psram.data(), min(stream.size(), bsxcartridge.psram.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::save(unsigned id, const stream &stream) {
|
||||
if(id == ID::RAM) {
|
||||
stream.write(cartridge.ram.data(), cartridge.ram.size());
|
||||
}
|
||||
|
||||
if(id == ID::RTC) {
|
||||
stream.write(srtc.rtc, sizeof srtc.rtc);
|
||||
}
|
||||
|
||||
if(id == ID::SPC7110RTC) {
|
||||
stream.write(spc7110.rtc, sizeof srtc.rtc);
|
||||
}
|
||||
|
||||
if(id == ID::BsxRAM) {
|
||||
stream.write(bsxcartridge.sram.data(), bsxcartridge.sram.size());
|
||||
}
|
||||
|
||||
if(id == ID::BsxPSRAM) {
|
||||
stream.write(bsxcartridge.psram.data(), bsxcartridge.psram.size());
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::unload() {
|
||||
|
@ -48,25 +105,92 @@ Interface::Interface() {
|
|||
information.name = "Super Famicom";
|
||||
information.width = 256;
|
||||
information.height = 240;
|
||||
information.aspectRatio = 8.0 / 7.0;
|
||||
information.frequency = 32040;
|
||||
information.ports = 2;
|
||||
information.resettable = true;
|
||||
|
||||
information.media.append({"Super Famicom", "*.sfc"});
|
||||
information.media.append({"BS-X Satellaview", "*.bs"});
|
||||
information.media.append({"Sufami Turbo", "*.st"});
|
||||
information.media.append({"Super Game Boy", "*.gb"});
|
||||
|
||||
{
|
||||
Firmware firmware;
|
||||
firmware.displayname = "Super Famicom";
|
||||
firmware.name = "Super Famicom.sys/spc700.rom";
|
||||
firmware.id = 0;
|
||||
firmware.id = ID::IPLROM;
|
||||
this->firmware.append(firmware);
|
||||
}
|
||||
|
||||
{
|
||||
Media media;
|
||||
media.displayname = "Super Famicom";
|
||||
media.name = "program.rom";
|
||||
media.filter = "*.sfc";
|
||||
media.id = 1;
|
||||
this->media.append(media);
|
||||
Schema schema;
|
||||
schema.displayname = "Super Famicom";
|
||||
schema.name = "program.rom";
|
||||
schema.filter = "*.sfc";
|
||||
schema.id = ID::ROM;
|
||||
this->schema.append(schema);
|
||||
}
|
||||
|
||||
{
|
||||
Schema schema;
|
||||
schema.displayname = "Super Game Boy";
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
Schema schema;
|
||||
schema.displayname = "BS-X Satellaview";
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
Schema schema;
|
||||
schema.displayname = "Sufami Turbo";
|
||||
schema.path = "Sufami Turbo.sfc/";
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -2,12 +2,29 @@
|
|||
namespace SuperFamicom {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
IPLROM,
|
||||
ROM,
|
||||
SuperGameBoyROM,
|
||||
BsxFlashROM,
|
||||
SufamiTurboSlotAROM,
|
||||
SufamiTurboSlotBROM,
|
||||
RAM,
|
||||
RTC,
|
||||
SPC7110RTC,
|
||||
BsxRAM,
|
||||
BsxPSRAM,
|
||||
};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
virtual string path(unsigned slot, const string &hint) { return ""; }
|
||||
virtual void message(const string &text) {}
|
||||
|
||||
bool loaded();
|
||||
void load(unsigned id, const stream &stream, const string &markup = "");
|
||||
void save(unsigned id, const stream &stream);
|
||||
void unload();
|
||||
|
||||
void power();
|
||||
|
|
|
@ -57,10 +57,10 @@ void System::serialize_all(serializer &s) {
|
|||
ppu.serialize(s);
|
||||
dsp.serialize(s);
|
||||
|
||||
if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.serialize(s);
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.serialize(s);
|
||||
if(cartridge.has_gb_slot()) icd2.serialize(s);
|
||||
#endif
|
||||
if(cartridge.has_st_slot()) sufamiturbo.serialize(s);
|
||||
if(cartridge.has_superfx()) superfx.serialize(s);
|
||||
if(cartridge.has_sa1()) sa1.serialize(s);
|
||||
if(cartridge.has_necdsp()) necdsp.serialize(s);
|
||||
|
|
|
@ -103,13 +103,12 @@ void System::load() {
|
|||
ppu.enable();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.load();
|
||||
if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.load();
|
||||
if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.load();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.load();
|
||||
if(cartridge.has_gb_slot()) icd2.load();
|
||||
#endif
|
||||
|
||||
if(cartridge.has_bsx_slot()) bsxflash.load();
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.load();
|
||||
if(cartridge.has_bs_slot()) bsxflash.load();
|
||||
if(cartridge.has_st_slot()) sufamiturbo.load();
|
||||
if(cartridge.has_nss_dip()) nss.load();
|
||||
if(cartridge.has_superfx()) superfx.load();
|
||||
if(cartridge.has_sa1()) sa1.load();
|
||||
|
@ -129,13 +128,12 @@ void System::load() {
|
|||
|
||||
void System::unload() {
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.unload();
|
||||
if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.unload();
|
||||
if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.unload();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.unload();
|
||||
if(cartridge.has_gb_slot()) icd2.unload();
|
||||
#endif
|
||||
|
||||
if(cartridge.has_bsx_slot()) bsxflash.unload();
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.unload();
|
||||
if(cartridge.has_bs_slot()) bsxflash.unload();
|
||||
if(cartridge.has_st_slot()) sufamiturbo.unload();
|
||||
if(cartridge.has_nss_dip()) nss.unload();
|
||||
if(cartridge.has_superfx()) superfx.unload();
|
||||
if(cartridge.has_sa1()) sa1.unload();
|
||||
|
@ -168,12 +166,11 @@ void System::power() {
|
|||
ppu.power();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.power();
|
||||
if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.power();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.power();
|
||||
if(cartridge.has_gb_slot()) icd2.power();
|
||||
#endif
|
||||
|
||||
if(cartridge.has_bsx_slot()) bsxflash.power();
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.power();
|
||||
if(cartridge.has_bs_slot()) bsxflash.power();
|
||||
if(cartridge.has_nss_dip()) nss.power();
|
||||
if(cartridge.has_superfx()) superfx.power();
|
||||
if(cartridge.has_sa1()) sa1.power();
|
||||
|
@ -197,13 +194,11 @@ void System::reset() {
|
|||
ppu.reset();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.reset();
|
||||
|
||||
if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.reset();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.reset();
|
||||
if(cartridge.has_gb_slot()) icd2.reset();
|
||||
#endif
|
||||
|
||||
if(cartridge.has_bsx_slot()) bsxflash.reset();
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.reset();
|
||||
if(cartridge.has_bs_slot()) bsxflash.reset();
|
||||
if(cartridge.has_nss_dip()) nss.reset();
|
||||
if(cartridge.has_superfx()) superfx.reset();
|
||||
if(cartridge.has_sa1()) sa1.reset();
|
||||
|
@ -218,7 +213,7 @@ void System::reset() {
|
|||
if(cartridge.has_link()) link.reset();
|
||||
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2);
|
||||
if(cartridge.has_gb_slot()) cpu.coprocessors.append(&icd2);
|
||||
#endif
|
||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||
|
|
|
@ -7,7 +7,7 @@ include gb/Makefile
|
|||
include gba/Makefile
|
||||
name := ethos
|
||||
|
||||
ui_objects := ui-ethos ui-interface ui-utility ui-input ui-general ui-settings
|
||||
ui_objects := ui-ethos ui-configuration ui-interface ui-utility ui-input ui-general ui-settings
|
||||
ui_objects += phoenix ruby
|
||||
ui_objects += $(if $(call streq,$(platform),win),resource)
|
||||
|
||||
|
@ -39,6 +39,7 @@ objects := $(ui_objects) $(objects)
|
|||
objects := $(patsubst %,obj/%.o,$(objects))
|
||||
|
||||
obj/ui-ethos.o: $(ui)/ethos.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-configuration.o: $(ui)/configuration/configuration.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
|
||||
|
|
|
@ -16,6 +16,7 @@ void Application::bootstrap() {
|
|||
system->callback.videoRefresh = {&Interface::videoRefresh, interface};
|
||||
system->callback.audioSample = {&Interface::audioSample, interface};
|
||||
system->callback.inputPoll = {&Interface::inputPoll, interface};
|
||||
system->callback.mediaRequest = {&Interface::mediaRequest, interface};
|
||||
system->updatePalette();
|
||||
|
||||
for(auto &firmware : system->firmware) {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#include "../ethos.hpp"
|
||||
Configuration *config = nullptr;
|
||||
|
||||
Configuration::Configuration() {
|
||||
append(video.scaleMode = 0, "Video::ScaleMode");
|
||||
append(video.aspectCorrection = true, "Video::AspectCorrection");
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
void Configuration::load() {
|
||||
configuration::load(application->path("settings.cfg"));
|
||||
save(); //creates file if it does not exist
|
||||
}
|
||||
|
||||
void Configuration::save() {
|
||||
configuration::save(application->path("settings.cfg"));
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
struct Configuration : configuration {
|
||||
struct Video {
|
||||
unsigned scaleMode;
|
||||
bool aspectCorrection;
|
||||
} video;
|
||||
|
||||
void load();
|
||||
void save();
|
||||
Configuration();
|
||||
};
|
||||
|
||||
extern Configuration *config;
|
|
@ -61,6 +61,7 @@ Application::Application(int argc, char **argv) {
|
|||
monospaceFont = "Liberation Mono, 8";
|
||||
}
|
||||
|
||||
config = new Configuration;
|
||||
utility = new Utility;
|
||||
inputManager = new InputManager;
|
||||
browser = new Browser;
|
||||
|
@ -93,7 +94,7 @@ Application::Application(int argc, char **argv) {
|
|||
dspaudio.setPrecision(16);
|
||||
dspaudio.setVolume(2.0);
|
||||
dspaudio.setBalance(0.0);
|
||||
dspaudio.setResampler(DSP::ResampleEngine::Linear);
|
||||
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
|
||||
dspaudio.setResamplerFrequency(48000u);
|
||||
|
||||
while(quit == false) {
|
||||
|
@ -101,6 +102,8 @@ Application::Application(int argc, char **argv) {
|
|||
run();
|
||||
}
|
||||
|
||||
if(active && system().loaded()) utility->unload();
|
||||
config->save();
|
||||
browser->saveConfiguration();
|
||||
inputManager->saveConfiguration();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <nall/map.hpp>
|
||||
#include <nall/stream/file.hpp>
|
||||
#include <nall/stream/memory.hpp>
|
||||
#include <nall/stream/mmap.hpp>
|
||||
#include <nall/stream/vector.hpp>
|
||||
using namespace nall;
|
||||
|
||||
|
@ -16,6 +17,7 @@ using namespace phoenix;
|
|||
#include <ruby/ruby.hpp>
|
||||
using namespace ruby;
|
||||
|
||||
#include "configuration/configuration.hpp"
|
||||
#include "interface/interface.hpp"
|
||||
#include "utility/utility.hpp"
|
||||
#include "input/input.hpp"
|
||||
|
|
|
@ -41,6 +41,7 @@ Browser::Browser() {
|
|||
|
||||
fileList.onChange = {&Browser::synchronize, this};
|
||||
fileList.onActivate = openButton.onActivate = {&Browser::fileListActivate, this};
|
||||
onClose = [&] { dialogActive = false; };
|
||||
|
||||
synchronize();
|
||||
}
|
||||
|
@ -49,7 +50,7 @@ void Browser::synchronize() {
|
|||
openButton.setEnabled(fileList.selected());
|
||||
if(fileList.selected()) {
|
||||
for(auto &folder : folderList) {
|
||||
if(folder.filter == media.filter) {
|
||||
if(folder.filter == filter) {
|
||||
folder.selection = fileList.selection();
|
||||
}
|
||||
}
|
||||
|
@ -62,10 +63,10 @@ void Browser::saveConfiguration() {
|
|||
|
||||
void Browser::bootstrap() {
|
||||
for(auto &emulator : application->emulator) {
|
||||
for(auto &media : emulator->media) {
|
||||
for(auto &media : emulator->information.media) {
|
||||
bool found = false;
|
||||
for(auto &folder : folderList) {
|
||||
if(folder.filter == media.filter) {
|
||||
if(folder.filter == filter) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -89,16 +90,13 @@ void Browser::bootstrap() {
|
|||
config.save(application->path("paths.cfg"));
|
||||
}
|
||||
|
||||
void Browser::open(Emulator::Interface::Media &media, function<void (string)> callback) {
|
||||
this->media = media;
|
||||
this->callback = callback;
|
||||
|
||||
setTitle({"Load ", media.displayname});
|
||||
string Browser::select(const string &title, const string &filter) {
|
||||
this->filter = filter;
|
||||
|
||||
string path;
|
||||
unsigned selection = 0;
|
||||
for(auto &folder : folderList) {
|
||||
if(folder.filter == media.filter) {
|
||||
if(folder.filter == filter) {
|
||||
path = folder.path;
|
||||
selection = folder.selection;
|
||||
break;
|
||||
|
@ -107,15 +105,25 @@ void Browser::open(Emulator::Interface::Media &media, function<void (string)> ca
|
|||
if(path.empty()) path = application->basepath;
|
||||
setPath(path, selection);
|
||||
|
||||
filterLabel.setText({"Files of type: ", media.filter});
|
||||
filterLabel.setText({"Files of type: ", filter});
|
||||
|
||||
setTitle(title);
|
||||
setModal();
|
||||
setVisible();
|
||||
fileList.setFocused();
|
||||
dialogActive = true;
|
||||
outputFilename = "";
|
||||
while(dialogActive == true) {
|
||||
OS::processEvents();
|
||||
}
|
||||
|
||||
return outputFilename;
|
||||
}
|
||||
|
||||
void Browser::setPath(const string &path, unsigned selection) {
|
||||
//save path for next browser selection
|
||||
for(auto &folder : folderList) {
|
||||
if(folder.filter == media.filter) folder.path = path;
|
||||
if(folder.filter == filter) folder.path = path;
|
||||
}
|
||||
|
||||
this->path = path;
|
||||
|
@ -127,7 +135,7 @@ void Browser::setPath(const string &path, unsigned selection) {
|
|||
lstring contents = directory::folders(path);
|
||||
|
||||
for(auto &filename : contents) {
|
||||
string filter = {media.filter, "/"};
|
||||
string filter = {this->filter, "/"};
|
||||
if(!filename.wildcard(R"(*.??/)") && !filename.wildcard(R"(*.???/)")) {
|
||||
string name = filename;
|
||||
name.rtrim<1>("/");
|
||||
|
@ -138,7 +146,7 @@ void Browser::setPath(const string &path, unsigned selection) {
|
|||
}
|
||||
|
||||
for(auto &filename : contents) {
|
||||
string filter = {media.filter, "/"};
|
||||
string filter = {this->filter, "/"};
|
||||
if(filename.wildcard(R"(*.??/)") || filename.wildcard(R"(*.???/)")) {
|
||||
if(filename.wildcard(filter)) {
|
||||
string name = filename;
|
||||
|
@ -158,8 +166,10 @@ void Browser::setPath(const string &path, unsigned selection) {
|
|||
void Browser::fileListActivate() {
|
||||
unsigned selection = fileList.selection();
|
||||
string filename = filenameList[selection];
|
||||
string filter = {media.filter, "/"};
|
||||
string filter = {this->filter, "/"};
|
||||
if(filename.wildcard(filter) == false) return setPath({path, filename});
|
||||
|
||||
setVisible(false);
|
||||
if(callback) callback({path, filename});
|
||||
dialogActive = false;
|
||||
outputFilename = {path, filename};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ struct Browser : Window {
|
|||
Label filterLabel;
|
||||
Button openButton;
|
||||
|
||||
void open(Emulator::Interface::Media &media, function<void (string)> callback);
|
||||
string select(const string &title, const string &filter);
|
||||
void saveConfiguration();
|
||||
void synchronize();
|
||||
void bootstrap();
|
||||
|
@ -24,8 +24,10 @@ private:
|
|||
};
|
||||
vector<Folder> folderList;
|
||||
|
||||
Emulator::Interface::Media media;
|
||||
function<void (string)> callback;
|
||||
bool dialogActive;
|
||||
string outputFilename;
|
||||
|
||||
string filter;
|
||||
string path;
|
||||
lstring filenameList;
|
||||
|
||||
|
|
|
@ -6,9 +6,16 @@ void Presentation::synchronize() {
|
|||
if(system->interface == application->active) {
|
||||
activeSystem = system;
|
||||
system->menu.setVisible(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(config->video.scaleMode) {
|
||||
case 0: centerVideo.setChecked(); break;
|
||||
case 1: scaleVideo.setChecked(); break;
|
||||
case 2: stretchVideo.setChecked(); break;
|
||||
}
|
||||
aspectCorrection.setChecked(config->video.aspectCorrection);
|
||||
resizeWindow.setVisible(application->active && config->video.scaleMode != 2);
|
||||
}
|
||||
|
||||
void Presentation::setSystemName(const string &name) {
|
||||
|
@ -28,22 +35,39 @@ Presentation::Presentation() : activeSystem(nullptr) {
|
|||
|
||||
loadMenu.setText("Load");
|
||||
settingsMenu.setText("Settings");
|
||||
videoMenu.setText("Video");
|
||||
centerVideo.setText("Center");
|
||||
scaleVideo.setText("Scale");
|
||||
stretchVideo.setText("Stretch");
|
||||
RadioItem::group(centerVideo, scaleVideo, stretchVideo);
|
||||
aspectCorrection.setText("Correct Aspect Ratio");
|
||||
configurationSettings.setText("Configuration ...");
|
||||
toolsMenu.setText("Tools");
|
||||
resizeWindow.setText("Resize Window");
|
||||
|
||||
append(loadMenu);
|
||||
for(auto &item : loadList) loadMenu.append(*item);
|
||||
for(auto &system : emulatorList) append(system->menu);
|
||||
append(settingsMenu);
|
||||
settingsMenu.append(videoMenu);
|
||||
videoMenu.append(centerVideo, scaleVideo, stretchVideo, *new Separator, aspectCorrection);
|
||||
settingsMenu.append(*new Separator);
|
||||
settingsMenu.append(configurationSettings);
|
||||
append(toolsMenu);
|
||||
toolsMenu.append(resizeWindow);
|
||||
|
||||
append(layout);
|
||||
layout.append(viewport, {0, 0, 720, 480});
|
||||
|
||||
onSize = [&] { utility->resize(); };
|
||||
onClose = [&] { application->quit = true; };
|
||||
|
||||
centerVideo.onActivate = [&] { config->video.scaleMode = 0; utility->resize(); };
|
||||
scaleVideo.onActivate = [&] { config->video.scaleMode = 1; utility->resize(); };
|
||||
stretchVideo.onActivate = [&] { config->video.scaleMode = 2; utility->resize(); };
|
||||
aspectCorrection.onToggle = [&] { config->video.aspectCorrection = aspectCorrection.checked(); utility->resize(); };
|
||||
configurationSettings.onActivate = [&] { settings->setVisible(); };
|
||||
resizeWindow.onActivate = [&] { utility->resize(true); };
|
||||
|
||||
synchronize();
|
||||
}
|
||||
|
@ -53,13 +77,11 @@ void Presentation::bootstrap() {
|
|||
System *system = new System;
|
||||
system->interface = emulator;
|
||||
|
||||
for(auto &media : emulator->media) {
|
||||
for(auto &schema : emulator->schema) {
|
||||
Item *item = new Item;
|
||||
item->setText({media.displayname, " ..."});
|
||||
item->onActivate = [=, &media] {
|
||||
browser->open(media, [=, &media](string filename) {
|
||||
utility->loadMedia(system->interface, media, filename);
|
||||
});
|
||||
item->setText({schema.displayname, " ..."});
|
||||
item->onActivate = [=, &schema] {
|
||||
utility->loadSchema(system->interface, schema);
|
||||
};
|
||||
loadList.append(item);
|
||||
}
|
||||
|
|
|
@ -15,10 +15,16 @@ struct Presentation : Window {
|
|||
vector<System*> emulatorList;
|
||||
|
||||
Menu loadMenu;
|
||||
vector<Item*> loadList;
|
||||
vector<Action*> loadList;
|
||||
Menu settingsMenu;
|
||||
Menu videoMenu;
|
||||
RadioItem centerVideo;
|
||||
RadioItem scaleVideo;
|
||||
RadioItem stretchVideo;
|
||||
CheckItem aspectCorrection;
|
||||
Item configurationSettings;
|
||||
Menu toolsMenu;
|
||||
Item resizeWindow;
|
||||
|
||||
void synchronize();
|
||||
void setSystemName(const string &name);
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
void InputManager::appendHotkeys() {
|
||||
{
|
||||
auto hotkey = new HotkeyInput;
|
||||
hotkey->name = "Toggle Fullscreen Mode";
|
||||
hotkey->mapping = "KB0::Alt,KB0::Return";
|
||||
hotkey->logic = 1;
|
||||
hotkeyMap.append(hotkey);
|
||||
|
||||
hotkey->press = [] {
|
||||
utility->toggleFullScreen();
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
auto hotkey = new HotkeyInput;
|
||||
hotkey->name = "Fast Forward";
|
||||
|
|
|
@ -46,3 +46,13 @@ int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
|
|||
unsigned guid = system().port[port].device[device].input[input].guid;
|
||||
return inputManager->inputMap[guid]->poll();
|
||||
}
|
||||
|
||||
void Interface::mediaRequest(Emulator::Interface::Media media) {
|
||||
string pathname = browser->select({"Load ", media.displayname}, media.filter);
|
||||
if(pathname.empty()) return;
|
||||
|
||||
string markup;
|
||||
markup.readfile({pathname, "manifest.xml"});
|
||||
mmapstream stream({pathname, media.name});
|
||||
system().load(media.id, stream, markup);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ struct Interface {
|
|||
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
|
||||
void audioSample(int16_t lsample, int16_t rsample);
|
||||
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
|
||||
void mediaRequest(Emulator::Interface::Media media);
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -16,7 +16,6 @@ struct Settings : Window {
|
|||
ListView panelList;
|
||||
|
||||
void panelChanged();
|
||||
|
||||
Settings();
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,15 @@ void Utility::setInterface(Emulator::Interface *emulator) {
|
|||
presentation->synchronize();
|
||||
}
|
||||
|
||||
void Utility::loadSchema(Emulator::Interface *emulator, Emulator::Interface::Schema &schema) {
|
||||
string pathname;
|
||||
if(!schema.path.empty()) pathname = application->path(schema.path);
|
||||
if(!directory::exists(pathname)) pathname = browser->select(schema.displayname, schema.filter);
|
||||
if(!directory::exists(pathname)) return;
|
||||
|
||||
loadMedia(emulator, schema, pathname);
|
||||
}
|
||||
|
||||
void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname) {
|
||||
unload();
|
||||
setInterface(emulator);
|
||||
|
@ -29,11 +38,13 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
|
|||
displayname.rtrim<1>("/");
|
||||
presentation->setTitle(notdir(nall::basename(displayname)));
|
||||
presentation->setSystemName(media.displayname);
|
||||
resize();
|
||||
}
|
||||
|
||||
void Utility::saveMedia() {
|
||||
for(auto &memory : system().memory) {
|
||||
file::write({pathname, memory.name}, memory.data, memory.size);
|
||||
filestream fs({pathname, memory.name}, file::mode::write);
|
||||
system().save(memory.id, fs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +68,70 @@ void Utility::unload() {
|
|||
video.clear();
|
||||
}
|
||||
|
||||
void Utility::resize(bool resizeWindow) {
|
||||
if(application->active == nullptr) return;
|
||||
Geometry geometry = presentation->geometry();
|
||||
unsigned width = system().information.width;
|
||||
unsigned height = system().information.height;
|
||||
|
||||
unsigned scaledWidth = geometry.width / width;
|
||||
unsigned scaledHeight = geometry.height / height;
|
||||
unsigned multiplier = max(1u, min(scaledWidth, scaledHeight));
|
||||
|
||||
if(config->video.aspectCorrection) {
|
||||
width *= system().information.aspectRatio;
|
||||
}
|
||||
|
||||
width *= multiplier;
|
||||
height *= multiplier;
|
||||
|
||||
unsigned scaleMode = 0;
|
||||
|
||||
if(config->video.scaleMode == 1) {
|
||||
width = (double)width * ((double)geometry.height / height);
|
||||
height = geometry.height;
|
||||
}
|
||||
|
||||
if(config->video.scaleMode == 2) {
|
||||
width = geometry.width;
|
||||
height = geometry.height;
|
||||
}
|
||||
|
||||
if(resizeWindow == false) {
|
||||
if(geometry.width < width ) width = geometry.width;
|
||||
if(geometry.height < height) height = geometry.height;
|
||||
|
||||
presentation->viewport.setGeometry({
|
||||
(geometry.width - width) / 2, (geometry.height - height) / 2, width, height
|
||||
});
|
||||
} else {
|
||||
presentation->setGeometry({geometry.x, geometry.y, width, height});
|
||||
presentation->viewport.setGeometry({0, 0, width, height});
|
||||
}
|
||||
|
||||
presentation->synchronize();
|
||||
}
|
||||
|
||||
void Utility::toggleFullScreen() {
|
||||
static Geometry geometry;
|
||||
|
||||
if(presentation->fullScreen() == false) {
|
||||
geometry = presentation->geometry();
|
||||
presentation->setMenuVisible(false);
|
||||
presentation->setStatusVisible(false);
|
||||
presentation->setFullScreen(true);
|
||||
input.acquire();
|
||||
} else {
|
||||
input.unacquire();
|
||||
presentation->setMenuVisible(true);
|
||||
presentation->setStatusVisible(true);
|
||||
presentation->setFullScreen(false);
|
||||
presentation->setGeometry(geometry);
|
||||
}
|
||||
|
||||
resize();
|
||||
}
|
||||
|
||||
void Utility::setStatusText(const string &text) {
|
||||
presentation->setStatusText(text);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ struct Utility {
|
|||
string pathname;
|
||||
|
||||
void setInterface(Emulator::Interface *emulator);
|
||||
void loadSchema(Emulator::Interface *emulator, Emulator::Interface::Schema &schema);
|
||||
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
|
||||
void saveMedia();
|
||||
|
||||
|
@ -9,6 +10,8 @@ struct Utility {
|
|||
void reset();
|
||||
void unload();
|
||||
|
||||
void resize(bool resizeWindow = false);
|
||||
void toggleFullScreen();
|
||||
void setStatusText(const string &text);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue