Update to v106r07 release.

byuu says:

Changelog:

  - Super Game Boy: for the 50th time, higan won't segfault if you
    cancel the Game Boy cartridge load request
  - icarus: moved to new manifest syntax for all remaining systems
  - Game Boy: moved to new manifest syntax

Errata:

  - Game Boy: save RAM does not appear to be working for some reason
  - Famicom: higan won't even start to run this system; it just acts
    like a cartridge was never loaded ...
  - cores outside of the Super Famicom and Game Boy/Color will not run
    due to icarus/higan manifest syntax differences
This commit is contained in:
Tim Allen 2018-02-21 11:12:09 +11:00
parent 61091167b8
commit c49d3b2006
37 changed files with 915 additions and 570 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.06";
static const string Version = "106.07";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -19,6 +19,12 @@ Cartridge cartridge;
auto Cartridge::load() -> bool {
information = {};
rom = {};
ram = {};
rtc = {};
mapper = &mbc0;
accelerometer = false;
rumble = false;
if(Model::GameBoy()) {
if(auto loaded = platform->load(ID::GameBoy, "Game Boy", "gb")) {
@ -43,10 +49,9 @@ auto Cartridge::load() -> bool {
} else return false;
auto document = BML::unserialize(information.manifest);
auto board = document["board"];
information.title = document["information/title"].text();
information.title = document["game/label"].text();
auto mapperID = document["board/mapper"].text();
auto mapperID = document["game/board"].text();
if(mapperID == "MBC0" ) mapper = &mbc0;
if(mapperID == "MBC1" ) mapper = &mbc1;
if(mapperID == "MBC1M") mapper = &mbc1m;
@ -59,32 +64,37 @@ auto Cartridge::load() -> bool {
if(mapperID == "HuC1" ) mapper = &huc1;
if(mapperID == "HuC3" ) mapper = &huc3;
if(mapperID == "TAMA" ) mapper = &tama;
if(!mapper) mapper = &mbc0;
accelerometer = (bool)document["board/accelerometer"];
rumble = (bool)document["board/rumble"];
accelerometer = (bool)document["game/board/accelerometer"];
rumble = (bool)document["game/board/rumble"];
rom.size = max(0x4000, document["board/rom/size"].natural());
rom.data = (uint8*)memory::allocate(rom.size, 0xff);
if(auto name = document["board/rom/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, min(rom.size, fp->size()));
if(auto node = document["game/memory[type=ROM]"]) {
rom.size = max(0x4000, node["size"].natural());
rom.data = (uint8*)memory::allocate(rom.size, 0xff);
if(auto name = node["name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, min(rom.size, fp->size()));
}
}
}
ram.size = document["board/ram/size"].natural();
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
if(auto name = document["board/ram/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
fp->read(ram.data, min(ram.size, fp->size()));
if(auto node = document["game/memory[type=NVRAM]"]) {
ram.size = node["size"].natural();
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
if(auto name = node["name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
fp->read(ram.data, min(ram.size, fp->size()));
}
}
}
rtc.size = document["board/rtc/size"].natural();
rtc.data = (uint8*)memory::allocate(rtc.size, 0xff);
if(auto name = document["board/rtc/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
fp->read(rtc.data, min(rtc.size, fp->size()));
if(auto node = document["game/memory[type=RTC]"]) {
rtc.size = node["size"].natural();
rtc.data = (uint8*)memory::allocate(rtc.size, 0xff);
if(auto name = node["name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
fp->read(rtc.data, min(rtc.size, fp->size()));
}
}
}
@ -95,15 +105,19 @@ auto Cartridge::load() -> bool {
auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
if(auto name = document["board/ram/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size);
if(auto node = document["game/memory[type=NVRAM]"]) {
if(auto name = node["name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size);
}
}
}
if(auto name = document["board/rtc/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(rtc.data, rtc.size);
if(auto node = document["game/memory[type=RTC]"]) {
if(auto name = node["name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(rtc.data, rtc.size);
}
}
}
}

View File

@ -22,10 +22,15 @@ auto Cartridge::loadBoard(Markup::Node node) -> Markup::Node {
}
if(!matched) continue;
if(region.endsWith("-USA")
|| region.endsWith("-CAN")
|| region.endsWith("-JPN")
|| region.endsWith("-KOR")
if(region.endsWith("BRA")
|| region.endsWith("CAN")
|| region.endsWith("HKG")
|| region.endsWith("JPN")
|| region.endsWith("KOR")
|| region.endsWith("LTN")
|| region.endsWith("ROC")
|| region.endsWith("USA")
|| region.beginsWith("SHVC-")
|| region == "NTSC") {
output.append("region=ntsc\n");
} else {
@ -62,7 +67,6 @@ auto Cartridge::loadBoard(Markup::Node node) -> Markup::Node {
}
}
print(output, "\n");
return BML::unserialize(output);
}

View File

@ -1,5 +1,5 @@
database
revision: 2018-02-14
revision: 2018-02-16
//BS Memory (JPN)

View File

@ -1,5 +1,5 @@
database
revision: 2018-02-14
revision: 2018-02-16
//Sufami Turbo (JPN)

View File

@ -1,5 +1,34 @@
database
revision: 2018-02-14
revision: 2018-02-16
//Prototypes (JPN)
database
revision: 2018-02-16
game
sha256: 182cd72c2ef57119b56bef1f7c18660498422a912f1bb652771d465cd183b04e
region: JPN
revision: 1.0
board: SHVC-4PV5B-01
name: From TV Animation - Slam Dunk - Shuueisha Limited
label: From TV Animation - Slam Dunk - 集英社Limited
memory
type: EPROM
size: 0x180000
name: program.rom
game
sha256: bcba4ca39f0279f7a52657bccbffa84564eaea455e2565597b93942ec245fdb1
region: JPN
revision: 1.0
board: SHVC-2P3B-01
name: Kunio-kun no Dodgeball da yo - Zen'in Shuugou! - Tournament Special
label: くにおくんのドッジボールだよ 全員集合! トーナメントスペシャル
memory
type: EPROM
size: 0x80000
name: program.rom
//Super Comboy (KOR)
@ -82,7 +111,55 @@ game
//Super Famicom (JPN)
database
revision: 2018-02-14
revision: 2018-02-16
game
sha256: 6f6bacdd73aef29ff6a015c25db4a5cd8ba31142b2cc3fe56261d23bbf8329ea
region: SHVC-AAZJ-JPN
revision: SHVC-AAZJ-0
board: SHVC-1J5M-01
name: Angelique - Premium Box
label: アンジェリーク プレミアムBox
memory
type: ROM
size: 0x200000
name: program.rom
memory
type: NVRAM
size: 0x8000
name: save.ram
game
sha256: 1f7619ea0d02f16e2b2dcbb36013bb3405eb791885f23884b583eb63768614c4
region: SHVC-AQLJ-JPN
revision: SHVC-AQLJ-0
board: SHVC-1J5M-01
name: Angelique - Voice Fantasy
label: アンジェリーク ヴォイス・ファンタジー
memory
type: ROM
size: 0x300000
name: program.rom
memory
type: NVRAM
size: 0x8000
name: save.ram
game
sha256: ecd772c4a21101d079a795e47abbe00052bef69cc1c854a328f0077016c53311
region: SHVC-AH9J-JPN
revision: SHVC-AH9J-0
board: SHVC-1J3M-20
name: Bomberman B-Daman
label: ボンバーマン ビーダマン
memory
type: ROM
size: 0x100000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
game
sha256: 3ce321496edc5d77038de2034eb3fb354d7724afd0bc7fd0319f3eb5d57b984d
@ -104,6 +181,22 @@ game
size: 0x80000
name: download.ram
game
sha256: c195641a1b472590cb3d0be0c48d682b9fee94d7b700dd7bd3297bb995b49307
region: SHVC-B5
revision: SHVC-B5-0
board: SHVC-1J3B-01
name: Conveni Wars - Barcode Battler Senki - Super Senshi Shutsugeki seyo!
label: Conveni Wars Barcode Battler 戦記 スーパー戦士 出撃せよ!
memory
type: ROM
size: 0x80000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
game
sha256: 38a855229eab468c3ede7573db73082c66b157adfc7af787ccac50559b747f5f
region: SHVC-ZDBJ-JPN
@ -120,6 +213,54 @@ game
size: 0x8000
name: save.ram
game
sha256: dcb14c95f058a32f40cc329793f5d95fd6cf1755cffe02c0594d1c583a06d356
region: SHVC-AEMJ-JPN
revision: SHVC-AEMJ-0
board: SHVC-1J3M-20
name: Emit Vol. 1 - Toki no Maigo
label: エミット Vol. 1 時の迷子
memory
type: ROM
size: 0x300000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
game
sha256: dc1ecf27d9ce4fdf674c9405339016f5a812f7c4687e588cc6404e2b3b92541a
region: SHVC-AEIJ-JPN
revision: SHVC-AEIJ-0
board: SHVC-1J3M-20
name: Emit Vol. 2 - Inochigake no Tabi
label: エミット Vol. 2 命がけの旅
memory
type: ROM
size: 0x300000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
game
sha256: f49417cb8759a30e12439e846f7d581afd1519c625e6a0522876666098521fcc
region: SHVC-AETJ-JPN
revision: SHVC-AETJ-0
board: SHVC-1J3M-20
name: Emit Vol. 3 - Watashi ni Sayonara o
label: エミット Vol. 3 私にさよならを
memory
type: ROM
size: 0x300000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
game
sha256: 74aa3a26b66f34819fbbdcdb2475cf9161cc2590fb1ec89fb24940ef10e44332
region: SHVC-F4
@ -136,6 +277,22 @@ game
size: 0x2000
name: save.ram
game
sha256: db337a2e8cf6de653d092ba3489cabc658f91c63ec8a9db4e1866400aadf913f
region: SHVC-AGHJ-JPN
revision: SHVC-AGHJ-0
board: SHVC-2J3M-20
name: Get in the Hole
label: ゲットインザホール
memory
type: ROM
size: 0x180000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
game
sha256: 4dfba33201de6b5dec952d0f327aeb44ed784c025a72c982356dd41b52efc219
region: SHVC-ZBPJ-JPN
@ -172,6 +329,66 @@ game
size: 0x2000
name: save.ram
game
sha256: fe44f9d0db9f04f704764577b94e5bf2e18bc7a1c4ff1e6bdaca06d49ed6813c
region: SHVC-LK
revision: SHVC-LK-0
board: SHVC-1A0N-20
name: Lethal Enforcers
label: リーサルエンフォーサーズ
memory
type: ROM
size: 0x200000
name: program.rom
game
sha256: c51c5930b344f553415d54c3c964c050e1eb6355b10f5966deabb686e70e1750
region: SHVC-WE
revision: SHVC-WE-0
board: SHVC-YA0N-01
name: Mario & Wario
label: マリオとワリオ
memory
type: ROM
size: 0x100000
name: program.rom
game
sha256: e842cac1a4301be196f1e137fbd1a16866d5c913f24dbca313f4dd8bd7472f45
region: SHVC-MP
revision: SHVC-MP-0
board: SHVC-1A5B-04
name: Mario Paint
label: マリオペイント
memory
type: ROM
size: 0x100000
name: program.rom
memory
type: NVRAM
size: 0x8000
name: save.ram
game
sha256: 2298d92acdfecc7270a6c9a57a6ddc55d7fa841fe9c0e7c0d64e33682fffa429
region: SHVC-A4WJ-JPN
revision: SHVC-A4WJ-0
board: SHVC-1L5B-20
name: Mini Yonku Shining Scorpion - Let's & Go!!
label: ミニ四駆シャイニングスコーピオン レッツ&ゴー!!
memory
type: ROM
size: 0x400000
name: program.rom
memory
type: NVRAM
size: 0x8000
name: save.ram
memory
type: RAM
size: 0x800
name: internal.ram
game
sha256: d712adecbde70a74c4a580fe90a45d0d19f2641d1b4e091d507bddeec9601de1
region: SHVC-ZMCJ-JPN
@ -268,6 +485,34 @@ game
size: 0x40000
name: program.rom
game
sha256: 442397be57b3740ca236cfb37633b95f88a2c80dafc94b56a805229793563ce1
region: SHVC-9B
revision: SHVC-9B-0
board: SHVC-1J0N-10
name: Super Bomberman 2 - Taikenban
label: スーパーボンバーマン2 体験版
memory
type: ROM
size: 0x100000
name: program.rom
game
sha256: 5f8e6894f71c62bc5e70485715dbd2e2d8f3c0383ec54211dc5fe180098d0e3f
region: SHVC-AK8J-JPN
revision: SHVC-AK8J-0
board: SHVC-1J3M-20
name: Super Bomberman 5 - CoroCoro Comic
label: スーパーボンバーマン5 コロコロコミック
memory
type: ROM
size: 0x200000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
game
sha256: 4d7fc331a811b8dc630b469262fd6f45e289243cef83101f32038158967d1b28
region: SHVC-SGB
@ -348,6 +593,66 @@ game
size: 0x2000
name: save.ram
game
sha256: ecd462c64516169cc83dd266af354fe676fcf53811863a361d78cc918619da0d
region: SHVC-XL
revision: SHVC-XL-1
board: SHVC-1A5M-20
name: Super Sangokushi II
label: スーパー三国志II 復刻版
memory
type: ROM
size: 0x100000
name: program.rom
memory
type: NVRAM
size: 0x8000
name: save.ram
game
sha256: fde83367c1caf6edfb41a0c609bacc90084e9808b32ba52b13d204eb59535ab5
region: SHVC-LR
revision: SHVC-LR-0
board: SHVC-1A0N-20
name: Super Scope 6
label: スーパースコープ6
memory
type: ROM
size: 0x100000
name: program.rom
game
sha256: 8dda3b0888a32005041f2feb9be4e14807d40291f951a4612461cf41dac9cb78
region: SHVC-T2
revision: SHVC-T2-0
board: SHVC-1A1B-06
name: Super Tetris 2 + Bombliss
label: Super Tetris 2 + Bombliss
memory
type: ROM
size: 0x100000
name: program.rom
memory
type: NVRAM
size: 0x800
name: save.ram
game
sha256: b66da2a23f249e525b1dd444596a3f10559cb3c30fa3c0bca83ed8f4405fcfcf
region: SHVC-ANZJ-JPN
revision: SHVC-ANZJ-0
board: SHVC-1J3M-20
name: Undake 30 - Same Game Daisakusen - Mario Version
label: Undake 30 鮫亀大作戦 マリオバージョン
memory
type: ROM
size: 0x60000
name: program.rom
memory
type: NVRAM
size: 0x2000
name: save.ram
//Super Famicom (ROC)
database

View File

@ -5,10 +5,9 @@ auto Icarus::bsMemoryManifest(string location) -> string {
}
auto Icarus::bsMemoryManifest(vector<uint8_t>& buffer, string location) -> string {
auto digest = Hash::SHA256(buffer).digest();
if(settings["icarus/UseDatabase"].boolean()) {
for(auto game : database.bsMemory.find("game")) {
auto digest = Hash::SHA256(buffer).digest();
for(auto game : Database::BSMemory.find("game")) {
if(game["sha256"].text() == digest) return BML::serialize(game);
}
}

View File

@ -1,18 +1,18 @@
Icarus::Icarus() {
database.famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
database.superFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.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.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.bsMemory = BML::unserialize(string::read(locate("Database/BS Memory.bml")));
database.sufamiTurbo = BML::unserialize(string::read(locate("Database/Sufami Turbo.bml")));
Database::Famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
Database::SuperFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.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::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::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 {
@ -35,7 +35,7 @@ auto Icarus::failure(string message) -> string {
auto Icarus::manifest(string location) -> string {
location.transform("\\", "/").trimRight("/").append("/");
if(!directory::exists(location)) return "";
if(!directory::exists(location)) return {};
auto type = Location::suffix(location).downcase();
if(type == ".fc") return famicomManifest(location);
@ -53,7 +53,7 @@ auto Icarus::manifest(string location) -> string {
if(type == ".bs") return bsMemoryManifest(location);
if(type == ".st") return sufamiTurboManifest(location);
return "";
return {};
}
auto Icarus::import(string location) -> string {

View File

@ -38,7 +38,7 @@ struct Icarus {
//famicom.cpp
auto famicomManifest(string location) -> string;
auto famicomManifest(vector<uint8_t>& buffer, string location, uint* prgrom = nullptr, uint* chrrom = nullptr) -> string;
auto famicomManifest(vector<uint8_t>& buffer, string location) -> string;
auto famicomImport(vector<uint8_t>& buffer, string location) -> string;
//super-famicom.cpp
@ -109,21 +109,21 @@ struct Icarus {
private:
string errorMessage;
string_vector missingFiles;
struct {
Markup::Node famicom;
Markup::Node superFamicom;
Markup::Node masterSystem;
Markup::Node megaDrive;
Markup::Node pcEngine;
Markup::Node superGrafx;
Markup::Node gameBoy;
Markup::Node gameBoyColor;
Markup::Node gameBoyAdvance;
Markup::Node gameGear;
Markup::Node wonderSwan;
Markup::Node wonderSwanColor;
Markup::Node bsMemory;
Markup::Node sufamiTurbo;
} database;
};
namespace Database {
Markup::Node Famicom;
Markup::Node SuperFamicom;
Markup::Node MasterSystem;
Markup::Node MegaDrive;
Markup::Node PCEngine;
Markup::Node SuperGrafx;
Markup::Node GameBoy;
Markup::Node GameBoyColor;
Markup::Node GameBoyAdvance;
Markup::Node GameGear;
Markup::Node WonderSwan;
Markup::Node WonderSwanColor;
Markup::Node BSMemory;
Markup::Node SufamiTurbo;
};

View File

@ -6,35 +6,20 @@ auto Icarus::famicomManifest(string location) -> string {
return famicomManifest(buffer, location);
}
auto Icarus::famicomManifest(vector<uint8_t>& buffer, string location, uint* prgrom, uint* chrrom) -> string {
string markup;
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
if(settings["icarus/UseDatabase"].boolean() && !markup) {
for(auto node : database.famicom) {
if(node["sha256"].text() == digest) {
markup.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !markup) {
FamicomCartridge cartridge{buffer.data(), buffer.size()};
if(markup = cartridge.markup) {
markup.append("\n");
markup.append("information\n");
markup.append(" title: ", Location::prefix(location), "\n");
markup.append(" sha256: ", digest, "\n");
markup.append(" note: ", "heuristically generated by icarus\n");
}
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::Famicom game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
auto document = BML::unserialize(markup);
if(prgrom) *prgrom = document["board/prg/rom/size"].natural(); //0 if node does not exist
if(chrrom) *chrrom = document["board/chr/rom/size"].natural(); //0 if node does not exist
return markup;
return {};
}
auto Icarus::famicomImport(vector<uint8_t>& buffer, string location) -> string {
@ -42,20 +27,22 @@ auto Icarus::famicomImport(vector<uint8_t>& buffer, string location) -> string {
auto source = Location::path(location);
string target{settings["Library/Location"].text(), "Famicom/", name, ".fc/"};
uint prgrom = 0;
uint chrrom = 0;
auto markup = famicomManifest(buffer, location, &prgrom, &chrrom);
if(!markup) return failure("failed to parse ROM image");
auto manifest = famicomManifest(buffer, location);
if(!manifest) return failure("failed to parse ROM image");
auto document = BML::unserialize(manifest);
uint prgrom = document["game/memory[name=program.rom]"]["size"].natural();
uint chrrom = document["game/memory[name=character.rom]"]["size"].natural();
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"}, markup);
write({target, "ines.rom"}, buffer.data(), 16);
write({target, "program.rom"}, buffer.data() + 16, prgrom);
if(settings["icarus/CreateManifests"].boolean()) write({target, "manifest.bml"}, manifest);
write({target, "ines.rom"}, &buffer[0], 16);
write({target, "program.rom"}, &buffer[16], prgrom);
if(!chrrom) return success(target);
write({target, "character.rom"}, buffer.data() + 16 + prgrom, chrrom);
write({target, "character.rom"}, &buffer[16 + prgrom], chrrom);
return success(target);
}

View File

@ -5,30 +5,19 @@ auto Icarus::gameBoyAdvanceManifest(string location) -> string {
}
auto Icarus::gameBoyAdvanceManifest(vector<uint8_t>& buffer, string location) -> string {
string markup;
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
if(settings["icarus/UseDatabase"].boolean() && !markup) {
for(auto node : database.gameBoyAdvance) {
if(node["sha256"].text() == digest) {
markup.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !markup) {
GameBoyAdvanceCartridge cartridge{buffer.data(), buffer.size()};
if(markup = cartridge.markup) {
markup.append("\n");
markup.append("information\n");
markup.append(" title: ", Location::prefix(location), "\n");
markup.append(" sha256: ", digest, "\n");
markup.append(" note: ", "heuristically generated by icarus\n");
}
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::GameBoyAdvance game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return markup;
return {};
}
auto Icarus::gameBoyAdvanceImport(vector<uint8_t>& buffer, string location) -> string {
@ -36,15 +25,15 @@ auto Icarus::gameBoyAdvanceImport(vector<uint8_t>& buffer, string location) -> s
auto source = Location::path(location);
string target{settings["Library/Location"].text(), "Game Boy Advance/", name, ".gba/"};
auto markup = gameBoyAdvanceManifest(buffer, location);
if(!markup) return failure("failed to parse ROM image");
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"}, markup);
if(settings["icarus/CreateManifests"].boolean()) write({target, "manifest.bml"}, manifest);
write({target, "program.rom"}, buffer);
return success(target);
}

View File

@ -5,30 +5,20 @@ auto Icarus::gameBoyColorManifest(string location) -> string {
}
auto Icarus::gameBoyColorManifest(vector<uint8_t>& buffer, string location) -> string {
string markup;
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
auto digest = Hash::SHA256(buffer).digest();
if(settings["icarus/UseDatabase"].boolean() && !markup) {
for(auto node : database.gameBoyColor) {
if(node["sha256"].text() == digest) {
markup.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !markup) {
GameBoyCartridge cartridge{buffer.data(), buffer.size()};
if(markup = cartridge.markup) {
markup.append("\n");
markup.append("information\n");
markup.append(" title: ", Location::prefix(location), "\n");
markup.append(" sha256: ", digest, "\n");
markup.append(" note: ", "heuristically generated by icarus\n");
}
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::GameBoy game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return markup;
return {};
}
auto Icarus::gameBoyColorImport(vector<uint8_t>& buffer, string location) -> string {
@ -36,15 +26,15 @@ auto Icarus::gameBoyColorImport(vector<uint8_t>& buffer, string location) -> str
auto source = Location::path(location);
string target{settings["Library/Location"].text(), "Game Boy Color/", name, ".gbc/"};
auto markup = gameBoyColorManifest(buffer, location);
if(!markup) return failure("failed to parse ROM image");
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"}, markup);
if(settings["icarus/CreateManifests"].boolean()) write({target, "manifest.bml"}, manifest);
write({target, "program.rom"}, buffer);
return success(target);
}

View File

@ -5,30 +5,20 @@ auto Icarus::gameBoyManifest(string location) -> string {
}
auto Icarus::gameBoyManifest(vector<uint8_t>& buffer, string location) -> string {
string markup;
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
auto digest = Hash::SHA256(buffer).digest();
if(settings["icarus/UseDatabase"].boolean() && !markup) {
for(auto node : database.gameBoy) {
if(node["sha256"].text() == digest) {
markup.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !markup) {
GameBoyCartridge cartridge{buffer.data(), buffer.size()};
if(markup = cartridge.markup) {
markup.append("\n");
markup.append("information\n");
markup.append(" title: ", Location::prefix(location), "\n");
markup.append(" sha256: ", digest, "\n");
markup.append(" note: ", "heuristically generated by icarus\n");
}
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::GameBoy game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return markup;
return {};
}
auto Icarus::gameBoyImport(vector<uint8_t>& buffer, string location) -> string {
@ -36,15 +26,15 @@ auto Icarus::gameBoyImport(vector<uint8_t>& buffer, string location) -> string {
auto source = Location::path(location);
string target{settings["Library/Location"].text(), "Game Boy/", name, ".gb/"};
auto markup = gameBoyManifest(buffer, location);
if(!markup) return failure("failed to parse ROM image");
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"}, markup);
if(settings["icarus/CreateManifests"].boolean()) write({target, "manifest.bml"}, manifest);
write({target, "program.rom"}, buffer);
return success(target);
}

View File

@ -5,24 +5,19 @@ auto Icarus::gameGearManifest(string location) -> string {
}
auto Icarus::gameGearManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.gameGear) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !manifest) {
GameGearCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::GameGear game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return manifest;
return {};
}
auto Icarus::gameGearImport(vector<uint8_t>& buffer, string location) -> string {

View File

@ -5,24 +5,19 @@ auto Icarus::masterSystemManifest(string location) -> string {
}
auto Icarus::masterSystemManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.masterSystem) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !manifest) {
MasterSystemCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::MasterSystem game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return manifest;
return {};
}
auto Icarus::masterSystemImport(vector<uint8_t>& buffer, string location) -> string {

View File

@ -5,24 +5,19 @@ auto Icarus::megaDriveManifest(string location) -> string {
}
auto Icarus::megaDriveManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.megaDrive) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !manifest) {
MegaDriveCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::MegaDrive game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return manifest;
return {};
}
auto Icarus::megaDriveImport(vector<uint8_t>& buffer, string location) -> string {

View File

@ -5,24 +5,19 @@ auto Icarus::pcEngineManifest(string location) -> string {
}
auto Icarus::pcEngineManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.pcEngine) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !manifest) {
PCEngineCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::PCEngine game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return manifest;
return {};
}
auto Icarus::pcEngineImport(vector<uint8_t>& buffer, string location) -> string {

View File

@ -5,10 +5,9 @@ auto Icarus::sufamiTurboManifest(string location) -> string {
}
auto Icarus::sufamiTurboManifest(vector<uint8_t>& buffer, string location) -> string {
auto digest = Hash::SHA256(buffer).digest();
if(settings["icarus/UseDatabase"].boolean()) {
for(auto game : database.sufamiTurbo.find("game")) {
auto digest = Hash::SHA256(buffer).digest();
for(auto game : Database::SufamiTurbo.find("game")) {
if(game["sha256"].text() == digest) return BML::serialize(game);
}
}

View File

@ -10,10 +10,9 @@ auto Icarus::superFamicomManifest(string location) -> string {
}
auto Icarus::superFamicomManifest(vector<uint8_t>& buffer, string location) -> string {
auto digest = Hash::SHA256(buffer).digest();
if(settings["icarus/UseDatabase"].boolean()) {
for(auto game : database.superFamicom.find("game")) {
auto digest = Hash::SHA256(buffer).digest();
for(auto game : Database::SuperFamicom.find("game")) {
if(game["sha256"].text() == digest) return BML::serialize(game);
}
}

View File

@ -5,24 +5,19 @@ auto Icarus::superGrafxManifest(string location) -> string {
}
auto Icarus::superGrafxManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.superGrafx) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !manifest) {
SuperGrafxCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::SuperGrafx game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return manifest;
return {};
}
auto Icarus::superGrafxImport(vector<uint8_t>& buffer, string location) -> string {

View File

@ -5,24 +5,19 @@ auto Icarus::wonderSwanColorManifest(string location) -> string {
}
auto Icarus::wonderSwanColorManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.wonderSwanColor) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !manifest) {
WonderSwanCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::WonderSwan game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return manifest;
return {};
}
auto Icarus::wonderSwanColorImport(vector<uint8_t>& buffer, string location) -> string {

View File

@ -5,24 +5,19 @@ auto Icarus::wonderSwanManifest(string location) -> string {
}
auto Icarus::wonderSwanManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.wonderSwan) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
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() && !manifest) {
WonderSwanCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
if(settings["icarus/UseHeuristics"].boolean()) {
Heuristics::WonderSwan game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return manifest;
return {};
}
auto Icarus::wonderSwanImport(vector<uint8_t>& buffer, string location) -> string {

View File

@ -1,9 +1,8 @@
namespace Heuristics {
struct BSMemory {
struct BSMemory : Heuristics {
BSMemory(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
private:
@ -19,17 +18,14 @@ BSMemory::operator bool() const {
}
auto BSMemory::manifest() const -> string {
if(!operator bool()) return "";
if(!operator bool()) return {};
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" memory\n");
output.append(" type: NAND\n");
output.append(" size: 0x", hex(data.size()), "\n");
output.append(" name: program.rom\n");
output.append(memory("NAND", data.size(), "program.rom"));
return output;
}

View File

@ -1,161 +1,173 @@
struct FamicomCartridge {
FamicomCartridge(const uint8_t* data, uint size);
namespace Heuristics {
string markup;
struct Famicom : Heuristics {
Famicom(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
//private:
uint mapper;
uint mirror;
uint prgrom;
uint prgram;
uint chrrom;
uint chrram;
private:
vector<uint8_t>& data;
string location;
};
FamicomCartridge::FamicomCartridge(const uint8_t* data, uint size) {
if(size < 16) return;
if(data[0] != 'N') return;
if(data[1] != 'E') return;
if(data[2] != 'S') return;
if(data[3] != 26) return;
Famicom::Famicom(vector<uint8_t>& data, string location) : data(data), location(location) {
}
mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
prgrom = data[4] * 0x4000;
chrrom = data[5] * 0x2000;
prgram = 0u;
chrram = chrrom == 0u ? 8192u : 0u;
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;
}
markup.append("board ");
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(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
switch(mapper) {
default:
markup.append("id:NES-NROM-256\n");
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
output.append(" board: NES-NROM-256\n");
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 1:
markup.append("id:NES-SXROM\n");
markup.append(" chip type=MMC1B2\n");
output.append(" board: NES-SXROM\n");
output.append(" chip type=MMC1B2\n");
prgram = 8192;
break;
case 2:
markup.append("id:NES-UOROM\n");
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
output.append(" board: NES-UOROM\n");
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 3:
markup.append("id:NES-CNROM\n");
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
output.append(" board: NES-CNROM\n");
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 4:
//MMC3
markup.append("id:NES-TLROM\n");
markup.append(" chip type=MMC3B\n");
output.append(" board: NES-TLROM\n");
output.append(" chip type=MMC3B\n");
prgram = 8192;
//MMC6
//markup.append("id:NES-HKROM\n");
//markup.append(" chip type=MMC6n");
//output.append(" board: NES-HKROM\n");
//output.append(" chip type=MMC6\n");
//prgram = 1024;
break;
case 5:
markup.append("id:NES-ELROM\n");
markup.append(" chip type=MMC5\n");
output.append(" board: NES-ELROM\n");
output.append(" chip type=MMC5\n");
prgram = 65536;
break;
case 7:
markup.append("id:NES-AOROM\n");
output.append(" board: NES-AOROM\n");
break;
case 9:
markup.append("id:NES-PNROM\n");
markup.append(" chip type=MMC2\n");
output.append(" board: NES-PNROM\n");
output.append(" chip type=MMC2\n");
prgram = 8192;
break;
case 10:
markup.append("id:NES-FKROM\n");
markup.append(" chip type=MMC4\n");
output.append(" board: NES-FKROM\n");
output.append(" chip type=MMC4\n");
prgram = 8192;
break;
case 16:
markup.append("id:BANDAI-FCG\n");
markup.append(" chip type=LZ93D50\n");
output.append(" board: BANDAI-FCG\n");
output.append(" chip type=LZ93D50\n");
break;
case 21:
case 23:
case 25:
//VRC4
markup.append("id:KONAMI-VRC-4\n");
markup.append(" chip type=VRC4\n");
markup.append(" pinout a0=1 a1=0\n");
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
markup.append("id:KONAMI-VRC-2\n");
markup.append(" chip type=VRC2\n");
markup.append(" pinout a0=0 a1=1\n");
output.append(" board: KONAMI-VRC-2\n");
output.append(" chip type=VRC2\n");
output.append(" pinout a0=0 a1=1\n");
break;
case 24:
markup.append("id:KONAMI-VRC-6\n");
markup.append(" chip type=VRC6\n");
output.append(" board: KONAMI-VRC-6\n");
output.append(" chip type=VRC6\n");
break;
case 26:
markup.append("id:KONAMI-VRC-6\n");
markup.append(" chip type=VRC6\n");
output.append(" board: KONAMI-VRC-6\n");
output.append(" chip type=VRC6\n");
prgram = 8192;
break;
case 34:
markup.append("id:NES-BNROM\n");
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
output.append(" board: NES-BNROM\n");
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 66:
markup.append("id:NES-GNROM\n");
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
output.append(" board: NES-GNROM\n");
output.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 69:
markup.append("id:SUNSOFT-5B\n");
markup.append(" chip type=5B\n");
output.append(" board: SUNSOFT-5B\n");
output.append(" chip type=5B\n");
prgram = 8192;
break;
case 73:
markup.append("id:KONAMI-VRC-3\n");
markup.append(" chip type=VRC3\n");
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
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:
markup.append("id:KONAMI-VRC-1\n");
markup.append(" chip type=VRC1\n");
output.append(" board: KONAMI-VRC-1\n");
output.append(" chip type=VRC1\n");
break;
case 85:
markup.append("id:KONAMI-VRC-7\n");
markup.append(" chip type=VRC7\n");
output.append(" board: KONAMI-VRC-7\n");
output.append(" chip type=VRC7\n");
prgram = 8192;
break;
}
markup.append(" prg\n");
if(prgrom) markup.append(" rom name=program.rom size=0x", hex(prgrom), "\n");
if(prgram) markup.append(" ram name=save.ram size=0x", hex(prgram), "\n");
if(prgrom) output.append(memory("ROM", prgrom, "program.rom"));
if(prgram) output.append(memory("NVRAM", prgram, "save.ram"));
if(chrrom) output.append(memory("ROM", chrrom, "character.rom"));
if(chrram) output.append(memory("RAM", chrram, "character.ram"));
return output;
}
markup.append(" chr\n");
if(chrrom) markup.append(" rom name=character.rom size=0x", hex(chrrom), "\n");
if(chrram) markup.append(" ram size=0x", hex(chrram), "\n");
}

View File

@ -1,50 +1,66 @@
struct GameBoyAdvanceCartridge {
GameBoyAdvanceCartridge(const uint8_t* data, unsigned size);
namespace Heuristics {
string markup;
string identifiers;
struct GameBoyAdvance : Heuristics {
GameBoyAdvance(vector<uint8_t>& buffer, string location);
explicit operator bool() const;
auto manifest() const -> string;
private:
vector<uint8_t>& data;
string location;
};
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t* data, unsigned size) {
struct Identifier {
string name;
unsigned size;
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 {};
string_vector identifiers = {
"SRAM_V",
"SRAM_F_V",
"EEPROM_V",
"FLASH_V",
"FLASH512_V",
"FLASH1M_V",
};
vector<Identifier> idlist;
idlist.append({"SRAM_V", 6});
idlist.append({"SRAM_F_V", 8});
idlist.append({"EEPROM_V", 8});
idlist.append({"FLASH_V", 7});
idlist.append({"FLASH512_V", 10});
idlist.append({"FLASH1M_V", 9});
string_vector list;
for(auto& id : idlist) {
for(signed n = 0; n < size - 16; n++) {
if(!memcmp(data + n, (const char*)id.name, id.size)) {
const char* p = (const char*)data + n + id.size;
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];
memcpy(text, data + n, id.size + 3);
text[id.size + 3] = 0;
memory::copy(text, &data[n], identifier.size() + 3);
text[identifier.size() + 3] = 0;
if(!list.find(text)) list.append(text);
}
}
}
}
identifiers = list.merge(",");
markup.append("board\n");
markup.append(" rom type=mrom name=program.rom size=0x", hex(size), "\n");
if(0);
else if(identifiers.beginsWith("SRAM_V" )) markup.append(" ram type=sram name=save.ram size=0x8000\n");
else if(identifiers.beginsWith("SRAM_F_V" )) markup.append(" ram type=sram name=save.ram size=0x8000\n");
else if(identifiers.beginsWith("EEPROM_V" )) markup.append(" ram type=eeprom name=save.ram size=0x0\n");
else if(identifiers.beginsWith("FLASH_V" )) markup.append(" ram type=flash name=save.ram size=0x10000\n");
else if(identifiers.beginsWith("FLASH512_V")) markup.append(" ram type=flash name=save.ram size=0x10000\n");
else if(identifiers.beginsWith("FLASH1M_V" )) markup.append(" ram type=flash name=save.ram size=0x20000\n");
//if(identifiers) markup.append(" #detected: ", identifiers, "\n");
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
if(!list);
else if(list.left().beginsWith("SRAM_V" )) output.append(memory("NVRAM", 0x8000, "save.ram"));
else if(list.left().beginsWith("SRAM_F_V" )) output.append(memory("NVRAM", 0x8000, "save.ram"));
else if(list.left().beginsWith("EEPROM_V" )) output.append(memory("EEPROM", 0x0, "save.ram"));
else if(list.left().beginsWith("FLASH_V" )) output.append(memory("NAND", 0x10000, "save.ram"));
else if(list.left().beginsWith("FLASH512_V")) output.append(memory("NAND", 0x10000, "save.ram"));
else if(list.left().beginsWith("FLASH1M_V" )) output.append(memory("NAND", 0x20000, "save.ram"));
return output;
}
}

View File

@ -1,12 +1,38 @@
struct GameBoyCartridge {
GameBoyCartridge(uint8_t* data, uint size);
namespace Heuristics {
string markup;
struct GameBoy : Heuristics {
GameBoy(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
bool black = false; //cartridge works in DMG+CGB mode
bool clear = false; //cartridge works in CGB mode only
private:
auto read(uint offset) const -> uint8_t { return data[headerAddress + offset]; }
vector<uint8_t>& data;
string location;
uint headerAddress = 0;
};
GameBoy::GameBoy(vector<uint8_t>& data, string location) : data(data), location(location) {
headerAddress = data.size() < 0x8000 ? data.size() : data.size() - 0x8000;
if(read(0x0104) == 0xce && read(0x0105) == 0xed && read(0x0106) == 0x66 && read(0x0107) == 0x66
&& read(0x0108) == 0xcc && read(0x0109) == 0x0d && read(0x0147) >= 0x0b && read(0x0147) <= 0x0d
) { //MMM01 stores header at bottom of data[]
} else { //all other mappers store header at top of data[]
headerAddress = 0;
}
}
GameBoy::operator bool() const {
return data.size() >= 0x4000;
}
auto GameBoy::manifest() const -> string {
if(!operator bool()) return {};
bool black = (read(0x0143) & 0xc0) == 0x80; //cartridge works in DMG+CGB mode
bool clear = (read(0x0143) & 0xc0) == 0xc0; //cartridge works in CGB mode only
string mapper = "MBC0";
bool flash = false;
bool battery = false;
bool ram = false;
@ -18,27 +44,10 @@ struct GameBoyCartridge {
uint romSize = 0;
uint ramSize = 0;
uint rtcSize = 0;
};
GameBoyCartridge::GameBoyCartridge(uint8_t* data, uint size) {
if(size < 0x4000) return;
string mapper = "MBC0";
uint index = size < 0x8000 ? size : size - 0x8000;
if(data[index + 0x0104] == 0xce && data[index + 0x0105] == 0xed
&& data[index + 0x0106] == 0x66 && data[index + 0x0107] == 0x66
&& data[index + 0x0108] == 0xcc && data[index + 0x0109] == 0x0d
&& data[index + 0x0147] >= 0x0b && data[index + 0x0147] <= 0x0d
) {
//MMM01 stores header at bottom of data[]
} else {
//all other mappers store header at top of data[]
index = 0;
}
black = (data[index + 0x0143] & 0xc0) == 0x80;
clear = (data[index + 0x0143] & 0xc0) == 0xc0;
switch(data[index + 0x0147]) {
switch(read(0x0147)) {
case 0x00:
mapper = "MBC0";
@ -195,7 +204,7 @@ GameBoyCartridge::GameBoyCartridge(uint8_t* data, uint size) {
}
switch(data[index + 0x0148]) { default:
switch(read(0x0148)) { default:
case 0x00: romSize = 2 * 16 * 1024; break;
case 0x01: romSize = 4 * 16 * 1024; break;
case 0x02: romSize = 8 * 16 * 1024; break;
@ -211,7 +220,7 @@ GameBoyCartridge::GameBoyCartridge(uint8_t* data, uint size) {
if(mapper == "MBC6" && flash) flashSize = 1024 * 1024;
switch(data[index + 0x0149]) { default:
switch(read(0x0149)) { default:
case 0x00: ramSize = 0 * 1024; break;
case 0x01: ramSize = 2 * 1024; break;
case 0x02: ramSize = 8 * 1024; break;
@ -226,9 +235,19 @@ GameBoyCartridge::GameBoyCartridge(uint8_t* data, uint size) {
if(mapper == "MBC3" && rtc) rtcSize = 13;
if(mapper == "TAMA" && rtc) rtcSize = 21;
markup.append("board mapper=", mapper, accelerometer ? " accelerometer" : "", rumble ? " rumble" : "", "\n");
markup.append(" rom name=program.rom size=0x", hex(romSize), "\n");
if(flash && flashSize) markup.append(" flash name=download.rom size=0x", hex(flashSize), "\n");
if(ram && ramSize) markup.append(" ram ", battery ? "name=save.ram " : "", "size=0x", hex(ramSize), "\n");
if(rtc && rtcSize) markup.append(" rtc ", battery ? "name=rtc.ram " : "", "size=0x", hex(rtcSize), "\n");
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" board: ", mapper, "\n");
if(accelerometer) output.append(" accelerometer\n");
if(rumble) output.append(" rumble\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
if(flash && flashSize) output.append(memory("NAND", flashSize, "download.rom"));
if(ram && ramSize) output.append(memory(battery ? "NVRAM" : "RAM", ramSize, "save.ram"));
if(rtc && rtcSize) output.append(memory("RTC", rtcSize, "rtc.ram"));
return output;
}
}

View File

@ -1,20 +1,30 @@
struct GameGearCartridge {
GameGearCartridge(string location, uint8_t* data, uint size);
namespace Heuristics {
string manifest;
struct GameGear : Heuristics {
GameGear(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
//private:
struct Information {
} information;
private:
vector<uint8_t>& data;
string location;
};
GameGearCartridge::GameGearCartridge(string location, uint8_t* data, uint size) {
manifest.append("board\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
manifest.append("\n");
manifest.append("information\n");
manifest.append(" title: ", Location::prefix(location), "\n");
manifest.append(" sha256: ", Hash::SHA256(data, size).digest(), "\n");
manifest.append("\n");
manifest.append("note: heuristically generated by icarus\n");
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(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
return output;
}
}

View File

@ -0,0 +1,12 @@
namespace Heuristics {
auto Heuristics::memory(string type, uint size, string name) const -> string {
string output;
output.append(" memory\n");
output.append(" type: ", type, "\n");
output.append(" size: 0x", hex(size), "\n");
output.append(" name: ", name, "\n");
return output;
}
}

View File

@ -0,0 +1,7 @@
namespace Heuristics {
struct Heuristics {
auto memory(string type, uint size, string name) const -> string;
};
}

View File

@ -1,21 +1,31 @@
struct MasterSystemCartridge {
MasterSystemCartridge(string location, uint8_t* data, uint size);
namespace Heuristics {
string manifest;
struct MasterSystem : Heuristics {
MasterSystem(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
//private:
struct Information {
} information;
private:
vector<uint8_t>& data;
string location;
};
MasterSystemCartridge::MasterSystemCartridge(string location, uint8_t* data, uint size) {
manifest.append("board\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
manifest.append(" ram name=save.ram size=0x8000\n");
manifest.append("\n");
manifest.append("information\n");
manifest.append(" title: ", Location::prefix(location), "\n");
manifest.append(" sha256: ", Hash::SHA256(data, size).digest(), "\n");
manifest.append("\n");
manifest.append("note: heuristically generated by icarus\n");
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(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
output.append(memory("NVRAM", 0x8000, "save.ram"));
return output;
}
}

View File

@ -1,15 +1,24 @@
struct MegaDriveCartridge {
MegaDriveCartridge(string location, uint8_t* data, uint size);
namespace Heuristics {
string manifest;
struct MegaDrive : Heuristics {
MegaDrive(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
//private:
struct Information {
} information;
private:
vector<uint8_t>& data;
string location;
};
MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size) {
if(size < 0x200) return;
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";
@ -40,32 +49,38 @@ MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size
string_vector 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(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(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(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");
regions.append("NTSC-J");
}
manifest.append("board region=", regions.left(), "\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
if(ramSize && ramMode != "none")
manifest.append(" ram name=save.ram size=0x", hex(ramSize), " offset=0x", hex(ramFrom), " mode=", ramMode, "\n");
manifest.append("\n");
manifest.append("information\n");
manifest.append(" title: ", Location::prefix(location), "\n");
manifest.append("\n");
manifest.append("note: heuristically generated by icarus\n");
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" region: ", regions.left(), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
if(ramSize && ramMode != "none") {
output.append(memory("NVRAM", ramSize, "save.ram"));
output.append(" mode: ", ramMode, "\n");
output.append(" offset: 0x", hex(ramFrom), "\n");
}
return output;
}
}

View File

@ -1,23 +1,35 @@
struct PCEngineCartridge {
PCEngineCartridge(string location, uint8_t* data, uint size);
namespace Heuristics {
string manifest;
struct PCEngine : Heuristics {
PCEngine(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
//private:
struct Information {
} information;
private:
vector<uint8_t>& data;
string location;
};
PCEngineCartridge::PCEngineCartridge(string location, uint8_t* data, uint size) {
//skip header
if((size & 0x1fff) == 512) data += 512, size -= 512;
manifest.append("board\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
manifest.append("\n");
manifest.append("information\n");
manifest.append(" title: ", Location::prefix(location), "\n");
manifest.append(" sha256: ", Hash::SHA256(data, size).digest(), "\n");
manifest.append("\n");
manifest.append("note: heuristically generated by icarus\n");
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(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
return output;
}
}

View File

@ -1,6 +1,6 @@
namespace Heuristics {
struct SufamiTurbo {
struct SufamiTurbo : Heuristics {
SufamiTurbo(vector<uint8_t>& data, string location);
explicit operator bool() const;
@ -19,27 +19,18 @@ SufamiTurbo::operator bool() const {
}
auto SufamiTurbo::manifest() const -> string {
if(!operator bool()) return "";
if(!operator bool()) return {};
uint romSize = data[0x36] * 0x20000; //128KB
uint ramSize = data[0x37] * 0x800; // 2KB
string output;
output.append("game\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
if(romSize) {
output.append(" memory\n");
output.append(" type: ROM\n");
output.append(" size: 0x", hex(data.size()), "\n");
output.append(" name: program.rom\n");
}
if(ramSize) {
output.append(" memory\n");
output.append(" type: NVRAM\n");
output.append(" size: 0x", hex(ramSize), "\n");
output.append(" name: save.ram\n");
}
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
if(ramSize) output.append(memory("NVRAM", ramSize, "save.ram"));
return output;
}

View File

@ -1,6 +1,6 @@
namespace Heuristics {
struct SuperFamicom {
struct SuperFamicom : Heuristics {
SuperFamicom(vector<uint8_t>& data, string location);
explicit operator bool() const;
@ -17,7 +17,6 @@ struct SuperFamicom {
private:
auto size() const -> uint { return data.size(); }
auto memory(string type, uint size, string name) const -> string;
auto scoreHeader(uint address) -> uint;
auto firmwareARM() const -> string;
auto firmwareHITACHI() const -> string;
@ -123,15 +122,6 @@ auto SuperFamicom::manifest() const -> string {
return output;
}
auto SuperFamicom::memory(string type, uint size, string name) const -> string {
string output;
output.append(" memory\n");
output.append(" type: ", type, "\n");
output.append(" size: 0x", hex(size), "\n");
output.append(" name: ", name, "\n");
return output;
}
auto SuperFamicom::region() const -> string {
string region;

View File

@ -1,20 +1,30 @@
struct SuperGrafxCartridge {
SuperGrafxCartridge(string location, uint8_t* data, uint size);
namespace Heuristics {
string manifest;
struct SuperGrafx : Heuristics {
SuperGrafx(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
//private:
struct Information {
} information;
private:
vector<uint8_t>& data;
string location;
};
SuperGrafxCartridge::SuperGrafxCartridge(string location, uint8_t* data, uint size) {
manifest.append("board\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
manifest.append("\n");
manifest.append("information\n");
manifest.append(" title: ", Location::prefix(location), "\n");
manifest.append(" sha256: ", Hash::SHA256(data, size).digest(), "\n");
manifest.append("\n");
manifest.append("note: heuristically generated by icarus\n");
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(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(memory("ROM", data.size(), "program.rom"));
return output;
}
}

View File

@ -1,53 +1,55 @@
struct WonderSwanCartridge {
WonderSwanCartridge(string location, uint8_t* data, uint size);
namespace Heuristics {
string manifest;
struct WonderSwan : Heuristics {
WonderSwan(vector<uint8_t>& buffer, string location);
explicit operator bool() const;
auto manifest() const -> string;
//private:
struct Information {
bool color;
string ramType;
uint ramSize;
bool orientation; //0 = horizontal; 1 = vertical
bool hasRTC;
} information;
private:
vector<uint8_t>& data;
string location;
};
WonderSwanCartridge::WonderSwanCartridge(string location, uint8_t* data, uint size) {
if(size < 0x10000) return;
WonderSwan::WonderSwan(vector<uint8_t>& data, string location) : data(data), location(location) {
}
auto metadata = data + size - 16;
WonderSwan::operator bool() const {
return data.size() >= 0x10000;
}
information.color = metadata[7];
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]) {
default: information.ramType = ""; information.ramSize = 0; break;
case 0x01: information.ramType = "sram"; information.ramSize = 8 * 1024; break;
case 0x02: information.ramType = "sram"; information.ramSize = 32 * 1024; break;
case 0x03: information.ramType = "sram"; information.ramSize = 128 * 1024; break;
case 0x04: information.ramType = "sram"; information.ramSize = 256 * 1024; break;
case 0x05: information.ramType = "sram"; information.ramSize = 512 * 1024; break;
case 0x10: information.ramType = "eeprom"; information.ramSize = 128; break;
case 0x20: information.ramType = "eeprom"; information.ramSize = 2048; break;
case 0x50: information.ramType = "eeprom"; information.ramSize = 1024; break;
case 0x01: ramType = "NVRAM"; ramSize = 8 * 1024; break;
case 0x02: ramType = "NVRAM"; ramSize = 32 * 1024; break;
case 0x03: ramType = "NVRAM"; ramSize = 128 * 1024; break;
case 0x04: ramType = "NVRAM"; ramSize = 256 * 1024; break;
case 0x05: ramType = "NVRAM"; 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;
}
information.orientation = metadata[12] & 1;
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(" name: ", Location::prefix(location), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" orientation: ", !orientation ? "horizontal" : "vertical", "\n");
output.append(memory("ROM", data.size(), "program.rom"));
if(ramType && ramSize) output.append(memory(ramType, ramSize, "save.ram"));
if(hasRTC) output.append(memory("NVRAM", 16, "rtc.ram"));
return output;
}
information.hasRTC = metadata[13] & 1;
manifest.append("board\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
if(information.ramType && information.ramSize)
manifest.append(" ram name=save.ram type=", information.ramType, " size=0x", hex(information.ramSize), "\n");
if(information.hasRTC)
manifest.append(" rtc name=rtc.ram size=16\n");
manifest.append("\n");
manifest.append("information\n");
manifest.append(" title: ", Location::prefix(location), "\n");
manifest.append(" orientation: ", !information.orientation ? "horizontal" : "vertical", "\n");
manifest.append(" sha256: ", Hash::SHA256(data, size).digest(), "\n");
manifest.append("\n");
manifest.append("note: heuristically generated by icarus\n");
}

View File

@ -15,6 +15,8 @@ auto locate(string name) -> string {
#include "settings.cpp"
Settings settings;
#include "heuristics/heuristics.hpp"
#include "heuristics/heuristics.cpp"
#include "heuristics/famicom.cpp"
#include "heuristics/super-famicom.cpp"
#include "heuristics/master-system.cpp"