From 6adfe7183651abd3ba53bb4bb125e32bc7a63562 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 19 Nov 2015 20:27:56 +1100 Subject: [PATCH] Update to icarus 20151117. byuu says: This release adds a settings dialog that lets you control the library path, optionally generate manifest.bml files, and optionally bypass the internal games database (so far this is only the US SNES set.) Also, the settings.bml file can exist in the same folder as the binary now (portable mode). Plus it can share the same config file as higan/tomoko itself does. This will allow you to change the library location in either program and have it affect the other program as well. It's a bit hackish, but it works >_> Note: don't use this with higan v095.06 or earlier, or bad things will happen. --- icarus/core/bsx-satellaview.cpp | 8 ++++---- icarus/core/famicom.cpp | 6 +++--- icarus/core/game-boy-advance.cpp | 6 +++--- icarus/core/game-boy-color.cpp | 6 +++--- icarus/core/game-boy.cpp | 6 +++--- icarus/core/sufami-turbo.cpp | 8 ++++---- icarus/core/super-famicom.cpp | 12 ++++++------ icarus/icarus.cpp | 10 ++++++++++ icarus/settings.cpp | 33 +++++++++++++------------------- icarus/ui/scan-dialog.cpp | 19 ++++++++++-------- icarus/ui/settings-dialog.cpp | 24 +++++++++++++++++++++++ icarus/ui/ui.hpp | 18 +++++++++++++++-- 12 files changed, 100 insertions(+), 56 deletions(-) create mode 100644 icarus/ui/settings-dialog.cpp diff --git a/icarus/core/bsx-satellaview.cpp b/icarus/core/bsx-satellaview.cpp index 3ec5cca3..9d23a939 100644 --- a/icarus/core/bsx-satellaview.cpp +++ b/icarus/core/bsx-satellaview.cpp @@ -20,12 +20,12 @@ auto Icarus::bsxSatellaviewManifest(vector& buffer, const string& locat auto Icarus::bsxSatellaviewImport(vector& buffer, const string& location) -> bool { auto name = prefixname(location); auto source = pathname(location); - string target{settings.libraryPath, "BS-X Satellaview/", name, ".bs/"}; + string target{settings["Library/Location"].text(), "BS-X Satellaview/", name, ".bs/"}; //if(directory::exists(target)) return failure("game already exists"); string markup; - if(settings.useDatabase && !markup) { + if(settings["icarus/UseDatabase"].boolean() && !markup) { auto digest = Hash::SHA256(buffer.data(), buffer.size()).digest(); for(auto node : database.bsxSatellaview) { if(node.name() != "release") continue; @@ -37,7 +37,7 @@ auto Icarus::bsxSatellaviewImport(vector& buffer, const string& locatio } } - if(settings.useHeuristics && !markup) { + if(settings["icarus/UseHeuristics"].boolean() && !markup) { BsxSatellaviewCartridge cartridge{buffer.data(), buffer.size()}; if(markup = cartridge.markup) { markup.append("\n"); @@ -50,7 +50,7 @@ auto Icarus::bsxSatellaviewImport(vector& buffer, const string& locatio if(!markup) return failure("failed to parse ROM image"); if(!directory::create(target)) return failure("library path unwritable"); - if(settings.createManifests) file::write({target, "manifest.bml"}, markup); + if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); return success(); } diff --git a/icarus/core/famicom.cpp b/icarus/core/famicom.cpp index 25e1a46c..738a4faf 100644 --- a/icarus/core/famicom.cpp +++ b/icarus/core/famicom.cpp @@ -22,12 +22,12 @@ auto Icarus::famicomManifest(vector& buffer, const string& location) -> auto Icarus::famicomImport(vector& buffer, const string& location) -> bool { auto name = prefixname(location); auto source = pathname(location); - string target{settings.libraryPath, "Famicom/", name, ".fc/"}; + string target{settings["Library/Location"].text(), "Famicom/", name, ".fc/"}; //if(directory::exists(target)) return failure("game already exists"); string markup; -//if(settings.useHeuristics && !markup) { +//if(settings["icarus/UseHeuristics"].boolean() && !markup) { FamicomCartridge cartridge{buffer.data(), buffer.size()}; if(markup = cartridge.markup) { markup.append("\n"); @@ -40,7 +40,7 @@ auto Icarus::famicomImport(vector& buffer, const string& location) -> b if(!markup) return failure("failed to parse ROM image"); if(!directory::create(target)) return failure("library path unwritable"); - if(settings.createManifests) file::write({target, "manifest.bml"}, markup); + if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "ines.rom"}, buffer.data(), 16); file::write({target, "program.rom"}, buffer.data() + 16, cartridge.prgrom); if(!cartridge.chrrom) return success(); diff --git a/icarus/core/game-boy-advance.cpp b/icarus/core/game-boy-advance.cpp index b0d46d99..aab3f7a3 100644 --- a/icarus/core/game-boy-advance.cpp +++ b/icarus/core/game-boy-advance.cpp @@ -20,12 +20,12 @@ auto Icarus::gameBoyAdvanceManifest(vector& buffer, const string& locat auto Icarus::gameBoyAdvanceImport(vector& buffer, const string& location) -> bool { auto name = prefixname(location); auto source = pathname(location); - string target{settings.libraryPath, "Game Boy Advance/", name, ".gba/"}; + string target{settings["Library/Location"].text(), "Game Boy Advance/", name, ".gba/"}; //if(directory::exists(target)) return failure("game already exists"); string markup; - if(settings.useHeuristics && !markup) { + if(settings["icarus/UseHeuristics"].boolean() && !markup) { GameBoyAdvanceCartridge cartridge{buffer.data(), buffer.size()}; if(markup = cartridge.markup) { markup.append("\n"); @@ -38,7 +38,7 @@ auto Icarus::gameBoyAdvanceImport(vector& buffer, const string& locatio if(!markup) return failure("failed to parse ROM image"); if(!directory::create(target)) return failure("library path unwritable"); - if(settings.createManifests) file::write({target, "manifest.bml"}, markup); + if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); return success(); } diff --git a/icarus/core/game-boy-color.cpp b/icarus/core/game-boy-color.cpp index 4f522f1e..b7ede8dc 100644 --- a/icarus/core/game-boy-color.cpp +++ b/icarus/core/game-boy-color.cpp @@ -20,12 +20,12 @@ auto Icarus::gameBoyColorManifest(vector& buffer, const string& locatio auto Icarus::gameBoyColorImport(vector& buffer, const string& location) -> bool { auto name = prefixname(location); auto source = pathname(location); - string target{settings.libraryPath, "Game Boy Color/", name, ".gbc/"}; + string target{settings["Library/Location"].text(), "Game Boy Color/", name, ".gbc/"}; //if(directory::exists(target)) return failure("game already exists"); string markup; - if(settings.useHeuristics && !markup) { + if(settings["icarus/UseHeuristics"].boolean() && !markup) { GameBoyCartridge cartridge{buffer.data(), buffer.size()}; if(markup = cartridge.markup) { markup.append("\n"); @@ -38,7 +38,7 @@ auto Icarus::gameBoyColorImport(vector& buffer, const string& location) if(!markup) return failure("failed to parse ROM image"); if(!directory::create(target)) return failure("library path unwritable"); - if(settings.createManifests) file::write({target, "manifest.bml"}, markup); + if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); return success(); } diff --git a/icarus/core/game-boy.cpp b/icarus/core/game-boy.cpp index df2155fa..fdb58a15 100644 --- a/icarus/core/game-boy.cpp +++ b/icarus/core/game-boy.cpp @@ -20,12 +20,12 @@ auto Icarus::gameBoyManifest(vector& buffer, const string& location) -> auto Icarus::gameBoyImport(vector& buffer, const string& location) -> bool { auto name = prefixname(location); auto source = pathname(location); - string target{settings.libraryPath, "Game Boy/", name, ".gb/"}; + string target{settings["Library/Location"].text(), "Game Boy/", name, ".gb/"}; //if(directory::exists(target)) return failure("game already exists"); string markup; - if(settings.useHeuristics && !markup) { + if(settings["icarus/UseHeuristics"].boolean() && !markup) { GameBoyCartridge cartridge{buffer.data(), buffer.size()}; if(markup = cartridge.markup) { markup.append("\n"); @@ -38,7 +38,7 @@ auto Icarus::gameBoyImport(vector& buffer, const string& location) -> b if(!markup) return failure("failed to parse ROM image"); if(!directory::create(target)) return failure("library path unwritable"); - if(settings.createManifests) file::write({target, "manifest.bml"}, markup); + if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); return success(); } diff --git a/icarus/core/sufami-turbo.cpp b/icarus/core/sufami-turbo.cpp index 1d67a2ba..9eddc270 100644 --- a/icarus/core/sufami-turbo.cpp +++ b/icarus/core/sufami-turbo.cpp @@ -20,12 +20,12 @@ auto Icarus::sufamiTurboManifest(vector& buffer, const string& location auto Icarus::sufamiTurboImport(vector& buffer, const string& location) -> bool { auto name = prefixname(location); auto source = pathname(location); - string target{settings.libraryPath, "Sufami Turbo/", name, ".st/"}; + string target{settings["Library/Location"].text(), "Sufami Turbo/", name, ".st/"}; //if(directory::exists(target)) return failure("game already exists"); string markup; - if(settings.useDatabase && !markup) { + if(settings["icarus/UseDatabase"].boolean() && !markup) { auto digest = Hash::SHA256(buffer.data(), buffer.size()).digest(); for(auto node : database.sufamiTurbo) { if(node.name() != "release") continue; @@ -37,7 +37,7 @@ auto Icarus::sufamiTurboImport(vector& buffer, const string& location) } } - if(settings.useHeuristics && !markup) { + if(settings["icarus/UseHeuristics"].boolean() && !markup) { SufamiTurboCartridge cartridge{buffer.data(), buffer.size()}; if(markup = cartridge.markup) { markup.append("\n"); @@ -50,7 +50,7 @@ auto Icarus::sufamiTurboImport(vector& buffer, const string& location) if(!markup) return failure("failed to parse ROM image"); if(!directory::create(target)) return failure("library path unwritable"); - if(settings.createManifests) file::write({target, "manifest.bml"}, markup); + if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); return success(); } diff --git a/icarus/core/super-famicom.cpp b/icarus/core/super-famicom.cpp index 60f084a1..aa5e567b 100644 --- a/icarus/core/super-famicom.cpp +++ b/icarus/core/super-famicom.cpp @@ -25,14 +25,14 @@ auto Icarus::superFamicomManifest(vector& buffer, const string& locatio auto Icarus::superFamicomImport(vector& buffer, const string& location) -> bool { auto name = prefixname(location); auto source = pathname(location); - string target{settings.libraryPath, "Super Famicom/", name, ".sfc/"}; + string target{settings["Library/Location"].text(), "Super Famicom/", name, ".sfc/"}; //if(directory::exists(target)) return failure("game already exists"); string markup; vector roms; bool firmwareAppended = true; - if(settings.useDatabase && !markup) { + if(settings["icarus/UseDatabase"].boolean() && !markup) { auto digest = Hash::SHA256(buffer.data(), buffer.size()).digest(); for(auto node : database.superFamicom) { if(node.name() != "release") continue; @@ -44,7 +44,7 @@ auto Icarus::superFamicomImport(vector& buffer, const string& location) } } - if(settings.useHeuristics && !markup) { + if(settings["icarus/UseHeuristics"].boolean() && !markup) { SuperFamicomCartridge cartridge{buffer.data(), buffer.size()}; if(markup = cartridge.markup) { firmwareAppended = cartridge.firmware_appended; @@ -59,7 +59,7 @@ auto Icarus::superFamicomImport(vector& buffer, const string& location) superFamicomImportScanManifest(roms, document["cartridge"]); for(auto rom : roms) { auto name = rom["name"].text(); - auto size = rom["size"].decimal(); + auto size = rom["size"].natural(); if(name == "program.rom" || name == "data.rom" || firmwareAppended) continue; if(file::size({source, name}) != size) return failure({"firmware (", name, ") missing or invalid"}); } @@ -67,11 +67,11 @@ auto Icarus::superFamicomImport(vector& buffer, const string& location) if(!markup) return failure("failed to parse ROM image"); if(!directory::create(target)) return failure("library path unwritable"); - if(settings.createManifests) file::write({target, "manifest.bml"}, markup); + if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); unsigned offset = (buffer.size() & 0x7fff) == 512 ? 512 : 0; //skip header if present for(auto rom : roms) { auto name = rom["name"].text(); - auto size = rom["size"].decimal(); + auto size = rom["size"].natural(); if(name == "program.rom" || name == "data.rom" || firmwareAppended) { if(size > buffer.size() - offset) return failure("ROM image is missing data"); file::write({target, name}, buffer.data() + offset, size); diff --git a/icarus/icarus.cpp b/icarus/icarus.cpp index 73cf5c28..4d4fd8c3 100644 --- a/icarus/icarus.cpp +++ b/icarus/icarus.cpp @@ -4,6 +4,14 @@ using namespace nall; #include using namespace hiro; +//if file already exists in the same path as the binary; use it (portable mode) +//if not, use default requested path (*nix/user mode) +auto locate(string pathname, string filename) -> string { + string location{programpath(), filename}; + if(file_system_object::exists(location)) return location; + return {pathname, filename}; +} + #include "settings.cpp" Settings settings; @@ -27,6 +35,7 @@ Icarus icarus; #include "ui/ui.hpp" #include "ui/scan-dialog.cpp" +#include "ui/settings-dialog.cpp" #include "ui/import-dialog.cpp" #include "ui/error-dialog.cpp" @@ -38,6 +47,7 @@ auto nall::main(lstring args) -> void { } new ScanDialog; + new SettingsDialog; new ImportDialog; new ErrorDialog; scanDialog->show(); diff --git a/icarus/settings.cpp b/icarus/settings.cpp index bb331dc6..90194ba8 100644 --- a/icarus/settings.cpp +++ b/icarus/settings.cpp @@ -1,31 +1,24 @@ -struct Settings : Configuration::Document { +struct Settings : Markup::Node { Settings(); ~Settings(); - - Configuration::Node root; - string activePath; - string libraryPath; - bool createManifests = false; - bool useDatabase = true; - bool useHeuristics = true; }; Settings::Settings() { - root.append(activePath, "ActivePath"); - root.append(libraryPath, "LibraryPath"); - root.append(createManifests, "CreateManifests"); - root.append(useDatabase, "UseDatabase"); - root.append(useHeuristics, "UseHeuristics"); - append(root, "Settings"); + Markup::Node::operator=(BML::unserialize(string::read(locate({configpath(), "icarus/"}, "settings.bml")))); - directory::create({configpath(), "icarus/"}); - load({configpath(), "icarus/settings.bml"}); - save({configpath(), "icarus/settings.bml"}); + auto set = [&](const string& name, const string& value) { + //create node and set to default value only if it does not already exist + if(!operator[](name)) operator()(name).setValue(value); + }; - if(!activePath) activePath = userpath(); - if(!libraryPath) libraryPath = {userpath(), "Emulation/"}; + set("Library/Location", {userpath(), "Emulation/"}); + + set("icarus/Path", userpath()); + set("icarus/CreateManifests", false); + set("icarus/UseDatabase", true); + set("icarus/UseHeuristics", true); } Settings::~Settings() { - save({configpath(), "icarus/settings.bml"}); + file::write(locate({configpath(), "icarus/"}, "settings.bml"), BML::serialize(*this)); } diff --git a/icarus/ui/scan-dialog.cpp b/icarus/ui/scan-dialog.cpp index 6d6b0cdf..4d0e917f 100644 --- a/icarus/ui/scan-dialog.cpp +++ b/icarus/ui/scan-dialog.cpp @@ -5,7 +5,7 @@ ScanDialog::ScanDialog() { layout.setMargin(5); pathEdit.onActivate([&] { refresh(); }); refreshButton.setImage(Icon::Action::Refresh).setBordered(false).onActivate([&] { - pathEdit.setText(settings.activePath); + pathEdit.setText(settings["icarus/Path"].text()); refresh(); }); homeButton.setImage(Icon::Go::Home).setBordered(false).onActivate([&] { @@ -13,7 +13,7 @@ ScanDialog::ScanDialog() { refresh(); }); upButton.setImage(Icon::Go::Up).setBordered(false).onActivate([&] { - pathEdit.setText(dirname(settings.activePath)); + pathEdit.setText(dirname(settings["icarus/Path"].text())); refresh(); }); scanList.onActivate([&] { activate(); }); @@ -27,8 +27,10 @@ ScanDialog::ScanDialog() { if(item.cell(0).checkable()) item.cell(0).setChecked(false); } }); - createManifestsLabel.setChecked(settings.createManifests).setText("Create Manifests").onToggle([&] { - settings.createManifests = createManifestsLabel.checked(); + settingsButton.setText("Settings ...").onActivate([&] { + settingsDialog->setCentered(*this); + settingsDialog->setVisible(); + settingsDialog->setFocused(); }); importButton.setText("Import ...").onActivate([&] { import(); }); @@ -39,7 +41,7 @@ ScanDialog::ScanDialog() { auto ScanDialog::show() -> void { setVisible(); - pathEdit.setText(settings.activePath); + pathEdit.setText(settings["icarus/Path"].text()); refresh(); } @@ -50,7 +52,8 @@ auto ScanDialog::refresh() -> void { auto pathname = pathEdit.text().transform("\\", "/").rtrim("/").append("/"); if(!directory::exists(pathname)) return; - pathEdit.setText(settings.activePath = pathname); + settings["icarus/Path"].setValue(pathname); + pathEdit.setText(pathname); auto contents = directory::icontents(pathname); for(auto& name : contents) { @@ -72,7 +75,7 @@ auto ScanDialog::refresh() -> void { auto ScanDialog::activate() -> void { if(auto item = scanList.selected()) { - string location{settings.activePath, item.cell(0).text()}; + string location{settings["icarus/Path"].text(), item.cell(0).text()}; if(directory::exists(location) && !gamePakType(suffixname(location))) { pathEdit.setText(location); refresh(); @@ -84,7 +87,7 @@ auto ScanDialog::import() -> void { lstring filenames; for(auto& item : scanList.items()) { if(item.cell(0).checked()) { - filenames.append(string{settings.activePath, item.cell(0).text()}); + filenames.append(string{settings["icarus/Path"].text(), item.cell(0).text()}); } } diff --git a/icarus/ui/settings-dialog.cpp b/icarus/ui/settings-dialog.cpp new file mode 100644 index 00000000..80640d3f --- /dev/null +++ b/icarus/ui/settings-dialog.cpp @@ -0,0 +1,24 @@ +SettingsDialog::SettingsDialog() { + settingsDialog = this; + + layout.setMargin(5); + locationLabel.setText("Library Location:"); + locationEdit.setEditable(false).setText(settings["Library/Location"].text()); + changeLocationButton.setText("Change ...").onActivate([&] { + if(auto location = BrowserDialog().setParent(*this).setTitle("Select Library Location").selectFolder()) { + settings["Library/Location"].setValue(location); + locationEdit.setText(location); + } + }); + createManifestsOption.setText("Create Manifests (not recommended; breaks backward-compatibility)") + .setChecked(settings["icarus/CreateManifests"].boolean()).onToggle([&] { + settings["icarus/CreateManifests"].setValue(createManifestsOption.checked()); + }); + useDatabaseOption.setText("Use Database (highly recommended; provides bit-perfect memory mapping)") + .setChecked(settings["icarus/UseDatabase"].boolean()).onToggle([&] { + settings["icarus/UseDatabase"].setValue(useDatabaseOption.checked()); + }); + + setTitle("icarus Settings"); + setSize({480, layout.minimumSize().height()}); +} diff --git a/icarus/ui/ui.hpp b/icarus/ui/ui.hpp index 09294d88..dff89c63 100644 --- a/icarus/ui/ui.hpp +++ b/icarus/ui/ui.hpp @@ -19,8 +19,21 @@ struct ScanDialog : Window { HorizontalLayout controlLayout{&layout, Size{~0, 0}}; Button selectAllButton{&controlLayout, Size{100, 0}}; Button unselectAllButton{&controlLayout, Size{100, 0}}; - CheckLabel createManifestsLabel{&controlLayout, Size{~0, 0}}; - Button importButton{&controlLayout, Size{80, 0}}; + Widget controlSpacer{&controlLayout, Size{~0, 0}}; + Button settingsButton{&controlLayout, Size{100, 0}}; + Button importButton{&controlLayout, Size{100, 0}}; +}; + +struct SettingsDialog : Window { + SettingsDialog(); + + VerticalLayout layout{this}; + HorizontalLayout locationLayout{&layout, Size{~0, 0}}; + Label locationLabel{&locationLayout, Size{0, 0}}; + LineEdit locationEdit{&locationLayout, Size{~0, 0}}; + Button changeLocationButton{&locationLayout, Size{80, 0}}; + CheckLabel createManifestsOption{&layout, Size{~0, 0}, 2}; + CheckLabel useDatabaseOption{&layout, Size{~0, 0}}; }; struct ImportDialog : Window { @@ -50,5 +63,6 @@ struct ErrorDialog : Window { }; ScanDialog* scanDialog = nullptr; +SettingsDialog* settingsDialog = nullptr; ImportDialog* importDialog = nullptr; ErrorDialog* errorDialog = nullptr;