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:
Tim Allen 2012-05-03 22:36:47 +10:00
parent 9ad8b7eaac
commit 8703d57030
53 changed files with 663 additions and 209 deletions

View File

@ -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";
}

View File

@ -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

View File

@ -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);

View File

@ -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() {
@ -40,20 +50,22 @@ void Interface::updatePalette() {
Interface::Interface() {
interface = this;
information.name = "Famicom";
information.width = 256;
information.height = 240;
information.frequency = 1789772;
information.ports = 2;
information.resettable = true;
information.name = "Famicom";
information.width = 256;
information.height = 240;
information.aspectRatio = 8.0 / 7.0;
information.frequency = 1789772;
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);
}
{

View File

@ -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();

View File

@ -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() {

View File

@ -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() {
@ -49,18 +65,21 @@ void Interface::updatePalette() {
Interface::Interface() {
interface = this;
information.name = "Game Boy";
information.width = 160;
information.height = 144;
information.frequency = 4194304;
information.ports = 1;
information.resettable = false;
information.name = "Game Boy";
information.width = 160;
information.height = 144;
information.aspectRatio = 1.0;
information.frequency = 4194304;
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);
}
{

View File

@ -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();

View File

@ -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"});
}
}

View File

@ -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();
}
@ -51,28 +69,30 @@ void Interface::updatePalette() {
Interface::Interface() {
interface = this;
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.frequency = 32768;
information.ports = 1;
information.resettable = false;
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.aspectRatio = 1.0;
information.frequency = 32768;
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);
}
{

View File

@ -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();

View File

@ -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;

View File

@ -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; }

View File

@ -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);

View File

@ -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);

View File

@ -18,6 +18,7 @@ struct Window::State {
set<Menu&> menu;
string menuFont;
bool menuVisible;
bool modal;
bool resizable;
string statusFont;
string statusText;
@ -29,11 +30,12 @@ struct Window::State {
State() {
backgroundColorOverride = false;
backgroundColor = { 0, 0, 0, 255 };
backgroundColor = {0, 0, 0, 255};
fullScreen = false;
geometry = { 128, 128, 256, 256 };
geometry = {128, 128, 256, 256};
ignore = false;
menuVisible = false;
modal = false;
resizable = true;
statusVisible = false;
visible = false;
@ -115,7 +117,7 @@ struct Widget::State {
State() {
abstract = false;
enabled = true;
geometry = { 0, 0, 0, 0 };
geometry = {0, 0, 0, 0};
visible = true;
}
};

View File

@ -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);

View File

@ -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);

View File

@ -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!

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -8,7 +8,7 @@ void pWindow::append(Widget &widget) {
}
Color pWindow::backgroundColor() {
return { 0, 0, 0, 255 };
return {0, 0, 0, 255};
}
bool pWindow::focused() {
@ -16,11 +16,11 @@ bool pWindow::focused() {
}
Geometry pWindow::frameMargin() {
return { 0, 0, 0, 0 };
return {0, 0, 0, 0};
}
Geometry pWindow::geometry() {
return { 0, 0, 0, 0 };
return {0, 0, 0, 0};
}
void pWindow::remove(Layout &layout) {
@ -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) {
}

View File

@ -1,4 +1,4 @@
array<pObject*> pObject::objects;
vector<pObject*> pObject::objects;
pObject::pObject(Object &object) : object(object) {
static unsigned uniqueId = 100;

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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&);

View File

@ -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;

View File

@ -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() {

View File

@ -8,14 +8,14 @@ SPC7110 spc7110;
#include "serialization.cpp"
#include "decomp.cpp"
const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
const unsigned SPC7110::months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
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() {

View File

@ -7,14 +7,14 @@ SRTC srtc;
#include "serialization.cpp"
const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
const unsigned SRTC::months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
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() {

View File

@ -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() {
@ -45,28 +102,95 @@ void Interface::updatePalette() {
Interface::Interface() {
interface = this;
information.name = "Super Famicom";
information.width = 256;
information.height = 240;
information.frequency = 32040;
information.ports = 2;
information.resettable = true;
information.name = "Super Famicom";
information.width = 256;
information.height = 240;
information.aspectRatio = 8.0 / 7.0;
information.frequency = 32040;
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);
}
{

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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)/)

View File

@ -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) {

View File

@ -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"));
}

View File

@ -0,0 +1,12 @@
struct Configuration : configuration {
struct Video {
unsigned scaleMode;
bool aspectCorrection;
} video;
void load();
void save();
Configuration();
};
extern Configuration *config;

View File

@ -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();
}

View File

@ -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"

View File

@ -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};
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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";

View File

@ -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);
}

View File

@ -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;

View File

@ -16,7 +16,6 @@ struct Settings : Window {
ListView panelList;
void panelChanged();
Settings();
};

View File

@ -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);
}

View File

@ -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);
};