diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 3aea4054..8266a662 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.16"; + static const string Version = "106.17"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/systems/Pocket Challenge V2.sys/manifest.bml b/higan/systems/Pocket Challenge V2.sys/manifest.bml new file mode 100644 index 00000000..5a24ecb5 --- /dev/null +++ b/higan/systems/Pocket Challenge V2.sys/manifest.bml @@ -0,0 +1,2 @@ +system name:Pocket Challenge V2 + eeprom name=internal.ram size=128 diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index dedc6458..4d0bda6f 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -6,7 +6,7 @@ unique_pointer presentation; Presentation::Presentation() { presentation = this; - libraryMenu.setText("Systems"); + systemsMenu.setText("Systems"); systemMenu.setVisible(false); resetSystem.setText("Soft Reset").onActivate([&] { program->softReset(); }); @@ -67,10 +67,10 @@ Presentation::Presentation() { statusBar.setVisible(showStatusBar.checked()); if(visible()) resizeViewport(); }); - showSystemSettings.setText("Systems ...").onActivate([&] { settingsManager->show(0); }); - showVideoSettings.setText("Video ...").onActivate([&] { settingsManager->show(1); }); - showAudioSettings.setText("Audio ...").onActivate([&] { settingsManager->show(2); }); - showInputSettings.setText("Input ...").onActivate([&] { + showSystemSettings.setIcon(Icon::Device::Storage).setText("Systems ...").onActivate([&] { settingsManager->show(0); }); + showVideoSettings.setIcon(Icon::Device::Display).setText("Video ...").onActivate([&] { settingsManager->show(1); }); + showAudioSettings.setIcon(Icon::Device::Speaker).setText("Audio ...").onActivate([&] { settingsManager->show(2); }); + showInputSettings.setIcon(Icon::Device::Joypad).setText("Input ...").onActivate([&] { if(emulator) { //default input panel to current core's input settings for(auto item : settingsManager->input.emulatorList.items()) { @@ -83,8 +83,8 @@ Presentation::Presentation() { } settingsManager->show(3); }); - showHotkeySettings.setText("Hotkeys ...").onActivate([&] { settingsManager->show(4); }); - showAdvancedSettings.setText("Advanced ...").onActivate([&] { settingsManager->show(5); }); + showHotkeySettings.setIcon(Icon::Device::Keyboard).setText("Hotkeys ...").onActivate([&] { settingsManager->show(4); }); + showAdvancedSettings.setIcon(Icon::Action::Settings).setText("Advanced ...").onActivate([&] { settingsManager->show(5); }); toolsMenu.setText("Tools").setVisible(false); saveQuickStateMenu.setText("Save Quick State"); @@ -100,19 +100,19 @@ Presentation::Presentation() { loadSlot4.setText("Slot 4").onActivate([&] { program->loadState(4); }); loadSlot5.setText("Slot 5").onActivate([&] { program->loadState(5); }); pauseEmulation.setText("Pause Emulation").onToggle([&] { program->togglePause(); }); - cheatEditor.setText("Cheat Editor ...").onActivate([&] { toolsManager->show(0); }); - stateManager.setText("State Manager ...").onActivate([&] { toolsManager->show(1); }); - manifestViewer.setText("Manifest Viewer ...").onActivate([&] { toolsManager->show(2); }); - gameNotes.setText("Game Notes ...").onActivate([&] { toolsManager->show(3); }); + cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsManager->show(0); }); + stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsManager->show(1); }); + manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsManager->show(2); }); + gameNotes.setIcon(Icon::Emblem::Text).setText("Game Notes ...").onActivate([&] { toolsManager->show(3); }); helpMenu.setText("Help"); - documentation.setText("Documentation ...").onActivate([&] { + documentation.setIcon(Icon::Application::Browser).setText("Documentation ...").onActivate([&] { invoke("https://doc.byuu.org/higan/"); }); - credits.setText("Credits ...").onActivate([&] { + credits.setIcon(Icon::Application::Browser).setText("Credits ...").onActivate([&] { invoke("https://doc.byuu.org/higan/credits/"); }); - about.setText("About ...").onActivate([&] { + about.setIcon(Icon::Prompt::Question).setText("About ...").onActivate([&] { aboutWindow->setVisible().setFocused(); }); @@ -304,32 +304,34 @@ auto Presentation::toggleFullScreen() -> void { } auto Presentation::loadSystems() -> void { - libraryMenu.reset(); + systemsMenu.reset(); for(auto system : settings.find("Systems/System")) { - if(!system["Show"].boolean()) continue; + if(!system["Visible"].boolean()) continue; MenuItem item; - string boot = system["Boot"].text(); - item.setText({system["Name"].text(), " ..."}).onActivate([=] { - bool booted = false; + string name = system.text(); + string filename = system["Load"].text(); + string load = Location::base(filename).trimRight("/", 1L); + string alias = system["Alias"].text(); + item + .setIcon(load ? Icon::Emblem::Folder : Icon::Device::Storage) + .setText({alias ? alias : load ? load : name, " ..."}).onActivate([=] { for(auto& emulator : program->emulators) { - if(boot == emulator->information.name) { + if(name == emulator->information.name) { + if(filename) program->mediumQueue.append(filename); program->loadMedium(*emulator, emulator->media(0)); - booted = true; break; } } - if(!booted && directory::exists(boot)) { - program->mediumQueue.append(boot); - program->loadMedium(); - } }); - libraryMenu.append(item); + systemsMenu.append(item); } //add icarus menu option -- but only if icarus binary is present if(execute("icarus", "--name").output.strip() == "icarus") { - if(libraryMenu.actionCount()) libraryMenu.append(MenuSeparator()); - libraryMenu.append(MenuItem().setText("Load ROM File ...").onActivate([&] { + if(systemsMenu.actionCount()) systemsMenu.append(MenuSeparator()); + systemsMenu.append(MenuItem() + .setIcon(Icon::Emblem::File) + .setText("Load ROM File ...").onActivate([&] { audio->clear(); if(auto location = execute("icarus", "--import")) { program->mediumQueue.append(location.output.strip()); diff --git a/higan/target-tomoko/presentation/presentation.hpp b/higan/target-tomoko/presentation/presentation.hpp index f790818c..670fb12f 100644 --- a/higan/target-tomoko/presentation/presentation.hpp +++ b/higan/target-tomoko/presentation/presentation.hpp @@ -18,7 +18,7 @@ struct Presentation : Window { auto loadShaders() -> void; MenuBar menuBar{this}; - Menu libraryMenu{&menuBar}; + Menu systemsMenu{&menuBar}; Menu systemMenu{&menuBar}; Menu inputPort1{&systemMenu}; Menu inputPort2{&systemMenu}; @@ -74,6 +74,7 @@ struct Presentation : Window { Menu helpMenu{&menuBar}; MenuItem documentation{&helpMenu}; MenuItem credits{&helpMenu}; + MenuSeparator helpMenuSeparator{&helpMenu}; MenuItem about{&helpMenu}; FixedLayout layout{this}; diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index a25b4c23..622de498 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -29,6 +29,7 @@ Program::Program(string_vector args) { emulators.append(new MasterSystem::GameGearInterface); emulators.append(new WonderSwan::WonderSwanInterface); emulators.append(new WonderSwan::WonderSwanColorInterface); + emulators.append(new WonderSwan::PocketChallengeV2Interface); new Presentation; presentation->setVisible(); diff --git a/higan/target-tomoko/settings/settings.hpp b/higan/target-tomoko/settings/settings.hpp index 505ccfd0..63bf864e 100644 --- a/higan/target-tomoko/settings/settings.hpp +++ b/higan/target-tomoko/settings/settings.hpp @@ -4,13 +4,16 @@ struct SystemProperties : Window { auto modify(Markup::Node) -> void; VerticalLayout layout{this}; - HorizontalLayout nameLayout{&layout, Size{~0, 0}}; - Label nameLabel{&nameLayout, Size{40, 0}}; - LineEdit nameEdit{&nameLayout, Size{~0, 0}}; - HorizontalLayout bootLayout{&layout, Size{~0, 0}}; - Label bootLabel{&bootLayout, Size{40, 0}}; - ComboEdit bootEdit{&bootLayout, Size{~0, 0}}; - Button bootBrowse{&bootLayout, Size{80, 0}}; + HorizontalLayout systemLayout{&layout, Size{~0, 0}}; + Label systemLabel{&systemLayout, Size{50, 0}}; + ComboButton systemOption{&systemLayout, Size{~0, 0}}; + HorizontalLayout loadLayout{&layout, Size{~0, 0}}; + Label loadLabel{&loadLayout, Size{50, 0}}; + LineEdit loadEdit{&loadLayout, Size{~0, 0}}; + Button loadBrowse{&loadLayout, Size{80, 0}}; + HorizontalLayout aliasLayout{&layout, Size{~0, 0}}; + Label aliasLabel{&aliasLayout, Size{50, 0}}; + LineEdit aliasEdit{&aliasLayout, Size{~0, 0}}; HorizontalLayout controlLayout{&layout, Size{~0, 0}}; Widget spacer{&controlLayout, Size{~0, 0}}; Button acceptButton{&controlLayout, Size{80, 0}}; diff --git a/higan/target-tomoko/settings/system-properties.cpp b/higan/target-tomoko/settings/system-properties.cpp index 1533ab43..66a855f7 100644 --- a/higan/target-tomoko/settings/system-properties.cpp +++ b/higan/target-tomoko/settings/system-properties.cpp @@ -2,13 +2,13 @@ SystemProperties::SystemProperties() { systemProperties = this; layout.setMargin(5); - nameLabel.setText("Name:"); - bootLabel.setText("Boot:"); - bootEdit.setEditable(false); + systemLabel.setAlignment(1.0).setText("System:"); for(auto& emulator : program->emulators) { - bootEdit.append(ComboEditItem().setText(emulator->information.name)); + systemOption.append(ComboButtonItem().setText(emulator->information.name)); } - bootBrowse.setText("Browse ...").onActivate([&] { + loadLabel.setAlignment(1.0).setText("Load:"); + loadEdit.setEditable(false); + loadBrowse.setText("Browse ...").onActivate([&] { string filters = "Games|"; for(auto& emulator : program->emulators) { for(auto& media : emulator->media) { @@ -21,9 +21,10 @@ SystemProperties::SystemProperties() { .setPath(settings["Library/Location"].text()) .setFilters(filters) .openFolder()) { - bootEdit.setText(location); + loadEdit.setText(location); } }); + aliasLabel.setAlignment(1.0).setText("Alias:"); acceptButton.onActivate([&] { setVisible(false); settingsManager->systems.accept(); @@ -39,20 +40,25 @@ SystemProperties::SystemProperties() { auto SystemProperties::append() -> void { setCentered(*settingsManager); - nameEdit.setText(""); - bootEdit.setText(""); + systemOption.item(0).setSelected(); + loadEdit.setText(""); + aliasEdit.setText(""); acceptButton.setText("Append"); setFocused(); setVisible(); - nameEdit.setFocused(); + systemOption.setFocused(); } auto SystemProperties::modify(Markup::Node system) -> void { setCentered(*settingsManager); - nameEdit.setText(system["Name"].text()); - bootEdit.setText(system["Boot"].text()); + systemOption.item(0).setSelected(); + for(auto item : systemOption.items()) { + if(item.text() == system.text()) item.setSelected(); + } + loadEdit.setText(system["Load"].text()); + aliasEdit.setText(system["Alias"].text()); acceptButton.setText("Modify"); setFocused(); setVisible(); - nameEdit.setFocused(); + systemOption.setFocused(); } diff --git a/higan/target-tomoko/settings/systems.cpp b/higan/target-tomoko/settings/systems.cpp index 1bba928d..34a765d2 100644 --- a/higan/target-tomoko/settings/systems.cpp +++ b/higan/target-tomoko/settings/systems.cpp @@ -34,17 +34,20 @@ auto SystemSettings::reload() -> void { systemList.reset(); systemList.append(TableViewHeader().setVisible() .append(TableViewColumn()) - .append(TableViewColumn().setText("Name")) - .append(TableViewColumn().setText("Boot").setExpandable()) + .append(TableViewColumn().setText("System").setExpandable()) ); for(auto system : settings.find("Systems/System")) { - string boot = Location::base(system["Boot"].text()); + string name = system.text(); + string load = Location::base(system["Load"].text()).trimRight("/", 1L); + string alias = system["Alias"].text(); systemList.append(TableViewItem() - .append(TableViewCell().setCheckable().setChecked(system["Show"].boolean())) - .append(TableViewCell().setText(system["Name"].text())) .append(TableViewCell() - .setIcon(boot.endsWith("/") ? Icon::Emblem::Folder : Icon::Device::Storage) - .setText(string{boot}.trimRight("/", 1L)) + .setCheckable() + .setChecked(system["Visible"].boolean()) + ) + .append(TableViewCell() + .setIcon(load ? Icon::Emblem::Folder : Icon::Device::Storage) + .setText(alias ? alias : load ? load : name) ) ); } @@ -55,7 +58,7 @@ auto SystemSettings::reload() -> void { auto SystemSettings::toggle(TableViewCell cell) -> void { if(auto item = cell->parentTableViewItem()) { if(auto system = settings.find("Systems/System")[item->offset()]) { - system("Show").setValue(item->cell(0).checked()); + system("Visible").setValue(item->cell(0).checked()); presentation->loadSystems(); } } @@ -93,8 +96,8 @@ auto SystemSettings::remove() -> void { if(auto item = systemList.selected()) { if(auto system = settings.find("Systems/System")[item.offset()]) { if(MessageDialog().setParent(*settingsManager).setText({ - "Are you sure you want to delete this system?\n\n" - "Name: ", system["Name"].text() + "Are you sure you want to delete this system?\n\n", + item.cell(1).text() }).question() == "Yes") { settings["Systems"].remove(system); reload(); @@ -105,16 +108,17 @@ auto SystemSettings::remove() -> void { auto SystemSettings::accept() -> void { if(systemProperties->acceptButton.text() == "Append") { - Markup::Node system{"System"}; - system.append({"Name", systemProperties->nameEdit.text()}); - system.append({"Boot", systemProperties->bootEdit.text()}); - system.append({"Show", "true"}); + Markup::Node system{"System", systemProperties->systemOption.selected().text()}; + system.append({"Load", systemProperties->loadEdit.text()}); + system.append({"Alias", systemProperties->aliasEdit.text()}); + system.append({"Visible", "true"}); settings["Systems"].append(system); } else if(systemProperties->acceptButton.text() == "Modify") { if(auto item = systemList.selected()) { if(auto system = settings.find("Systems/System")[item.offset()]) { - system("Name").setValue(systemProperties->nameEdit.text()); - system("Boot").setValue(systemProperties->bootEdit.text()); + system.setValue(systemProperties->systemOption.selected().text()); + system("Load").setValue(systemProperties->loadEdit.text()); + system("Alias").setValue(systemProperties->aliasEdit.text()); } } } diff --git a/higan/ws/cartridge/cartridge.cpp b/higan/ws/cartridge/cartridge.cpp index 3ee6d967..61e0e12e 100644 --- a/higan/ws/cartridge/cartridge.cpp +++ b/higan/ws/cartridge/cartridge.cpp @@ -56,6 +56,12 @@ auto Cartridge::load() -> bool { } else return false; } + if(Model::PocketChallengeV2()) { + if(auto loaded = platform->load(ID::PocketChallengeV2, "Pocket Challenge V2", "pc2")) { + information.pathID = loaded.pathID(); + } else return false; + } + if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) { information.manifest = fp->reads(); } else return false; diff --git a/higan/ws/cpu/cpu.cpp b/higan/ws/cpu/cpu.cpp index 25ffdc58..90dcff0d 100644 --- a/higan/ws/cpu/cpu.cpp +++ b/higan/ws/cpu/cpu.cpp @@ -51,7 +51,7 @@ auto CPU::power() -> void { bus.map(this, 0x00a0); bus.map(this, 0x00b0, 0x00b6); - if(!Model::WonderSwan()) { + if(Model::WonderSwanColor() || Model::SwanCrystal()) { bus.map(this, 0x0040, 0x0049); bus.map(this, 0x0062); } diff --git a/higan/ws/cpu/io.cpp b/higan/ws/cpu/io.cpp index 9e809aa8..8b0dee14 100644 --- a/higan/ws/cpu/io.cpp +++ b/higan/ws/cpu/io.cpp @@ -48,10 +48,10 @@ auto CPU::portRead(uint16 addr) -> uint8 { //HW_FLAGS if(addr == 0x00a0) { - bool color = !Model::WonderSwan(); + bool color = Model::WonderSwanColor() || Model::SwanCrystal(); return ( 1 << 0 //0 = BIOS mapped; 1 = cartridge mapped - | color << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal + | color << 1 //0 = WonderSwan or Pocket Challenge V2; 1 = WonderSwan Color or SwanCrystal | 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width | 1 << 7 //1 = built-in self-test passed ); @@ -59,7 +59,7 @@ auto CPU::portRead(uint16 addr) -> uint8 { //INT_BASE if(addr == 0x00b0) return ( - r.interruptBase | (Model::WonderSwan() ? 3 : 0) + r.interruptBase | (Model::WonderSwan() || Model::PocketChallengeV2() ? 3 : 0) ); //SER_DATA @@ -122,7 +122,7 @@ auto CPU::portWrite(uint16 addr, uint8 data) -> void { //INT_BASE if(addr == 0x00b0) { - r.interruptBase = Model::WonderSwan() ? data & ~7 : data & ~1; + r.interruptBase = Model::WonderSwan() || Model::PocketChallengeV2() ? data & ~7 : data & ~1; } //SER_DATA diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index 7fa2f67c..fa9c2992 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -5,6 +5,7 @@ namespace WonderSwan { Settings settings; #include "wonderswan.cpp" #include "wonderswan-color.cpp" +#include "pocket-challenge-v2.cpp" Interface::Interface() { Port hardwarePort{ID::Port::Hardware, "Hardware"}; diff --git a/higan/ws/interface/interface.hpp b/higan/ws/interface/interface.hpp index 1d10137b..079a6b24 100644 --- a/higan/ws/interface/interface.hpp +++ b/higan/ws/interface/interface.hpp @@ -5,6 +5,7 @@ struct ID { System, WonderSwan, WonderSwanColor, + PocketChallengeV2, }; struct Port { enum : uint { @@ -64,6 +65,17 @@ struct WonderSwanColorInterface : Interface { auto load(uint id) -> bool override; }; +struct PocketChallengeV2Interface : Interface { + using Emulator::Interface::load; + + PocketChallengeV2Interface(); + + auto videoColors() -> uint32 override; + auto videoColor(uint32 color) -> uint64 override; + + auto load(uint id) -> bool override; +}; + struct Settings { bool blurEmulation = true; bool colorEmulation = true; diff --git a/higan/ws/interface/pocket-challenge-v2.cpp b/higan/ws/interface/pocket-challenge-v2.cpp new file mode 100644 index 00000000..5be62257 --- /dev/null +++ b/higan/ws/interface/pocket-challenge-v2.cpp @@ -0,0 +1,40 @@ +PocketChallengeV2Interface::PocketChallengeV2Interface() { + information.manufacturer = "Benesse"; + information.name = "Pocket Challenge V2"; + information.overscan = false; + + media.append({ID::PocketChallengeV2, "Pocket Challenge V2", "pc2"}); +} + +//todo: this should be generating grayscale colors +//instead, the PPU is selecting grayscale colors from the color palette + +auto PocketChallengeV2Interface::videoColors() -> uint32 { + return 1 << 12; +} + +auto PocketChallengeV2Interface::videoColor(uint32 color) -> uint64 { + uint b = color.bits(0, 3); + uint g = color.bits(4, 7); + uint r = color.bits(8,11); + + uint64_t R = image::normalize(r, 4, 16); + uint64_t G = image::normalize(g, 4, 16); + uint64_t B = image::normalize(b, 4, 16); + + if(settings.colorEmulation) { + R = (r * 26 + g * 4 + b * 2); + G = ( g * 24 + b * 8); + B = (r * 6 + g * 4 + b * 22); + R = image::normalize(min(480, R), 9, 16); + G = image::normalize(min(480, G), 9, 16); + B = image::normalize(min(480, B), 9, 16); + } + + return R << 32 | G << 16 | B << 0; +} + +auto PocketChallengeV2Interface::load(uint id) -> bool { + if(id == ID::PocketChallengeV2) return system.load(this, System::Model::PocketChallengeV2); + return false; +} diff --git a/higan/ws/memory/memory.cpp b/higan/ws/memory/memory.cpp index 2d46cab6..645805ba 100644 --- a/higan/ws/memory/memory.cpp +++ b/higan/ws/memory/memory.cpp @@ -10,7 +10,7 @@ auto InternalRAM::power() -> void { } auto InternalRAM::serialize(serializer& s) -> void { - s.array(memory, Model::WonderSwan() ? 0x4000 : 0x10000); + s.array(memory, Model::WonderSwan() || Model::PocketChallengeV2() ? 0x4000 : 0x10000); } auto InternalRAM::read(uint16 addr, uint size) -> uint32 { diff --git a/higan/ws/system/system.hpp b/higan/ws/system/system.hpp index ee590d79..136ecd7c 100644 --- a/higan/ws/system/system.hpp +++ b/higan/ws/system/system.hpp @@ -1,5 +1,5 @@ struct System : IO { - enum class Model : uint { WonderSwan, WonderSwanColor, SwanCrystal }; + enum class Model : uint { WonderSwan, WonderSwanColor, SwanCrystal, PocketChallengeV2 }; auto loaded() const -> bool { return _loaded; } auto model() const -> Model { return _model; } @@ -67,3 +67,4 @@ extern System system; auto Model::WonderSwan() -> bool { return system.model() == System::Model::WonderSwan; } auto Model::WonderSwanColor() -> bool { return system.model() == System::Model::WonderSwanColor; } auto Model::SwanCrystal() -> bool { return system.model() == System::Model::SwanCrystal; } +auto Model::PocketChallengeV2() -> bool { return system.model() == System::Model::PocketChallengeV2; } diff --git a/higan/ws/ws.hpp b/higan/ws/ws.hpp index bf055fe4..d50f7ce5 100644 --- a/higan/ws/ws.hpp +++ b/higan/ws/ws.hpp @@ -32,9 +32,10 @@ namespace WonderSwan { }; struct Model { - inline static auto WonderSwan() -> bool; //SW-001 (ASWAN) - inline static auto WonderSwanColor() -> bool; //WSC-001 (SPHINX) - inline static auto SwanCrystal() -> bool; //SCT-001 (SPHINX2) + inline static auto WonderSwan() -> bool; //SW-001 (ASWAN) + inline static auto WonderSwanColor() -> bool; //WSC-001 (SPHINX) + inline static auto SwanCrystal() -> bool; //SCT-001 (SPHINX2) + inline static auto PocketChallengeV2() -> bool; //(ASWAN) }; #include diff --git a/icarus/core/core.cpp b/icarus/core/core.cpp index a6358034..8a3fd306 100644 --- a/icarus/core/core.cpp +++ b/icarus/core/core.cpp @@ -11,6 +11,7 @@ Icarus::Icarus() { Database::GameGear = BML::unserialize(string::read(locate("Database/Game Gear.bml"))); Database::WonderSwan = BML::unserialize(string::read(locate("Database/WonderSwan.bml"))); Database::WonderSwanColor = BML::unserialize(string::read(locate("Database/WonderSwan Color.bml"))); + Database::PocketChallengeV2 = BML::unserialize(string::read(locate("Database/Pocket Challenge V2.bml"))); Database::BSMemory = BML::unserialize(string::read(locate("Database/BS Memory.bml"))); Database::SufamiTurbo = BML::unserialize(string::read(locate("Database/Sufami Turbo.bml"))); } @@ -50,6 +51,7 @@ auto Icarus::manifest(string location) -> string { if(type == ".gg") return gameGearManifest(location); if(type == ".ws") return wonderSwanManifest(location); if(type == ".wsc") return wonderSwanColorManifest(location); + if(type == ".pc2") return pocketChallengeV2Manifest(location); if(type == ".bs") return bsMemoryManifest(location); if(type == ".st") return sufamiTurboManifest(location); @@ -93,6 +95,7 @@ auto Icarus::import(string location) -> string { if(type == ".gg") return gameGearImport(buffer, location); if(type == ".ws") return wonderSwanImport(buffer, location); if(type == ".wsc") return wonderSwanColorImport(buffer, location); + if(type == ".pc2") return pocketChallengeV2Import(buffer, location); if(type == ".bs") return bsMemoryImport(buffer, location); if(type == ".st") return sufamiTurboImport(buffer, location); diff --git a/icarus/core/core.hpp b/icarus/core/core.hpp index 4686420d..98d8b558 100644 --- a/icarus/core/core.hpp +++ b/icarus/core/core.hpp @@ -96,6 +96,11 @@ struct Icarus { auto wonderSwanColorManifest(vector& buffer, string location) -> string; auto wonderSwanColorImport(vector& buffer, string location) -> string; + //pocket-challenge-v2.cpp + auto pocketChallengeV2Manifest(string location) -> string; + auto pocketChallengeV2Manifest(vector& buffer, string location) -> string; + auto pocketChallengeV2Import(vector& buffer, string location) -> string; + //bs-memory.cpp auto bsMemoryManifest(string location) -> string; auto bsMemoryManifest(vector& buffer, string location) -> string; @@ -124,6 +129,7 @@ namespace Database { Markup::Node GameGear; Markup::Node WonderSwan; Markup::Node WonderSwanColor; + Markup::Node PocketChallengeV2; Markup::Node BSMemory; Markup::Node SufamiTurbo; }; diff --git a/icarus/core/pocket-challenge-v2.cpp b/icarus/core/pocket-challenge-v2.cpp new file mode 100644 index 00000000..6028166b --- /dev/null +++ b/icarus/core/pocket-challenge-v2.cpp @@ -0,0 +1,39 @@ +auto Icarus::pocketChallengeV2Manifest(string location) -> string { + vector buffer; + concatenate(buffer, {location, "program.rom"}); + return pocketChallengeV2Manifest(buffer, location); +} + +auto Icarus::pocketChallengeV2Manifest(vector& buffer, string location) -> string { + if(settings["icarus/UseDatabase"].boolean()) { + auto digest = Hash::SHA256(buffer).digest(); + for(auto game : Database::PocketChallengeV2.find("game")) { + if(game["sha256"].text() == digest) return BML::serialize(game); + } + } + + if(settings["icarus/UseHeuristics"].boolean()) { + Heuristics::WonderSwan game{buffer, location}; + if(auto manifest = game.manifest()) return manifest; + } + + return {}; +} + +auto Icarus::pocketChallengeV2Import(vector& buffer, string location) -> string { + auto name = Location::prefix(location); + auto source = Location::path(location); + string target{settings["Library/Location"].text(), "Pocket Challenge V2/", name, ".pc2/"}; + + auto manifest = pocketChallengeV2Manifest(buffer, location); + if(!manifest) return failure("failed to parse ROM image"); + + if(!create(target)) return failure("library path unwritable"); + if(exists({source, name, ".sav"}) && !exists({target, "save.ram"})) { + copy({source, name, ".sav"}, {target, "save.ram"}); + } + + if(settings["icarus/CreateManifests"].boolean()) write({target, "manifest.bml"}, manifest); + write({target, "program.rom"}, buffer); + return success(target); +} diff --git a/icarus/icarus.cpp b/icarus/icarus.cpp index db2e032e..6f4be737 100644 --- a/icarus/icarus.cpp +++ b/icarus/icarus.cpp @@ -44,6 +44,7 @@ Settings settings; #include "core/game-gear.cpp" #include "core/wonderswan.cpp" #include "core/wonderswan-color.cpp" +#include "core/pocket-challenge-v2.cpp" #include "core/bs-memory.cpp" #include "core/sufami-turbo.cpp" @@ -92,6 +93,7 @@ auto nall::main(string_vector args) -> void { "*.gg:" "*.ws:" "*.wsc:" + "*.pc2:" "*.bs:" "*.st:" "*.zip" diff --git a/icarus/ui/scan-dialog.cpp b/icarus/ui/scan-dialog.cpp index aa0d4f43..b84bf78c 100644 --- a/icarus/ui/scan-dialog.cpp +++ b/icarus/ui/scan-dialog.cpp @@ -110,6 +110,9 @@ auto ScanDialog::gamePakType(const string& type) -> bool { || type == ".gbc" || type == ".gba" || type == ".gg" + || type == ".ws" + || type == ".wsc" + || type == ".pc2" || type == ".bs" || type == ".st"; } @@ -126,6 +129,9 @@ auto ScanDialog::gameRomType(const string& type) -> bool { || type == ".gbc" || type == ".gba" || type == ".gg" + || type == ".ws" + || type == ".wsc" + || type == ".pc2" || type == ".bs" || type == ".st"; }