diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index 8348d070..4e55060d 100755 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "bsnes"; - static const char Version[] = "089.07"; + static const char Version[] = "089.08"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; } diff --git a/bsnes/emulator/interface.hpp b/bsnes/emulator/interface.hpp index 92c7d382..f0d1ca79 100755 --- a/bsnes/emulator/interface.hpp +++ b/bsnes/emulator/interface.hpp @@ -11,6 +11,10 @@ struct Interface { bool overscan; double aspectRatio; bool resettable; + struct Capability { + bool states; + bool cheats; + } capability; } information; struct Media { @@ -18,7 +22,6 @@ struct Interface { string name; string type; string path; - string extension; }; vector firmware; @@ -30,7 +33,7 @@ struct Interface { string name; struct Input { unsigned id; - unsigned type; //0 = digital, 1 = analog + unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute) string name; unsigned guid; }; @@ -46,7 +49,7 @@ struct Interface { vector port; struct Bind { - virtual void loadRequest(unsigned, const string&, const string&, const string&) {} + virtual void loadRequest(unsigned, const string&, const string&) {} virtual void loadRequest(unsigned, const string&) {} virtual void saveRequest(unsigned, const string&) {} virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; } @@ -58,7 +61,7 @@ struct Interface { } *bind; //callback bindings (provided by user interface) - void loadRequest(unsigned id, const string &name, const string &type, const string &path) { return bind->loadRequest(id, name, type, path); } + void loadRequest(unsigned id, const string &name, const string &type) { return bind->loadRequest(id, name, type); } void loadRequest(unsigned id, const string &path) { return bind->loadRequest(id, path); } void saveRequest(unsigned id, const string &path) { return bind->saveRequest(id, path); } uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); } diff --git a/bsnes/fc/interface/interface.cpp b/bsnes/fc/interface/interface.cpp index a049e051..d15e37e6 100755 --- a/bsnes/fc/interface/interface.cpp +++ b/bsnes/fc/interface/interface.cpp @@ -109,8 +109,10 @@ Interface::Interface() { information.overscan = true; information.aspectRatio = 8.0 / 7.0; information.resettable = true; + information.capability.states = true; + information.capability.cheats = true; - media.append({ID::Famicom, "Famicom", "sys", "program.rom", "fc"}); + media.append({ID::Famicom, "Famicom", "sys", "fc"}); { Device device{0, ID::Port1 | ID::Port2, "Controller"}; diff --git a/bsnes/gb/cartridge/cartridge.cpp b/bsnes/gb/cartridge/cartridge.cpp index 3cb92d05..062f975e 100755 --- a/bsnes/gb/cartridge/cartridge.cpp +++ b/bsnes/gb/cartridge/cartridge.cpp @@ -14,7 +14,7 @@ namespace GameBoy { #include "serialization.cpp" Cartridge cartridge; -void Cartridge::load(System::Revision revision, const string &manifest, bool preloaded) { +void Cartridge::load(System::Revision revision, const string &manifest) { information.markup = manifest; information.mapper = Mapper::Unknown; information.ram = false; @@ -49,7 +49,8 @@ void Cartridge::load(System::Revision revision, const string &manifest, bool pre ramsize = numeral(ram["size"].data); ramdata = allocate(ramsize, 0xff); - if(preloaded == false) { + //Super Game Boy core loads memory from Super Famicom core + if(revision != System::Revision::SuperGameBoy) { if(rom["name"].exists()) interface->loadRequest(ID::ROM, rom["name"].data); if(ram["name"].exists()) interface->loadRequest(ID::RAM, ram["name"].data); if(ram["name"].exists()) memory.append({ID::RAM, ram["name"].data}); diff --git a/bsnes/gb/cartridge/cartridge.hpp b/bsnes/gb/cartridge/cartridge.hpp index 992f19cd..90640ce3 100755 --- a/bsnes/gb/cartridge/cartridge.hpp +++ b/bsnes/gb/cartridge/cartridge.hpp @@ -51,7 +51,7 @@ struct Cartridge : MMIO, property { MMIO *mapper; bool bootrom_enable; - void load(System::Revision revision, const string &manifest, bool preloaded = false); + void load(System::Revision revision, const string &manifest); void unload(); uint8 rom_read(unsigned addr); diff --git a/bsnes/gb/interface/interface.cpp b/bsnes/gb/interface/interface.cpp index 7667f7b8..507e5dce 100755 --- a/bsnes/gb/interface/interface.cpp +++ b/bsnes/gb/interface/interface.cpp @@ -120,13 +120,15 @@ Interface::Interface() { information.overscan = false; information.aspectRatio = 1.0; information.resettable = false; + information.capability.states = true; + information.capability.cheats = true; firmware.append({ID::GameBoyBootROM, "Game Boy", "sys", "boot.rom"}); firmware.append({ID::SuperGameBoyBootROM, "Super Game Boy", "sfc", "boot.rom"}); firmware.append({ID::GameBoyColorBootROM, "Game Boy Color", "sys", "boot.rom"}); - media.append({ID::GameBoy, "Game Boy", "sys", "program.rom", "gb" }); - media.append({ID::GameBoyColor, "Game Boy Color", "sys", "program.rom", "gbc"}); + media.append({ID::GameBoy, "Game Boy", "sys", "gb" }); + media.append({ID::GameBoyColor, "Game Boy Color", "sys", "gbc"}); { Device device{0, ID::Device, "Controller"}; diff --git a/bsnes/gba/interface/interface.cpp b/bsnes/gba/interface/interface.cpp index 79c53085..ad9b4871 100755 --- a/bsnes/gba/interface/interface.cpp +++ b/bsnes/gba/interface/interface.cpp @@ -101,10 +101,12 @@ Interface::Interface() { information.overscan = false; information.aspectRatio = 1.0; information.resettable = false; + information.capability.states = true; + information.capability.cheats = false; firmware.append({ID::BIOS, "Game Boy Advance", "sys", "bios.rom"}); - media.append({ID::GameBoyAdvance, "Game Boy Advance", "sys", "program.rom", "gba"}); + media.append({ID::GameBoyAdvance, "Game Boy Advance", "sys", "gba"}); { Device device{0, ID::Device, "Controller"}; diff --git a/bsnes/profile/Sufami Turbo.sfc/manifest.xml b/bsnes/profile/Sufami Turbo.sfc/manifest.xml index e426b806..7fb246dc 100755 --- a/bsnes/profile/Sufami Turbo.sfc/manifest.xml +++ b/bsnes/profile/Sufami Turbo.sfc/manifest.xml @@ -1,6 +1,6 @@ - + @@ -10,7 +10,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/bsnes/sfc/cartridge/cartridge.cpp b/bsnes/sfc/cartridge/cartridge.cpp index 4e79c734..a9e65f19 100755 --- a/bsnes/sfc/cartridge/cartridge.cpp +++ b/bsnes/sfc/cartridge/cartridge.cpp @@ -5,7 +5,6 @@ namespace SuperFamicom { #include "markup.cpp" #include "serialization.cpp" - Cartridge cartridge; void Cartridge::load(const string &manifest) { @@ -59,63 +58,63 @@ void Cartridge::load(const string &manifest) { loaded = true; } -void Cartridge::load(const string &markup, const stream &stream) { - rom.copy(stream); +void Cartridge::load_super_game_boy(const string &manifest) { + XML::Document document(manifest); + auto &rom = document["cartridge"]["rom"]; + auto &ram = document["cartridge"]["ram"]; - region = Region::NTSC; -//ram_size = 0; + GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, manifest); - has_gb_slot = false; - has_bs_cart = false; - has_bs_slot = false; - has_st_slots = false; - has_nss_dip = false; - has_sa1 = false; - has_superfx = false; - has_armdsp = false; - has_hitachidsp = false; - has_necdsp = false; - has_epsonrtc = false; - has_sharprtc = false; - has_spc7110 = false; - has_sdd1 = false; - has_obc1 = false; - has_msu1 = false; - has_link = false; + if(rom["name"].exists()) interface->loadRequest(ID::SuperGameBoyROM, rom["name"].data); + if(ram["name"].exists()) interface->loadRequest(ID::SuperGameBoyRAM, ram["name"].data); + if(ram["name"].exists()) memory.append({ID::SuperGameBoyRAM, ram["name"].data}); +} - parse_markup(markup); -//print(markup, "\n\n"); +void Cartridge::load_satellaview(const string &manifest) { + XML::Document document(manifest); + auto &rom = document["cartridge"]["rom"]; - //Super Game Boy - if(cartridge.has_gb_slot()) { - sha256 = nall::sha256(GameBoy::cartridge.romdata, GameBoy::cartridge.romsize); + if(rom["name"].exists()) { + interface->loadRequest(ID::BsxFlashROM, rom["name"].data); + } +} + +void Cartridge::load_sufami_turbo_a(const string &manifest) { + XML::Document document(manifest); + auto &rom = document["cartridge"]["rom"]; + auto &ram = document["cartridge"]["ram"]; + + if(rom["name"].exists()) { + interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].data); } - //Broadcast Satellaview - else if(cartridge.has_bs_cart() && cartridge.has_bs_slot()) { - sha256 = nall::sha256(bsxflash.memory.data(), bsxflash.memory.size()); + if(ram["name"].exists()) { + unsigned size = numeral(ram["size"].data); + sufamiturbo.slotA.ram.map(allocate(size, 0xff), size); + interface->loadRequest(ID::SufamiTurboSlotARAM, ram["name"].data); + memory.append({ID::SufamiTurboSlotARAM, ram["name"].data}); } - //Sufami Turbo - else if(cartridge.has_st_slots()) { - sha256 = nall::sha256(sufamiturbo.slotA.rom.data(), sufamiturbo.slotA.rom.size()); + if(document["cartridge"]["linkable"].data == "true") { + interface->loadRequest(ID::SufamiTurboSlotB, "Sufami Turbo - Slot B", "st"); + } +} + +void Cartridge::load_sufami_turbo_b(const string &manifest) { + XML::Document document(manifest); + auto &rom = document["cartridge"]["rom"]; + auto &ram = document["cartridge"]["ram"]; + + if(rom["name"].exists()) { + interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].data); } - //Super Famicom - else { - sha256 = nall::sha256(rom.data(), rom.size()); + if(ram["name"].exists()) { + unsigned size = numeral(ram["size"].data); + sufamiturbo.slotB.ram.map(allocate(size, 0xff), size); + interface->loadRequest(ID::SufamiTurboSlotBRAM, ram["name"].data); + memory.append({ID::SufamiTurboSlotBRAM, ram["name"].data}); } - -// if(ram_size > 0) { -// ram.map(allocate(ram_size, 0xff), ram_size); -// interface->memory.append({ID::RAM, "save.ram"}); -// } - - rom.write_protect(true); - ram.write_protect(false); - - system.load(); - loaded = true; } void Cartridge::unload() { @@ -131,7 +130,6 @@ void Cartridge::unload() { Cartridge::Cartridge() { loaded = false; - unload(); } Cartridge::~Cartridge() { diff --git a/bsnes/sfc/cartridge/cartridge.hpp b/bsnes/sfc/cartridge/cartridge.hpp index 2a8eb695..c14de4e0 100755 --- a/bsnes/sfc/cartridge/cartridge.hpp +++ b/bsnes/sfc/cartridge/cartridge.hpp @@ -63,7 +63,10 @@ struct Cartridge : property { vector memory; void load(const string &manifest); - void load(const string &markup, const stream &stream); + void load_super_game_boy(const string &manifest); + void load_satellaview(const string &manifest); + void load_sufami_turbo_a(const string &manifest); + void load_sufami_turbo_b(const string &manifest); void unload(); void serialize(serializer&); diff --git a/bsnes/sfc/cartridge/markup.cpp b/bsnes/sfc/cartridge/markup.cpp index 03ef7efc..2e00fe1e 100755 --- a/bsnes/sfc/cartridge/markup.cpp +++ b/bsnes/sfc/cartridge/markup.cpp @@ -103,14 +103,7 @@ void Cartridge::parse_markup_icd2(XML::Node &root) { if(root.exists() == false) return; has_gb_slot = true; - //Game Boy requires a cartridge to be loaded ... - //load "empty" cartridge, in case loadRequest() does not load one - vector stream; - stream.resize(32768); - for(auto &byte : stream) byte = 0xff; - interface->load(ID::SuperGameBoyROM, vectorstream{stream}, ""); - - interface->loadRequest(ID::SuperGameBoyROM, "Game Boy", "gb", "program.rom"); + interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb"); icd2.revision = max(1, numeral(root["revision"].data)); @@ -127,7 +120,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) { has_bs_cart = root["mmio"].exists(); has_bs_slot = true; - interface->loadRequest(ID::BsxFlashROM, "BS-X Satellaview", "bs", "program.rom"); + interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs"); if(has_bs_cart) { parse_markup_memory(bsxcartridge.psram, root["psram"], ID::BsxPSRAM, true); @@ -135,6 +128,8 @@ void Cartridge::parse_markup_bsx(XML::Node &root) { for(auto &node : root["slot"]) { if(node.name != "map") continue; + if(bsxflash.memory.size() == 0) continue; + Mapping m(bsxflash.memory); parse_markup_map(m, node); mapping.append(m); @@ -159,8 +154,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) { if(root.exists() == false) return; has_st_slots = true; - interface->loadRequest(ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "st", "program.rom"); - interface->loadRequest(ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "st", "program.rom"); + //load required slot A (will request slot B if slot A cartridge is linkable) + interface->loadRequest(ID::SufamiTurboSlotA, "Sufami Turbo - Slot A", "st"); for(auto &slot : root) { if(slot.name != "slot") continue; @@ -170,6 +165,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) { for(auto &leaf : node) { if(leaf.name != "map") continue; SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.rom : sufamiturbo.slotB.rom; + if(memory.size() == 0) continue; + Mapping m(memory); parse_markup_map(m, leaf); if(m.size == 0) m.size = memory.size(); @@ -181,9 +178,11 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) { for(auto &leaf : node) { if(leaf.name != "map") continue; SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.ram : sufamiturbo.slotB.ram; + if(memory.size() == 0) continue; + Mapping m(memory); parse_markup_map(m, leaf); - if(m.size == 0) m.size = ram_size; + if(m.size == 0) m.size = memory.size(); if(m.size) mapping.append(m); } } diff --git a/bsnes/sfc/chip/sufamiturbo/sufamiturbo.cpp b/bsnes/sfc/chip/sufamiturbo/sufamiturbo.cpp index 2870799d..ac7236d2 100755 --- a/bsnes/sfc/chip/sufamiturbo/sufamiturbo.cpp +++ b/bsnes/sfc/chip/sufamiturbo/sufamiturbo.cpp @@ -7,16 +7,6 @@ namespace SuperFamicom { SufamiTurbo sufamiturbo; void SufamiTurbo::load() { - slotA.ram.map(allocate(128 * 1024, 0xff), 128 * 1024); - slotB.ram.map(allocate(128 * 1024, 0xff), 128 * 1024); - - if(slotA.rom.data() == nullptr) { - slotA.rom.map(allocate(128 * 1024, 0xff), 128 * 1024); - } - - if(slotB.rom.data() == nullptr) { - slotB.rom.map(allocate(128 * 1024, 0xff), 128 * 1024); - } } void SufamiTurbo::unload() { diff --git a/bsnes/sfc/interface/interface.cpp b/bsnes/sfc/interface/interface.cpp index c39d11aa..971a858a 100755 --- a/bsnes/sfc/interface/interface.cpp +++ b/bsnes/sfc/interface/interface.cpp @@ -34,14 +34,18 @@ unsigned Interface::group(unsigned id) { case ID::NecDSPRAM: case ID::BsxPSRAM: return 0; + case ID::SuperGameBoy: case ID::SuperGameBoyROM: case ID::SuperGameBoyRAM: return 1; + case ID::Satellaview: case ID::BsxFlashROM: return 2; + case ID::SufamiTurboSlotA: case ID::SufamiTurboSlotAROM: case ID::SufamiTurboSlotARAM: return 3; + case ID::SufamiTurboSlotB: case ID::SufamiTurboSlotBROM: case ID::SufamiTurboSlotBRAM: return 4; @@ -50,7 +54,11 @@ unsigned Interface::group(unsigned id) { } void Interface::load(unsigned id, const string &manifest) { - cartridge.load(manifest); + if(id == ID::SuperFamicom) cartridge.load(manifest); + if(id == ID::SuperGameBoy) cartridge.load_super_game_boy(manifest); + if(id == ID::Satellaview) cartridge.load_satellaview(manifest); + if(id == ID::SufamiTurboSlotA) cartridge.load_sufami_turbo_a(manifest); + if(id == ID::SufamiTurboSlotB) cartridge.load_sufami_turbo_b(manifest); } void Interface::save() { @@ -107,12 +115,11 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest) } if(id == ID::SuperGameBoyROM) { - GameBoy::interface->load(GameBoy::ID::ROM, stream); - GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, manifest, true); + stream.read(GameBoy::cartridge.romdata, min(GameBoy::cartridge.romsize, stream.size())); } if(id == ID::SuperGameBoyRAM) { - stream.read(GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize); + stream.read(GameBoy::cartridge.ramdata, min(GameBoy::cartridge.ramsize, stream.size())); } if(id == ID::BsxFlashROM) { @@ -132,11 +139,11 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest) } if(id == ID::SufamiTurboSlotARAM) { - sufamiturbo.slotA.ram.copy(stream); + stream.read(sufamiturbo.slotA.ram.data(), min(sufamiturbo.slotA.ram.size(), stream.size())); } if(id == ID::SufamiTurboSlotBRAM) { - sufamiturbo.slotB.ram.copy(stream); + stream.read(sufamiturbo.slotB.ram.data(), min(sufamiturbo.slotB.ram.size(), stream.size())); } } @@ -284,13 +291,15 @@ Interface::Interface() { information.overscan = true; information.aspectRatio = 8.0 / 7.0; information.resettable = true; + information.capability.states = true; + information.capability.cheats = true; firmware.append({ID::IPLROM, "Super Famicom", "sys", "spc700.rom"}); - media.append({ID::SuperFamicom, "Super Famicom", "sys", "program.rom", "sfc"}); - media.append({ID::SuperFamicom, "Super Game Boy", "sfc", "program.rom", "gb" }); - media.append({ID::SuperFamicom, "BS-X Satellaview", "sfc", "program.rom", "bs" }); - media.append({ID::SuperFamicom, "Sufami Turbo", "sfc", "program.rom", "st" }); + media.append({ID::SuperFamicom, "Super Famicom", "sys", "sfc"}); + media.append({ID::SuperFamicom, "Super Game Boy", "sfc", "gb" }); + media.append({ID::SuperFamicom, "BS-X Satellaview", "sfc", "bs" }); + media.append({ID::SuperFamicom, "Sufami Turbo", "sfc", "st" }); { Device device{0, ID::Port1 | ID::Port2, "Controller"}; diff --git a/bsnes/sfc/interface/interface.hpp b/bsnes/sfc/interface/interface.hpp index e1c0f704..2b6bebcb 100755 --- a/bsnes/sfc/interface/interface.hpp +++ b/bsnes/sfc/interface/interface.hpp @@ -4,10 +4,14 @@ namespace SuperFamicom { struct ID { enum : unsigned { + //cartridges SuperFamicom, - }; + SuperGameBoy, + Satellaview, + SufamiTurboSlotA, + SufamiTurboSlotB, - enum : unsigned { + //memory IPLROM, ROM, @@ -32,9 +36,8 @@ struct ID { SufamiTurboSlotBROM, SufamiTurboSlotARAM, SufamiTurboSlotBRAM, - }; - enum : unsigned { + //controller ports Port1 = 1, Port2 = 2, }; diff --git a/bsnes/target-ethos/ethos.cpp b/bsnes/target-ethos/ethos.cpp index 716ae8ce..555cd550 100755 --- a/bsnes/target-ethos/ethos.cpp +++ b/bsnes/target-ethos/ethos.cpp @@ -27,7 +27,7 @@ void Application::commandLineLoad(string pathname) { for(auto &emulator : this->emulator) { for(auto &media : emulator->media) { if(media.type != "sys") continue; - if(type != media.extension) continue; + if(type != media.path) continue; return utility->loadMedia(emulator, media, pathname); } } diff --git a/bsnes/target-ethos/general/browser.cpp b/bsnes/target-ethos/general/browser.cpp index f14385b4..a385321b 100755 --- a/bsnes/target-ethos/general/browser.cpp +++ b/bsnes/target-ethos/general/browser.cpp @@ -67,7 +67,7 @@ void Browser::bootstrap() { for(auto &media : emulator->media) { bool found = false; for(auto &folder : folderList) { - if(folder.extension == media.extension) { + if(folder.extension == media.path) { found = true; break; } @@ -75,7 +75,7 @@ void Browser::bootstrap() { if(found == true) continue; Folder folder; - folder.extension = media.extension; + folder.extension = media.path; folder.path = application->basepath; folder.selection = 0; folderList.append(folder); diff --git a/bsnes/target-ethos/general/presentation.cpp b/bsnes/target-ethos/general/presentation.cpp index 94632922..cdd421fe 100755 --- a/bsnes/target-ethos/general/presentation.cpp +++ b/bsnes/target-ethos/general/presentation.cpp @@ -28,9 +28,19 @@ void Presentation::synchronize() { synchronizeVideo.setChecked(config->video.synchronize); synchronizeAudio.setChecked(config->audio.synchronize); muteAudio.setChecked(config->audio.mute); - toolsMenu.setVisible(application->active); - synchronizeTime.setVisible(application->active && system().rtc()); - resizeWindow.setVisible(config->video.scaleMode != 2); + + if(application->active == nullptr) { + toolsMenu.setVisible(false); + } else { + toolsMenu.setVisible(true); + saveStateMenu.setVisible(system().information.capability.states); + loadStateMenu.setVisible(system().information.capability.states); + stateMenuSeparator.setVisible(system().information.capability.states); + resizeWindow.setVisible(config->video.scaleMode != 2); + stateManager.setVisible(system().information.capability.states); + cheatEditor.setVisible(system().information.capability.cheats); + synchronizeTime.setVisible(system().rtc()); + } } void Presentation::setSystemName(const string &name) { @@ -69,10 +79,10 @@ Presentation::Presentation() : active(nullptr) { for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n}); loadStateMenu.setText("Load State"); for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n}); - synchronizeTime.setText("Synchronize Time"); resizeWindow.setText("Resize Window"); - cheatEditor.setText("Cheat Editor"); stateManager.setText("State Manager"); + cheatEditor.setText("Cheat Editor"); + synchronizeTime.setText("Synchronize Time"); append(loadMenu); for(auto &item : loadListSystem) loadMenu.append(*item); @@ -97,9 +107,8 @@ Presentation::Presentation() : active(nullptr) { for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]); toolsMenu.append(loadStateMenu); for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]); - toolsMenu.append(synchronizeTime); toolsMenu.append(stateMenuSeparator); - toolsMenu.append(resizeWindow, cheatEditor, stateManager); + toolsMenu.append(resizeWindow, stateManager, cheatEditor, synchronizeTime); append(layout); layout.append(viewport, {0, 0, 720, 480}); @@ -120,10 +129,10 @@ Presentation::Presentation() : active(nullptr) { configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); }; for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); }; for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); }; - synchronizeTime.onActivate = [&] { system().rtcsync(); }; resizeWindow.onActivate = [&] { utility->resize(true); }; - cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); }; stateManager.onActivate = [&] { ::stateManager->setVisible(); }; + cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); }; + synchronizeTime.onActivate = [&] { system().rtcsync(); }; synchronize(); } diff --git a/bsnes/target-ethos/general/presentation.hpp b/bsnes/target-ethos/general/presentation.hpp index 597083a3..1a44aa32 100755 --- a/bsnes/target-ethos/general/presentation.hpp +++ b/bsnes/target-ethos/general/presentation.hpp @@ -43,11 +43,11 @@ struct Presentation : Window { Item saveStateItem[5]; Menu loadStateMenu; Item loadStateItem[5]; - Item synchronizeTime; Separator stateMenuSeparator; Item resizeWindow; - Item cheatEditor; Item stateManager; + Item cheatEditor; + Item synchronizeTime; void synchronize(); void setSystemName(const string &name); diff --git a/bsnes/target-ethos/input/input.cpp b/bsnes/target-ethos/input/input.cpp index e731fe2a..365565b5 100755 --- a/bsnes/target-ethos/input/input.cpp +++ b/bsnes/target-ethos/input/input.cpp @@ -102,7 +102,7 @@ int16_t DigitalInput::poll() { // -bool AnalogInput::bind(unsigned scancode, int16_t value) { +bool RelativeInput::bind(unsigned scancode, int16_t value) { using nall::Keyboard; using nall::Mouse; @@ -118,16 +118,9 @@ bool AnalogInput::bind(unsigned scancode, int16_t value) { if(Joypad::isAnyAxis(scancode)) return append(encode); return false; - -append: - if(mapping.position(encode)) return false; //mapping already bound - if(mapping.empty() || mapping == "None") mapping = encode; //remove "None" - else mapping.append(",", encode); //add to existing mapping list - AbstractInput::bind(); - return true; } -int16_t AnalogInput::poll() { +int16_t RelativeInput::poll() { int16_t result = 0; for(auto &item : inputList) { @@ -144,6 +137,76 @@ int16_t AnalogInput::poll() { // +bool AbsoluteInput::bind(unsigned scancode, int16_t value) { + using nall::Keyboard; + using nall::Mouse; + + if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) { + inputList.reset(); + mapping = "None"; + return true; + } + + string encode = Scancode::encode(scancode); + + if(Mouse::isAnyAxis(scancode)) { + //only one input can be assigned for absolute positioning + inputList.reset(); + mapping = encode; + return true; + } + + return false; +} + +int16_t AbsoluteInput::poll() { + int16_t result = -32768; //offscreen value + + using nall::Mouse; + + Position position = phoenix::Mouse::position(); + Geometry geometry = presentation->geometry(); + + if(position.x < geometry.x + || position.y < geometry.y + || position.x >= geometry.x + geometry.width + || position.y >= geometry.y + geometry.height) { + //cursor is offscreen + position.x = -32768; + position.y = -32768; + } else { + //convert from screen to viewport coordinates + double x = position.x - geometry.x; + double y = position.y - geometry.y; + + //scale coordinate range to -0.5 to +0.5 (0.0 = center) + x = x * 1.0 / geometry.width - 0.5; + y = y * 1.0 / geometry.height - 0.5; + + //scale coordinates to -32767 to +32767 + signed px = (signed)(x * 65535.0); + signed py = (signed)(y * 65535.0); + + //clamp to valid range + position.x = max(-32767, min(+32767, px)); + position.y = max(-32767, min(+32767, py)); + } + + for(auto &item : inputList) { + if(item.scancode == mouse(0)[Mouse::Xaxis]) { + result = position.x; + } + + if(item.scancode == mouse(0)[Mouse::Yaxis]) { + result = position.y; + } + } + + return result; +} + +// + HotkeyInput::HotkeyInput() { logic = 1; //AND inputManager->hotkeyMap.append(this); @@ -201,8 +264,9 @@ void InputManager::bootstrap() { AbstractInput *abstract = nullptr; if(input.type == 0) abstract = new DigitalInput; - if(input.type == 1) abstract = new AnalogInput; - if(input.type >= 2) continue; + if(input.type == 1) abstract = new RelativeInput; + if(input.type == 2) abstract = new AbsoluteInput; + if(input.type >= 3) continue; abstract->name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name}; abstract->name.replace(" ", ""); diff --git a/bsnes/target-ethos/input/input.hpp b/bsnes/target-ethos/input/input.hpp index 9a982f58..c6f7e2b3 100755 --- a/bsnes/target-ethos/input/input.hpp +++ b/bsnes/target-ethos/input/input.hpp @@ -23,7 +23,13 @@ struct DigitalInput : AbstractInput { int16_t poll(); }; -struct AnalogInput : AbstractInput { +struct RelativeInput : AbstractInput { + using AbstractInput::bind; + bool bind(unsigned scancode, int16_t value); + int16_t poll(); +}; + +struct AbsoluteInput : AbstractInput { using AbstractInput::bind; bool bind(unsigned scancode, int16_t value); int16_t poll(); diff --git a/bsnes/target-ethos/interface/interface.cpp b/bsnes/target-ethos/interface/interface.cpp index 68368d3e..9b8ecc62 100755 --- a/bsnes/target-ethos/interface/interface.cpp +++ b/bsnes/target-ethos/interface/interface.cpp @@ -1,8 +1,8 @@ #include "../ethos.hpp" Interface *interface = nullptr; -void Interface::loadRequest(unsigned id, const string &name, const string &type, const string &path) { - return utility->loadRequest(id, name, type, path); +void Interface::loadRequest(unsigned id, const string &name, const string &type) { + return utility->loadRequest(id, name, type); } void Interface::loadRequest(unsigned id, const string &path) { diff --git a/bsnes/target-ethos/interface/interface.hpp b/bsnes/target-ethos/interface/interface.hpp index 9fc581c3..8d449c9a 100755 --- a/bsnes/target-ethos/interface/interface.hpp +++ b/bsnes/target-ethos/interface/interface.hpp @@ -1,7 +1,7 @@ struct Interface : Emulator::Interface::Bind { + void loadRequest(unsigned id, const string &name, const string &type); void loadRequest(unsigned id, const string &path); void saveRequest(unsigned id, const string &path); - void loadRequest(unsigned id, const string &name, const string &type, const string &path); uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue); void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height); void audioSample(int16_t lsample, int16_t rsample); diff --git a/bsnes/target-ethos/settings/input.cpp b/bsnes/target-ethos/settings/input.cpp index 3c4bdd04..9ccb155b 100755 --- a/bsnes/target-ethos/settings/input.cpp +++ b/bsnes/target-ethos/settings/input.cpp @@ -72,7 +72,8 @@ void InputSettings::synchronize() { assign[2].setVisible(true); } - if(dynamic_cast(selectedInput)) { + if(dynamic_cast(selectedInput) + || dynamic_cast(selectedInput)) { assign[0].setText("Mouse X-axis"); assign[1].setText("Mouse Y-axis"); assign[0].setVisible(true); @@ -170,7 +171,8 @@ void InputSettings::assignMouseInput(unsigned n) { return inputEvent(mouse(0).button(n), 1, true); } - if(dynamic_cast(activeInput)) { + if(dynamic_cast(activeInput) + || dynamic_cast(activeInput)) { return inputEvent(mouse(0).axis(n), 1, true); } } diff --git a/bsnes/target-ethos/utility/utility.cpp b/bsnes/target-ethos/utility/utility.cpp index caf489f9..6faec475 100755 --- a/bsnes/target-ethos/utility/utility.cpp +++ b/bsnes/target-ethos/utility/utility.cpp @@ -11,7 +11,7 @@ void Utility::setInterface(Emulator::Interface *emulator) { void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media) { string pathname; if(media.type != "sys") pathname = application->path({media.name, ".", media.type, "/"}); - if(!directory::exists(pathname)) pathname = browser->select({"Load ", media.name}, media.extension); + if(!directory::exists(pathname)) pathname = browser->select({"Load ", media.name}, media.path); if(!directory::exists(pathname)) return; if(!file::exists({pathname, "manifest.xml"})) return; loadMedia(emulator, media, pathname); @@ -35,7 +35,7 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi } //request from emulation core to load non-volatile media folder -void Utility::loadRequest(unsigned id, const string &name, const string &type, const string &path) { +void Utility::loadRequest(unsigned id, const string &name, const string &type) { string pathname = browser->select({"Load ", name}, type); if(pathname.empty()) return; this->path(system().group(id)) = pathname; @@ -43,8 +43,7 @@ void Utility::loadRequest(unsigned id, const string &name, const string &type, c string manifest; manifest.readfile({pathname, "manifest.xml"}); - mmapstream stream({pathname, path}); - system().load(id, stream, manifest); + system().load(id, manifest); } //request from emulation core to load non-volatile media file @@ -62,29 +61,6 @@ void Utility::saveRequest(unsigned id, const string &path) { return system().save(id, stream); } -void Utility::loadMemory() { -// for(auto &memory : system().memory) { -// string pathname = path(system().group(memory.id)); -// if(file::exists({pathname, memory.name}) == false) continue; -// filestream fs({pathname, memory.name}); -// system().load(memory.id, fs); -// } - - cheatEditor->load({pathname[0], "cheats.xml"}); - stateManager->load({pathname[0], "bsnes/states.bsa"}, 1); -} - -void Utility::saveMemory() { -// for(auto &memory : system().memory) { -// string pathname = path(system().group(memory.id)); -// filestream fs({pathname, memory.name}, file::mode::write); -// system().save(memory.id, fs); -// } - - cheatEditor->save({pathname[0], "cheats.xml"}); - stateManager->save({pathname[0], "bsnes/states.bsa"}, 1); -} - void Utility::connect(unsigned port, unsigned device) { if(application->active == nullptr) return; system().connect(port, device); @@ -110,7 +86,8 @@ void Utility::load() { title.rtrim<1>(" + "); presentation->setTitle(title); - loadMemory(); + cheatEditor->load({pathname[0], "cheats.xml"}); + stateManager->load({pathname[0], "bsnes/states.bsa"}, 1); system().paletteUpdate(); synchronizeDSP(); @@ -124,7 +101,9 @@ void Utility::unload() { if(application->active == nullptr) return; if(tracerEnable) tracerToggle(); - saveMemory(); + cheatEditor->save({pathname[0], "cheats.xml"}); + stateManager->save({pathname[0], "bsnes/states.bsa"}, 1); + system().unload(); path.reset(); pathname.reset(); @@ -135,8 +114,9 @@ void Utility::unload() { video.clear(); audio.clear(); presentation->setTitle({Emulator::Name, " v", Emulator::Version}); - cheatEditor->synchronize(); - stateManager->synchronize(); + cheatDatabase->setVisible(false); + cheatEditor->setVisible(false); + stateManager->setVisible(false); } void Utility::saveState(unsigned slot) { diff --git a/bsnes/target-ethos/utility/utility.hpp b/bsnes/target-ethos/utility/utility.hpp index f8f17708..dc5f3a11 100755 --- a/bsnes/target-ethos/utility/utility.hpp +++ b/bsnes/target-ethos/utility/utility.hpp @@ -4,11 +4,9 @@ struct Utility { void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media); void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname); - void loadRequest(unsigned id, const string &name, const string &type, const string &path); + void loadRequest(unsigned id, const string &name, const string &type); void loadRequest(unsigned id, const string &path); void saveRequest(unsigned id, const string &path); - void loadMemory(); - void saveMemory(); void connect(unsigned port, unsigned device); void power();