mirror of https://github.com/bsnes-emu/bsnes.git
264 lines
9.1 KiB
C++
Executable File
264 lines
9.1 KiB
C++
Executable File
#include <nall/platform.hpp>
|
|
#include <nall/crc32.hpp>
|
|
#include <nall/directory.hpp>
|
|
#include <nall/file.hpp>
|
|
#include <nall/filemap.hpp>
|
|
#include <nall/string.hpp>
|
|
#include <nall/zip.hpp>
|
|
#include <nall/nes/cartridge.hpp>
|
|
#include <nall/snes/cartridge.hpp>
|
|
#include <nall/gb/cartridge.hpp>
|
|
#include <nall/gba/cartridge.hpp>
|
|
using namespace nall;
|
|
|
|
#include <phoenix/phoenix.hpp>
|
|
using namespace phoenix;
|
|
|
|
struct Application : Window {
|
|
file database;
|
|
string source;
|
|
string target;
|
|
|
|
bool loadFile(const string &filename, string &suffix, uint8_t *&data, unsigned &size);
|
|
|
|
void scanFamicom(const string &filename, uint8_t *data, unsigned size);
|
|
void outputFamicom(const string &filename, uint8_t *data, unsigned size);
|
|
|
|
void scanSuperFamicom(const string &filename, uint8_t *data, unsigned size);
|
|
void outputSuperFamicom(const string &filename, uint8_t *data, unsigned size);
|
|
|
|
void scanGameBoy(const string &filename, uint8_t *data, unsigned size);
|
|
void outputGameBoy(const string &filename, uint8_t *data, unsigned size);
|
|
|
|
void scanGameBoyColor(const string &filename, uint8_t *data, unsigned size);
|
|
void outputGameBoyColor(const string &filename, uint8_t *data, unsigned size);
|
|
|
|
void scanGameBoyAdvance(const string &filename, uint8_t *data, unsigned size);
|
|
void outputGameBoyAdvance(const string &filename, uint8_t *data, unsigned size);
|
|
|
|
void scanDirectory();
|
|
void outputDirectory();
|
|
};
|
|
|
|
bool Application::loadFile(const string &filename, string &suffix, uint8_t *&data, unsigned &size) {
|
|
print("-> ", notdir(filename), "\n");
|
|
|
|
if(filename.endswith(".zip")) {
|
|
zip archive;
|
|
if(archive.open(filename) == false) return print("* failed to open archive\n"), false;
|
|
if(archive.file.size() != 1) return print("* file count (", archive.file.size(), ") incorrect\n"), false;
|
|
if(archive.extract(archive.file[0], data, size) == false) return print("* failed to extract ", filename, "\n"), false;
|
|
uint32_t crc32 = crc32_calculate(data, size);
|
|
if(crc32 != archive.file[0].crc32) return delete[] data, print("* CRC32 mismatch"), false;
|
|
suffix = extension(archive.file[0].name);
|
|
return true;
|
|
}
|
|
suffix = extension(filename);
|
|
return file::read(filename, data, size);
|
|
}
|
|
|
|
//<famicom>
|
|
|
|
void Application::scanFamicom(const string &filename, uint8_t *data, unsigned size) {
|
|
if(size & 255 != 16) return print("* ", filename, " missing iNES header\n");
|
|
|
|
string sha256 = nall::sha256(data, size);
|
|
database.print(sha256, "{}");
|
|
database.print("0x", hex<8>(size - 16), "{}");
|
|
for(unsigned n = 0; n < 16; n++) {
|
|
database.print(hex<2>(data[n]), n != 15 ? " " : "{}");
|
|
}
|
|
database.print(notdir(nall::basename(filename)), "\n");
|
|
}
|
|
|
|
void Application::outputFamicom(const string &filename, uint8_t *data, unsigned size) {
|
|
if(size & 255 != 16) return print("* ", filename, " missing iNES header\n");
|
|
|
|
string markup = FamicomCartridge(data, size).markup;
|
|
string path = {target, nall::basename(filename), ".fc/"};
|
|
mkdir(path, 0777);
|
|
if(file::exists({path, "program.rom"}) == false) {
|
|
file::write({path, "program.rom"}, data + 16, size - 16);
|
|
}
|
|
file::write({path, "manifest.xml"}, (const uint8_t*)markup(), markup.length());
|
|
}
|
|
|
|
//</famicom>
|
|
|
|
//<superFamicom>
|
|
|
|
void Application::scanSuperFamicom(const string &filename, uint8_t *data, unsigned size) {
|
|
if(size & 32767 == 512) size -= 512, data += 512;
|
|
|
|
string sha256 = nall::sha256(data, size);
|
|
database.print(sha256, "{}");
|
|
database.print("0x", hex<8>(size), "{}");
|
|
database.print(notdir(nall::basename(filename)), "\n");
|
|
}
|
|
|
|
void Application::outputSuperFamicom(const string &filename, uint8_t *data, unsigned size) {
|
|
if(size & 32767 == 512) size -= 512, data += 512;
|
|
|
|
string markup = SuperFamicomCartridge(data, size).markup;
|
|
string path = {target, nall::basename(filename), ".sfc/"};
|
|
mkdir(path, 0777);
|
|
if(file::exists({path, "program.rom"}) == false) {
|
|
file::write({path, "program.rom"}, data, size);
|
|
}
|
|
file::write({path, "manifest.xml"}, (const uint8_t*)markup(), markup.length());
|
|
}
|
|
|
|
//</superFamicom>
|
|
|
|
//<gameBoy>
|
|
|
|
void Application::scanGameBoy(const string &filename, uint8_t *data, unsigned size) {
|
|
string markup = GameBoyCartridge(data, size).markup;
|
|
XML::Document document(markup);
|
|
|
|
string sha256 = nall::sha256(data, size);
|
|
database.print(sha256, "{}");
|
|
database.print("0x", hex<8>(size), "{}");
|
|
database.print(document["cartridge"]["mapper"].data, "{}");
|
|
database.print(notdir(nall::basename(filename)), "\n");
|
|
}
|
|
|
|
void Application::outputGameBoy(const string &filename, uint8_t *data, unsigned size) {
|
|
string markup = GameBoyCartridge(data, size).markup;
|
|
string path = {target, nall::basename(filename), ".gb/"};
|
|
mkdir(path, 0777);
|
|
if(file::exists({path, "program.rom"}) == false) {
|
|
file::write({path, "program.rom"}, data, size);
|
|
}
|
|
file::write({path, "manifest.xml"}, (const uint8_t*)markup(), markup.length());
|
|
}
|
|
|
|
//</gameBoy>
|
|
|
|
//<gameBoyColor>
|
|
|
|
void Application::scanGameBoyColor(const string &filename, uint8_t *data, unsigned size) {
|
|
string markup = GameBoyCartridge(data, size).markup;
|
|
XML::Document document(markup);
|
|
|
|
string sha256 = nall::sha256(data, size);
|
|
database.print(sha256, "{}");
|
|
database.print("0x", hex<8>(size), "{}");
|
|
database.print(document["cartridge"]["mapper"].data, "{}");
|
|
database.print(data[0x0143] == 0xc0 ? "N" : "Y", "{}");
|
|
database.print(notdir(nall::basename(filename)), "\n");
|
|
}
|
|
|
|
void Application::outputGameBoyColor(const string &filename, uint8_t *data, unsigned size) {
|
|
string markup = GameBoyCartridge(data, size).markup;
|
|
string path = {target, nall::basename(filename), data[0x0143] == 0xc0 ? ".gbc/" : ".gbb/"};
|
|
mkdir(path, 0777);
|
|
if(file::exists({path, "program.rom"}) == false) {
|
|
file::write({path, "program.rom"}, data, size);
|
|
}
|
|
file::write({path, "manifest.xml"}, (const uint8_t*)markup(), markup.length());
|
|
}
|
|
|
|
//</gameBoyColor>
|
|
|
|
//<gameBoyAdvance>
|
|
|
|
void Application::scanGameBoyAdvance(const string &filename, uint8_t *data, unsigned size) {
|
|
string markup = GameBoyAdvanceCartridge(data, size).markup;
|
|
string identifiers;
|
|
if(auto start = markup.position("detected: ")) {
|
|
if(auto finish = markup.position(" -->\n")) {
|
|
identifiers = substr(markup, start() + 10, finish() - start() - 10);
|
|
}
|
|
}
|
|
|
|
string sha256 = nall::sha256(data, size);
|
|
database.print(sha256, "{}");
|
|
database.print("0x", hex<8>(size), "{}");
|
|
database.print(identifiers, "{}");
|
|
database.print(notdir(nall::basename(filename)), "\n");
|
|
}
|
|
|
|
void Application::outputGameBoyAdvance(const string &filename, uint8_t *data, unsigned size) {
|
|
string markup = GameBoyAdvanceCartridge(data, size).markup;
|
|
string path = {target, nall::basename(filename), ".gba/"};
|
|
mkdir(path, 0777);
|
|
if(file::exists({path, "program.rom"}) == false) {
|
|
file::write({path, "program.rom"}, data, size);
|
|
}
|
|
file::write({path, "manifest.xml"}, (const uint8_t*)markup(), markup.length());
|
|
}
|
|
|
|
//</gameBoyAdvance>
|
|
|
|
void Application::scanDirectory() {
|
|
if(directory::exists(source) == false) return print("* source directory does not exist\n");
|
|
if(database.open(target, file::mode::write) == false) return print("* cannot open target file\n");
|
|
|
|
lstring files = directory::files(source);
|
|
for(auto &filename : files) {
|
|
string suffix;
|
|
uint8_t *data;
|
|
unsigned size;
|
|
if(loadFile({source, filename}, suffix, data, size) == false) continue;
|
|
|
|
if(suffix == "nes" || suffix == "fc") scanFamicom(filename, data, size);
|
|
if(suffix == "sfc" || suffix == "smc") scanSuperFamicom(filename, data, size);
|
|
if(suffix == "gb") scanGameBoy(filename, data, size);
|
|
if(suffix == "gbc" || suffix == "gbb") scanGameBoyColor(filename, data, size);
|
|
if(suffix == "gba") scanGameBoyAdvance(filename, data, size);
|
|
|
|
delete[] data;
|
|
}
|
|
|
|
database.close();
|
|
}
|
|
|
|
void Application::outputDirectory() {
|
|
if(directory::exists(source) == false) return print("* source directory does not exist\n");
|
|
if(directory::exists(target) == false) return print("* target directory does not exist\n");
|
|
|
|
lstring files = directory::files(source);
|
|
for(auto &filename : files) {
|
|
string suffix;
|
|
uint8_t *data;
|
|
unsigned size;
|
|
if(loadFile({source, filename}, suffix, data, size) == false) continue;
|
|
|
|
if(suffix == "nes" || suffix == "fc") outputFamicom(filename, data, size);
|
|
if(suffix == "sfc" || suffix == "smc") outputSuperFamicom(filename, data, size);
|
|
if(suffix == "gb") outputGameBoy(filename, data, size);
|
|
if(suffix == "gbc" || suffix == "gbb") outputGameBoyColor(filename, data, size);
|
|
if(suffix == "gba") outputGameBoyAdvance(filename, data, size);
|
|
|
|
delete[] data;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
Application *application = new Application;
|
|
|
|
if(argc == 4 && cstring{argv[1]} == "scan") {
|
|
application->source = argv[2];
|
|
application->target = argv[3];
|
|
application->scanDirectory();
|
|
}
|
|
|
|
else if(argc == 4 && cstring{argv[1]} == "output") {
|
|
application->source = argv[2];
|
|
application->target = argv[3];
|
|
application->outputDirectory();
|
|
}
|
|
|
|
else {
|
|
print("purify v01\n");
|
|
print("usage: purify [mode] source target\n\n");
|
|
print("modes:\n");
|
|
print(" scan - create database\n");
|
|
print(" output - create folder images\n");
|
|
}
|
|
|
|
delete application;
|
|
return 0;
|
|
}
|