Remove icarus, merge heuristics into bsnes.

This commit is contained in:
byuu 2019-08-01 09:05:01 +09:00
parent 0e56b27228
commit 9e8913cea0
66 changed files with 6 additions and 2575 deletions

View File

@ -14,7 +14,6 @@ linux-x86_64-binaries_task:
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a icarus/Database/* bsnes-nightly/Database
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
@ -37,7 +36,6 @@ freebsd-x86_64-binaries_task:
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a icarus/Database/* bsnes-nightly/Database
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
@ -60,7 +58,6 @@ windows-x86_64-binaries_task:
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a icarus/Database/* bsnes-nightly/Database
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly

View File

@ -30,7 +30,6 @@ ifeq ($(platform),macos)
mkdir -p out/$(name).app/Contents/Resources/
mv out/$(name) out/$(name).app/Contents/MacOS/$(name)
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
sips -s format icns $(ui)/resource/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
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).png $(prefix)/share/icons/$(name).png
cp Database/* $(prefix)/share/$(name)/Database/
cp ../icarus/Database/* $(prefix)/share/$(name)/Database/
cp Locale/* $(prefix)/share/$(name)/Locale/
endif

View File

@ -1,10 +1,10 @@
#include <nall/encode/bmp.hpp>
#include <icarus/heuristics/heuristics.hpp>
#include <icarus/heuristics/heuristics.cpp>
#include <icarus/heuristics/super-famicom.cpp>
#include <icarus/heuristics/game-boy.cpp>
#include <icarus/heuristics/bs-memory.cpp>
#include <icarus/heuristics/sufami-turbo.cpp>
#include <heuristics/heuristics.hpp>
#include <heuristics/heuristics.cpp>
#include <heuristics/super-famicom.cpp>
#include <heuristics/game-boy.cpp>
#include <heuristics/bs-memory.cpp>
#include <heuristics/sufami-turbo.cpp>
//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 {

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +0,0 @@
1 24 "icarus.Manifest"
2 ICON DISCARDABLE "icarus.ico"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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