mirror of https://github.com/bsnes-emu/bsnes.git
Remove icarus, merge heuristics into bsnes.
This commit is contained in:
parent
0e56b27228
commit
9e8913cea0
|
@ -14,7 +14,6 @@ linux-x86_64-binaries_task:
|
||||||
- mkdir bsnes-nightly/Firmware
|
- mkdir bsnes-nightly/Firmware
|
||||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||||
- cp -a icarus/Database/* bsnes-nightly/Database
|
|
||||||
- cp -a GPLv3.txt bsnes-nightly
|
- cp -a GPLv3.txt bsnes-nightly
|
||||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||||
|
|
||||||
|
@ -37,7 +36,6 @@ freebsd-x86_64-binaries_task:
|
||||||
- mkdir bsnes-nightly/Firmware
|
- mkdir bsnes-nightly/Firmware
|
||||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||||
- cp -a icarus/Database/* bsnes-nightly/Database
|
|
||||||
- cp -a GPLv3.txt bsnes-nightly
|
- cp -a GPLv3.txt bsnes-nightly
|
||||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||||
|
|
||||||
|
@ -60,7 +58,6 @@ windows-x86_64-binaries_task:
|
||||||
- mkdir bsnes-nightly/Firmware
|
- mkdir bsnes-nightly/Firmware
|
||||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
|
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
|
||||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||||
- cp -a icarus/Database/* bsnes-nightly/Database
|
|
||||||
- cp -a GPLv3.txt bsnes-nightly
|
- cp -a GPLv3.txt bsnes-nightly
|
||||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ ifeq ($(platform),macos)
|
||||||
mkdir -p out/$(name).app/Contents/Resources/
|
mkdir -p out/$(name).app/Contents/Resources/
|
||||||
mv out/$(name) out/$(name).app/Contents/MacOS/$(name)
|
mv out/$(name) out/$(name).app/Contents/MacOS/$(name)
|
||||||
cp Database/* out/$(name).app/Contents/MacOS/Database/
|
cp Database/* out/$(name).app/Contents/MacOS/Database/
|
||||||
cp ../icarus/Database/* out/$(name).app/Contents/MacOS/Database/
|
|
||||||
cp $(ui)/resource/$(name).plist out/$(name).app/Contents/Info.plist
|
cp $(ui)/resource/$(name).plist out/$(name).app/Contents/Info.plist
|
||||||
sips -s format icns $(ui)/resource/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
|
sips -s format icns $(ui)/resource/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
|
||||||
endif
|
endif
|
||||||
|
@ -58,7 +57,6 @@ else ifneq ($(filter $(platform),linux bsd),)
|
||||||
cp $(ui)/resource/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
cp $(ui)/resource/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
||||||
cp $(ui)/resource/$(name).png $(prefix)/share/icons/$(name).png
|
cp $(ui)/resource/$(name).png $(prefix)/share/icons/$(name).png
|
||||||
cp Database/* $(prefix)/share/$(name)/Database/
|
cp Database/* $(prefix)/share/$(name)/Database/
|
||||||
cp ../icarus/Database/* $(prefix)/share/$(name)/Database/
|
|
||||||
cp Locale/* $(prefix)/share/$(name)/Locale/
|
cp Locale/* $(prefix)/share/$(name)/Locale/
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#include <nall/encode/bmp.hpp>
|
#include <nall/encode/bmp.hpp>
|
||||||
#include <icarus/heuristics/heuristics.hpp>
|
#include <heuristics/heuristics.hpp>
|
||||||
#include <icarus/heuristics/heuristics.cpp>
|
#include <heuristics/heuristics.cpp>
|
||||||
#include <icarus/heuristics/super-famicom.cpp>
|
#include <heuristics/super-famicom.cpp>
|
||||||
#include <icarus/heuristics/game-boy.cpp>
|
#include <heuristics/game-boy.cpp>
|
||||||
#include <icarus/heuristics/bs-memory.cpp>
|
#include <heuristics/bs-memory.cpp>
|
||||||
#include <icarus/heuristics/sufami-turbo.cpp>
|
#include <heuristics/sufami-turbo.cpp>
|
||||||
|
|
||||||
//ROM data is held in memory to support compressed archives, soft-patching, and game hacks
|
//ROM data is held in memory to support compressed archives, soft-patching, and game hacks
|
||||||
auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file {
|
auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,38 +0,0 @@
|
||||||
auto Icarus::bsMemoryManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
//only one of these should exist
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
concatenate(buffer, {location, "program.flash"});
|
|
||||||
return bsMemoryManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::bsMemoryManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::BSMemory.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::BSMemory game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::bsMemoryImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "BS Memory/", name, ".bs/"};
|
|
||||||
|
|
||||||
auto manifest = bsMemoryManifest(buffer, location);
|
|
||||||
if(!manifest) return failure("failed to parse ROM image");
|
|
||||||
|
|
||||||
if(!create(target)) return failure("library path unwritable");
|
|
||||||
|
|
||||||
if(settings["icarus/CreateManifests"].boolean()) write({target, "manifest.bml"}, manifest);
|
|
||||||
write({target, "program.rom"}, buffer);
|
|
||||||
return success(target);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::colecoVisionManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return colecoVisionManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::colecoVisionManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::ColecoVision.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::ColecoVision game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::colecoVisionImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "ColecoVision/", name, ".cv/"};
|
|
||||||
|
|
||||||
auto manifest = colecoVisionManifest(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);
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
Icarus::Icarus() {
|
|
||||||
Database::Famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
|
|
||||||
Database::SuperFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.bml")));
|
|
||||||
Database::SG1000 = BML::unserialize(string::read(locate("Database/SG-1000.bml")));
|
|
||||||
Database::SC3000 = BML::unserialize(string::read(locate("Database/SC-3000.bml")));
|
|
||||||
Database::MasterSystem = BML::unserialize(string::read(locate("Database/Master System.bml")));
|
|
||||||
Database::MegaDrive = BML::unserialize(string::read(locate("Database/Mega Drive.bml")));
|
|
||||||
Database::PCEngine = BML::unserialize(string::read(locate("Database/PC Engine.bml")));
|
|
||||||
Database::SuperGrafx = BML::unserialize(string::read(locate("Database/SuperGrafx.bml")));
|
|
||||||
Database::ColecoVision = BML::unserialize(string::read(locate("Database/ColecoVision.bml")));
|
|
||||||
Database::MSX = BML::unserialize(string::read(locate("Database/MSX.bml")));
|
|
||||||
Database::GameBoy = BML::unserialize(string::read(locate("Database/Game Boy.bml")));
|
|
||||||
Database::GameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.bml")));
|
|
||||||
Database::GameBoyAdvance = BML::unserialize(string::read(locate("Database/Game Boy Advance.bml")));
|
|
||||||
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::NeoGeoPocket = BML::unserialize(string::read(locate("Database/Neo Geo Pocket.bml")));
|
|
||||||
Database::NeoGeoPocketColor = BML::unserialize(string::read(locate("Database/Neo Geo Pocket Color.bml")));
|
|
||||||
Database::BSMemory = BML::unserialize(string::read(locate("Database/BS Memory.bml")));
|
|
||||||
Database::SufamiTurbo = BML::unserialize(string::read(locate("Database/Sufami Turbo.bml")));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::error() const -> string {
|
|
||||||
return errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::missing() const -> vector<string> {
|
|
||||||
return missingFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::success(string location) -> string {
|
|
||||||
errorMessage = "";
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::failure(string message) -> string {
|
|
||||||
errorMessage = message;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::manifest(string location) -> string {
|
|
||||||
location.transform("\\", "/").trimRight("/").append("/");
|
|
||||||
if(!directory::exists(location)) return {};
|
|
||||||
|
|
||||||
auto type = Location::suffix(location).downcase();
|
|
||||||
if(type == ".fc") return famicomManifest(location);
|
|
||||||
if(type == ".sfc") return superFamicomManifest(location);
|
|
||||||
if(type == ".sg1000") return sg1000Manifest(location);
|
|
||||||
if(type == ".sc3000") return sc3000Manifest(location);
|
|
||||||
if(type == ".ms") return masterSystemManifest(location);
|
|
||||||
if(type == ".md") return megaDriveManifest(location);
|
|
||||||
if(type == ".pce") return pcEngineManifest(location);
|
|
||||||
if(type == ".sgx") return superGrafxManifest(location);
|
|
||||||
if(type == ".cv") return colecoVisionManifest(location);
|
|
||||||
if(type == ".msx") return msxManifest(location);
|
|
||||||
if(type == ".gb") return gameBoyManifest(location);
|
|
||||||
if(type == ".gbc") return gameBoyColorManifest(location);
|
|
||||||
if(type == ".gba") return gameBoyAdvanceManifest(location);
|
|
||||||
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 == ".ngp") return neoGeoPocketManifest(location);
|
|
||||||
if(type == ".ngpc") return neoGeoPocketColorManifest(location);
|
|
||||||
if(type == ".bs") return bsMemoryManifest(location);
|
|
||||||
if(type == ".st") return sufamiTurboManifest(location);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::import(string location) -> string {
|
|
||||||
errorMessage = {};
|
|
||||||
missingFiles = {};
|
|
||||||
|
|
||||||
location.transform("\\", "/").trimRight("/");
|
|
||||||
if(!file::exists(location)) return failure("file does not exist");
|
|
||||||
if(!file::readable(location)) return failure("file is unreadable");
|
|
||||||
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto type = Location::suffix(location).downcase();
|
|
||||||
if(!name || !type) return failure("invalid file name");
|
|
||||||
|
|
||||||
auto buffer = file::read(location);
|
|
||||||
if(!buffer) return failure("file is empty");
|
|
||||||
|
|
||||||
if(type == ".zip") {
|
|
||||||
Decode::ZIP zip;
|
|
||||||
if(!zip.open(location)) return failure("ZIP archive is invalid");
|
|
||||||
if(!zip.file) return failure("ZIP archive is empty");
|
|
||||||
|
|
||||||
name = Location::prefix(zip.file[0].name);
|
|
||||||
type = Location::suffix(zip.file[0].name).downcase();
|
|
||||||
buffer = zip.extract(zip.file[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(type == ".fc" || type == ".nes") return famicomImport(buffer, location);
|
|
||||||
if(type == ".sfc" || type == ".smc") return superFamicomImport(buffer, location);
|
|
||||||
if(type == ".sg1000" || type == ".sg") return sg1000Import(buffer, location);
|
|
||||||
if(type == ".sc3000" || type == ".sc") return sc3000Import(buffer, location);
|
|
||||||
if(type == ".ms" || type == ".sms") return masterSystemImport(buffer, location);
|
|
||||||
if(type == ".md" || type == ".smd" || type == ".gen") return megaDriveImport(buffer, location);
|
|
||||||
if(type == ".pce") return pcEngineImport(buffer, location);
|
|
||||||
if(type == ".sgx") return superGrafxImport(buffer, location);
|
|
||||||
if(type == ".cv" || type == ".col") return colecoVisionImport(buffer, location);
|
|
||||||
if(type == ".msx") return msxImport(buffer, location);
|
|
||||||
if(type == ".gb") return gameBoyImport(buffer, location);
|
|
||||||
if(type == ".gbc") return gameBoyColorImport(buffer, location);
|
|
||||||
if(type == ".gba") return gameBoyAdvanceImport(buffer, location);
|
|
||||||
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 == ".ngp") return neoGeoPocketImport(buffer, location);
|
|
||||||
if(type == ".ngpc" || type == ".ngc") return neoGeoPocketColorImport(buffer, location);
|
|
||||||
if(type == ".bs") return bsMemoryImport(buffer, location);
|
|
||||||
if(type == ".st") return sufamiTurboImport(buffer, location);
|
|
||||||
|
|
||||||
return failure("unrecognized file extension");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::concatenate(vector<uint8_t>& output, string location) -> void {
|
|
||||||
if(auto input = file::read(location)) {
|
|
||||||
auto size = output.size();
|
|
||||||
output.resize(size + input.size());
|
|
||||||
memory::copy(output.data() + size, input.data(), input.size());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,171 +0,0 @@
|
||||||
struct Icarus {
|
|
||||||
virtual auto create(const string& pathname) -> bool {
|
|
||||||
return directory::create(pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual auto exists(const string& filename) -> bool {
|
|
||||||
return file::exists(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual auto copy(const string& target, const string& source) -> bool {
|
|
||||||
return file::copy(target, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual auto write(const string& filename, const uint8_t* data, uint size) -> bool {
|
|
||||||
return file::write(filename, {data, size});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto write(const string& filename, const vector<uint8_t>& buffer) -> bool {
|
|
||||||
return write(filename, buffer.data(), buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto write(const string& filename, const string& text) -> bool {
|
|
||||||
return write(filename, (const uint8_t*)text.data(), text.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
//core.cpp
|
|
||||||
Icarus();
|
|
||||||
|
|
||||||
auto error() const -> string;
|
|
||||||
auto missing() const -> vector<string>;
|
|
||||||
auto success(string location) -> string;
|
|
||||||
auto failure(string message) -> string;
|
|
||||||
|
|
||||||
auto manifest(string location) -> string;
|
|
||||||
auto import(string location) -> string;
|
|
||||||
|
|
||||||
auto concatenate(vector<uint8_t>& output, string location) -> void;
|
|
||||||
|
|
||||||
//famicom.cpp
|
|
||||||
auto famicomManifest(string location) -> string;
|
|
||||||
auto famicomManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto famicomImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//super-famicom.cpp
|
|
||||||
auto superFamicomManifest(string location) -> string;
|
|
||||||
auto superFamicomManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto superFamicomImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//sg-1000.cpp
|
|
||||||
auto sg1000Manifest(string location) -> string;
|
|
||||||
auto sg1000Manifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto sg1000Import(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//sc-3000.cpp
|
|
||||||
auto sc3000Manifest(string location) -> string;
|
|
||||||
auto sc3000Manifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto sc3000Import(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//master-system.cpp
|
|
||||||
auto masterSystemManifest(string location) -> string;
|
|
||||||
auto masterSystemManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto masterSystemImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//mega-drive.cpp
|
|
||||||
auto megaDriveManifest(string location) -> string;
|
|
||||||
auto megaDriveManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto megaDriveImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//pc-engine.cpp
|
|
||||||
auto pcEngineManifest(string location) -> string;
|
|
||||||
auto pcEngineManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto pcEngineImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//supergrafx.cpp
|
|
||||||
auto superGrafxManifest(string location) -> string;
|
|
||||||
auto superGrafxManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto superGrafxImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//colecovision.cpp
|
|
||||||
auto colecoVisionManifest(string location) -> string;
|
|
||||||
auto colecoVisionManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto colecoVisionImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//msx.cpp
|
|
||||||
auto msxManifest(string location) -> string;
|
|
||||||
auto msxManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto msxImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//game-boy.cpp
|
|
||||||
auto gameBoyManifest(string location) -> string;
|
|
||||||
auto gameBoyManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto gameBoyImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//game-boy-color.cpp
|
|
||||||
auto gameBoyColorManifest(string location) -> string;
|
|
||||||
auto gameBoyColorManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto gameBoyColorImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//game-boy-advance.cpp
|
|
||||||
auto gameBoyAdvanceManifest(string location) -> string;
|
|
||||||
auto gameBoyAdvanceManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto gameBoyAdvanceImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//game-gear.cpp
|
|
||||||
auto gameGearManifest(string location) -> string;
|
|
||||||
auto gameGearManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto gameGearImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//wonderswan.cpp
|
|
||||||
auto wonderSwanManifest(string location) -> string;
|
|
||||||
auto wonderSwanManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto wonderSwanImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//wonderswan-color.cpp
|
|
||||||
auto wonderSwanColorManifest(string location) -> string;
|
|
||||||
auto wonderSwanColorManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto wonderSwanColorImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//pocket-challenge-v2.cpp
|
|
||||||
auto pocketChallengeV2Manifest(string location) -> string;
|
|
||||||
auto pocketChallengeV2Manifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto pocketChallengeV2Import(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//neo-geo-pocket.cpp
|
|
||||||
auto neoGeoPocketManifest(string location) -> string;
|
|
||||||
auto neoGeoPocketManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto neoGeoPocketImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//neo-geo-pocket-color.cpp
|
|
||||||
auto neoGeoPocketColorManifest(string location) -> string;
|
|
||||||
auto neoGeoPocketColorManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto neoGeoPocketColorImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//bs-memory.cpp
|
|
||||||
auto bsMemoryManifest(string location) -> string;
|
|
||||||
auto bsMemoryManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto bsMemoryImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
//sufami-turbo.cpp
|
|
||||||
auto sufamiTurboManifest(string location) -> string;
|
|
||||||
auto sufamiTurboManifest(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
auto sufamiTurboImport(vector<uint8_t>& buffer, string location) -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
string errorMessage;
|
|
||||||
vector<string> missingFiles;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Database {
|
|
||||||
Markup::Node Famicom;
|
|
||||||
Markup::Node SuperFamicom;
|
|
||||||
Markup::Node SG1000;
|
|
||||||
Markup::Node SC3000;
|
|
||||||
Markup::Node MasterSystem;
|
|
||||||
Markup::Node MegaDrive;
|
|
||||||
Markup::Node PCEngine;
|
|
||||||
Markup::Node SuperGrafx;
|
|
||||||
Markup::Node ColecoVision;
|
|
||||||
Markup::Node MSX;
|
|
||||||
Markup::Node GameBoy;
|
|
||||||
Markup::Node GameBoyColor;
|
|
||||||
Markup::Node GameBoyAdvance;
|
|
||||||
Markup::Node GameGear;
|
|
||||||
Markup::Node WonderSwan;
|
|
||||||
Markup::Node WonderSwanColor;
|
|
||||||
Markup::Node PocketChallengeV2;
|
|
||||||
Markup::Node NeoGeoPocket;
|
|
||||||
Markup::Node NeoGeoPocketColor;
|
|
||||||
Markup::Node BSMemory;
|
|
||||||
Markup::Node SufamiTurbo;
|
|
||||||
};
|
|
|
@ -1,58 +0,0 @@
|
||||||
auto Icarus::famicomManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "ines.rom"});
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
concatenate(buffer, {location, "character.rom"});
|
|
||||||
return famicomManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::famicomManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::Famicom.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::Famicom game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::famicomImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Famicom/", name, ".fc/"};
|
|
||||||
|
|
||||||
auto manifest = famicomManifest(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);
|
|
||||||
auto document = BML::unserialize(manifest);
|
|
||||||
uint offset = 0;
|
|
||||||
if(true) {
|
|
||||||
//todo: support images without iNES headers (via database lookup)
|
|
||||||
uint size = 16;
|
|
||||||
write({target, "ines.rom"}, &buffer[offset], size);
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
if(auto program = document["game/board/memory(type=ROM,content=Program)"]) {
|
|
||||||
uint size = program["size"].natural();
|
|
||||||
write({target, "program.rom"}, &buffer[offset], size);
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
if(auto character = document["game/board/memory(type=ROM,content=Character)"]) {
|
|
||||||
uint size = character["size"].natural();
|
|
||||||
write({target, "character.rom"}, &buffer[offset], size);
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
return success(target);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::gameBoyAdvanceManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return gameBoyAdvanceManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameBoyAdvanceManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::GameBoyAdvance.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::GameBoyAdvance game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameBoyAdvanceImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Game Boy Advance/", name, ".gba/"};
|
|
||||||
|
|
||||||
auto manifest = gameBoyAdvanceManifest(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);
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
auto Icarus::gameBoyColorManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return gameBoyColorManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameBoyColorManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
for(auto game : Database::GameBoyColor.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::GameBoy game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameBoyColorImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Game Boy Color/", name, ".gbc/"};
|
|
||||||
|
|
||||||
auto manifest = gameBoyColorManifest(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);
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
auto Icarus::gameBoyManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return gameBoyManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameBoyManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
for(auto game : Database::GameBoy.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::GameBoy game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameBoyImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Game Boy/", name, ".gb/"};
|
|
||||||
|
|
||||||
auto manifest = gameBoyManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::gameGearManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return gameGearManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameGearManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::GameGear.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::GameGear game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::gameGearImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Game Gear/", name, ".gg/"};
|
|
||||||
|
|
||||||
auto manifest = gameGearManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::masterSystemManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return masterSystemManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::masterSystemManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::MasterSystem.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::MasterSystem game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::masterSystemImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Master System/", name, ".ms/"};
|
|
||||||
|
|
||||||
auto manifest = masterSystemManifest(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);
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
auto Icarus::megaDriveManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
concatenate(buffer, {location, "patch.rom" });
|
|
||||||
return megaDriveManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::megaDriveManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::MegaDrive.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::MegaDrive game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::megaDriveImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Mega Drive/", name, ".md/"};
|
|
||||||
|
|
||||||
auto manifest = megaDriveManifest(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);
|
|
||||||
uint offset = 0;
|
|
||||||
auto document = BML::unserialize(manifest);
|
|
||||||
for(auto rom : document.find("game/board/memory(type=ROM)")) {
|
|
||||||
auto name = string{rom["content"].text(), ".rom"}.downcase();
|
|
||||||
auto size = rom["size"].natural();
|
|
||||||
if(size > buffer.size() - offset) {
|
|
||||||
missingFiles.append(name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
write({target, name}, buffer.data() + offset, size);
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
if(missingFiles) return failure({"ROM image is missing data: ", missingFiles.merge("; ")});
|
|
||||||
return success(target);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::msxManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return msxManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::msxManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::MSX.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::MSX game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::msxImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "MSX/", name, ".msx/"};
|
|
||||||
|
|
||||||
auto manifest = msxManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::neoGeoPocketColorManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return neoGeoPocketColorManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::neoGeoPocketColorManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::NeoGeoPocketColor.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::NeoGeoPocketColor game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::neoGeoPocketColorImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Neo Geo Pocket Color/", name, ".ngpc/"};
|
|
||||||
|
|
||||||
auto manifest = neoGeoPocketColorManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::neoGeoPocketManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return neoGeoPocketManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::neoGeoPocketManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::NeoGeoPocket.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::NeoGeoPocket game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::neoGeoPocketImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Neo Geo Pocket/", name, ".ngp/"};
|
|
||||||
|
|
||||||
auto manifest = neoGeoPocketManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::pcEngineManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return pcEngineManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::pcEngineManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::PCEngine.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::PCEngine game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::pcEngineImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "PC Engine/", name, ".pce/"};
|
|
||||||
|
|
||||||
auto manifest = pcEngineManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::pocketChallengeV2Manifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return pocketChallengeV2Manifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::pocketChallengeV2Manifest(vector<uint8_t>& 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<uint8_t>& 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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::sc3000Manifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return sc3000Manifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::sc3000Manifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::SC3000.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::SC3000 game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::sc3000Import(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "SC-3000/", name, ".sc3000/"};
|
|
||||||
|
|
||||||
auto manifest = sc3000Manifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::sg1000Manifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return sg1000Manifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::sg1000Manifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::SG1000.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::SG1000 game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::sg1000Import(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "SG-1000/", name, ".sg1000/"};
|
|
||||||
|
|
||||||
auto manifest = sg1000Manifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::sufamiTurboManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return sufamiTurboManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::sufamiTurboManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::SufamiTurbo.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::SufamiTurbo game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::sufamiTurboImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Sufami Turbo/", name, ".st/"};
|
|
||||||
|
|
||||||
auto manifest = sufamiTurboManifest(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);
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
auto Icarus::superFamicomManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
auto files = directory::files(location, "*.rom");
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
concatenate(buffer, {location, "data.rom" });
|
|
||||||
for(auto& file : files.match("*.program.rom")) concatenate(buffer, {location, file});
|
|
||||||
for(auto& file : files.match("*.data.rom" )) concatenate(buffer, {location, file});
|
|
||||||
for(auto& file : files.match("*.boot.rom" )) concatenate(buffer, {location, file});
|
|
||||||
return superFamicomManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::superFamicomManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::SuperFamicom.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::SuperFamicom game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::superFamicomImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "Super Famicom/", name, ".sfc/"};
|
|
||||||
|
|
||||||
auto manifest = superFamicomManifest(buffer, location);
|
|
||||||
if(!manifest) return failure("failed to parse ROM image");
|
|
||||||
|
|
||||||
if(!create(target)) return failure("library path unwritable");
|
|
||||||
if(exists({source, name, ".srm"}) && !exists({target, "save.ram"})) {
|
|
||||||
copy({source, name, ".srm"}, {target, "save.ram"});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/CreateManifests"].boolean()) write({target, "manifest.bml"}, manifest);
|
|
||||||
uint offset = 0;
|
|
||||||
auto document = BML::unserialize(manifest);
|
|
||||||
for(auto rom : document.find("game/board/memory(type=ROM)")) {
|
|
||||||
auto name = string{rom["architecture"].text(), ".", rom["content"].text(), ".rom"}.trimLeft(".", 1L).downcase();
|
|
||||||
auto size = rom["size"].natural();
|
|
||||||
if(size > buffer.size() - offset) {
|
|
||||||
auto firmware = string{rom["identifier"].text(), ".", rom["content"].text(), ".rom"}.trimLeft(".", 1L).downcase();
|
|
||||||
auto location = locate({"Firmware/", firmware});
|
|
||||||
if(location && file::size(location) == size) {
|
|
||||||
write({target, name}, file::read(location));
|
|
||||||
} else {
|
|
||||||
missingFiles.append(firmware);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
write({target, name}, buffer.data() + offset, size);
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
if(missingFiles) return failure({"ROM image is missing data: ", missingFiles.merge("; ")});
|
|
||||||
return success(target);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::superGrafxManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return superGrafxManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::superGrafxManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::SuperGrafx.find("game")) {
|
|
||||||
if(game["sha256"].text() == digest) return BML::serialize(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(settings["icarus/UseHeuristics"].boolean()) {
|
|
||||||
Heuristics::SuperGrafx game{buffer, location};
|
|
||||||
if(auto manifest = game.manifest()) return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::superGrafxImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "SuperGrafx/", name, ".sg/"};
|
|
||||||
|
|
||||||
auto manifest = superGrafxManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::wonderSwanColorManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return wonderSwanColorManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::wonderSwanColorManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::WonderSwanColor.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::wonderSwanColorImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "WonderSwan Color/", name, ".wsc/"};
|
|
||||||
|
|
||||||
auto manifest = wonderSwanColorManifest(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);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
auto Icarus::wonderSwanManifest(string location) -> string {
|
|
||||||
vector<uint8_t> buffer;
|
|
||||||
concatenate(buffer, {location, "program.rom"});
|
|
||||||
return wonderSwanManifest(buffer, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Icarus::wonderSwanManifest(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
if(settings["icarus/UseDatabase"].boolean()) {
|
|
||||||
auto digest = Hash::SHA256(buffer).digest();
|
|
||||||
for(auto game : Database::WonderSwan.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::wonderSwanImport(vector<uint8_t>& buffer, string location) -> string {
|
|
||||||
auto name = Location::prefix(location);
|
|
||||||
auto source = Location::path(location);
|
|
||||||
string target{settings["Library/Location"].text(), "WonderSwan/", name, ".ws/"};
|
|
||||||
|
|
||||||
auto manifest = wonderSwanManifest(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);
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
|
||||||
<assemblyIdentity type="win32" name="icarus" version="1.0.0.0" processorArchitecture="*"/>
|
|
||||||
<dependency>
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
|
||||||
</dependentAssembly>
|
|
||||||
</dependency>
|
|
||||||
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
|
||||||
<dpiAware>false</dpiAware>
|
|
||||||
</asmv3:windowsSettings>
|
|
||||||
</asmv3:application>
|
|
||||||
</assembly>
|
|
|
@ -1,8 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Name=icarus
|
|
||||||
Comment=Emulator
|
|
||||||
Exec=icarus
|
|
||||||
Icon=icarus
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Game;Emulator;
|
|
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>org.byuu.icarus</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>icarus</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>icarus</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>icarus.icns</string>
|
|
||||||
<key>NSHighResolutionCapable</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
|
@ -1,2 +0,0 @@
|
||||||
1 24 "icarus.Manifest"
|
|
||||||
2 ICON DISCARDABLE "icarus.ico"
|
|
|
@ -1,80 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="256mm"
|
|
||||||
height="256mm"
|
|
||||||
viewBox="0 0 256 256"
|
|
||||||
version="1.1"
|
|
||||||
id="svg8"
|
|
||||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
|
||||||
sodipodi:docname="icarus.svg">
|
|
||||||
<defs
|
|
||||||
id="defs2" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="0.5"
|
|
||||||
inkscape:cx="62.34093"
|
|
||||||
inkscape:cy="560"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1028"
|
|
||||||
inkscape:window-x="-8"
|
|
||||||
inkscape:window-y="-8"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata5">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(0,-41)">
|
|
||||||
<circle
|
|
||||||
id="path10"
|
|
||||||
cx="128.0"
|
|
||||||
cy="169.0"
|
|
||||||
r="120.0"
|
|
||||||
style="stroke-width:0.25;fill:#b8b8ff;fill-opacity:1" />
|
|
||||||
<g
|
|
||||||
aria-label="氷"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:260.07336426px;line-height:1.25;font-family:KaiTi;-inkscape-font-specification:KaiTi;letter-spacing:0px;word-spacing:0px;fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="text818">
|
|
||||||
<path
|
|
||||||
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 130.80961,146.24049 q 5.07956,5.07956 14.22276,15.23868 14.22277,-13.20685 25.39779,-27.42962 12.19094,-15.23867 11.17503,-24.38187 0,-10.15912 10.15912,-4.06365 10.15911,6.09547 14.22276,12.19094 4.06364,5.07956 -2.03182,7.11138 -6.09547,1.01591 -22.35006,14.22276 -15.23867,12.19094 -32.50917,26.4137 13.20685,11.17503 23.36597,20.31823 11.17502,9.14321 24.38187,18.28641 14.22277,8.1273 27.42962,14.22276 14.22276,5.07956 21.33414,7.11139 7.11138,2.03182 -3.04773,5.07955 -9.14321,2.03183 -23.36597,3.04774 -14.22276,0 -21.33414,-2.03182 -6.09547,-3.04774 -11.17503,-8.1273 -4.06365,-5.07956 -24.38188,-27.42961 -19.30232,-23.36597 -31.49326,-39.62055 1.01591,41.65237 2.03182,64.00243 2.03183,22.35005 0,34.54099 -2.03182,12.19094 -8.12729,19.30232 -5.07956,7.11138 -8.12729,4.06365 -2.03182,-2.03183 -7.11138,-11.17503 -5.07956,-8.12729 -16.254587,-15.23867 -11.175027,-8.1273 1.015912,-5.07956 12.190935,2.03182 16.254585,2.03182 4.06365,-1.01591 6.09547,-7.11138 2.03182,-7.11138 2.03182,-46.73193 0,-40.63646 -1.01591,-77.20928 -1.01591,-37.58873 -6.09547,-45.716022 -5.07956,-9.143205 4.06365,-7.111382 10.15911,1.015912 16.25458,5.079558 7.11138,3.047735 4.06365,9.143205 -3.04774,5.079557 -4.06365,15.238673 -1.01591,10.159118 -1.01591,51.811488 z"
|
|
||||||
id="path822" />
|
|
||||||
<path
|
|
||||||
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 88.141325,149.28823 q 5.079558,1.01591 14.222765,7.11138 9.1432,6.09547 4.06364,10.15911 -5.07955,4.06365 -12.190935,18.28641 -6.09547,14.22276 -14.222763,25.39779 -8.127292,10.15912 -19.30232,19.30232 -11.175027,8.12729 -21.334143,12.19094 -9.143204,3.04774 -16.254585,5.07956 -6.095469,1.01591 5.079558,-6.09547 11.175027,-7.11138 21.334143,-17.2705 11.175027,-10.15911 18.286408,-21.33414 8.127293,-12.19094 11.175028,-20.31823 3.047735,-9.14321 3.047735,-14.22276 1.015911,-5.07956 -3.047735,-5.07956 -4.063646,0 -15.238674,4.06364 -10.159116,4.06365 -15.238674,6.09547 -4.063646,2.03183 -13.20685,-3.04773 -8.127293,-6.09547 2.031823,-6.09547 11.175027,-1.01591 24.381878,-5.07956 14.222762,-5.07956 17.270497,-7.11138 4.063646,-3.04773 9.143204,-2.03182 z"
|
|
||||||
id="path820" />
|
|
||||||
<path
|
|
||||||
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 63.759447,101.54038 q 9.143204,1.01591 18.286409,5.07956 9.143204,4.06365 11.175027,11.17503 2.031823,7.11138 -1.015912,11.17503 -3.047734,4.06364 -15.238673,-4.06365 -11.175028,-9.1432 -16.254586,-16.25459 -5.079557,-8.12729 3.047735,-7.11138 z"
|
|
||||||
id="path815" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.9 KiB |
|
@ -1,32 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct ColecoVision {
|
|
||||||
ColecoVision(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
ColecoVision::ColecoVision(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ColecoVision::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ColecoVision::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
output.append(Memory{}.type("RAM").size(0x8000).content("Save").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct Famicom {
|
|
||||||
Famicom(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
Famicom::Famicom(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Famicom::operator bool() const {
|
|
||||||
if(data.size() < 16) return false;
|
|
||||||
if(data[0] != 'N') return false;
|
|
||||||
if(data[1] != 'E') return false;
|
|
||||||
if(data[2] != 'S') return false;
|
|
||||||
if(data[3] != 26) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Famicom::manifest() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
uint mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
|
|
||||||
uint mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
|
|
||||||
uint prgrom = data[4] * 0x4000;
|
|
||||||
uint chrrom = data[5] * 0x2000;
|
|
||||||
uint prgram = 0u;
|
|
||||||
uint chrram = chrrom == 0u ? 8192u : 0u;
|
|
||||||
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256({&data[16], data.size() - 16}).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
|
|
||||||
switch(mapper) {
|
|
||||||
default:
|
|
||||||
output.append(" board: NES-NROM-256\n");
|
|
||||||
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
output.append(" board: NES-SXROM\n");
|
|
||||||
output.append(" chip type=MMC1B2\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
output.append(" board: NES-UOROM\n");
|
|
||||||
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
output.append(" board: NES-CNROM\n");
|
|
||||||
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
//MMC3
|
|
||||||
output.append(" board: NES-TLROM\n");
|
|
||||||
output.append(" chip type=MMC3B\n");
|
|
||||||
prgram = 8192;
|
|
||||||
//MMC6
|
|
||||||
//output.append(" board: NES-HKROM\n");
|
|
||||||
//output.append(" chip type=MMC6\n");
|
|
||||||
//prgram = 1024;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
output.append(" board: NES-ELROM\n");
|
|
||||||
output.append(" chip type=MMC5\n");
|
|
||||||
prgram = 65536;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
|
||||||
output.append(" board: NES-AOROM\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 9:
|
|
||||||
output.append(" board: NES-PNROM\n");
|
|
||||||
output.append(" chip type=MMC2\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 10:
|
|
||||||
output.append(" board: NES-FKROM\n");
|
|
||||||
output.append(" chip type=MMC4\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 16:
|
|
||||||
output.append(" board: BANDAI-FCG\n");
|
|
||||||
output.append(" chip type=LZ93D50\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 21:
|
|
||||||
case 23:
|
|
||||||
case 25:
|
|
||||||
//VRC4
|
|
||||||
output.append(" board: KONAMI-VRC-4\n");
|
|
||||||
output.append(" chip type=VRC4\n");
|
|
||||||
output.append(" pinout a0=1 a1=0\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 22:
|
|
||||||
//VRC2
|
|
||||||
output.append(" board: KONAMI-VRC-2\n");
|
|
||||||
output.append(" chip type=VRC2\n");
|
|
||||||
output.append(" pinout a0=0 a1=1\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 24:
|
|
||||||
output.append(" board: KONAMI-VRC-6\n");
|
|
||||||
output.append(" chip type=VRC6\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 26:
|
|
||||||
output.append(" board: KONAMI-VRC-6\n");
|
|
||||||
output.append(" chip type=VRC6\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 34:
|
|
||||||
output.append(" board: NES-BNROM\n");
|
|
||||||
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 66:
|
|
||||||
output.append(" board: NES-GNROM\n");
|
|
||||||
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 69:
|
|
||||||
output.append(" board: SUNSOFT-5B\n");
|
|
||||||
output.append(" chip type=5B\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 73:
|
|
||||||
output.append(" board: KONAMI-VRC-3\n");
|
|
||||||
output.append(" chip type=VRC3\n");
|
|
||||||
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 75:
|
|
||||||
output.append(" board: KONAMI-VRC-1\n");
|
|
||||||
output.append(" chip type=VRC1\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 85:
|
|
||||||
output.append(" board: KONAMI-VRC-7\n");
|
|
||||||
output.append(" chip type=VRC7\n");
|
|
||||||
prgram = 8192;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(prgrom) output.append(Memory{}.type("ROM").size(prgrom).content("Program").text());
|
|
||||||
if(prgram) output.append(Memory{}.type("RAM").size(prgram).content("Save").text());
|
|
||||||
|
|
||||||
if(chrrom) output.append(Memory{}.type("ROM").size(chrrom).content("Character").text());
|
|
||||||
if(chrram) output.append(Memory{}.type("RAM").size(chrram).content("Character").isVolatile().text());
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct GameBoyAdvance {
|
|
||||||
GameBoyAdvance(vector<uint8_t>& buffer, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
GameBoyAdvance::GameBoyAdvance(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
GameBoyAdvance::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto GameBoyAdvance::manifest() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
vector<string> identifiers = {
|
|
||||||
"SRAM_V",
|
|
||||||
"SRAM_F_V",
|
|
||||||
"EEPROM_V",
|
|
||||||
"FLASH_V",
|
|
||||||
"FLASH512_V",
|
|
||||||
"FLASH1M_V",
|
|
||||||
};
|
|
||||||
|
|
||||||
vector<string> list;
|
|
||||||
for(auto& identifier : identifiers) {
|
|
||||||
for(int n : range(data.size() - 16)) {
|
|
||||||
if(!memory::compare(&data[n], identifier.data(), identifier.size())) {
|
|
||||||
auto p = (const char*)&data[n + identifier.size()];
|
|
||||||
if(p[0] >= '0' && p[0] <= '9'
|
|
||||||
&& p[1] >= '0' && p[1] <= '9'
|
|
||||||
&& p[2] >= '0' && p[2] <= '9'
|
|
||||||
) {
|
|
||||||
char text[16];
|
|
||||||
memory::copy(text, &data[n], identifier.size() + 3);
|
|
||||||
text[identifier.size() + 3] = 0;
|
|
||||||
if(!list.find(text)) list.append(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
if(!list);
|
|
||||||
else if(list.left().beginsWith("SRAM_V" )) output.append(Memory{}.type("RAM" ).size( 0x8000).content("Save").text());
|
|
||||||
else if(list.left().beginsWith("SRAM_F_V" )) output.append(Memory{}.type("RAM" ).size( 0x8000).content("Save").text());
|
|
||||||
else if(list.left().beginsWith("EEPROM_V" )) output.append(Memory{}.type("EEPROM").size( 0x0).content("Save").text());
|
|
||||||
else if(list.left().beginsWith("FLASH_V" )) output.append(Memory{}.type("Flash" ).size(0x10000).content("Save").manufacturer("Macronix").text());
|
|
||||||
else if(list.left().beginsWith("FLASH512_V")) output.append(Memory{}.type("Flash" ).size(0x10000).content("Save").manufacturer("Macronix").text());
|
|
||||||
else if(list.left().beginsWith("FLASH1M_V" )) output.append(Memory{}.type("Flash" ).size(0x20000).content("Save").manufacturer("Macronix").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct GameGear {
|
|
||||||
GameGear(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
GameGear::GameGear(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
GameGear::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto GameGear::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
output.append(Memory{}.type("RAM").size(0x8000).content("Save").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct MasterSystem {
|
|
||||||
MasterSystem(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
MasterSystem::MasterSystem(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
MasterSystem::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MasterSystem::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
output.append(Memory{}.type("RAM").size(0x8000).content("Save").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct MegaDrive {
|
|
||||||
MegaDrive(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
MegaDrive::MegaDrive(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
MegaDrive::operator bool() const {
|
|
||||||
return data.size() >= 0x200;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MegaDrive::manifest() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
string ramMode = "none";
|
|
||||||
|
|
||||||
uint32_t ramFrom = 0;
|
|
||||||
ramFrom |= data[0x01b4] << 24;
|
|
||||||
ramFrom |= data[0x01b5] << 16;
|
|
||||||
ramFrom |= data[0x01b6] << 8;
|
|
||||||
ramFrom |= data[0x01b7] << 0;
|
|
||||||
|
|
||||||
uint32_t ramTo = 0;
|
|
||||||
ramTo |= data[0x01b8] << 24;
|
|
||||||
ramTo |= data[0x01b9] << 16;
|
|
||||||
ramTo |= data[0x01ba] << 8;
|
|
||||||
ramTo |= data[0x01bb] << 0;
|
|
||||||
|
|
||||||
if(!(ramFrom & 1) && !(ramTo & 1)) ramMode = "hi";
|
|
||||||
if( (ramFrom & 1) && (ramTo & 1)) ramMode = "lo";
|
|
||||||
if(!(ramFrom & 1) && (ramTo & 1)) ramMode = "word";
|
|
||||||
if(data[0x01b0] != 'R' || data[0x01b1] != 'A') ramMode = "none";
|
|
||||||
|
|
||||||
uint32_t ramSize = ramTo - ramFrom + 1;
|
|
||||||
if(ramMode == "hi") ramSize = (ramTo >> 1) - (ramFrom >> 1) + 1;
|
|
||||||
if(ramMode == "lo") ramSize = (ramTo >> 1) - (ramFrom >> 1) + 1;
|
|
||||||
if(ramMode == "word") ramSize = ramTo - ramFrom + 1;
|
|
||||||
if(ramMode != "none") ramSize = bit::round(min(0x20000, ramSize));
|
|
||||||
if(ramMode == "none") ramSize = 0;
|
|
||||||
|
|
||||||
vector<string> regions;
|
|
||||||
string region = slice((const char*)&data[0x1f0], 0, 16).trimRight(" ");
|
|
||||||
if(!regions) {
|
|
||||||
if(region == "JAPAN" ) regions.append("NTSC-J");
|
|
||||||
if(region == "EUROPE") regions.append("PAL");
|
|
||||||
}
|
|
||||||
if(!regions) {
|
|
||||||
if(region.find("J")) regions.append("NTSC-J");
|
|
||||||
if(region.find("U")) regions.append("NTSC-U");
|
|
||||||
if(region.find("E")) regions.append("PAL");
|
|
||||||
if(region.find("W")) regions.append("NTSC-J", "NTSC-U", "PAL");
|
|
||||||
}
|
|
||||||
if(!regions && region.size() == 1) {
|
|
||||||
uint8_t field = region.hex();
|
|
||||||
if(field & 0x01) regions.append("NTSC-J");
|
|
||||||
if(field & 0x04) regions.append("NTSC-U");
|
|
||||||
if(field & 0x08) regions.append("PAL");
|
|
||||||
}
|
|
||||||
if(!regions) {
|
|
||||||
regions.append("NTSC-J");
|
|
||||||
}
|
|
||||||
|
|
||||||
string domesticName;
|
|
||||||
domesticName.resize(48);
|
|
||||||
memory::copy(domesticName.get(), &data[0x0120], domesticName.size());
|
|
||||||
for(auto& c : domesticName) if(c < 0x20 || c > 0x7e) c = ' ';
|
|
||||||
while(domesticName.find(" ")) domesticName.replace(" ", " ");
|
|
||||||
domesticName.strip();
|
|
||||||
|
|
||||||
string internationalName;
|
|
||||||
internationalName.resize(48);
|
|
||||||
memory::copy(internationalName.get(), &data[0x0150], internationalName.size());
|
|
||||||
for(auto& c : internationalName) if(c < 0x20 || c > 0x7e) c = ' ';
|
|
||||||
while(internationalName.find(" ")) internationalName.replace(" ", " ");
|
|
||||||
internationalName.strip();
|
|
||||||
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" title: ", domesticName, "\n");
|
|
||||||
output.append(" region: ", regions.left(), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
if(domesticName == "Game Genie") {
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
output.append(Slot{}.type("MegaDrive").text());
|
|
||||||
} else if(domesticName == "SONIC & KNUCKLES") {
|
|
||||||
output.append(Memory{}.type("ROM").size(0x200000).content("Program").text());
|
|
||||||
output.append(Memory{}.type("ROM").size( 0x40000).content("Patch").text());
|
|
||||||
output.append(Slot{}.type("MegaDrive").text());
|
|
||||||
} else {
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
}
|
|
||||||
if(ramSize && ramMode != "none") {
|
|
||||||
output.append(Memory{}.type("RAM").size(ramSize).content("Save").text());
|
|
||||||
output.append(" mode: ", ramMode, "\n");
|
|
||||||
output.append(" offset: 0x", hex(ramFrom), "\n");
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct MSX {
|
|
||||||
MSX(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
MSX::MSX(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
MSX::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MSX::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct NeoGeoPocketColor {
|
|
||||||
NeoGeoPocketColor(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
auto title() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
NeoGeoPocketColor::NeoGeoPocketColor(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
NeoGeoPocketColor::operator bool() const {
|
|
||||||
switch(data.size()) {
|
|
||||||
case 0x080000: return true; // 4mbit
|
|
||||||
case 0x100000: return true; // 8mbit
|
|
||||||
case 0x200000: return true; //16mbit
|
|
||||||
case 0x280000: return true; //20mbit
|
|
||||||
case 0x300000: return true; //24mbit
|
|
||||||
case 0x400000: return true; //32mbit
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto NeoGeoPocketColor::manifest() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" title: ", title(), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto NeoGeoPocketColor::title() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
string title;
|
|
||||||
title.size(12);
|
|
||||||
for(uint index : range(12)) {
|
|
||||||
char letter = data[0x24 + index];
|
|
||||||
if(letter >= 0x20 && letter <= 0x7e) title.get()[index] = letter;
|
|
||||||
}
|
|
||||||
return title.strip();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct NeoGeoPocket {
|
|
||||||
NeoGeoPocket(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
auto title() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
NeoGeoPocket::NeoGeoPocket(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
NeoGeoPocket::operator bool() const {
|
|
||||||
switch(data.size()) {
|
|
||||||
case 0x080000: return true; // 4mbit
|
|
||||||
case 0x100000: return true; // 8mbit
|
|
||||||
case 0x200000: return true; //16mbit
|
|
||||||
case 0x280000: return true; //20mbit
|
|
||||||
case 0x300000: return true; //24mbit
|
|
||||||
case 0x400000: return true; //32mbit
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto NeoGeoPocket::manifest() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" title: ", title(), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto NeoGeoPocket::title() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
string title;
|
|
||||||
title.size(12);
|
|
||||||
for(uint index : range(12)) {
|
|
||||||
char letter = data[0x24 + index];
|
|
||||||
if(letter >= 0x20 && letter <= 0x7e) title.get()[index] = letter;
|
|
||||||
}
|
|
||||||
return title.strip();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct PCEngine {
|
|
||||||
PCEngine(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
PCEngine::PCEngine(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
if((data.size() & 0x1fff) == 512) {
|
|
||||||
//remove header if present
|
|
||||||
memory::move(&data[0], &data[512], data.size() - 512);
|
|
||||||
data.resize(data.size() - 512);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PCEngine::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto PCEngine::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct SC3000 {
|
|
||||||
SC3000(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
SC3000::SC3000(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SC3000::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SC3000::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
output.append(Memory{}.type("RAM").size(0x8000).content("Save").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct SG1000 {
|
|
||||||
SG1000(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
SG1000::SG1000(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SG1000::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SG1000::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
output.append(Memory{}.type("RAM").size(0x8000).content("Save").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct SuperGrafx {
|
|
||||||
SuperGrafx(vector<uint8_t>& data, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
SuperGrafx::operator bool() const {
|
|
||||||
return (bool)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
SuperGrafx::SuperGrafx(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SuperGrafx::manifest() const -> string {
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
namespace Heuristics {
|
|
||||||
|
|
||||||
struct WonderSwan {
|
|
||||||
WonderSwan(vector<uint8_t>& buffer, string location);
|
|
||||||
explicit operator bool() const;
|
|
||||||
auto manifest() const -> string;
|
|
||||||
|
|
||||||
private:
|
|
||||||
vector<uint8_t>& data;
|
|
||||||
string location;
|
|
||||||
};
|
|
||||||
|
|
||||||
WonderSwan::WonderSwan(vector<uint8_t>& data, string location) : data(data), location(location) {
|
|
||||||
}
|
|
||||||
|
|
||||||
WonderSwan::operator bool() const {
|
|
||||||
return data.size() >= 0x10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto WonderSwan::manifest() const -> string {
|
|
||||||
if(!operator bool()) return {};
|
|
||||||
|
|
||||||
auto metadata = &data[data.size() - 16];
|
|
||||||
|
|
||||||
bool color = metadata[7];
|
|
||||||
|
|
||||||
string ramType;
|
|
||||||
uint ramSize = 0;
|
|
||||||
switch(metadata[11]) {
|
|
||||||
case 0x01: ramType = "RAM"; ramSize = 8 * 1024; break;
|
|
||||||
case 0x02: ramType = "RAM"; ramSize = 32 * 1024; break;
|
|
||||||
case 0x03: ramType = "RAM"; ramSize = 128 * 1024; break;
|
|
||||||
case 0x04: ramType = "RAM"; ramSize = 256 * 1024; break;
|
|
||||||
case 0x05: ramType = "RAM"; ramSize = 512 * 1024; break;
|
|
||||||
case 0x10: ramType = "EEPROM"; ramSize = 128; break;
|
|
||||||
case 0x20: ramType = "EEPROM"; ramSize = 2048; break;
|
|
||||||
case 0x50: ramType = "EEPROM"; ramSize = 1024; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool orientation = metadata[12] & 1; //0 = horizontal; 1 = vertical
|
|
||||||
bool hasRTC = metadata[13] & 1;
|
|
||||||
|
|
||||||
string output;
|
|
||||||
output.append("game\n");
|
|
||||||
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
|
|
||||||
output.append(" label: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" name: ", Location::prefix(location), "\n");
|
|
||||||
output.append(" orientation: ", !orientation ? "horizontal" : "vertical", "\n");
|
|
||||||
output.append(" board\n");
|
|
||||||
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
|
|
||||||
if(ramType && ramSize)
|
|
||||||
output.append(Memory{}.type(ramType).size(ramSize).content("Save").text());
|
|
||||||
if(hasRTC)
|
|
||||||
output.append(Memory{}.type("RTC").size(0x10).content("Time").text());
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
#include <nall/nall.hpp>
|
|
||||||
using namespace nall;
|
|
||||||
|
|
||||||
#include <hiro/hiro.hpp>
|
|
||||||
using namespace hiro;
|
|
||||||
|
|
||||||
auto locate(string name) -> string {
|
|
||||||
string location = {Path::program(), name};
|
|
||||||
if(inode::exists(location)) return location;
|
|
||||||
|
|
||||||
location = {Path::userData(), "bsnes/"};
|
|
||||||
if(inode::exists(location)) return location;
|
|
||||||
|
|
||||||
directory::create({Path::userSettings(), "bsnes/"});
|
|
||||||
return {Path::userSettings(), "bsnes/", name};
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "settings.cpp"
|
|
||||||
Settings settings;
|
|
||||||
|
|
||||||
#include "heuristics/heuristics.hpp"
|
|
||||||
#include "heuristics/heuristics.cpp"
|
|
||||||
#include "heuristics/famicom.cpp"
|
|
||||||
#include "heuristics/super-famicom.cpp"
|
|
||||||
#include "heuristics/sg-1000.cpp"
|
|
||||||
#include "heuristics/sc-3000.cpp"
|
|
||||||
#include "heuristics/master-system.cpp"
|
|
||||||
#include "heuristics/mega-drive.cpp"
|
|
||||||
#include "heuristics/pc-engine.cpp"
|
|
||||||
#include "heuristics/supergrafx.cpp"
|
|
||||||
#include "heuristics/colecovision.cpp"
|
|
||||||
#include "heuristics/msx.cpp"
|
|
||||||
#include "heuristics/game-boy.cpp"
|
|
||||||
#include "heuristics/game-boy-advance.cpp"
|
|
||||||
#include "heuristics/game-gear.cpp"
|
|
||||||
#include "heuristics/wonderswan.cpp"
|
|
||||||
#include "heuristics/neo-geo-pocket.cpp"
|
|
||||||
#include "heuristics/neo-geo-pocket-color.cpp"
|
|
||||||
#include "heuristics/bs-memory.cpp"
|
|
||||||
#include "heuristics/sufami-turbo.cpp"
|
|
||||||
|
|
||||||
#include "core/core.hpp"
|
|
||||||
#include "core/core.cpp"
|
|
||||||
#include "core/famicom.cpp"
|
|
||||||
#include "core/super-famicom.cpp"
|
|
||||||
#include "core/sg-1000.cpp"
|
|
||||||
#include "core/sc-3000.cpp"
|
|
||||||
#include "core/master-system.cpp"
|
|
||||||
#include "core/mega-drive.cpp"
|
|
||||||
#include "core/pc-engine.cpp"
|
|
||||||
#include "core/supergrafx.cpp"
|
|
||||||
#include "core/colecovision.cpp"
|
|
||||||
#include "core/msx.cpp"
|
|
||||||
#include "core/game-boy.cpp"
|
|
||||||
#include "core/game-boy-color.cpp"
|
|
||||||
#include "core/game-boy-advance.cpp"
|
|
||||||
#include "core/game-gear.cpp"
|
|
||||||
#include "core/wonderswan.cpp"
|
|
||||||
#include "core/wonderswan-color.cpp"
|
|
||||||
#include "core/neo-geo-pocket.cpp"
|
|
||||||
#include "core/neo-geo-pocket-color.cpp"
|
|
||||||
#include "core/pocket-challenge-v2.cpp"
|
|
||||||
#include "core/bs-memory.cpp"
|
|
||||||
#include "core/sufami-turbo.cpp"
|
|
||||||
|
|
||||||
#if !defined(ICARUS_LIBRARY)
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
auto hiro::initialize() -> void {
|
|
||||||
Application::setName("icarus");
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <nall/main.hpp>
|
|
||||||
auto nall::main(Arguments arguments) -> void {
|
|
||||||
if(arguments.size() == 1 && arguments[0] == "--name") {
|
|
||||||
return print("icarus");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(arguments.size() == 2 && arguments[0] == "--manifest" && directory::exists(arguments[1])) {
|
|
||||||
return print(icarus.manifest(arguments[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(arguments.size() == 2 && arguments[0] == "--import" && file::exists(arguments[1])) {
|
|
||||||
if(string target = icarus.import(arguments[1])) {
|
|
||||||
return print(target, "\n");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(arguments.size() == 1 && arguments[0] == "--import") {
|
|
||||||
if(string source = BrowserDialog()
|
|
||||||
.setTitle("Load ROM File")
|
|
||||||
.setPath(settings["icarus/Path"].text())
|
|
||||||
.setFilters("ROM Files|"
|
|
||||||
"*.fc:*.nes:"
|
|
||||||
"*.sfc:*.smc:"
|
|
||||||
"*.sg1000:*.sg:"
|
|
||||||
"*.sc3000:*.sc:"
|
|
||||||
"*.ms:*.sms:"
|
|
||||||
"*.md:*.smd:*.gen:"
|
|
||||||
"*.pce:"
|
|
||||||
"*.sgx:"
|
|
||||||
"*.cv:*.col:"
|
|
||||||
"*.msx:"
|
|
||||||
"*.gb:"
|
|
||||||
"*.gbc:"
|
|
||||||
"*.gba:"
|
|
||||||
"*.gg:"
|
|
||||||
"*.ws:"
|
|
||||||
"*.wsc:"
|
|
||||||
"*.pc2:"
|
|
||||||
"*.ngp:"
|
|
||||||
"*.ngpc:*.ngc:"
|
|
||||||
"*.bs:"
|
|
||||||
"*.st:"
|
|
||||||
"*.zip"
|
|
||||||
).openFile()) {
|
|
||||||
if(string target = icarus.import(source)) {
|
|
||||||
settings["icarus/Path"].setValue(Location::path(source));
|
|
||||||
return print(target, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new ScanDialog;
|
|
||||||
new SettingsDialog;
|
|
||||||
new ImportDialog;
|
|
||||||
new ErrorDialog;
|
|
||||||
|
|
||||||
#if defined(PLATFORM_MACOS)
|
|
||||||
Application::Cocoa::onAbout([&] {
|
|
||||||
MessageDialog().setTitle("About icarus").setText({
|
|
||||||
"icarus\n\n"
|
|
||||||
"Author: byuu\n"
|
|
||||||
"License: GPLv3\n"
|
|
||||||
"Website: https://byuu.org/\n"
|
|
||||||
}).information();
|
|
||||||
});
|
|
||||||
Application::Cocoa::onPreferences([&] {
|
|
||||||
scanDialog->settingsButton.doActivate();
|
|
||||||
});
|
|
||||||
Application::Cocoa::onQuit([&] {
|
|
||||||
Application::quit();
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
scanDialog->show();
|
|
||||||
Application::run();
|
|
||||||
settings.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,24 +0,0 @@
|
||||||
struct Settings : Markup::Node {
|
|
||||||
Settings();
|
|
||||||
auto save() -> void;
|
|
||||||
};
|
|
||||||
|
|
||||||
Settings::Settings() {
|
|
||||||
Markup::Node::operator=(BML::unserialize(string::read(locate("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);
|
|
||||||
};
|
|
||||||
|
|
||||||
set("Library/Location", {Path::user(), "Emulation/"});
|
|
||||||
|
|
||||||
set("icarus/Path", Path::user());
|
|
||||||
set("icarus/CreateManifests", false);
|
|
||||||
set("icarus/UseDatabase", true);
|
|
||||||
set("icarus/UseHeuristics", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Settings::save() -> void {
|
|
||||||
file::write(locate("settings.bml"), BML::serialize(*this));
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
ErrorDialog::ErrorDialog() {
|
|
||||||
errorDialog = this;
|
|
||||||
|
|
||||||
onClose([&] { setVisible(false); scanDialog->show(); });
|
|
||||||
layout.setPadding(5);
|
|
||||||
errorLog.setEditable(false);
|
|
||||||
closeButton.setText("Close").onActivate([&] { doClose(); });
|
|
||||||
|
|
||||||
setSize({800, 360});
|
|
||||||
setCentered();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ErrorDialog::show(const string& text) -> void {
|
|
||||||
errorLog.setText(text);
|
|
||||||
setVisible();
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
ImportDialog::ImportDialog() {
|
|
||||||
importDialog = this;
|
|
||||||
|
|
||||||
onClose([&] {
|
|
||||||
stopButton.setEnabled(false).setText("Stopping ...");
|
|
||||||
abort = true;
|
|
||||||
});
|
|
||||||
layout.setPadding(5);
|
|
||||||
stopButton.setText("Stop").onActivate([&] { doClose(); });
|
|
||||||
|
|
||||||
setTitle("icarus - Importing ...");
|
|
||||||
setSize({480, layout.minimumSize().height()});
|
|
||||||
setCentered();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ImportDialog::run(vector<string> locations) -> void {
|
|
||||||
abort = false;
|
|
||||||
errors.reset();
|
|
||||||
unsigned position = 0;
|
|
||||||
|
|
||||||
setVisible(true);
|
|
||||||
for(auto& location : locations) {
|
|
||||||
auto name = Location::base(location);
|
|
||||||
|
|
||||||
if(abort) {
|
|
||||||
errors.append(string{"[", name, "] aborted"});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusLabel.setText(name);
|
|
||||||
double progress = 100.0 * (double)position++ / (double)locations.size() + 0.5;
|
|
||||||
progressBar.setPosition((unsigned)progress);
|
|
||||||
Application::processEvents();
|
|
||||||
|
|
||||||
if(!icarus.import(location)) {
|
|
||||||
errors.append(string{"[", name, "] ", icarus.error()});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setVisible(false);
|
|
||||||
|
|
||||||
if(errors) {
|
|
||||||
string message{"Import completed, but with ", errors.size(), " error", errors.size() ? "s" : "", ". View log?"};
|
|
||||||
if(MessageDialog().setTitle("icarus").setText(message).question() == "Yes") {
|
|
||||||
errorDialog->show(errors.merge("\n"));
|
|
||||||
} else {
|
|
||||||
scanDialog->show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
MessageDialog().setTitle("icarus").setText("Import completed successfully.").information();
|
|
||||||
scanDialog->show();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
ScanDialog::ScanDialog() {
|
|
||||||
scanDialog = this;
|
|
||||||
|
|
||||||
onClose(&Application::quit);
|
|
||||||
layout.setPadding(5);
|
|
||||||
pathEdit.onActivate([&] { refresh(); });
|
|
||||||
refreshButton.setIcon(Icon::Action::Refresh).setBordered(false).onActivate([&] {
|
|
||||||
pathEdit.setText(settings["icarus/Path"].text());
|
|
||||||
refresh();
|
|
||||||
});
|
|
||||||
homeButton.setIcon(Icon::Go::Home).setBordered(false).onActivate([&] {
|
|
||||||
pathEdit.setText(Path::user());
|
|
||||||
refresh();
|
|
||||||
});
|
|
||||||
upButton.setIcon(Icon::Go::Up).setBordered(false).onActivate([&] {
|
|
||||||
pathEdit.setText(Location::dir(settings["icarus/Path"].text()));
|
|
||||||
refresh();
|
|
||||||
});
|
|
||||||
scanList.onActivate([&] { activate(); });
|
|
||||||
selectAllButton.setText("Select All").onActivate([&] {
|
|
||||||
for(auto& item : scanList.items()) {
|
|
||||||
if(item.checkable()) item.setChecked(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
unselectAllButton.setText("Unselect All").onActivate([&] {
|
|
||||||
for(auto& item : scanList.items()) {
|
|
||||||
if(item.checkable()) item.setChecked(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
settingsButton.setText("Settings ...").onActivate([&] {
|
|
||||||
settingsDialog->setCentered(*this);
|
|
||||||
settingsDialog->setVisible();
|
|
||||||
settingsDialog->setFocused();
|
|
||||||
});
|
|
||||||
importButton.setText("Import ...").onActivate([&] { import(); });
|
|
||||||
|
|
||||||
setTitle("icarus");
|
|
||||||
setSize({800, 480});
|
|
||||||
setCentered();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ScanDialog::show() -> void {
|
|
||||||
setVisible();
|
|
||||||
pathEdit.setText(settings["icarus/Path"].text());
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ScanDialog::refresh() -> void {
|
|
||||||
scanList.reset();
|
|
||||||
|
|
||||||
auto pathname = pathEdit.text().transform("\\", "/");
|
|
||||||
if((pathname || Path::root() == "/") && !pathname.endsWith("/")) pathname.append("/");
|
|
||||||
|
|
||||||
settings["icarus/Path"].setValue(pathname);
|
|
||||||
pathEdit.setText(pathname);
|
|
||||||
auto contents = directory::icontents(pathname);
|
|
||||||
|
|
||||||
for(auto& name : contents) {
|
|
||||||
if(!name.endsWith("/")) continue;
|
|
||||||
if(gamePakType(Location::suffix(name))) continue;
|
|
||||||
scanList.append(ListViewItem().setIcon(Icon::Emblem::Folder).setText(name.trimRight("/")));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto& name : contents) {
|
|
||||||
if(name.endsWith("/")) continue;
|
|
||||||
if(!gameRomType(Location::suffix(name).downcase())) continue;
|
|
||||||
scanList.append(ListViewItem().setCheckable().setIcon(Icon::Emblem::File).setText(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
Application::processEvents();
|
|
||||||
scanList.setFocused();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ScanDialog::activate() -> void {
|
|
||||||
if(auto item = scanList.selected()) {
|
|
||||||
string location{settings["icarus/Path"].text(), item.text()};
|
|
||||||
if(!gamePakType(Location::suffix(location))) {
|
|
||||||
pathEdit.setText(location);
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ScanDialog::import() -> void {
|
|
||||||
vector<string> filenames;
|
|
||||||
for(auto& item : scanList.items()) {
|
|
||||||
if(item.checked()) {
|
|
||||||
filenames.append(string{settings["icarus/Path"].text(), item.text()});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!filenames) {
|
|
||||||
MessageDialog().setParent(*this).setText("Nothing selected to import.").error();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setVisible(false);
|
|
||||||
importDialog->run(filenames);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ScanDialog::gamePakType(const string& type) -> bool {
|
|
||||||
return type == ".sys"
|
|
||||||
|| type == ".fc"
|
|
||||||
|| type == ".sfc"
|
|
||||||
|| type == ".sg1000"
|
|
||||||
|| type == ".sc3000"
|
|
||||||
|| type == ".ms"
|
|
||||||
|| type == ".md"
|
|
||||||
|| type == ".pce"
|
|
||||||
|| type == ".sgx"
|
|
||||||
|| type == ".msx"
|
|
||||||
|| type == ".gb"
|
|
||||||
|| type == ".gbc"
|
|
||||||
|| type == ".gba"
|
|
||||||
|| type == ".gg"
|
|
||||||
|| type == ".ws"
|
|
||||||
|| type == ".wsc"
|
|
||||||
|| type == ".pc2"
|
|
||||||
|| type == ".ngp"
|
|
||||||
|| type == ".ngpc"
|
|
||||||
|| type == ".bs"
|
|
||||||
|| type == ".st";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ScanDialog::gameRomType(const string& type) -> bool {
|
|
||||||
return type == ".zip"
|
|
||||||
|| type == ".fc" || type == ".nes"
|
|
||||||
|| type == ".sfc" || type == ".smc"
|
|
||||||
|| type == ".sg1000" || type == ".sg"
|
|
||||||
|| type == ".sc3000" || type == ".sc"
|
|
||||||
|| type == ".ms" || type == ".sms"
|
|
||||||
|| type == ".md" || type == ".smd" || type == ".gen"
|
|
||||||
|| type == ".pce"
|
|
||||||
|| type == ".sgx"
|
|
||||||
|| type == ".msx"
|
|
||||||
|| type == ".gb"
|
|
||||||
|| type == ".gbc"
|
|
||||||
|| type == ".gba"
|
|
||||||
|| type == ".gg"
|
|
||||||
|| type == ".ws"
|
|
||||||
|| type == ".wsc"
|
|
||||||
|| type == ".pc2"
|
|
||||||
|| type == ".ngp"
|
|
||||||
|| type == ".ngpc" || type == ".ngc"
|
|
||||||
|| type == ".bs"
|
|
||||||
|| type == ".st";
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
SettingsDialog::SettingsDialog() {
|
|
||||||
settingsDialog = this;
|
|
||||||
|
|
||||||
layout.setPadding(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("Settings");
|
|
||||||
setSize({480, layout.minimumSize().height()});
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
struct ScanDialog : Window {
|
|
||||||
ScanDialog();
|
|
||||||
|
|
||||||
auto show() -> void;
|
|
||||||
auto refresh() -> void;
|
|
||||||
auto activate() -> void;
|
|
||||||
auto import() -> void;
|
|
||||||
|
|
||||||
auto gamePakType(const string& type) -> bool;
|
|
||||||
auto gameRomType(const string& type) -> bool;
|
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
|
||||||
HorizontalLayout pathLayout{&layout, Size{~0, 0}};
|
|
||||||
LineEdit pathEdit{&pathLayout, Size{~0, 0}, 0};
|
|
||||||
Button refreshButton{&pathLayout, Size{0, 0}, 0};
|
|
||||||
Button homeButton{&pathLayout, Size{0, 0}, 0};
|
|
||||||
Button upButton{&pathLayout, Size{0, 0}, 0};
|
|
||||||
ListView scanList{&layout, Size{~0, ~0}};
|
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
|
||||||
Button selectAllButton{&controlLayout, Size{100, 0}};
|
|
||||||
Button unselectAllButton{&controlLayout, Size{100, 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 {
|
|
||||||
ImportDialog();
|
|
||||||
auto run(vector<string> locations) -> void;
|
|
||||||
|
|
||||||
bool abort;
|
|
||||||
vector<string> errors;
|
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
|
||||||
Label statusLabel{&layout, Size{~0, 0}};
|
|
||||||
ProgressBar progressBar{&layout, Size{~0, 0}};
|
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
|
||||||
Button stopButton{&controlLayout, Size{80, 0}};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ErrorDialog : Window {
|
|
||||||
ErrorDialog();
|
|
||||||
auto show(const string& text) -> void;
|
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
|
||||||
TextEdit errorLog{&layout, Size{~0, ~0}};
|
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
|
||||||
Button closeButton{&controlLayout, Size{80, 0}};
|
|
||||||
};
|
|
||||||
|
|
||||||
ScanDialog* scanDialog = nullptr;
|
|
||||||
SettingsDialog* settingsDialog = nullptr;
|
|
||||||
ImportDialog* importDialog = nullptr;
|
|
||||||
ErrorDialog* errorDialog = nullptr;
|
|
Loading…
Reference in New Issue