Update to purify v02 release.

byuu says:

purify has been rewritten. It now resembles the older snespurify, and
lets you import multiple game files+archives and regenerate manifests
for multiple game folders. It is also recursive.

So you can now import all of your games for all systems at once, or you
can update all of your bsnes v091 game folders to the new higan v092
format at once.

Caveats:

First, I am now using std::thread, so that the GUI doesn't freeze.
Instead, you get a nice progress bar. Unfortunately, I was mislead and
TDM/GCC 4.7 still does not have std::thread support. So ... sorry, but
I can't compile purify for Windows. I am sick and tired of not being
able to write multi-threaded code, so fuck it. If anyone can get it to
build on Windows, whether that be by using Windows threads, hacking in
std::thread support, skipping threading all together, whatever ...
that'll be great. Otherwise, sorry, purify is Linux only until MinGW can
get its god damned shit together and offers threading support.

Second, there's no way to regenerate Famicom (NES) manifests, because we
discard the iNES header. We are going to need a database for that. So,
all I can suggest is that if you use bsnes/higan, keep all your iNES
images around to re-import as new releases come out.

Third, when you purify game folders, it will back up the ROM and RAM
files only. Your save states, cheat codes, debug logs, etc will be wiped
out. There's a whole lot of reasons for this, the most pertinent is that
it's meant to clean up the folder to a pristine state. It also fixes the
game folder name, etc. So ... sorry, but this is how it works. New
releases rarely if ever allow old save states to work anyway.

Lastly, I am not going to have purify contain infinite backward
compatibility for updating manifests. You will want to keep up with
purifying the collection, otherwise you'll have to grab older purify
copies and convert your way along. Although hopefully the format won't
be so volatile and this won't be necessary very often.
This commit is contained in:
Tim Allen 2013-01-17 22:19:42 +11:00
parent 8d88337e28
commit b6575ca02a
77 changed files with 1900 additions and 2827 deletions

View File

@ -1,42 +1,49 @@
include nall/Makefile
include phoenix/Makefile
application := purify
resource :=
flags := -std=gnu++0x -I. -O3 -fomit-frame-pointer
path := /usr/local/bin
flags := -I. -O3 -fomit-frame-pointer
link := -s
objects := obj/phoenix.o obj/purify.o
ifeq ($(platform),win)
resource := resource.o
flags := -mwindows $(flags)
link := -mwindows $(flags)
ifeq ($(platform),x)
link += -Wl,-export-dynamic
endif
all: phoenix.o $(application).o
ifeq ($(platform),win)
windres phoenix/windows/phoenix.rc $(resource)
all: build;
obj/phoenix.o: phoenix/phoenix.cpp
$(cpp) $(flags) -o obj/phoenix.o -c phoenix/phoenix.cpp $(phoenixflags)
obj/purify.o: purify.cpp
$(cpp) $(flags) -o obj/purify.o -c purify.cpp
build: $(objects)
ifeq ($(platform),x)
$(cpp) $(link) -o purify $(objects) $(phoenixlink)
else ifeq ($(platform),win)
$(cpp) -shared -o phoenix.dll obj/phoenix.o $(phoenixlink)
$(cpp) -o purify obj/purify.o $(link) -L. -lphoenix
endif
$(cpp) -o $(application) phoenix.o $(application).o $(resource) $(link) $(phoenixlink)
phoenix.o:
$(cpp) -c -o phoenix.o phoenix/phoenix.cpp $(flags) $(phoenixflags)
$(application).o: $(application).cpp
$(cpp) -c -o $(application).o $(application).cpp $(flags)
resource: force
sourcery resource/resource.xml resource/resource.cpp resource/resource.hpp
install:
sudo cp $(application) /usr/local/bin/$(application)
uninstall:
sudo rm /usr/local/bin/$(application)
sourcery resource/resource.bml resource/resource.cpp resource/resource.hpp
clean:
-@$(call delete,*.o)
-@$(call delete,obj/*.o)
install: uninstall
ifeq ($(platform),x)
sudo cp ./purify $(path)/purify
endif
uninstall:
ifeq ($(platform),x)
if [ -f $(path)/purify ]; then sudo rm $(path)/purify; fi
endif
sync:
ifeq ($(shell id -un),byuu)
if [ -d ./nall ]; then rm -r ./nall; fi
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
cp -r ../nall ./nall
@ -44,5 +51,6 @@ sync:
rm -r nall/test
rm -r phoenix/nall
rm -r phoenix/test
endif
force:

View File

@ -45,7 +45,12 @@ ifeq ($(compiler),)
endif
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
cpp := $(subst cc,++,$(compiler)) -std=gnu++11
ifeq ($(arch),x86)
c := $(c) -m32
cpp := $(cpp) -m32
endif
ifeq ($(prefix),)
prefix := /usr/local

View File

@ -1,33 +1,38 @@
#ifndef NALL_BASE64_HPP
#define NALL_BASE64_HPP
#include <string.h>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct base64 {
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
output = new char[inlength * 8 / 6 + 6]();
output = new char[inlength * 8 / 6 + 8]();
unsigned i = 0, o = 0;
while(i < inlength) {
switch(i % 3) {
case 0: {
output[o++] = enc(input[i] >> 2);
output[o] = enc((input[i] & 3) << 4);
} break;
break;
}
case 1: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 4));
output[o] = enc((input[i] & 15) << 2);
} break;
break;
}
case 2: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 6));
output[o++] = enc(input[i] & 63);
} break;
break;
}
}
i++;
@ -36,32 +41,46 @@ namespace nall {
return true;
}
static string encode(const string &data) {
char *buffer = nullptr;
encode(buffer, (const uint8_t*)(const char*)data, data.length());
string result = buffer;
delete[] buffer;
return result;
}
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
unsigned inlength = strlen(input), infix = 0;
output = new uint8_t[inlength]();
output = new uint8_t[inlength + 1]();
unsigned i = 0, o = 0;
while(i < inlength) {
uint8_t x = dec(input[i]);
switch(i++ & 3) {
case 0: {
output[o] = x << 2;
} break;
break;
}
case 1: {
output[o++] |= x >> 4;
output[o] = (x & 15) << 4;
} break;
break;
}
case 2: {
output[o++] |= x >> 2;
output[o] = (x & 3) << 6;
} break;
break;
}
case 3: {
output[o++] |= x;
} break;
break;
}
}
}
@ -69,9 +88,18 @@ namespace nall {
return true;
}
static string decode(const string &data) {
uint8_t *buffer = nullptr;
unsigned size = 0;
decode(buffer, size, (const char*)data);
string result = (const char*)buffer;
delete[] buffer;
return result;
}
private:
static char enc(uint8_t n) {
//base64 for URL encodings
//base64 for URL encodings (URL = -_, MIME = +/)
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}

View File

@ -0,0 +1,84 @@
#ifndef NALL_BEAT_ARCHIVE_HPP
#define NALL_BEAT_ARCHIVE_HPP
#include <nall/beat/base.hpp>
namespace nall {
struct beatArchive : beatBase {
bool create(const string &beatname, string pathname, const string &metadata = "") {
if(fp.open(beatname, file::mode::write) == false) return false;
if(pathname.endswith("/") == false) pathname.append("/");
checksum = ~0;
writeString("BPA1");
writeNumber(metadata.length());
writeString(metadata);
lstring list;
ls(list, pathname, pathname);
for(auto &name : list) {
if(name.endswith("/")) {
name.rtrim<1>("/");
writeNumber(0 | ((name.length() - 1) << 1));
writeString(name);
} else {
file stream;
if(stream.open({pathname, name}, file::mode::read) == false) return false;
writeNumber(1 | ((name.length() - 1) << 1));
writeString(name);
unsigned size = stream.size();
writeNumber(size);
uint32_t checksum = ~0;
while(size--) {
uint8_t data = stream.read();
write(data);
checksum = crc32_adjust(checksum, data);
}
writeChecksum(~checksum);
}
}
writeChecksum(~checksum);
fp.close();
return true;
}
bool unpack(const string &beatname, string pathname) {
if(fp.open(beatname, file::mode::read) == false) return false;
if(pathname.endswith("/") == false) pathname.append("/");
checksum = ~0;
if(readString(4) != "BPA1") return false;
unsigned length = readNumber();
while(length--) read();
directory::create(pathname);
while(fp.offset() < fp.size() - 4) {
unsigned data = readNumber();
string name = readString((data >> 1) + 1);
if(name.position("\\") || name.position("../")) return false; //block path exploits
if((data & 1) == 0) {
directory::create({pathname, name});
} else {
file stream;
if(stream.open({pathname, name}, file::mode::write) == false) return false;
unsigned size = readNumber();
uint32_t checksum = ~0;
while(size--) {
uint8_t data = read();
stream.write(data);
checksum = crc32_adjust(checksum, data);
}
if(readChecksum(~checksum) == false) return false;
}
}
return readChecksum(~checksum);
}
};
}
#endif

92
purify/nall/beat/base.hpp Normal file
View File

@ -0,0 +1,92 @@
#ifndef NALL_BEAT_BASE_HPP
#define NALL_BEAT_BASE_HPP
namespace nall {
struct beatBase {
protected:
file fp;
uint32_t checksum;
void ls(lstring &list, const string &path, const string &basepath) {
lstring paths = directory::folders(path);
for(auto &pathname : paths) {
list.append(string{path, pathname}.ltrim<1>(basepath));
ls(list, {path, pathname}, basepath);
}
lstring files = directory::files(path);
for(auto &filename : files) {
list.append(string{path, filename}.ltrim<1>(basepath));
}
}
void write(uint8_t data) {
fp.write(data);
checksum = crc32_adjust(checksum, data);
}
void writeNumber(uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) return write(0x80 | x);
write(x);
data--;
}
}
void writeString(const string &text) {
unsigned length = text.length();
for(unsigned n = 0; n < length; n++) write(text[n]);
}
void writeChecksum(uint32_t checksum) {
write(checksum >> 0);
write(checksum >> 8);
write(checksum >> 16);
write(checksum >> 24);
}
uint8_t read() {
uint8_t data = fp.read();
checksum = crc32_adjust(checksum, data);
return data;
}
uint64_t readNumber() {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
}
string readString(unsigned length) {
string text;
text.reserve(length + 1);
for(unsigned n = 0; n < length; n++) {
text[n] = fp.read();
checksum = crc32_adjust(checksum, text[n]);
}
text[length] = 0;
return text;
}
bool readChecksum(uint32_t source) {
uint32_t checksum = 0;
checksum |= read() << 0;
checksum |= read() << 8;
checksum |= read() << 16;
checksum |= read() << 24;
return checksum == source;
}
};
}
#endif

View File

@ -1,5 +1,5 @@
#ifndef NALL_BPS_DELTA_HPP
#define NALL_BPS_DELTA_HPP
#ifndef NALL_BEAT_DELTA_HPP
#define NALL_BEAT_DELTA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>

View File

@ -1,5 +1,5 @@
#ifndef NALL_BPS_LINEAR_HPP
#define NALL_BPS_LINEAR_HPP
#ifndef NALL_BEAT_LINEAR_HPP
#define NALL_BEAT_LINEAR_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>

View File

@ -1,5 +1,5 @@
#ifndef NALL_BPS_METADATA_HPP
#define NALL_BPS_METADATA_HPP
#ifndef NALL_BEAT_METADATA_HPP
#define NALL_BEAT_METADATA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>

242
purify/nall/beat/multi.hpp Normal file
View File

@ -0,0 +1,242 @@
#ifndef NALL_BEAT_MULTI_HPP
#define NALL_BEAT_MULTI_HPP
#include <nall/beat/patch.hpp>
#include <nall/beat/linear.hpp>
#include <nall/beat/delta.hpp>
namespace nall {
struct bpsmulti {
enum : unsigned {
CreatePath = 0,
CreateFile = 1,
ModifyFile = 2,
MirrorFile = 3,
};
enum : unsigned {
OriginSource = 0,
OriginTarget = 1,
};
bool create(const string &patchName, const string &sourcePath, const string &targetPath, bool delta = false, const string &metadata = "") {
if(fp.open()) fp.close();
fp.open(patchName, file::mode::write);
checksum = ~0;
writeString("BPM1"); //signature
writeNumber(metadata.length());
writeString(metadata);
lstring sourceList, targetList;
ls(sourceList, sourcePath, sourcePath);
ls(targetList, targetPath, targetPath);
for(auto &targetName : targetList) {
if(targetName.endswith("/")) {
targetName.rtrim<1>("/");
writeNumber(CreatePath | ((targetName.length() - 1) << 2));
writeString(targetName);
} else if(auto position = sourceList.find(targetName)) { //if sourceName == targetName
file sp, dp;
sp.open({sourcePath, targetName}, file::mode::read);
dp.open({targetPath, targetName}, file::mode::read);
bool identical = sp.size() == dp.size();
uint32_t cksum = ~0;
for(unsigned n = 0; n < sp.size(); n++) {
uint8_t byte = sp.read();
if(identical && byte != dp.read()) identical = false;
cksum = crc32_adjust(cksum, byte);
}
if(identical) {
writeNumber(MirrorFile | ((targetName.length() - 1) << 2));
writeString(targetName);
writeNumber(OriginSource);
writeChecksum(~cksum);
} else {
writeNumber(ModifyFile | ((targetName.length() - 1) << 2));
writeString(targetName);
writeNumber(OriginSource);
if(delta == false) {
bpslinear patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
} else {
bpsdelta patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
}
auto buffer = file::read({temppath(), "temp.bps"});
writeNumber(buffer.size());
for(auto &byte : buffer) write(byte);
}
} else {
writeNumber(CreateFile | ((targetName.length() - 1) << 2));
writeString(targetName);
auto buffer = file::read({targetPath, targetName});
writeNumber(buffer.size());
for(auto &byte : buffer) write(byte);
writeChecksum(crc32_calculate(buffer.data(), buffer.size()));
}
}
//checksum
writeChecksum(~checksum);
fp.close();
return true;
}
bool apply(const string &patchName, const string &sourcePath, const string &targetPath) {
directory::remove(targetPath); //start with a clean directory
directory::create(targetPath);
if(fp.open()) fp.close();
fp.open(patchName, file::mode::read);
checksum = ~0;
if(readString(4) != "BPM1") return false;
auto metadataLength = readNumber();
while(metadataLength--) read();
while(fp.offset() < fp.size() - 4) {
auto encoding = readNumber();
unsigned action = encoding & 3;
unsigned targetLength = (encoding >> 2) + 1;
string targetName = readString(targetLength);
if(action == CreatePath) {
directory::create({targetPath, targetName, "/"});
} else if(action == CreateFile) {
file fp;
fp.open({targetPath, targetName}, file::mode::write);
auto fileSize = readNumber();
while(fileSize--) fp.write(read());
uint32_t cksum = readChecksum();
} else if(action == ModifyFile) {
auto encoding = readNumber();
string originPath = encoding & 1 ? targetPath : sourcePath;
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
auto patchSize = readNumber();
vector<uint8_t> buffer;
buffer.resize(patchSize);
for(unsigned n = 0; n < patchSize; n++) buffer[n] = read();
bpspatch patch;
patch.modify(buffer.data(), buffer.size());
patch.source({originPath, sourceName});
patch.target({targetPath, targetName});
if(patch.apply() != bpspatch::result::success) return false;
} else if(action == MirrorFile) {
auto encoding = readNumber();
string originPath = encoding & 1 ? targetPath : sourcePath;
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
file::copy({originPath, sourceName}, {targetPath, targetName});
uint32_t cksum = readChecksum();
}
}
uint32_t cksum = ~checksum;
if(read() != (uint8_t)(cksum >> 0)) return false;
if(read() != (uint8_t)(cksum >> 8)) return false;
if(read() != (uint8_t)(cksum >> 16)) return false;
if(read() != (uint8_t)(cksum >> 24)) return false;
fp.close();
return true;
}
protected:
file fp;
uint32_t checksum;
//create() functions
void ls(lstring &list, const string &path, const string &basepath) {
lstring paths = directory::folders(path);
for(auto &pathname : paths) {
list.append(string{path, pathname}.ltrim<1>(basepath));
ls(list, {path, pathname}, basepath);
}
lstring files = directory::files(path);
for(auto &filename : files) {
list.append(string{path, filename}.ltrim<1>(basepath));
}
}
void write(uint8_t data) {
fp.write(data);
checksum = crc32_adjust(checksum, data);
}
void writeNumber(uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
}
void writeString(const string &text) {
unsigned length = text.length();
for(unsigned n = 0; n < length; n++) write(text[n]);
}
void writeChecksum(uint32_t cksum) {
write(cksum >> 0);
write(cksum >> 8);
write(cksum >> 16);
write(cksum >> 24);
}
//apply() functions
uint8_t read() {
uint8_t data = fp.read();
checksum = crc32_adjust(checksum, data);
return data;
}
uint64_t readNumber() {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
}
string readString(unsigned length) {
string text;
text.reserve(length + 1);
for(unsigned n = 0; n < length; n++) text[n] = read();
text[length] = 0;
return text;
}
uint32_t readChecksum() {
uint32_t checksum = 0;
checksum |= read() << 0;
checksum |= read() << 8;
checksum |= read() << 16;
checksum |= read() << 24;
return checksum;
}
};
}
#endif

View File

@ -1,5 +1,5 @@
#ifndef NALL_BPS_PATCH_HPP
#define NALL_BPS_PATCH_HPP
#ifndef NALL_BEAT_PATCH_HPP
#define NALL_BEAT_PATCH_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>

25
purify/nall/crc16.hpp Normal file
View File

@ -0,0 +1,25 @@
#ifndef NALL_CRC16_HPP
#define NALL_CRC16_HPP
#include <nall/stdint.hpp>
namespace nall {
inline uint16_t crc16_adjust(uint16_t crc16, uint8_t data) {
for(unsigned n = 0; n < 8; n++) {
if((crc16 & 1) ^ (data & 1)) crc16 = (crc16 >> 1) ^ 0x8408;
else crc16 >>= 1;
data >>= 1;
}
return crc16;
}
inline uint16_t crc16_calculate(const uint8_t *data, unsigned length) {
uint16_t crc16 = ~0;
for(unsigned n = 0; n < length; n++) {
crc16 = crc16_adjust(crc16, data[n]);
}
return ~crc16;
}
}
#endif

View File

@ -1,6 +1,7 @@
#ifndef NALL_DIRECTORY_HPP
#define NALL_DIRECTORY_HPP
#include <nall/file.hpp>
#include <nall/intrinsics.hpp>
#include <nall/sort.hpp>
#include <nall/string.hpp>
@ -18,28 +19,75 @@ namespace nall {
struct directory {
static bool create(const string &pathname, unsigned permissions = 0755); //recursive
static bool remove(const string &pathname);
static bool remove(const string &pathname); //recursive
static bool exists(const string &pathname);
static lstring folders(const string &pathname, const string &pattern = "*");
static lstring files(const string &pathname, const string &pattern = "*");
static lstring contents(const string &pathname, const string &pattern = "*");
static lstring folders(const string &pathname, const string &pattern = "*") {
lstring folders = directory::ufolders(pathname, pattern);
folders.sort();
return folders;
}
static lstring files(const string &pathname, const string &pattern = "*") {
lstring files = directory::ufiles(pathname, pattern);
files.sort();
return files;
}
static lstring contents(const string &pathname, const string &pattern = "*") {
lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files
lstring files = directory::ufiles(pathname, pattern);
folders.sort();
files.sort();
for(auto &file : files) folders.append(file);
return folders;
}
static lstring ifolders(const string &pathname, const string &pattern = "*") {
lstring folders = ufolders(pathname, pattern);
folders.isort();
return folders;
}
static lstring ifiles(const string &pathname, const string &pattern = "*") {
lstring files = ufiles(pathname, pattern);
files.isort();
return files;
}
static lstring icontents(const string &pathname, const string &pattern = "*") {
lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files
lstring files = directory::ufiles(pathname, pattern);
folders.isort();
files.isort();
for(auto &file : files) folders.append(file);
return folders;
}
private:
//internal functions; these return unsorted lists
static lstring ufolders(const string &pathname, const string &pattern = "*");
static lstring ufiles(const string &pathname, const string &pattern = "*");
};
#if defined(PLATFORM_WINDOWS)
inline bool directory::create(const string &pathname, unsigned permissions) {
string fullpath = pathname, path;
fullpath.transform("/", "\\");
fullpath.rtrim<1>("\\");
lstring pathpart = fullpath.split("\\");
bool result = false;
for(auto &part : pathpart) {
path.append(part, "\\");
result = _wmkdir(utf16_t(path)) == 0;
string path;
lstring list = string{pathname}.transform("\\", "/").rtrim<1>("/").split("/");
bool result = true;
for(auto &part : list) {
path.append(part, "/");
result &= (_wmkdir(utf16_t(path)) == 0);
}
return result;
}
inline bool directory::remove(const string &pathname) {
lstring list = directory::contents(pathname);
for(auto &name : list) {
if(name.endswith("/")) directory::remove({pathname, name});
else file::remove({pathname, name});
}
return _wrmdir(utf16_t(pathname)) == 0;
}
@ -51,7 +99,7 @@ struct directory {
return (result & FILE_ATTRIBUTE_DIRECTORY);
}
inline lstring directory::folders(const string &pathname, const string &pattern) {
inline lstring directory::ufolders(const string &pathname, const string &pattern) {
lstring list;
string path = pathname;
path.transform("/", "\\");
@ -77,12 +125,11 @@ struct directory {
}
FindClose(handle);
}
if(list.size() > 0) list.sort();
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::files(const string &pathname, const string &pattern) {
inline lstring directory::ufiles(const string &pathname, const string &pattern) {
lstring list;
string path = pathname;
path.transform("/", "\\");
@ -104,29 +151,26 @@ struct directory {
}
FindClose(handle);
}
if(list.size() > 0) list.sort();
return list;
}
inline lstring directory::contents(const string &pathname, const string &pattern) {
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
lstring files = directory::files(pathname, pattern);
for(auto &file : files) folders.append(file);
return folders;
}
#else
inline bool directory::create(const string &pathname, unsigned permissions) {
string fullpath = pathname, path = "/";
fullpath.trim<1>("/");
lstring pathpart = fullpath.split("/");
for(auto &part : pathpart) {
if(!directory::exists(path)) mkdir(path, permissions);
string path;
lstring list = string{pathname}.rtrim<1>("/").split("/");
bool result = true;
for(auto &part : list) {
path.append(part, "/");
result &= (mkdir(path, permissions) == 0);
}
return mkdir(path, permissions) == 0;
return result;
}
inline bool directory::remove(const string &pathname) {
lstring list = directory::contents(pathname);
for(auto &name : list) {
if(name.endswith("/")) directory::remove({pathname, name});
else file::remove({pathname, name});
}
return rmdir(pathname) == 0;
}
@ -137,7 +181,7 @@ struct directory {
return true;
}
inline lstring directory::folders(const string &pathname, const string &pattern) {
inline lstring directory::ufolders(const string &pathname, const string &pattern) {
lstring list;
DIR *dp;
struct dirent *ep;
@ -152,12 +196,11 @@ struct directory {
}
closedir(dp);
}
if(list.size() > 0) list.sort();
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::files(const string &pathname, const string &pattern) {
inline lstring directory::ufiles(const string &pathname, const string &pattern) {
lstring list;
DIR *dp;
struct dirent *ep;
@ -172,16 +215,8 @@ struct directory {
}
closedir(dp);
}
if(list.size() > 0) list.sort();
return list;
}
inline lstring directory::contents(const string &pathname, const string &pattern) {
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
lstring files = directory::files(pathname, pattern);
for(auto &file : files) folders.append(file);
return folders;
}
#endif
}

View File

@ -1,182 +0,0 @@
#ifndef NALL_EMULATION_FAMICOM_HPP
#define NALL_EMULATION_FAMICOM_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct FamicomCartridge {
string markup;
inline FamicomCartridge(const uint8_t *data, unsigned size);
//private:
unsigned mapper;
unsigned mirror;
unsigned prgrom;
unsigned prgram;
unsigned chrrom;
unsigned chrram;
};
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
markup = "";
if(size < 16) return;
if(data[0] != 'N') return;
if(data[1] != 'E') return;
if(data[2] != 'S') return;
if(data[3] != 26) return;
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
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;
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
switch(mapper) {
default:
markup.append(" <board type='NES-NROM-256'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 1:
markup.append(" <board type='NES-SXROM'/>\n");
markup.append(" <chip type='MMC1B2'/>\n");
prgram = 8192;
break;
case 2:
markup.append(" <board type='NES-UOROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 3:
markup.append(" <board type='NES-CNROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 4:
//MMC3
markup.append(" <board type='NES-TLROM'/>\n");
markup.append(" <chip type='MMC3B'/>\n");
prgram = 8192;
//MMC6
//markup.append(" <board type='NES-HKROM'/>\n");
//markup.append(" <chip type='MMC6'/>\n");
//prgram = 1024;
break;
case 5:
markup.append(" <board type='NES-ELROM'/>\n");
markup.append(" <chip type='MMC5'/>\n");
prgram = 65536;
break;
case 7:
markup.append(" <board type='NES-AOROM'/>\n");
break;
case 9:
markup.append(" <board type='NES-PNROM'/>\n");
markup.append(" <chip type='MMC2'/>\n");
prgram = 8192;
break;
case 10:
markup.append(" <board type='NES-FKROM'/>\n");
markup.append(" <chip type='MMC4'/>\n");
prgram = 8192;
break;
case 16:
markup.append(" <board type='BANDAI-FCG'/>\n");
markup.append(" <chip type='LZ93D50'/>\n");
break;
case 21:
case 23:
case 25:
//VRC4
markup.append(" <board type='KONAMI-VRC-4'/>\n");
markup.append(" <chip type='VRC4'>\n");
markup.append(" <pinout a0='1' a1='0'/>\n");
markup.append(" </chip>\n");
prgram = 8192;
break;
case 22:
//VRC2
markup.append(" <board type='KONAMI-VRC-2'/>\n");
markup.append(" <chip type='VRC2'>\n");
markup.append(" <pinout a0='0' a1='1'/>\n");
markup.append(" </chip>\n");
break;
case 24:
markup.append(" <board type='KONAMI-VRC-6'/>\n");
markup.append(" <chip type='VRC6'/>\n");
break;
case 26:
markup.append(" <board type='KONAMI-VRC-6'/>\n");
markup.append(" <chip type='VRC6'/>\n");
prgram = 8192;
break;
case 34:
markup.append(" <board type='NES-BNROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 66:
markup.append(" <board type='NES-GNROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 69:
markup.append(" <board type='SUNSOFT-5B'/>\n");
markup.append(" <chip type='5B'/>\n");
prgram = 8192;
break;
case 73:
markup.append(" <board type='KONAMI-VRC-3'/>\n");
markup.append(" <chip type='VRC3'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
prgram = 8192;
break;
case 75:
markup.append(" <board type='KONAMI-VRC-1'/>\n");
markup.append(" <chip type='VRC1'/>\n");
break;
case 85:
markup.append(" <board type='KONAMI-VRC-7/'>\n");
markup.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");
markup.append(" </prg>\n");
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");
markup.append(" </chr>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@ -1,68 +0,0 @@
#ifndef NALL_EMULATION_GAME_BOY_ADVANCE_HPP
#define NALL_EMULATION_GAME_BOY_ADVANCE_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
struct GameBoyAdvanceCartridge {
string markup;
string identifiers;
inline GameBoyAdvanceCartridge(const uint8_t *data, unsigned size);
};
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned size) {
struct Identifier {
string name;
unsigned size;
};
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});
lstring 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;
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;
list.appendonce(text);
}
}
}
}
identifiers = list.concatenate(",");
markup = "";
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
markup.append(" <rom name='program.rom' size='0x", hex(size), "'/>\n");
if(0);
else if(identifiers.beginswith("SRAM_V" )) markup.append(" <ram name='save.ram' type='SRAM' size='0x8000'/>\n");
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" <ram name='save.ram' type='FRAM' size='0x8000'/>\n");
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" <ram name='save.ram' type='EEPROM' size='0x0'/>\n");
else if(identifiers.beginswith("FLASH_V" )) markup.append(" <ram name='save.ram' type='FlashROM' size='0x10000'/>\n");
else if(identifiers.beginswith("FLASH512_V")) markup.append(" <ram name='save.ram' type='FlashROM' size='0x10000'/>\n");
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" <ram name='save.ram' type='FlashROM' size='0x20000'/>\n");
if(identifiers.empty() == false) markup.append(" <!-- detected: ", identifiers, " -->\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@ -1,122 +0,0 @@
#ifndef NALL_EMULATION_GAME_BOY_HPP
#define NALL_EMULATION_GAME_BOY_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct GameBoyCartridge {
string markup;
inline GameBoyCartridge(uint8_t *data, unsigned size);
//private:
struct Information {
string mapper;
bool ram;
bool battery;
bool rtc;
bool rumble;
unsigned romsize;
unsigned ramsize;
bool cgb;
bool cgbonly;
} info;
};
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
markup = "";
if(romsize < 0x4000) return;
info.mapper = "unknown";
info.ram = false;
info.battery = false;
info.rtc = false;
info.rumble = false;
info.romsize = 0;
info.ramsize = 0;
unsigned base = romsize - 0x8000;
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
) {
//MMM01 stores header at bottom of image
//flip this around for consistency with all other mappers
uint8_t header[0x8000];
memcpy(header, romdata + base, 0x8000);
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
memcpy(romdata, header, 0x8000);
}
info.cgb = (romdata[0x0143] & 0x80) == 0x80;
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0;
switch(romdata[0x0147]) {
case 0x00: info.mapper = "none"; break;
case 0x01: info.mapper = "MBC1"; break;
case 0x02: info.mapper = "MBC1"; info.ram = true; break;
case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break;
case 0x05: info.mapper = "MBC2"; info.ram = true; break;
case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break;
case 0x08: info.mapper = "none"; info.ram = true; break;
case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break;
case 0x0b: info.mapper = "MMM01"; break;
case 0x0c: info.mapper = "MMM01"; info.ram = true; break;
case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break;
case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break;
case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break;
case 0x11: info.mapper = "MBC3"; break;
case 0x12: info.mapper = "MBC3"; info.ram = true; break;
case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break;
case 0x19: info.mapper = "MBC5"; break;
case 0x1a: info.mapper = "MBC5"; info.ram = true; break;
case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break;
case 0x1c: info.mapper = "MBC5"; info.rumble = true; break;
case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break;
case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break;
case 0xfc: break; //Pocket Camera
case 0xfd: break; //Bandai TAMA5
case 0xfe: info.mapper = "HuC3"; break;
case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break;
}
switch(romdata[0x0148]) { default:
case 0x00: info.romsize = 2 * 16 * 1024; break;
case 0x01: info.romsize = 4 * 16 * 1024; break;
case 0x02: info.romsize = 8 * 16 * 1024; break;
case 0x03: info.romsize = 16 * 16 * 1024; break;
case 0x04: info.romsize = 32 * 16 * 1024; break;
case 0x05: info.romsize = 64 * 16 * 1024; break;
case 0x06: info.romsize = 128 * 16 * 1024; break;
case 0x07: info.romsize = 256 * 16 * 1024; break;
case 0x52: info.romsize = 72 * 16 * 1024; break;
case 0x53: info.romsize = 80 * 16 * 1024; break;
case 0x54: info.romsize = 96 * 16 * 1024; break;
}
switch(romdata[0x0149]) { default:
case 0x00: info.ramsize = 0 * 1024; break;
case 0x01: info.ramsize = 2 * 1024; break;
case 0x02: info.ramsize = 8 * 1024; break;
case 0x03: info.ramsize = 32 * 1024; break;
}
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
markup.append("<cartridge>\n");
markup.append(" <board type='", info.mapper, "'/>\n");
markup.append(" <rom name='program.rom' size='0x", hex(romsize), "'/>\n");
if(info.ramsize > 0) markup.append(" <ram name='save.ram' size='0x", hex(info.ramsize), "'/>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@ -1,26 +0,0 @@
#ifndef NALL_EMULATION_SATELLAVIEW_HPP
#define NALL_EMULATION_SATELLAVIEW_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct SatellaviewCartridge {
string markup;
inline SatellaviewCartridge(const uint8_t *data, unsigned size);
};
SatellaviewCartridge::SatellaviewCartridge(const uint8_t *data, unsigned size) {
markup = "";
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
markup.append("<cartridge sha256='", sha256(data, size) ,"'>\n");
markup.append(" <rom name='program.rom' size='0x", hex(size), "'/>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@ -1,33 +0,0 @@
#ifndef NALL_EMULATION_SUFAMI_TURBO_HPP
#define NALL_EMULATION_SUFAMI_TURBO_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct SufamiTurboCartridge {
string markup;
inline SufamiTurboCartridge(const uint8_t *data, unsigned size);
};
SufamiTurboCartridge::SufamiTurboCartridge(const uint8_t *data, unsigned size) {
markup = "";
if(size < 0x20000) return; //too small to be a valid game?
if(memcmp(data, "BANDAI SFC-ADX", 14)) return; //missing required header?
unsigned romsize = data[0x36] * 0x20000; //128KB
unsigned ramsize = data[0x37] * 0x800; //2KB
bool linkable = data[0x35] != 0x00; //TODO: unconfirmed
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
markup.append("<cartridge linkable='", linkable, "' sha256='", sha256(data, size) ,"'>\n");
markup.append(" <rom name='program.rom' size='0x", hex(romsize), "'/>\n");
markup.append(" <ram name='save.ram' size='0x", hex(ramsize), "'/>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@ -1,803 +0,0 @@
#ifndef NALL_EMULATION_SUPER_FAMICOM_HPP
#define NALL_EMULATION_SUPER_FAMICOM_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct SuperFamicomCartridge {
string markup;
inline SuperFamicomCartridge(const uint8_t *data, unsigned size);
//private:
inline void read_header(const uint8_t *data, unsigned size);
inline unsigned find_header(const uint8_t *data, unsigned size);
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
enum HeaderField {
CartName = 0x00,
Mapper = 0x15,
RomType = 0x16,
RomSize = 0x17,
RamSize = 0x18,
CartRegion = 0x19,
Company = 0x1a,
Version = 0x1b,
Complement = 0x1c, //inverse checksum
Checksum = 0x1e,
ResetVector = 0x3c,
};
enum Mode {
ModeNormal,
ModeBsxSlotted,
ModeBsx,
ModeSufamiTurbo,
ModeSuperGameBoy,
};
enum Type {
TypeNormal,
TypeBsxSlotted,
TypeBsxBios,
TypeBsx,
TypeSufamiTurboBios,
TypeSufamiTurbo,
TypeSuperGameBoy1Bios,
TypeSuperGameBoy2Bios,
TypeGameBoy,
TypeUnknown,
};
enum Region {
NTSC,
PAL,
};
enum MemoryMapper {
LoROM,
HiROM,
ExLoROM,
ExHiROM,
SuperFXROM,
SA1ROM,
SPC7110ROM,
BSCLoROM,
BSCHiROM,
BSXROM,
STROM,
};
enum DSP1MemoryMapper {
DSP1Unmapped,
DSP1LoROM1MB,
DSP1LoROM2MB,
DSP1HiROM,
};
bool loaded; //is a base cartridge inserted?
unsigned crc32; //crc32 of all cartridges (base+slot(s))
unsigned rom_size;
unsigned ram_size;
Mode mode;
Type type;
Region region;
MemoryMapper mapper;
DSP1MemoryMapper dsp1_mapper;
bool has_bsx_slot;
bool has_superfx;
bool has_sa1;
bool has_srtc;
bool has_sdd1;
bool has_spc7110;
bool has_spc7110rtc;
bool has_cx4;
bool has_dsp1;
bool has_dsp2;
bool has_dsp3;
bool has_dsp4;
bool has_obc1;
bool has_st010;
bool has_st011;
bool has_st018;
};
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
//skip copier header
if((size & 0x7fff) == 512) data += 512, size -= 512;
markup = "";
if(size < 0x8000) return;
read_header(data, size);
markup = "";
if(type == TypeGameBoy) return;
if(type == TypeBsx) return;
if(type == TypeSufamiTurbo) return;
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
markup.append("<cartridge region='", region == NTSC ? "NTSC" : "PAL", "'>\n");
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-7f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='80-ff:8000-ffff' id='rom' mode='linear'/>\n"
" <icd2 revision='1'>\n"
" <firmware name='boot.rom' size='256' sha256='0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360'/>\n"
" <map address='00-3f:6000-7fff' id='io'/>\n"
" <map address='80-bf:6000-7fff' id='io'/>\n"
" </icd2>\n"
);
else if(has_cx4) markup.append(
" <hitachidsp model='HG51B169' frequency='20000000'>\n"
" <firmware name='cx4.rom' size='3072' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'/>\n"
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-3f:6000-7fff' id='io'/>\n"
" <map address='80-bf:6000-7fff' id='io'/>\n"
" <map address='00-7f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='80-ff:8000-ffff' id='rom' mode='linear'/>\n"
" </hitachidsp>\n"
);
else if(has_spc7110) markup.append(
" <spc7110>\n"
" <prom name='program.rom' size='0x100000'/>\n"
" <drom name='data.rom' size='0x", hex(rom_size - 0x100000), "'/>\n"
" <ram name='save.rwm' size='0x2000'/>\n"
" <map address='00-3f:4800-483f' id='io'/>\n"
" <map address='80-bf:4800-483f' id='io'/>\n"
" <map address='50:0000-ffff' id='io'/>\n"
" <map address='00-3f:8000-ffff' id='rom'/>\n"
" <map address='80-bf:8000-ffff' id='rom'/>\n"
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
" <map address='00-3f:6000-7fff' id='ram'/>\n"
" <map address='80-bf:6000-7fff' id='ram'/>\n"
" </spc7110>\n"
);
else if(has_sdd1) markup.append(
" <sdd1>\n"
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='00-3f:4800-4807' id='io'/>\n"
" <map address='80-bf:4800-4807' id='io'/>\n"
" <map address='00-3f:8000-ffff' id='rom'/>\n"
" <map address='80-bf:8000-ffff' id='rom'/>\n"
" <map address='40-7f:0000-ffff' id='rom'/>\n"
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
" <map address='20-3f:6000-7fff' id='ram'/>\n"
" <map address='a0-bf:6000-7fff' id='ram'/>\n"
" <map address='70-7f:0000-7fff' id='ram'/>\n"
" </sdd1>\n"
);
else if(mapper == LoROM) {
markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-7f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='80-ff:8000-ffff' id='rom' mode='linear'/>\n"
);
if(ram_size > 0) markup.append(
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='70-7f:", range, "' id='ram' mode='linear'/>\n"
" <map address='f0-ff:", range, "' id='ram' mode='linear'/>\n"
);
}
else if(mapper == HiROM) {
markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-3f:8000-ffff' id='rom' mode='shadow'/>\n"
" <map address='80-bf:8000-ffff' id='rom' mode='shadow'/>\n"
" <map address='40-7f:0000-ffff' id='rom' mode='linear'/>\n"
" <map address='c0-ff:0000-ffff' id='rom' mode='linear'/>\n"
);
if(ram_size > 0) markup.append(
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='70-7f:", range, "' id='ram' mode='linear'/>\n"
);
}
else if(mapper == ExLoROM) {
markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-3f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='80-bf:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='40-7f:0000-ffff' id='rom' mode='linear'/>\n"
);
if(ram_size > 0) markup.append(
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='70-7f:0000-7fff' id='ram' mode='linear'/>\n"
);
}
else if(mapper == ExHiROM) {
markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-3f:8000-ffff' id='rom' mode='shadow' offset='0x400000'/>\n"
" <map address='80-bf:8000-ffff' id='rom' mode='shadow' offset='0x000000'/>\n"
" <map address='40-7f:0000-ffff' id='rom' mode='linear' offset='0x400000'/>\n"
" <map address='c0-ff:0000-ffff' id='rom' mode='linear' offset='0x000000'/>\n"
);
if(ram_size > 0) markup.append(
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='70-7f:", range, "' id='ram' mode='linear'/>\n"
);
}
else if(mapper == SuperFXROM) {
markup.append(
" <superfx revision='2'>\n"
" <map address='00-3f:3000-32ff' id='io'/>\n"
" <map address='80-bf:3000-32ff' id='io'/>\n"
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-3f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='80-bf:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='40-5f:0000-ffff' id='rom' mode='linear'/>\n"
" <map address='c0-df:0000-ffff' id='rom' mode='linear'/>\n"
);
if(ram_size > 0) markup.append(
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='00-3f:6000-7fff' id='ram' mode='linear' size='0x2000'/>\n"
" <map address='80-bf:6000-7fff' id='ram' mode='linear' size='0x2000'/>\n"
" <map address='60-7f:0000-ffff' id='ram' mode='linear'/>\n"
" <map address='e0-ff:0000-ffff' id='ram' mode='linear'/>\n"
);
markup.append(
" </superfx>\n"
);
}
else if(mapper == SA1ROM) {
markup.append(
" <sa1>\n"
" <map address='00-3f:2200-23ff' id='io'/>\n"
" <map address='80-bf:2200-23ff' id='io'/>\n"
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-3f:8000-ffff' id='rom'/>\n"
" <map address='80-bf:8000-ffff' id='rom'/>\n"
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
" <iram size='0x800'/>\n"
" <map address='00-3f:3000-37ff' id='iram'/>\n"
" <map address='80-bf:3000-37ff' id='iram'/>\n"
);
if(ram_size > 0) markup.append(
" <bwram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='00-3f:6000-7fff' id='bwram'/>\n"
" <map address='80-bf:6000-7fff' id='bwram'/>\n"
" <map address='40-4f:0000-ffff' id='bwram'/>\n"
);
markup.append(
" </sa1>\n"
);
}
else if(mapper == BSCLoROM) markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='00-1f:8000-ffff' id='rom' mode='linear' offset='0x000000'/>\n"
" <map address='20-3f:8000-ffff' id='rom' mode='linear' offset='0x100000'/>\n"
" <map address='80-9f:8000-ffff' id='rom' mode='linear' offset='0x200000'/>\n"
" <map address='a0-bf:8000-ffff' id='rom' mode='linear' offset='0x100000'/>\n"
" <map address='70-7f:0000-7fff' id='ram' mode='linear'/>\n"
" <map address='f0-ff:0000-7fff' id='ram' mode='linear'/>\n"
" <bsxslot>\n"
" <map address='c0-ef:0000-ffff' id='rom' mode='linear'/>\n"
" </bsxslot>\n"
);
else if(mapper == BSCHiROM) markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <map address='00-1f:8000-ffff' id='rom' mode='shadow'/>\n"
" <map address='80-9f:8000-ffff' id='rom' mode='shadow'/>\n"
" <map address='40-5f:0000-ffff' id='rom' mode='linear'/>\n"
" <map address='c0-df:0000-ffff' id='rom' mode='linear'/>\n"
" <map address='20-3f:6000-7fff' id='ram' mode='linear'/>\n"
" <map address='a0-bf:6000-7fff' id='ram' mode='linear'/>\n"
" <bsxslot>\n"
" <map address='20-3f:8000-ffff' id='rom' mode='shadow'/>\n"
" <map address='a0-bf:8000-ffff' id='rom' mode='shadow'/>\n"
" <map address='60-7f:0000-ffff' id='rom' mode='linear'/>\n"
" <map address='e0-ff:0000-ffff' id='rom' mode='linear'/>\n"
" </bsxslot>\n"
);
else if(mapper == BSXROM) markup.append(
" <bsx>\n"
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <ram name='save.rwm' size='0x", hex(ram_size), "'/>\n"
" <psram name='bsx.rwm' size='0x40000'/>\n"
" <map address='00-3f:5000-5fff' id='io'/>\n"
" <map address='80-bf:5000-5fff' id='io'/>\n"
" <map address='20-3f:6000-7fff' id='ram'/>\n"
" <map address='00-3f:8000-ffff' id='rom'/>\n"
" <map address='80-bf:8000-ffff' id='rom'/>\n"
" <map address='40-7f:0000-ffff' id='rom'/>\n"
" <map address='c0-ff:0000-ffff' id='rom'/>\n"
" </bsx>\n"
);
else if(mapper == STROM) markup.append(
" <rom name='program.rom' size='0x", hex(rom_size), "'/>\n"
" <map address='00-1f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='80-9f:8000-ffff' id='rom' mode='linear'/>\n"
" <sufamiturbo>\n"
" <slot id='A'>\n"
" <map address='20-3f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='a0-bf:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='60-63:8000-ffff' id='ram' mode='linear'/>\n"
" <map address='e0-e3:8000-ffff' id='ram' mode='linear'/>\n"
" </slot>\n"
" <slot id='B'>\n"
" <map address='40-5f:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='c0-df:8000-ffff' id='rom' mode='linear'/>\n"
" <map address='70-73:8000-ffff' id='ram' mode='linear'/>\n"
" <map address='f0-f3:8000-ffff' id='ram' mode='linear'/>\n"
" </slot>\n"
" </sufamiturbo>\n"
);
if(has_spc7110rtc) markup.append(
" <epsonrtc>\n"
" <ram name='rtc.rwm' size='0x10'/>\n"
" <map address='00-3f:4840-4842' id='io'/>\n"
" <map address='80-bf:4840-4842' id='io'/>\n"
" </epsonrtc>\n"
);
if(has_srtc) markup.append(
" <sharprtc>\n"
" <ram name='rtc.rwm' size='0x10'/>\n"
" <map address='00-3f:2800-2801' id='io'/>\n"
" <map address='80-bf:2800-2801' id='io'/>\n"
" </sharprtc>\n"
);
if(has_obc1) markup.append(
" <obc1>\n"
" <ram name='save.rwm' size='0x2000'/>\n"
" <map address='00-3f:6000-7fff' id='io'/>\n"
" <map address='80-bf:6000-7fff' id='io'/>\n"
" </obc1>\n"
);
if(has_dsp1) {
//91e87d11e1c30d172556bed2211cce2efa94ba595f58c5d264809ef4d363a97b dsp1.rom
markup.append(
" <necdsp model='uPD7725' frequency='8000000'>\n"
" <firmware name='dsp1b.rom' size='8192' sha256='d789cb3c36b05c0b23b6c6f23be7aa37c6e78b6ee9ceac8d2d2aa9d8c4d35fa9'/>\n"
);
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
" <map address='20-3f:8000-bfff' id='dr'/>\n"
" <map address='a0-bf:8000-bfff' id='dr'/>\n"
" <map address='20-3f:c000-ffff' id='sr'/>\n"
" <map address='a0-bf:c000-ffff' id='sr'/>\n"
);
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
" <map address='60-6f:0000-3fff' id='dr'/>\n"
" <map address='e0-ef:0000-3fff' id='dr'/>\n"
" <map address='60-6f:4000-7fff' id='sr'/>\n"
" <map address='e0-ef:4000-7fff' id='sr'/>\n"
);
if(dsp1_mapper == DSP1HiROM) markup.append(
" <map address='00-1f:6000-6fff' id='dr'/>\n"
" <map address='80-9f:6000-6fff' id='dr'/>\n"
" <map address='00-1f:7000-7fff' id='sr'/>\n"
" <map address='80-9f:7000-7fff' id='sr'/>\n"
);
markup.append(
" </necdsp>\n"
);
}
if(has_dsp2) markup.append(
" <necdsp model='uPD7725' frequency='8000000'>\n"
" <firmware name='dsp2.rom' size='8192' sha256='03ef4ef26c9f701346708cb5d07847b5203cf1b0818bf2930acd34510ffdd717'/>\n"
" <map address='20-3f:8000-bfff' id='dr'/>\n"
" <map address='a0-bf:8000-bfff' id='dr'/>\n"
" <map address='20-3f:c000-ffff' id='sr'/>\n"
" <map address='a0-bf:c000-ffff' id='sr'/>\n"
" </necdsp>\n"
);
if(has_dsp3) markup.append(
" <necdsp model='uPD7725' frequency='8000000'>\n"
" <firmware name='dsp3.rom' size='8192' sha256='0971b08f396c32e61989d1067dddf8e4b14649d548b2188f7c541b03d7c69e4e'/>\n"
" <map address='20-3f:8000-bfff' id='dr'/>\n"
" <map address='a0-bf:8000-bfff' id='dr'/>\n"
" <map address='20-3f:c000-ffff' id='sr'/>\n"
" <map address='a0-bf:c000-ffff' id='sr'/>\n"
" </necdsp>\n"
);
if(has_dsp4) markup.append(
" <necdsp model='uPD7725' frequency='8000000'>\n"
" <firmware name='dsp4.rom' size='8192' sha256='752d03b2d74441e430b7f713001fa241f8bbcfc1a0d890ed4143f174dbe031da'/>\n"
" <map address='30-3f:8000-bfff' id='dr'/>\n"
" <map address='b0-bf:8000-bfff' id='dr'/>\n"
" <map address='30-3f:c000-ffff' id='sr'/>\n"
" <map address='b0-bf:c000-ffff' id='sr'/>\n"
" </necdsp>\n"
);
if(has_st010) markup.append(
" <necdsp model='uPD96050' frequency='10000000'>\n"
" <firmware name='st010.rom' size='53248' sha256='fa9bced838fedea11c6f6ace33d1878024bdd0d02cc9485899d0bdd4015ec24c'/>\n"
" <ram name='save.rwm' size='0x1000'/>\n"
" <map address='60:0000' id='dr'/>\n"
" <map address='e0:0000' id='dr'/>\n"
" <map address='60:0001' id='sr'/>\n"
" <map address='e0:0001' id='sr'/>\n"
" <map address='68-6f:0000-0fff' id='ram'/>\n"
" <map address='e8-ef:0000-0fff' id='ram'/>\n"
" </necdsp>\n"
);
if(has_st011) markup.append(
" <necdsp model='uPD96050' frequency='15000000'>\n"
" <firmware name='st011.rom' size='53248' sha256='8b2b3f3f3e6e29f4d21d8bc736b400bc988b7d2214ebee15643f01c1fee2f364'/>\n"
" <ram name='save.rwm' size='0x1000'/>\n"
" <map address='60:0000' id='dr'/>\n"
" <map address='e0:0000' id='dr'/>\n"
" <map address='60:0001' id='sr'/>\n"
" <map address='e0:0001' id='sr'/>\n"
" <map address='68-6f:0000-0fff' id='ram'/>\n"
" <map address='e8-ef:0000-0fff' id='ram'/>\n"
" </necdsp>\n"
);
if(has_st018) markup.append(
" <armdsp frequency='21477272'>\n"
" <firmware name='st018.rom' size='163840' sha256='6df209ab5d2524d1839c038be400ae5eb20dafc14a3771a3239cd9e8acd53806'/>\n"
" <map address='00-3f:3800-38ff' id='io'/>\n"
" <map address='80-bf:3800-38ff' id='io'/>\n"
" </armdsp>\n"
);
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
void SuperFamicomCartridge::read_header(const uint8_t *data, unsigned size) {
type = TypeUnknown;
mapper = LoROM;
dsp1_mapper = DSP1Unmapped;
region = NTSC;
rom_size = size;
ram_size = 0;
has_bsx_slot = false;
has_superfx = false;
has_sa1 = false;
has_srtc = false;
has_sdd1 = false;
has_spc7110 = false;
has_spc7110rtc = false;
has_cx4 = false;
has_dsp1 = false;
has_dsp2 = false;
has_dsp3 = false;
has_dsp4 = false;
has_obc1 = false;
has_st010 = false;
has_st011 = false;
has_st018 = false;
//=====================
//detect Game Boy carts
//=====================
if(size >= 0x0140) {
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
type = TypeGameBoy;
return;
}
}
if(size < 32768) {
type = TypeUnknown;
return;
}
const unsigned index = find_header(data, size);
const uint8_t mapperid = data[index + Mapper];
const uint8_t rom_type = data[index + RomType];
const uint8_t rom_size = data[index + RomSize];
const uint8_t company = data[index + Company];
const uint8_t regionid = data[index + CartRegion] & 0x7f;
ram_size = 1024 << (data[index + RamSize] & 7);
if(ram_size == 1024) ram_size = 0; //no RAM present
//0, 1, 13 = NTSC; 2 - 12 = PAL
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
//=======================
//detect BS-X flash carts
//=======================
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
if(data[index + 0x14] == 0x00) {
const uint8_t n15 = data[index + 0x15];
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
type = TypeBsx;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return;
}
}
}
}
//=========================
//detect Sufami Turbo carts
//=========================
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
type = TypeSufamiTurboBios;
} else {
type = TypeSufamiTurbo;
}
mapper = STROM;
region = NTSC; //Sufami Turbo only released in Japan
return; //RAM size handled outside this routine
}
//==========================
//detect Super Game Boy BIOS
//==========================
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
type = TypeSuperGameBoy2Bios;
return;
}
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
type = TypeSuperGameBoy1Bios;
return;
}
//=====================
//detect standard carts
//=====================
//detect presence of BS-X flash cartridge connector (reads extended header information)
if(data[index - 14] == 'Z') {
if(data[index - 11] == 'J') {
uint8_t n13 = data[index - 13];
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
has_bsx_slot = true;
}
}
}
}
if(has_bsx_slot) {
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
//BS-X base cart
type = TypeBsxBios;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
} else {
type = TypeBsxSlotted;
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
region = NTSC; //BS-X slotted cartridges only released in Japan
}
} else {
//standard cart
type = TypeNormal;
if(index == 0x7fc0 && size >= 0x401000) {
mapper = ExLoROM;
} else if(index == 0x7fc0 && mapperid == 0x32) {
mapper = ExLoROM;
} else if(index == 0x7fc0) {
mapper = LoROM;
} else if(index == 0xffc0) {
mapper = HiROM;
} else { //index == 0x40ffc0
mapper = ExHiROM;
}
}
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
has_superfx = true;
mapper = SuperFXROM;
ram_size = 1024 << (data[index - 3] & 7);
if(ram_size == 1024) ram_size = 0;
}
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
has_sa1 = true;
mapper = SA1ROM;
}
if(mapperid == 0x35 && rom_type == 0x55) {
has_srtc = true;
}
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
has_sdd1 = true;
}
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
has_spc7110 = true;
has_spc7110rtc = (rom_type == 0xf9);
mapper = SPC7110ROM;
}
if(mapperid == 0x20 && rom_type == 0xf3) {
has_cx4 = true;
}
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
has_dsp1 = true;
}
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
has_dsp1 = true;
}
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
has_dsp1 = true;
}
if(has_dsp1 == true) {
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
dsp1_mapper = DSP1LoROM1MB;
} else if((mapperid & 0x2f) == 0x20) {
dsp1_mapper = DSP1LoROM2MB;
} else if((mapperid & 0x2f) == 0x21) {
dsp1_mapper = DSP1HiROM;
}
}
if(mapperid == 0x20 && rom_type == 0x05) {
has_dsp2 = true;
}
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
has_dsp3 = true;
}
if(mapperid == 0x30 && rom_type == 0x03) {
has_dsp4 = true;
}
if(mapperid == 0x30 && rom_type == 0x25) {
has_obc1 = true;
}
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
has_st010 = true;
}
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
has_st011 = true;
}
if(mapperid == 0x30 && rom_type == 0xf5) {
has_st018 = true;
}
}
unsigned SuperFamicomCartridge::find_header(const uint8_t *data, unsigned size) {
unsigned score_lo = score_header(data, size, 0x007fc0);
unsigned score_hi = score_header(data, size, 0x00ffc0);
unsigned score_ex = score_header(data, size, 0x40ffc0);
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
if(score_lo >= score_hi && score_lo >= score_ex) {
return 0x007fc0;
} else if(score_hi >= score_ex) {
return 0x00ffc0;
} else {
return 0x40ffc0;
}
}
unsigned SuperFamicomCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
if(size < addr + 64) return 0; //image too small to contain header at this location?
int score = 0;
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
//$00:[000-7fff] contains uninitialized RAM and MMIO.
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
if(resetvector < 0x8000) return 0;
//some images duplicate the header in multiple locations, and others have completely
//invalid header information that cannot be relied upon.
//below code will analyze the first opcode executed at the specified reset vector to
//determine the probability that this is the correct header.
//most likely opcodes
if(resetop == 0x78 //sei
|| resetop == 0x18 //clc (clc; xce)
|| resetop == 0x38 //sec (sec; xce)
|| resetop == 0x9c //stz $nnnn (stz $4200)
|| resetop == 0x4c //jmp $nnnn
|| resetop == 0x5c //jml $nnnnnn
) score += 8;
//plausible opcodes
if(resetop == 0xc2 //rep #$nn
|| resetop == 0xe2 //sep #$nn
|| resetop == 0xad //lda $nnnn
|| resetop == 0xae //ldx $nnnn
|| resetop == 0xac //ldy $nnnn
|| resetop == 0xaf //lda $nnnnnn
|| resetop == 0xa9 //lda #$nn
|| resetop == 0xa2 //ldx #$nn
|| resetop == 0xa0 //ldy #$nn
|| resetop == 0x20 //jsr $nnnn
|| resetop == 0x22 //jsl $nnnnnn
) score += 4;
//implausible opcodes
if(resetop == 0x40 //rti
|| resetop == 0x60 //rts
|| resetop == 0x6b //rtl
|| resetop == 0xcd //cmp $nnnn
|| resetop == 0xec //cpx $nnnn
|| resetop == 0xcc //cpy $nnnn
) score -= 4;
//least likely opcodes
if(resetop == 0x00 //brk #$nn
|| resetop == 0x02 //cop #$nn
|| resetop == 0xdb //stp
|| resetop == 0x42 //wdm
|| resetop == 0xff //sbc $nnnnnn,x
) score -= 8;
//at times, both the header and reset vector's first opcode will match ...
//fallback and rely on info validity in these cases to determine more likely header.
//a valid checksum is the biggest indicator of a valid header.
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
if(data[addr + RomType] < 0x08) score++;
if(data[addr + RomSize] < 0x10) score++;
if(data[addr + RamSize] < 0x08) score++;
if(data[addr + CartRegion] < 14) score++;
if(score < 0) score = 0;
return score;
}
}
#endif

View File

@ -74,6 +74,22 @@ namespace nall {
return true;
}
static bool write(const string &filename, const string &text) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.print(text);
fp.close();
return true;
}
static bool write(const string &filename, const vector<uint8_t> &buffer) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.write(buffer.data(), buffer.size());
fp.close();
return true;
}
static bool write(const string &filename, const uint8_t *data, unsigned size) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
@ -82,6 +98,11 @@ namespace nall {
return true;
}
static string sha256(const string &filename) {
auto buffer = read(filename);
return nall::sha256(buffer.data(), buffer.size());
}
uint8_t read() {
if(!fp) return 0xff; //file not open
if(file_mode == mode::write) return 0xff; //reads not permitted

View File

@ -24,6 +24,14 @@ struct image {
uint64_t mask;
unsigned depth;
unsigned shift;
inline bool operator==(const Channel &source) {
return mask == source.mask && depth == source.depth && shift == source.shift;
}
inline bool operator!=(const Channel &source) {
return !operator==(source);
}
} alpha, red, green, blue;
typedef double (*interpolation)(double, double, double, double, double);
@ -31,6 +39,9 @@ struct image {
static inline unsigned bitShift(uint64_t color);
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
inline bool operator==(const image &source);
inline bool operator!=(const image &source);
inline image& operator=(const image &source);
inline image& operator=(image &&source);
inline image(const image &source);
@ -89,6 +100,26 @@ uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetD
//public
bool image::operator==(const image &source) {
if(width != source.width) return false;
if(height != source.height) return false;
if(pitch != source.pitch) return false;
if(endian != source.endian) return false;
if(stride != source.stride) return false;
if(alpha != source.alpha) return false;
if(red != source.red) return false;
if(green != source.green) return false;
if(blue != source.blue) return false;
return memcmp(data, source.data, width * height * stride) == 0;
}
bool image::operator!=(const image &source) {
return !operator==(source);
}
image& image::operator=(const image &source) {
free();
@ -110,6 +141,8 @@ image& image::operator=(const image &source) {
}
image& image::operator=(image &&source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;

View File

@ -13,6 +13,7 @@
#include <nall/bit.hpp>
#include <nall/bmp.hpp>
#include <nall/config.hpp>
#include <nall/crc16.hpp>
#include <nall/crc32.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
@ -39,6 +40,7 @@
#include <nall/stream.hpp>
#include <nall/string.hpp>
#include <nall/traits.hpp>
#include <nall/unzip.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>

View File

@ -5,12 +5,6 @@
//example: property<owner>::readonly<type> implies that only owner has full
//access to type; and all other code has readonly access.
//
//this code relies on extended friend semantics from C++0x to work, as it
//declares a friend class via a template paramter. it also exploits a bug in
//G++ 4.x to work even in C++98 mode.
//
//if compiling elsewhere, simply remove the friend class and private semantics
//property can be used either of two ways:
//struct foo {
// property<foo>::readonly<bool> x;
@ -50,8 +44,6 @@
namespace nall {
template<typename C> struct property {
template<typename T> struct traits { typedef T type; };
template<typename T> struct readonly {
const T* operator->() const { return &value; }
const T& operator()() const { return value; }
@ -61,7 +53,7 @@ namespace nall {
operator T&() { return value; }
const T& operator=(const T& value_) { return value = value_; }
T value;
friend class traits<C>::type;
friend C;
};
template<typename T> struct writeonly {
@ -73,7 +65,7 @@ namespace nall {
T* operator->() { return &value; }
operator T&() { return value; }
T value;
friend class traits<C>::type;
friend C;
};
template<typename T> struct readwrite {

View File

@ -29,7 +29,7 @@ namespace nall {
for(signed i = 1, j; i < size; i++) {
T copy = std::move(list[i]);
for(j = i - 1; j >= 0; j--) {
if(lessthan(list[j], copy)) break;
if(!lessthan(copy, list[j])) break;
list[j + 1] = std::move(list[j]);
}
list[j + 1] = std::move(copy);
@ -55,7 +55,7 @@ namespace nall {
T *buffer = new T[size];
unsigned offset = 0, left = 0, right = middle;
while(left < middle && right < size) {
if(lessthan(list[left], list[right])) {
if(!lessthan(list[right], list[left])) {
buffer[offset++] = std::move(list[left++]);
} else {
buffer[offset++] = std::move(list[right++]);

View File

@ -1,7 +1,7 @@
#ifndef NALL_STREAM_ZIP_HPP
#define NALL_STREAM_ZIP_HPP
#include <nall/zip.hpp>
#include <nall/unzip.hpp>
namespace nall {
@ -14,7 +14,7 @@ struct zipstream : memorystream {
uint8_t *data = new uint8_t[size];
stream.read(data, size);
zip archive;
unzip archive;
if(archive.open(data, size) == false) return;
delete[] data;

View File

@ -22,14 +22,15 @@
#define NALL_STRING_INTERNAL_HPP
#include <nall/string/base.hpp>
#include <nall/string/bml.hpp>
#include <nall/string/bsv.hpp>
#include <nall/string/cast.hpp>
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/core.hpp>
#include <nall/string/cstring.hpp>
#include <nall/string/datetime.hpp>
#include <nall/string/filename.hpp>
#include <nall/string/format.hpp>
#include <nall/string/math-fixed-point.hpp>
#include <nall/string/math-floating-point.hpp>
#include <nall/string/platform.hpp>
@ -44,7 +45,10 @@
#include <nall/string/variadic.hpp>
#include <nall/string/wildcard.hpp>
#include <nall/string/wrapper.hpp>
#include <nall/string/xml.hpp>
#include <nall/string/markup/node.hpp>
#include <nall/string/markup/bml.hpp>
#include <nall/string/markup/xml.hpp>
#include <nall/string/markup/document.hpp>
#undef NALL_STRING_INTERNAL_HPP
#endif

View File

@ -23,8 +23,13 @@ namespace nall {
struct string {
inline static string read(const string &filename);
inline static string date();
inline static string time();
inline static string datetime();
inline void reserve(unsigned);
inline void resize(unsigned);
inline void clear(char);
inline bool empty() const;
template<typename... Args> inline string& assign(Args&&... args);
@ -61,10 +66,12 @@ namespace nall {
inline string& qlower();
inline string& qupper();
inline string& transform(const char *before, const char *after);
inline string& reverse();
template<unsigned limit = 0> inline string& ltrim(const char *key = " ");
template<unsigned limit = 0> inline string& rtrim(const char *key = " ");
template<unsigned limit = 0> inline string& trim(const char *key = " ", const char *rkey = 0);
inline string& strip();
inline optional<unsigned> position(const char *key) const;
inline optional<unsigned> iposition(const char *key) const;
@ -115,6 +122,7 @@ namespace nall {
inline optional<unsigned> find(const char*) const;
inline string concatenate(const char*) const;
inline void append() {}
inline void isort();
template<typename... Args> inline void append(const string&, Args&&...);
template<unsigned Limit = 0> inline lstring& split(const char*, const char*);
@ -154,6 +162,12 @@ namespace nall {
inline char* qstrupper(char *str);
inline char* strtr(char *dest, const char *before, const char *after);
//format.hpp
template<signed precision = 0, char padchar = ' '> inline string format(const string &value);
template<signed precision = 0, char padchar = '0'> inline string hex(uintmax_t value);
template<signed precision = 0, char padchar = '0'> inline string octal(uintmax_t value);
template<signed precision = 0, char padchar = '0'> inline string binary(uintmax_t value);
//math.hpp
inline bool strint(const char *str, int &result);
inline bool strmath(const char *str, int &result);
@ -163,6 +177,7 @@ namespace nall {
inline string realpath(const string &name);
inline string userpath();
inline string configpath();
inline string temppath();
//strm.hpp
inline unsigned strmcpy(char *target, const char *source, unsigned length);
@ -182,6 +197,7 @@ namespace nall {
template<unsigned limit = 0> inline char* ltrim(char *str, const char *key = " ");
template<unsigned limit = 0> inline char* rtrim(char *str, const char *key = " ");
template<unsigned limit = 0> inline char* trim(char *str, const char *key = " ", const char *rkey = 0);
inline char* strip(char *s);
//utility.hpp
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
@ -193,12 +209,11 @@ namespace nall {
inline char* integer(char *result, intmax_t value);
inline char* decimal(char *result, uintmax_t value);
//these functions are deprecated, use format() instead:
template<unsigned length = 0, char padding = ' '> inline string integer(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string linteger(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string decimal(uintmax_t value);
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
inline unsigned fp(char *str, long double value);
inline string fp(long double value);

View File

@ -1,151 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BML v1.0 parser
//revision 0.05
namespace nall {
namespace BML {
inline static string indent(const char *s, unsigned depth) {
vector<char> output;
do {
for(unsigned n = 0; n < depth; n++) output.append('\t');
do output.append(*s); while(*s && *s++ != '\n');
} while(*s);
return output.data();
}
struct Node {
cstring name;
cstring value;
private:
vector<Node> children;
inline bool valid(char p) const { //A-Za-z0-9-.
return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u;
}
inline unsigned parseDepth(char *&p) {
while(*p == '\n' || *p == '#') {
while(*p != '\n') *p++ = 0;
*p++ = 0; //'\n'
}
unsigned depth = 0;
while(p[depth] == '\t') depth++;
return depth;
}
inline void parseName(char *&p) {
if(valid(*p) == false) throw "Missing node name";
name = p;
while(valid(*p)) p++;
}
inline void parseValue(char *&p) {
char terminal = *p == ':' ? '\n' : ' '; //':' or '='
*p++ = 0;
value = p;
while(*p && *p != terminal && *p != '\n') p++;
}
inline void parseBlock(char *&p, unsigned depth) {
value = p;
char *w = p;
while(parseDepth(p) > depth) {
p += depth + 1;
while(*p && *p != '\n') *w++ = *p++;
if(*p && *p != '\n') throw "Multi-line value missing line feed";
*w++ = *p;
}
*(w - 1) = 0; //'\n'
}
inline void parseLine(char *&p) {
unsigned depth = parseDepth(p);
while(*p == '\t') p++;
parseName(p);
bool multiLine = *p == '~';
if(multiLine) *p++ = 0;
else if(*p == ':' || *p == '=') parseValue(p);
if(*p && *p != ' ' && *p != '\n') throw "Invalid character encountered";
while(*p == ' ') {
*p++ = 0;
Node node;
node.parseName(p);
if(*p == ':' || *p == '=') node.parseValue(p);
if(*p && *p != ' ' && *p != '\n') throw "Invalid character after node";
if(*p == '\n') *p++ = 0;
children.append(node);
}
if(multiLine) return parseBlock(p, depth);
while(parseDepth(p) > depth) {
Node node;
node.parseLine(p);
children.append(node);
}
}
inline void parse(char *&p) {
while(*p) {
Node node;
node.parseLine(p);
children.append(node);
}
}
public:
inline Node& operator[](const char *name) {
for(auto &node : children) {
if(node.name == name) return node;
}
static Node node;
node.name = nullptr;
return node;
}
inline bool exists() const { return name; }
unsigned size() const { return children.size(); }
Node* begin() { return children.begin(); }
Node* end() { return children.end(); }
const Node* begin() const { return children.begin(); }
const Node* end() const { return children.end(); }
inline Node() : name(""), value("") {}
friend class Document;
};
struct Document : Node {
cstring error;
inline bool load(const char *document) {
if(document == nullptr) return false;
this->document = strdup(document);
char *p = this->document;
try {
this->error = nullptr;
parse(p);
} catch(const char *error) {
this->error = error;
free(this->document);
this->document = nullptr;
children.reset();
return false;
}
return true;
}
inline Document(const char *document = "") : document(nullptr), error(nullptr) { if(*document) load(document); }
inline ~Document() { if(document) free(document); }
private:
char *document;
};
}
}
#endif

View File

@ -12,11 +12,18 @@ static void istring(string &output, const T &value, Args&&... args) {
}
void string::reserve(unsigned size_) {
if(size_ > size) {
if(size_ > size) resize(size_);
}
void string::resize(unsigned size_) {
size = size_;
data = (char*)realloc(data, size + 1);
data[size] = 0;
}
}
void string::clear(char c) {
for(unsigned n = 0; n < size; n++) data[n] = c;
data[size] = 0;
}
bool string::empty() const {
@ -151,6 +158,12 @@ template<typename... Args> void lstring::append(const string &data, Args&&... ar
append(std::forward<Args>(args)...);
}
void lstring::isort() {
nall::sort(pool, objectsize, [](const string &x, const string &y) {
return istrcmp(x, y) < 0;
});
}
bool lstring::operator==(const lstring &source) const {
if(this == &source) return true;
if(size() != source.size()) return false;

View File

@ -0,0 +1,31 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
string string::date() {
time_t timestamp = ::time(0);
tm *info = localtime(&timestamp);
return {
decimal<4, '0'>(1900 + info->tm_year), "-",
decimal<2, '0'>(1 + info->tm_mon), "-",
decimal<2, '0'>(info->tm_mday)
};
}
string string::time() {
time_t timestamp = ::time(0);
tm *info = localtime(&timestamp);
return {
decimal<2, '0'>(info->tm_hour), ":",
decimal<2, '0'>(info->tm_min), ":",
decimal<2, '0'>(info->tm_sec)
};
}
string string::datetime() {
return {string::date(), " ", string::time()};
}
}
#endif

View File

@ -0,0 +1,73 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
template<signed precision, char padchar> string format(const string &value) {
if(precision == 0) return value;
bool padright = precision >= 0;
unsigned padding = abs(precision);
unsigned length = value.length();
if(padding <= length) {
if(padright) return substr(value, length - padding);
else return substr(value, 0, padding);
}
string buffer;
buffer.resize(padding);
buffer.clear(padchar);
memcpy(buffer() + (padright ? padding - length : 0), value, length);
return buffer;
}
template<signed precision, char padchar> string hex(uintmax_t value) {
string buffer;
buffer.resize(sizeof(uintmax_t) * 2);
unsigned size = 0;
do {
unsigned n = value & 15;
buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
buffer[size] = 0;
buffer.reverse();
return format<precision, padchar>(buffer);
}
template<signed precision, char padchar> string octal(uintmax_t value) {
string buffer;
buffer.resize(sizeof(uintmax_t) * 3);
unsigned size = 0;
do {
buffer[size++] = '0' + (value & 7);
value >>= 3;
} while(value);
buffer[size] = 0;
buffer.reverse();
return format<precision, padchar>(buffer);
}
template<signed precision, char padchar> string binary(uintmax_t value) {
string buffer;
buffer.resize(sizeof(uintmax_t) * 8);
unsigned size = 0;
do {
buffer[size++] = '0' + (value & 1);
value >>= 1;
} while(value);
buffer[size] = 0;
buffer.reverse();
return format<precision, padchar>(buffer);
}
}
#endif

View File

@ -0,0 +1,147 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BML v1.0 parser
//revision 0.02
namespace nall {
namespace BML {
struct Node : Markup::Node {
protected:
//test to verify if a valid character for a node name
bool valid(char p) const { //A-Z, a-z, 0-9, -./
return p - 'A' < 26u || p - 'a' < 26u || p - '0' < 10u || p - '-' < 3u;
}
//determine indentation level, without incrementing pointer
unsigned readDepth(const char *p) {
unsigned depth = 0;
while(p[depth] == '\t' || p[depth] == ' ') depth++;
return depth;
}
//determine indentation level
unsigned parseDepth(const char *&p) {
unsigned depth = readDepth(p);
p += depth;
return depth;
}
//read name
void parseName(const char *&p) {
unsigned length = 0;
while(valid(p[length])) length++;
if(length == 0) throw "Invalid node name";
name = substr(p, 0, length);
p += length;
}
void parseData(const char *&p) {
if(*p == '=' && *(p + 1) == '\"') {
unsigned length = 2;
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
if(p[length] != '\"') throw "Unescaped value";
data = substr(p, 2, length - 2);
p += length + 1;
} else if(*p == '=') {
unsigned length = 1;
while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++;
if(p[length] == '\"') throw "Illegal character in value";
data = substr(p, 1, length - 1);
p += length;
} else if(*p == ':') {
unsigned length = 1;
while(p[length] && p[length] != '\n') length++;
data = {substr(p, 1, length - 1), "\n"};
p += length;
}
}
//read all attributes for a node
void parseAttributes(const char *&p) {
while(*p && *p != '\n') {
if(*p != ' ') throw "Invalid node name";
while(*p == ' ') p++; //skip excess spaces
Node node;
node.attribute = true;
unsigned length = 0;
while(valid(p[length])) length++;
if(length == 0) throw "Invalid attribute name";
node.name = substr(p, 0, length);
node.parseData(p += length);
children.append(node);
}
}
//read a node and all of its children nodes
void parseNode(const char *&p) {
level = parseDepth(p);
parseName(p);
parseData(p);
parseAttributes(p);
if(*p++ != '\n') throw "Missing line feed";
while(*p) {
if(*p == '\n') { p++; continue; }
unsigned depth = readDepth(p);
if(depth <= level) break;
if(p[depth] == ':') {
p += depth;
unsigned length = 0;
while(p[length] && p[length] != '\n') length++;
data.append(substr(p, 1, length - 1), "\n");
p += length;
continue;
}
Node node;
node.parseNode(p);
children.append(node);
}
data.rtrim<1>("\n");
}
//read top-level nodes
void parse(const char *p) {
while(*p) {
Node node;
node.parseNode(p);
if(node.level > 0) throw "Root nodes cannot be indented";
children.append(node);
}
}
friend class Document;
};
struct Document : Node {
string error;
bool load(string document) {
name = "{root}", data = "";
try {
document.replace("\r", "");
while(document.position("\n\n")) document.replace("\n\n", "\n");
parse(document);
} catch(const char *perror) {
error = perror;
children.reset();
return false;
}
return true;
}
Document(const string &document = "") {
load(document);
}
};
}
}
#endif

View File

@ -0,0 +1,14 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
namespace Markup {
inline Node Document(const string &markup) {
if(markup.beginswith("<")) return XML::Document(markup);
return BML::Document(markup);
}
}
}
#endif

View File

@ -0,0 +1,146 @@
#ifdef NALL_STRING_INTERNAL_HPP
//note: specific markups inherit from Markup::Node
//vector<Node> will slice any data; so derived nodes must not contain data nor virtual functions
//vector<Node*> would incur a large performance penalty and greatly increased complexity
namespace nall {
namespace Markup {
struct Node {
string name;
string data;
bool attribute;
bool exists() const {
return !name.empty();
}
string text() const {
return string{data}.strip();
}
intmax_t integer() const {
return numeral(text());
}
uintmax_t decimal() const {
return numeral(text());
}
void reset() {
children.reset();
}
bool evaluate(const string &query) const {
if(query.empty()) return true;
lstring rules = string{query}.replace(" ", "").split(",");
for(auto &rule : rules) {
enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE };
auto comparator = Comparator::ID;
if(rule.wildcard("*!=*")) comparator = Comparator::NE;
else if(rule.wildcard("*<=*")) comparator = Comparator::LE;
else if(rule.wildcard("*>=*")) comparator = Comparator::GE;
else if(rule.wildcard ("*=*")) comparator = Comparator::EQ;
else if(rule.wildcard ("*<*")) comparator = Comparator::LT;
else if(rule.wildcard ("*>*")) comparator = Comparator::GT;
if(comparator == Comparator::ID) {
if(find(rule).size()) continue;
return false;
}
lstring side;
switch(comparator) {
case Comparator::EQ: side = rule.split<1> ("="); break;
case Comparator::NE: side = rule.split<1>("!="); break;
case Comparator::LT: side = rule.split<1> ("<"); break;
case Comparator::LE: side = rule.split<1>("<="); break;
case Comparator::GT: side = rule.split<1> (">"); break;
case Comparator::GE: side = rule.split<1>(">="); break;
}
string data = text();
if(side(0).empty() == false) {
auto result = find(side(0));
if(result.size() == 0) return false;
data = result(0).data;
}
switch(comparator) {
case Comparator::EQ: if(data.wildcard(side(1)) == true) continue; break;
case Comparator::NE: if(data.wildcard(side(1)) == false) continue; break;
case Comparator::LT: if(numeral(data) < numeral(side(1))) continue; break;
case Comparator::LE: if(numeral(data) <= numeral(side(1))) continue; break;
case Comparator::GT: if(numeral(data) > numeral(side(1))) continue; break;
case Comparator::GE: if(numeral(data) >= numeral(side(1))) continue; break;
}
return false;
}
return true;
}
vector<Node> find(const string &query) const {
vector<Node> result;
lstring path = query.split("/");
string name = path.take(0), rule;
unsigned lo = 0u, hi = ~0u;
if(name.wildcard("*[*]")) {
lstring side = name.split<1>("[");
name = side(0);
side = side(1).rtrim<1>("]").split<1>("-");
lo = side(0).empty() ? 0u : numeral(side(0));
hi = side(1).empty() ? ~0u : numeral(side(1));
}
if(name.wildcard("*(*)")) {
lstring side = name.split<1>("(");
name = side(0);
rule = side(1).rtrim<1>(")");
}
unsigned position = 0;
for(auto &node : children) {
if(node.name.wildcard(name) == false) continue;
if(node.evaluate(rule) == false) continue;
bool inrange = position >= lo && position <= hi;
position++;
if(inrange == false) continue;
if(path.size() == 0) result.append(node);
else {
auto list = node.find(path.concatenate("/"));
for(auto &item : list) result.append(item);
}
}
return result;
}
Node operator[](const string &query) const {
auto result = find(query);
return result(0);
}
Node* begin() { return children.begin(); }
Node* end() { return children.end(); }
const Node* begin() const { return children.begin(); }
const Node* end() const { return children.end(); }
Node() : attribute(false), level(0) {}
protected:
unsigned level;
vector<Node> children;
};
}
}
#endif

View File

@ -1,19 +1,22 @@
#ifdef NALL_STRING_INTERNAL_HPP
//XML v1.0 subset parser
//revision 0.01
//revision 0.03
namespace nall {
namespace XML {
struct Node {
string name;
string data;
bool attribute;
vector<Node*> children;
inline bool exists() const {
return !name.empty();
struct Node : Markup::Node {
protected:
inline string escape() const {
string result = data;
result.replace("&", "&amp;");
result.replace("<", "&lt;");
result.replace(">", "&gt;");
if(attribute == false) return result;
result.replace("\'", "&apos;");
result.replace("\"", "&quot;");
return result;
}
inline bool isName(char c) const {
@ -124,15 +127,14 @@ struct Node {
if(*p == '?' || *p == '/' || *p == '>') break;
//parse attribute name
Node *attribute = new Node;
children.append(attribute);
attribute->attribute = true;
Node attribute;
attribute.attribute = true;
const char *nameStart = p;
while(isName(*p)) p++;
const char *nameEnd = p;
copy(attribute->name, nameStart, nameEnd - nameStart);
if(attribute->name.empty()) throw "missing attribute name";
copy(attribute.name, nameStart, nameEnd - nameStart);
if(attribute.name.empty()) throw "missing attribute name";
//parse attribute data
if(*p++ != '=') throw "missing attribute value";
@ -143,7 +145,8 @@ struct Node {
if(!*p) throw "missing attribute data terminal";
const char *dataEnd = p++; //skip closing terminal
copy(attribute->data, dataStart, dataEnd - dataStart);
copy(attribute.data, dataStart, dataEnd - dataStart);
children.append(attribute);
}
//parse closure
@ -155,10 +158,9 @@ struct Node {
//parse element and all of its child elements
inline void parseElement(const char *&p) {
Node *node = new Node;
Node node;
if(node.parseHead(p) == false) node.parse(p);
children.append(node);
if(node->parseHead(p) == true) return;
node->parse(p);
}
//return true if </tag> matches this node's name
@ -188,40 +190,6 @@ struct Node {
copy(data, dataStart, dataEnd - dataStart);
}
inline void reset() {
for(auto &child : children) delete child;
children.reset();
}
struct iterator {
inline bool operator!=(const iterator &source) const { return index != source.index; }
inline Node& operator*() { return *node.children[index]; }
inline iterator& operator++() { index++; return *this; }
inline iterator(const Node &node, unsigned index) : node(node), index(index) {}
private:
const Node &node;
unsigned index;
};
inline iterator begin() { return iterator(*this, 0); }
inline iterator end() { return iterator(*this, children.size()); }
inline const iterator begin() const { return iterator(*this, 0); }
inline const iterator end() const { return iterator(*this, children.size()); }
inline Node& operator[](const char *name) {
for(auto &node : *this) {
if(node.name == name) return node;
}
static Node node;
return node;
}
inline Node() : attribute(false) {}
inline ~Node() { reset(); }
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
};
struct Document : Node {

View File

@ -6,12 +6,12 @@ string activepath() {
string result;
#ifdef _WIN32
wchar_t path[PATH_MAX] = L"";
_wgetcwd(path, PATH_MAX);
auto unused = _wgetcwd(path, PATH_MAX);
result = (const char*)utf8_t(path);
result.transform("\\", "/");
#else
char path[PATH_MAX] = "";
getcwd(path, PATH_MAX);
auto unused = getcwd(path, PATH_MAX);
result = path;
#endif
if(result.empty()) result = ".";
@ -29,6 +29,7 @@ string realpath(const string &name) {
char path[PATH_MAX] = "";
if(::realpath(name, path)) result = path;
#endif
if(result.empty()) result = {activepath(), name};
return result;
}
@ -58,6 +59,17 @@ string configpath() {
#endif
}
string temppath() {
#ifdef _WIN32
wchar_t path[PATH_MAX] = L"";
GetTempPathW(PATH_MAX, path);
//path.transform("\\", "/");
return (const char*)utf8_t(path);
#else
return "/tmp/";
#endif
}
}
#endif

View File

@ -33,6 +33,23 @@ template<unsigned limit> char* trim(char *str, const char *key, const char *rkey
return ltrim<limit>(rtrim<limit>(str, key), key);
}
//remove whitespace characters from both left and right sides of string
char* strip(char *s) {
signed n = 0, p = 0;
while(s[n]) {
if(s[n] != ' ' && s[n] != '\t' && s[n] != '\r' && s[n] != '\n') break;
n++;
}
while(s[n]) s[p++] = s[n++];
s[p--] = 0;
while(p >= 0) {
if(s[p] != ' ' && s[p] != '\t' && s[p] != '\r' && s[p] != '\n') break;
p--;
}
s[++p] = 0;
return s;
}
}
#endif

View File

@ -74,7 +74,8 @@ char* integer(char *result, intmax_t value) {
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
if(negative) buffer[size++] = '-';
//buffer[size++] = negative ? '-' : '+';
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
result[size] = 0;
@ -110,7 +111,8 @@ template<unsigned length_, char padding> string integer(intmax_t value) {
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
if(negative) buffer[size++] = '-';
//buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
@ -137,7 +139,8 @@ template<unsigned length_, char padding> string linteger(intmax_t value) {
buffer[size++] = '0' + n;
value /= 10;
} while(value);
buffer[size++] = negative ? '-' : '+';
if(negative) buffer[size++] = '-';
//buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
@ -198,50 +201,6 @@ template<unsigned length_, char padding> string ldecimal(uintmax_t value) {
return (const char*)result;
}
template<unsigned length_, char padding> string hex(uintmax_t value) {
char buffer[64];
unsigned size = 0;
do {
unsigned n = value & 15;
buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
template<unsigned length_, char padding> string binary(uintmax_t value) {
char buffer[256];
unsigned size = 0;
do {
unsigned n = value & 1;
buffer[size++] = '0' + n;
value >>= 1;
} while(value);
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return (const char*)result;
}
//using sprintf is certainly not the most ideal method to convert
//a double to a string ... but attempting to parse a double by
//hand, digit-by-digit, results in subtle rounding errors.

View File

@ -27,10 +27,16 @@ string& string::upper() { nall::strupper(data); return *this; }
string& string::qlower() { nall::qstrlower(data); return *this; }
string& string::qupper() { nall::qstrupper(data); return *this; }
string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; }
string& string::reverse() {
unsigned length = strlen(data), pivot = length >> 1;
for(signed x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(data[x], data[y]);
return *this;
}
template<unsigned limit> string& string::ltrim(const char *key) { nall::ltrim<limit>(data, key); return *this; }
template<unsigned limit> string& string::rtrim(const char *key) { nall::rtrim<limit>(data, key); return *this; }
template<unsigned limit> string& string::trim(const char *key, const char *rkey) { nall::trim <limit>(data, key, rkey); return *this; }
string& string::strip() { nall::strip(data); return *this; }
optional<unsigned> string::position(const char *key) const { return strpos(data, key); }
optional<unsigned> string::iposition(const char *key) const { return istrpos(data, key); }

View File

@ -1,265 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
//XML v1.0 subset parser
//revision 0.05
namespace nall {
struct xml_attribute {
string name;
string content;
virtual string parse() const;
};
struct xml_element : xml_attribute {
string parse() const;
linear_vector<xml_attribute> attribute;
linear_vector<xml_element> element;
protected:
void parse_doctype(const char *&data);
bool parse_head(string data);
bool parse_body(const char *&data);
friend xml_element xml_parse(const char *data);
};
inline string xml_attribute::parse() const {
string data;
unsigned offset = 0;
const char *source = content;
while(*source) {
if(*source == '&') {
if(strbegin(source, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { data[offset++] = '"'; source += 6; continue; }
}
//reject illegal characters
if(*source == '&') return "";
if(*source == '<') return "";
if(*source == '>') return "";
data[offset++] = *source++;
}
data[offset] = 0;
return data;
}
inline string xml_element::parse() const {
string data;
unsigned offset = 0;
const char *source = content;
while(*source) {
if(*source == '&') {
if(strbegin(source, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { data[offset++] = '"'; source += 6; continue; }
}
if(strbegin(source, "<!--")) {
if(auto pos = strpos(source, "-->")) {
source += pos() + 3;
continue;
} else {
return "";
}
}
if(strbegin(source, "<![CDATA[")) {
if(auto pos = strpos(source, "]]>")) {
if(pos() - 9 > 0) {
string cdata = substr(source, 9, pos() - 9);
data.append(cdata);
offset += strlen(cdata);
}
source += 9 + offset + 3;
continue;
} else {
return "";
}
}
//reject illegal characters
if(*source == '&') return "";
if(*source == '<') return "";
if(*source == '>') return "";
data[offset++] = *source++;
}
data[offset] = 0;
return data;
}
inline void xml_element::parse_doctype(const char *&data) {
name = "!DOCTYPE";
const char *content_begin = data;
signed counter = 0;
while(*data) {
char value = *data++;
if(value == '<') counter++;
if(value == '>') counter--;
if(counter < 0) {
content = substr(content_begin, 0, data - content_begin - 1);
return;
}
}
throw "...";
}
inline bool xml_element::parse_head(string data) {
data.qreplace("\t", " ");
data.qreplace("\r", " ");
data.qreplace("\n", " ");
while(qstrpos(data, " ")) data.qreplace(" ", " ");
data.qreplace(" =", "=");
data.qreplace("= ", "=");
data.rtrim();
lstring part;
part.qsplit(" ", data);
name = part[0];
if(name == "") throw "...";
for(unsigned i = 1; i < part.size(); i++) {
lstring side;
side.qsplit("=", part[i]);
if(side.size() != 2) throw "...";
xml_attribute attr;
attr.name = side[0];
attr.content = side[1];
if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\"");
else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'");
else throw "...";
attribute.append(attr);
}
}
inline bool xml_element::parse_body(const char *&data) {
while(true) {
if(!*data) return false;
if(*data++ != '<') continue;
if(*data == '/') return false;
if(strbegin(data, "!DOCTYPE") == true) {
parse_doctype(data);
return true;
}
if(strbegin(data, "!--")) {
if(auto offset = strpos(data, "-->")) {
data += offset() + 3;
continue;
} else {
throw "...";
}
}
if(strbegin(data, "![CDATA[")) {
if(auto offset = strpos(data, "]]>")) {
data += offset() + 3;
continue;
} else {
throw "...";
}
}
auto offset = strpos(data, ">");
if(!offset) throw "...";
string tag = substr(data, 0, offset());
data += offset() + 1;
const char *content_begin = data;
bool self_terminating = false;
if(strend(tag, "?") == true) {
self_terminating = true;
tag.rtrim<1>("?");
} else if(strend(tag, "/") == true) {
self_terminating = true;
tag.rtrim<1>("/");
}
parse_head(tag);
if(self_terminating) return true;
while(*data) {
unsigned index = element.size();
xml_element node;
if(node.parse_body(data) == false) {
if(*data == '/') {
signed length = data - content_begin - 1;
if(length > 0) content = substr(content_begin, 0, length);
data++;
auto offset = strpos(data, ">");
if(!offset) throw "...";
tag = substr(data, 0, offset());
data += offset() + 1;
tag.replace("\t", " ");
tag.replace("\r", " ");
tag.replace("\n", " ");
while(strpos(tag, " ")) tag.replace(" ", " ");
tag.rtrim();
if(name != tag) throw "...";
return true;
}
} else {
element.append(node);
}
}
}
}
//ensure there is only one root element
inline bool xml_validate(xml_element &document) {
unsigned root_counter = 0;
for(unsigned i = 0; i < document.element.size(); i++) {
string &name = document.element[i].name;
if(strbegin(name, "?")) continue;
if(strbegin(name, "!")) continue;
if(++root_counter > 1) return false;
}
return true;
}
inline xml_element xml_parse(const char *data) {
xml_element self;
try {
while(*data) {
xml_element node;
if(node.parse_body(data) == false) {
break;
} else {
self.element.append(node);
}
}
if(xml_validate(self) == false) throw "...";
return self;
} catch(const char*) {
xml_element empty;
return empty;
}
}
}
#endif

126
purify/nall/unzip.hpp Normal file
View File

@ -0,0 +1,126 @@
#ifndef NALL_UNZIP_HPP
#define NALL_UNZIP_HPP
#include <nall/filemap.hpp>
#include <nall/inflate.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
struct unzip {
struct File {
string name;
const uint8_t *data;
unsigned size;
unsigned csize;
unsigned cmode; //0 = uncompressed, 8 = deflate
unsigned crc32;
};
inline bool open(const string &filename) {
close();
if(fm.open(filename, filemap::mode::read) == false) return false;
if(open(fm.data(), fm.size()) == false) {
fm.close();
return false;
}
return true;
}
inline bool open(const uint8_t *data, unsigned size) {
if(size < 22) return false;
filedata = data;
filesize = size;
file.reset();
const uint8_t *footer = data + size - 22;
while(true) {
if(footer <= data + 22) return false;
if(read(footer, 4) == 0x06054b50) {
unsigned commentlength = read(footer + 20, 2);
if(footer + 22 + commentlength == data + size) break;
}
footer--;
}
const uint8_t *directory = data + read(footer + 16, 4);
while(true) {
unsigned signature = read(directory + 0, 4);
if(signature != 0x02014b50) break;
File file;
file.cmode = read(directory + 10, 2);
file.crc32 = read(directory + 16, 4);
file.csize = read(directory + 20, 4);
file.size = read(directory + 24, 4);
unsigned namelength = read(directory + 28, 2);
unsigned extralength = read(directory + 30, 2);
unsigned commentlength = read(directory + 32, 2);
char *filename = new char[namelength + 1];
memcpy(filename, directory + 46, namelength);
filename[namelength] = 0;
file.name = filename;
delete[] filename;
unsigned offset = read(directory + 42, 4);
unsigned offsetNL = read(data + offset + 26, 2);
unsigned offsetEL = read(data + offset + 28, 2);
file.data = data + offset + 30 + offsetNL + offsetEL;
directory += 46 + namelength + extralength + commentlength;
this->file.append(file);
}
return true;
}
inline vector<uint8_t> extract(File &file) {
vector<uint8_t> buffer;
if(file.cmode == 0) {
buffer.resize(file.size);
memcpy(buffer.data(), file.data, file.size);
}
if(file.cmode == 8) {
buffer.resize(file.size);
if(inflate(buffer.data(), buffer.size(), file.data, file.csize) == false) {
buffer.reset();
}
}
return buffer;
}
inline void close() {
if(fm.open()) fm.close();
}
~unzip() {
close();
}
protected:
filemap fm;
const uint8_t *filedata;
unsigned filesize;
unsigned read(const uint8_t *data, unsigned size) {
unsigned result = 0, shift = 0;
while(size--) { result |= *data++ << shift; shift += 8; }
return result;
}
public:
vector<File> file;
};
}
#endif

View File

@ -23,6 +23,7 @@ namespace nall {
public:
operator bool() const { return pool; }
T* data() { return pool; }
const T* data() const { return pool; }
bool empty() const { return objectsize == 0; }
unsigned size() const { return objectsize; }
@ -47,14 +48,15 @@ namespace nall {
}
void reserve(unsigned size) {
unsigned outputsize = min(size, objectsize);
size = bit::round(size); //amortize growth
T *copy = (T*)calloc(size, sizeof(T));
for(unsigned n = 0; n < min(size, objectsize); n++) new(copy + n) T(pool[n]);
for(unsigned n = 0; n < outputsize; n++) new(copy + n) T(pool[n]);
for(unsigned n = 0; n < objectsize; n++) pool[n].~T();
free(pool);
pool = copy;
poolsize = size;
objectsize = min(size, objectsize);
objectsize = outputsize;
}
//requires trivial constructor
@ -121,8 +123,8 @@ namespace nall {
}
optional<unsigned> find(const T& data) {
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
return { false, 0u };
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return {true, n};
return {false, 0u};
}
T& first() {

View File

@ -1,124 +1,93 @@
#ifndef NALL_UNZIP_HPP
#define NALL_UNZIP_HPP
#ifndef NALL_ZIP_HPP
#define NALL_ZIP_HPP
#include <nall/filemap.hpp>
#include <nall/inflate.hpp>
//creates uncompressed ZIP archives
#include <nall/crc32.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
struct zip {
struct File {
string name;
const uint8_t *data;
unsigned size;
unsigned csize;
unsigned cmode; //0 = uncompressed, 8 = deflate
unsigned crc32;
};
inline bool open(const string &filename) {
close();
if(fm.open(filename, filemap::mode::read) == false) return false;
if(open(fm.data(), fm.size()) == false) {
fm.close();
return false;
}
return true;
zip(const string &filename) {
fp.open(filename, file::mode::write);
time_t currentTime = time(0);
tm *info = localtime(&currentTime);
dosTime = (info->tm_hour << 11) | (info->tm_min << 5) | (info->tm_sec >> 1);
dosDate = ((info->tm_year - 80) << 9) | ((1 + info->tm_mon) << 5) + (info->tm_mday);
}
inline bool open(const uint8_t *data, unsigned size) {
if(size < 22) return false;
//append path: append("path/");
//append file: append("path/file", data, size);
void append(string filename, const uint8_t *data = nullptr, unsigned size = 0u) {
filename.transform("\\", "/");
uint32_t checksum = crc32_calculate(data, size);
directory.append({filename, checksum, size, fp.offset()});
filedata = data;
filesize = size;
fp.writel(0x04034b50, 4); //signature
fp.writel(0x0014, 2); //minimum version (2.0)
fp.writel(0x0000, 2); //general purpose bit flags
fp.writel(0x0000, 2); //compression method (0 = uncompressed)
fp.writel(dosTime, 2);
fp.writel(dosDate, 2);
fp.writel(checksum, 4);
fp.writel(size, 4); //compressed size
fp.writel(size, 4); //uncompressed size
fp.writel(filename.length(), 2); //file name length
fp.writel(0x0000, 2); //extra field length
fp.print(filename); //file name
file.reset();
const uint8_t *footer = data + size - 22;
while(true) {
if(footer <= data + 22) return false;
if(read(footer, 4) == 0x06054b50) {
unsigned commentlength = read(footer + 20, 2);
if(footer + 22 + commentlength == data + size) break;
}
footer--;
}
const uint8_t *directory = data + read(footer + 16, 4);
while(true) {
unsigned signature = read(directory + 0, 4);
if(signature != 0x02014b50) break;
File file;
file.cmode = read(directory + 10, 2);
file.crc32 = read(directory + 16, 4);
file.csize = read(directory + 20, 4);
file.size = read(directory + 24, 4);
unsigned namelength = read(directory + 28, 2);
unsigned extralength = read(directory + 30, 2);
unsigned commentlength = read(directory + 32, 2);
char *filename = new char[namelength + 1];
memcpy(filename, directory + 46, namelength);
filename[namelength] = 0;
file.name = filename;
delete[] filename;
unsigned offset = read(directory + 42, 4);
unsigned offsetNL = read(data + offset + 26, 2);
unsigned offsetEL = read(data + offset + 28, 2);
file.data = data + offset + 30 + offsetNL + offsetEL;
directory += 46 + namelength + extralength + commentlength;
this->file.append(file);
}
return true;
}
inline vector<uint8_t> extract(File &file) {
vector<uint8_t> buffer;
if(file.cmode == 0) {
buffer.resize(file.size);
memcpy(buffer.data(), file.data, file.size);
}
if(file.cmode == 8) {
buffer.resize(file.size);
if(inflate(buffer.data(), buffer.size(), file.data, file.csize) == false) {
buffer.reset();
}
}
return buffer;
}
inline void close() {
if(fm.open()) fm.close();
fp.write(data, size); //file data
}
~zip() {
close();
//central directory
unsigned baseOffset = fp.offset();
for(auto &entry : directory) {
fp.writel(0x02014b50, 4); //signature
fp.writel(0x0014, 2); //version made by (2.0)
fp.writel(0x0014, 2); //version needed to extract (2.0)
fp.writel(0x0000, 2); //general purpose bit flags
fp.writel(0x0000, 2); //compression method (0 = uncompressed)
fp.writel(dosTime, 2);
fp.writel(dosDate, 2);
fp.writel(entry.checksum, 4);
fp.writel(entry.size, 4); //compressed size
fp.writel(entry.size, 4); //uncompressed size
fp.writel(entry.filename.length(), 2); //file name length
fp.writel(0x0000, 2); //extra field length
fp.writel(0x0000, 2); //file comment length
fp.writel(0x0000, 2); //disk number start
fp.writel(0x0000, 2); //internal file attributes
fp.writel(0x00000000, 4); //external file attributes
fp.writel(entry.offset, 4); //relative offset of file header
fp.print(entry.filename);
}
unsigned finishOffset = fp.offset();
//end of central directory
fp.writel(0x06054b50, 4); //signature
fp.writel(0x0000, 2); //number of this disk
fp.writel(0x0000, 2); //disk where central directory starts
fp.writel(directory.size(), 2); //number of central directory records on this disk
fp.writel(directory.size(), 2); //total number of central directory records
fp.writel(finishOffset - baseOffset, 4); //size of central directory
fp.writel(baseOffset, 4); //offset of central directory
fp.writel(0x0000, 2); //comment length
fp.close();
}
protected:
filemap fm;
const uint8_t *filedata;
unsigned filesize;
unsigned read(const uint8_t *data, unsigned size) {
unsigned result = 0, shift = 0;
while(size--) { result |= *data++ << shift; shift += 8; }
return result;
}
public:
vector<File> file;
file fp;
uint16_t dosTime, dosDate;
struct entry_t {
string filename;
uint32_t checksum;
uint32_t size;
uint32_t offset;
};
vector<entry_t> directory;
};
}

1
purify/obj/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.o

View File

@ -173,6 +173,10 @@ void OS::quit() {
return pOS::quit();
}
void OS::setName(const string &name) {
osState.name = name;
}
void OS::initialize() {
static bool initialized = false;
if(initialized == false) {
@ -339,6 +343,14 @@ void Window::setResizable(bool resizable) {
return p.setResizable(resizable);
}
void Window::setSmartGeometry(const Geometry &geometry) {
Geometry margin = p.frameMargin();
return setGeometry({
geometry.x + margin.x, geometry.y + margin.y,
geometry.width, geometry.height
});
}
void Window::setStatusFont(const string &font) {
state.statusFont = font;
return p.setStatusFont(font);
@ -666,6 +678,10 @@ bool Widget::enabled() {
return state.enabled;
}
bool Widget::focused() {
return p.focused();
}
string Widget::font() {
return state.font;
}

View File

@ -143,13 +143,14 @@ struct Object {
pObject &p;
};
struct OS : Object {
struct OS {
static void main();
static bool pendingEvents();
static void processEvents();
static void quit();
static void setName(const nall::string &name);
OS();
struct State;
static void initialize();
};
@ -202,6 +203,7 @@ struct Window : private nall::base_from_member<pWindow&>, Object {
void setMenuVisible(bool visible = true);
void setModal(bool modal = true);
void setResizable(bool resizable = true);
void setSmartGeometry(const Geometry &geometry);
void setStatusFont(const nall::string &font);
void setStatusText(const nall::string &text);
void setStatusVisible(bool visible = true);
@ -332,6 +334,7 @@ struct Layout : private nall::base_from_member<pLayout&>, Sizable {
struct Widget : private nall::base_from_member<pWidget&>, Sizable {
bool enabled();
bool focused();
nall::string font();
Geometry geometry();
Geometry minimumGeometry();

View File

@ -1,3 +1,10 @@
struct OS::State {
string name;
State() {
}
} osState;
struct Timer::State {
bool enabled;
unsigned milliseconds;

View File

@ -231,6 +231,7 @@ struct pWidget : public pSizable {
GtkWidget *gtkWidget;
bool enabled();
virtual bool focused();
virtual Geometry minimumGeometry();
void setEnabled(bool enabled);
virtual void setFocused();
@ -310,6 +311,7 @@ struct pHexEdit : public pWidget {
GtkTextBuffer *textBuffer;
GtkTextMark *textCursor;
bool focused();
void setColumns(unsigned columns);
void setLength(unsigned length);
void setOffset(unsigned offset);
@ -396,6 +398,7 @@ struct pListView : public pWidget {
void append(const lstring &text);
void autoSizeColumns();
bool checked(unsigned row);
bool focused();
void modify(unsigned row, const lstring &text);
void remove(unsigned row);
void reset();
@ -449,6 +452,7 @@ struct pTextEdit : public pWidget {
GtkWidget *subWidget;
GtkTextBuffer *textBuffer;
bool focused();
void setCursorPosition(unsigned position);
void setEditable(bool editable);
void setText(const string &text);

View File

@ -7,6 +7,10 @@ static bool HexEdit_scroll(GtkRange *range, GtkScrollType scroll, gdouble value,
return false;
}
bool pHexEdit::focused() {
return GTK_WIDGET_HAS_FOCUS(subWidget);
}
void pHexEdit::setColumns(unsigned columns) {
setScroll();
update();

View File

@ -35,6 +35,10 @@ bool pListView::checked(unsigned row) {
return state;
}
bool pListView::focused() {
return GTK_WIDGET_HAS_FOCUS(subWidget);
}
void pListView::modify(unsigned row, const lstring &text) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;

View File

@ -3,6 +3,10 @@ static void TextEdit_change(TextEdit *self) {
if(self->p.locked == false && self->onChange) self->onChange();
}
bool pTextEdit::focused() {
return GTK_WIDGET_HAS_FOCUS(subWidget);
}
void pTextEdit::setCursorPosition(unsigned position) {
GtkTextMark *mark = gtk_text_buffer_get_mark(textBuffer, "insert");
GtkTextIter iter;

View File

@ -1,11 +1,15 @@
Geometry pWidget::minimumGeometry() {
return { 0, 0, 0, 0 };
}
bool pWidget::enabled() {
return gtk_widget_get_sensitive(gtkWidget);
}
bool pWidget::focused() {
return GTK_WIDGET_HAS_FOCUS(gtkWidget);
}
Geometry pWidget::minimumGeometry() {
return {0, 0, 0, 0};
}
void pWidget::setEnabled(bool enabled) {
if(widget.state.abstract) enabled = false;
if(sizable.state.layout && sizable.state.layout->enabled() == false) enabled = false;

View File

@ -30,6 +30,7 @@ static gboolean Window_expose(GtkWidget *widget, GdkEvent *event, Window *window
static gboolean Window_configure(GtkWidget *widget, GdkEvent *event, Window *window) {
if(gtk_widget_get_realized(window->p.widget) == false) return false;
if(window->visible() == false) return false;
GdkWindow *gdkWindow = gtk_widget_get_window(widget);
GdkRectangle border, client;
@ -295,6 +296,15 @@ void pWindow::constructor() {
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
//if program was given a name, try and set the window taskbar icon from one of the pixmaps folders
if(osState.name.empty() == false) {
if(file::exists({"/usr/share/pixmaps/", osState.name, ".png"})) {
gtk_window_set_icon_from_file(GTK_WINDOW(widget), string{"/usr/share/pixmaps/", osState.name, ".png"}, nullptr);
} else if(file::exists({"/usr/local/share/pixmaps/", osState.name, ".png"})) {
gtk_window_set_icon_from_file(GTK_WINDOW(widget), string{"/usr/local/share/pixmaps/", osState.name, ".png"}, nullptr);
}
}
if(gdk_screen_is_composited(gdk_screen_get_default())) {
gtk_widget_set_colormap(widget, gdk_screen_get_rgba_colormap(gdk_screen_get_default()));
} else {

View File

@ -1,7 +1,7 @@
/****************************************************************************
** Meta object code from reading C++ file 'platform.moc.hpp'
**
** Created: Sun Jul 22 02:20:29 2012
** Created: Wed Dec 26 00:12:14 2012
** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3)
**
** WARNING! All changes made in this file will be lost!

View File

@ -266,6 +266,7 @@ struct pWidget : public pSizable {
Widget &widget;
QWidget *qtWidget;
bool focused();
virtual Geometry minimumGeometry();
void setEnabled(bool enabled);
void setFocused();

View File

@ -1,5 +1,9 @@
bool pWidget::focused() {
return qtWidget->hasFocus();
}
Geometry pWidget::minimumGeometry() {
return { 0, 0, 0, 0 };
return {0, 0, 0, 0};
}
void pWidget::setEnabled(bool enabled) {

View File

@ -179,6 +179,15 @@ void pWindow::constructor() {
qtWindow = new QtWindow(*this);
qtWindow->setWindowTitle(" ");
//if program was given a name, try and set the window taskbar icon to a matching pixmap image
if(osState.name.empty() == false) {
if(file::exists({"/usr/share/pixmaps/", osState.name, ".png"})) {
qtWindow->setWindowIcon(QIcon(string{"/usr/share/pixmaps/", osState.name, ".png"}));
} else if(file::exists({"/usr/local/share/pixmaps/", osState.name, ".png"})) {
qtWindow->setWindowIcon(QIcon(string{"/usr/local/share/pixmaps/", osState.name, ".png"}));
}
}
qtLayout = new QVBoxLayout(qtWindow);
qtLayout->setMargin(0);
qtLayout->setSpacing(0);

View File

@ -69,6 +69,8 @@ struct pTimer : public pObject {
struct pWindow : public pObject {
Window &window;
static Window& none();
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
@ -181,6 +183,7 @@ struct pWidget : public pSizable {
Widget &widget;
bool enabled();
bool focused();
Geometry minimumGeometry();
void setEnabled(bool enabled);
void setFocused();

View File

@ -2,8 +2,12 @@ bool pWidget::enabled() {
return false;
}
bool pWidget::focused() {
return false;
}
Geometry pWidget::minimumGeometry() {
return { 0, 0, 0, 0 };
return {0, 0, 0, 0};
}
void pWidget::setEnabled(bool enabled) {

View File

@ -1,3 +1,9 @@
Window& pWindow::none() {
static Window *window = nullptr;
if(window == nullptr) window = new Window;
return *window;
}
void pWindow::append(Layout &layout) {
}

View File

@ -56,16 +56,26 @@ string pDialogWindow::fileSave(Window &parent, const string &path, const lstring
return FileDialog(true, parent, path, filter);
}
static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT msg, LPARAM lparam, LPARAM lpdata) {
if(msg == BFFM_INITIALIZED) {
if(lpdata) SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpdata);
}
return 0;
}
string pDialogWindow::folderSelect(Window &parent, const string &path) {
wchar_t wfilename[PATH_MAX + 1] = L"";
utf16_t wpath(string{path}.transform("/", "\\"));
BROWSEINFO bi;
bi.hwndOwner = &parent != &Window::none() ? parent.p.hwnd : 0;
bi.pidlRoot = NULL;
bi.pszDisplayName = wfilename;
bi.lpszTitle = L"";
bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;
bi.lParam = 0;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)(wchar_t*)wpath;
bi.iImage = 0;
bool result = false;
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);

View File

@ -223,6 +223,7 @@ struct pWidget : public pSizable {
HFONT hfont;
bool enabled();
bool focused();
virtual Geometry minimumGeometry();
void setEnabled(bool enabled);
void setFocused();
@ -373,6 +374,8 @@ struct pLineEdit : public pWidget {
struct pListView : public pWidget {
ListView &listView;
HIMAGELIST imageList;
vector<vector<unsigned>> imageMap;
vector<image> images;
bool lostFocus;
void append(const lstring &text);
@ -396,7 +399,7 @@ struct pListView : public pWidget {
void destructor();
void orphan();
void setGeometry(const Geometry &geometry);
void setImageList();
void buildImageList();
};
struct pProgressBar : public pWidget {

View File

@ -63,10 +63,25 @@ void pButton::setImage(const image &image, Orientation orientation) {
}
Button_SetImageList(hwnd, &list);
}
setText(button.state.text); //update text to display nicely with image (or lack thereof)
}
void pButton::setText(const string &text) {
if(text.empty()) {
//bitmaps will not show up if text is empty
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP);
} else {
//text will not show up if BS_BITMAP is set
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_BITMAP);
}
if(OsVersion() >= WindowsVista && button.state.image.empty() == false && text.empty() == false) {
//Vista+ does not add spacing between the icon and text; causing them to run into each other
SetWindowText(hwnd, utf16_t(string{" ", text}));
} else {
SetWindowText(hwnd, utf16_t(text));
}
}
void pButton::constructor() {
@ -74,7 +89,7 @@ void pButton::constructor() {
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&button);
setDefaultFont();
setImage(button.state.image, button.state.orientation);
setText(button.state.text);
//setText(button.state.text); //called by setImage();
synchronize();
}

View File

@ -6,6 +6,34 @@ unsigned ListView_GetColumnCount(HWND hwnd) {
return --count;
}
void ListView_SetImage(HWND hwnd, HIMAGELIST imageList, unsigned row, unsigned column, unsigned imageID) {
//if this is the first image assigned, set image list now
//do not set sooner, or image blocks will appear in a list with no images
if(ListView_GetImageList(hwnd, LVSIL_SMALL) != imageList) {
ListView_SetImageList(hwnd, imageList, LVSIL_SMALL);
}
LVITEM item;
item.mask = LVIF_IMAGE;
item.iItem = row;
item.iSubItem = column;
item.iImage = imageID;
ListView_SetItem(hwnd, &item);
}
void ImageList_Append(HIMAGELIST imageList, const nall::image &source) {
auto image = source;
if(image.empty()) {
image.allocate(15, 15);
image.clear(GetSysColor(COLOR_WINDOW));
}
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
image.scale(15, 15, Interpolation::Linear);
HBITMAP bitmap = CreateBitmap(image);
ImageList_Add(imageList, bitmap, NULL);
DeleteObject(bitmap);
}
void pListView::append(const lstring &list) {
wchar_t empty[] = L"";
unsigned row = ListView_GetItemCount(hwnd);
@ -43,11 +71,11 @@ void pListView::modify(unsigned row, const lstring &list) {
void pListView::remove(unsigned row) {
ListView_DeleteItem(hwnd, row);
setImageList();
}
void pListView::reset() {
ListView_DeleteAllItems(hwnd);
buildImageList(); //free previously allocated images
}
bool pListView::selected() {
@ -103,7 +131,19 @@ void pListView::setHeaderVisible(bool visible) {
}
void pListView::setImage(unsigned row, unsigned column, const image &image) {
setImageList();
//assign existing image
for(unsigned n = 0; n < images.size(); n++) {
if(images[n] == image) {
imageMap(row)(column) = n;
return ListView_SetImage(hwnd, imageList, row, column, n);
}
}
//append and assign new image
imageMap(row)(column) = images.size();
images.append(image);
ImageList_Append(imageList, image);
ListView_SetImage(hwnd, imageList, row, column, imageMap(row)(column));
}
void pListView::setSelected(bool selected) {
@ -138,7 +178,7 @@ void pListView::constructor() {
setCheckable(listView.state.checkable);
for(auto &text : listView.state.text) append(text);
for(unsigned n = 0; n < listView.state.checked.size(); n++) setChecked(n, listView.state.checked[n]);
setImageList();
buildImageList();
if(listView.state.selected) setSelection(listView.state.selection);
autoSizeColumns();
synchronize();
@ -158,53 +198,46 @@ void pListView::setGeometry(const Geometry &geometry) {
autoSizeColumns();
}
void pListView::setImageList() {
void pListView::buildImageList() {
auto &list = listView.state.image;
unsigned columns = listView.state.text.size();
unsigned rows = max(1u, listView.state.headerText.size());
if(imageList) {
ImageList_Destroy(imageList);
imageList = nullptr;
}
ListView_SetImageList(hwnd, NULL, LVSIL_SMALL);
if(imageList) ImageList_Destroy(imageList);
imageList = ImageList_Create(15, 15, ILC_COLOR32, 1, 0);
imageMap.reset();
images.reset();
images.append(nall::image()); //empty icon for cells without an image assigned (I_IMAGENONE does not work)
//create a vector of unique images from all images used (many cells may use the same image)
for(unsigned y = 0; y < list.size(); y++) {
for(unsigned x = 0; x < list[y].size(); x++) {
bool found = false;
for(auto &row : listView.state.image) {
for(auto &column : row) {
if(column.empty() == false) {
for(unsigned z = 0; z < images.size(); z++) {
if(list[y][x] == images[z]) {
found = true;
imageMap(y)(x) = z;
break;
}
}
if(found == false) {
imageMap(y)(x) = images.size();
images.append(list[y][x]);
}
if(found == false) return;
imageList = ImageList_Create(15, 15, ILC_COLOR32, 1, 0);
nall::image image;
image.allocate(15, 15);
image.clear(GetSysColor(COLOR_WINDOW));
ImageList_Add(imageList, CreateBitmap(image), NULL);
for(unsigned row = 0; row < list.size(); row++) {
for(unsigned column = 0; column < list(row).size(); column++) {
nall::image image = list(row)(column);
if(image.empty()) continue;
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
image.scale(15, 15, Interpolation::Linear);
ImageList_Add(imageList, CreateBitmap(image), NULL);
}
}
ListView_SetImageList(hwnd, imageList, LVSIL_SMALL);
//build image list
for(auto &imageItem : images) ImageList_Append(imageList, imageItem);
if(images.size() <= 1) return;
unsigned ID = 1;
for(unsigned row = 0; row < list.size(); row++) {
for(unsigned column = 0; column < list(row).size(); column++) {
if(list(row)(column).empty()) continue; //I_IMAGENONE does not work properly
LVITEM item;
item.mask = LVIF_IMAGE;
item.iItem = row;
item.iSubItem = column;
item.iImage = ID++;
ListView_SetItem(hwnd, &item);
//set images for all cells
for(unsigned y = 0; y < columns; y++) {
for(unsigned x = 0; x < rows; x++) {
ListView_SetImage(hwnd, imageList, y, x, imageMap(y)(x));
}
}
}

View File

@ -2,8 +2,12 @@ bool pWidget::enabled() {
return IsWindowEnabled(hwnd);
}
bool pWidget::focused() {
return GetFocus() == hwnd;
}
Geometry pWidget::minimumGeometry() {
return { 0, 0, 0, 0 };
return {0, 0, 0, 0};
}
void pWidget::setEnabled(bool enabled) {

View File

@ -1,571 +1,209 @@
#include <nall/config.hpp>
#include <nall/directory.hpp>
#include <nall/file.hpp>
#include <nall/invoke.hpp>
#include <nall/string.hpp>
#include <nall/zip.hpp>
#include <nall/emulation/famicom.hpp>
#include <nall/emulation/super-famicom.hpp>
#include <nall/emulation/satellaview.hpp>
#include <nall/emulation/sufami-turbo.hpp>
#include <nall/emulation/game-boy.hpp>
#include <nall/emulation/game-boy-advance.hpp>
#include <nall/nall.hpp>
using namespace nall;
#include <phoenix/phoenix.hpp>
using namespace phoenix;
#include "resource/resource.hpp"
#include <thread>
#include "resource/resource.cpp"
struct Settings : configuration {
bool ui; //true if in user-interface mode (windows visible); false if in command-line mode
bool play; //true if emulator should be launched after game conversion
lstring extensions;
struct Application : Window {
library ananke;
string emulator;
string path;
string recent;
VerticalLayout layout;
HorizontalLayout pathLayout;
Label pathLabel;
LineEdit pathEdit;
Button browseButton;
ListView fileList;
ProgressBar progressBar;
HorizontalLayout controlLayout;
Button selectAllButton;
Button unselectAllButton;
Widget spacer;
Button purifyButton;
Settings() {
ui = false;
play = false;
extensions = {".fc", ".nes", ".sfc", ".smc", ".swc", ".fig", ".bs", ".st", ".gb", ".gbc", ".sgb", ".gba"};
lstring filenameList;
directory::create({configpath(), "purify/"});
append(emulator = "bsnes", "emulator");
append(path = {configpath(), "Emulation/"}, "path");
append(recent = {userpath(), "Desktop/"}, "recent");
load({configpath(), "purify/settings.cfg"});
save({configpath(), "purify/settings.cfg"});
}
Application();
void scanPath();
void scanPath(const string &path, const string &basepath);
void purify();
} *application = nullptr;
~Settings() {
save({configpath(), "purify/settings.cfg"});
}
} settings;
Application::Application() {
application = this;
void play(const string &pathname) {
settings.play = false;
invoke(settings.emulator, pathname);
}
bool valid_extension(string name) {
name.rtrim<1>("/");
for(auto &extension : settings.extensions) {
if(name.iendswith(extension)) return true;
}
return false;
}
void create_famicom(const string &filename, uint8_t *data, unsigned size) {
FamicomCartridge information(data, size);
if(information.markup.empty()) return;
string name = {nall::basename(notdir(filename)), ".fc/"};
print(name, "\n");
string path = {settings.path, "Famicom/", name};
directory::create(path, 0755);
//skip iNES header
data += 16, size -= 16;
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
if(information.prgrom > 0) file::write({path, "program.rom"}, data, information.prgrom);
if(information.chrrom > 0) file::write({path, "character.rom"}, data + information.prgrom, information.chrrom);
if(!file::exists({path, "save.rwm"})) file::copy({nall::basename(filename), ".sav"}, {path, "save.rwm"});
if(settings.play) play(path);
}
void create_super_famicom(const string &filename, uint8_t *data, unsigned size) {
SuperFamicomCartridge information(data, size);
if(information.markup.empty()) return;
string name = {nall::basename(notdir(filename)), ".sfc/"};
print(name, "\n");
string path = {settings.path, "Super Famicom/", name};
directory::create(path, 0755);
//skip copier header
if((size & 0x7fff) == 512) data += 512, size -= 512;
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
if(information.markup.position("<spc7110>") && size >= 0x100000) {
file::write({path, "program.rom"}, data, 0x100000);
file::write({path, "data.rom"}, data + 0x100000, size - 0x100000);
} else {
file::write({path, "program.rom"}, data, size);
}
if(!file::exists({path, "save.rwm"})) file::copy({nall::basename(filename), ".srm"}, {path, "save.rwm"});
//firmware
string firmwareID = "<firmware name=\"";
if(auto position = information.markup.position(firmwareID)) {
string firmware = substr(information.markup, position() + firmwareID.length());
if(auto position = firmware.position("\"")) {
firmware[position()] = 0;
if(file::copy({dir(filename), firmware}, {path, firmware}) == false) {
print("Warning: required firmware \"", firmware, "\" not found!\n");
}
}
}
if(settings.play) play(path);
}
void create_satellaview(const string &filename, uint8_t *data, unsigned size) {
SatellaviewCartridge information(data, size);
if(information.markup.empty()) return;
string name = {nall::basename(notdir(filename)), ".bs/"};
print(name, "\n");
string path = {settings.path, "BS-X Satellaview/", name};
directory::create(path, 0755);
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
file::write({path, "program.rom"}, data, size);
if(settings.play) play(path);
}
void create_sufami_turbo(const string &filename, uint8_t *data, unsigned size) {
SufamiTurboCartridge information(data, size);
if(information.markup.empty()) return;
string name = {nall::basename(notdir(filename)), ".st/"};
print(name, "\n");
string path = {settings.path, "Sufami Turbo/", name};
directory::create(path, 0755);
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
file::write({path, "program.rom"}, data, size);
if(!file::exists({path, "save.rwm"})) file::copy({nall::basename(filename), ".srm"}, {path, "save.rwm"});
}
void create_game_boy(const string &filename, uint8_t *data, unsigned size) {
GameBoyCartridge information(data, size);
if(information.markup.empty()) return;
string system = information.info.cgb ? "Game Boy Color/" : "Game Boy/";
string extension = information.info.cgb ? ".gbc/" : ".gb/";
string name = {nall::basename(notdir(filename)), extension};
print(name, "\n");
string path = {settings.path, system, name};
directory::create(path, 0755);
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
file::write({path, "program.rom"}, data, size);
if(!file::exists({path, "save.rwm"})) file::copy({nall::basename(filename), ".sav"}, {path, "save.rwm"});
if(settings.play) play(path);
}
void create_game_boy_advance(const string &filename, uint8_t *data, unsigned size) {
GameBoyAdvanceCartridge information(data, size);
if(information.markup.empty()) return;
string name = {nall::basename(notdir(filename)), ".gba/"};
print(name, "\n");
string path = {settings.path, "Game Boy Advance/", name};
directory::create(path, 0755);
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
file::write({path, "program.rom"}, data, size);
if(!file::exists({path, "save.rwm"})) file::copy({nall::basename(filename), ".sav"}, {path, "save.rwm"});
if(settings.play) play(path);
}
void create_data(const string &name, uint8_t *data, unsigned size) {
if(name.iendswith(".fc")
|| name.iendswith(".nes")
) return create_famicom(name, data, size);
if(name.iendswith(".sfc")
|| name.iendswith(".smc")
|| name.iendswith(".swc")
|| name.iendswith(".fig")
) return create_super_famicom(name, data, size);
if(name.iendswith(".bs")
) return create_satellaview(name, data, size);
if(name.iendswith(".st")
) return create_sufami_turbo(name, data, size);
if(name.iendswith(".gb")
|| name.iendswith(".gbc")
|| name.iendswith(".sgb")
) return create_game_boy(name, data, size);
if(name.iendswith(".gba")
) return create_game_boy_advance(name, data, size);
}
void create_file(const string &filename) {
auto buffer = file::read(filename);
if(buffer.size() == 0) return;
return create_data(filename, buffer.data(), buffer.size());
}
void create_zip(const string &filename) {
zip archive;
if(archive.open(filename) == false) return;
for(auto &file : archive.file) {
if(file.data == nullptr || file.size == 0) return;
if(valid_extension(file.name) == false) continue;
auto buffer = archive.extract(file);
if(buffer.size() == 0) continue;
string name = {nall::basename(filename), ".", extension(file.name)};
return create_data(name, buffer.data(), buffer.size());
}
}
void create_manifest(const string &path) {
string name = path;
name.rtrim<1>("/");
name = {notdir(name), "/"};
//Famicom manifests cannot be generated from PRG+CHR ROMs alone
//In the future, a games database may enable manifest generation
if(path.iendswith(".sfc/") && file::exists({path, "program.rom"})) {
print(name, "\n");
auto buffer = file::read({path, "program.rom"});
if(file::exists({path, "data.rom"})) { //SPC7110 ROMs consist of program.rom + data.rom
auto prom = buffer;
auto drom = file::read({path, "data.rom"});
buffer.resize(prom.size() + drom.size());
memcpy(buffer.data() + prom.size(), drom.data(), drom.size());
}
SuperFamicomCartridge information(buffer.data(), buffer.size());
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
}
if(path.iendswith(".bs/") && file::exists({path, "program.rom"})) {
print(name, "\n");
auto buffer = file::read({path, "program.rom"});
SatellaviewCartridge information(buffer.data(), buffer.size());
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
}
if(path.iendswith(".st/") && file::exists({path, "program.rom"})) {
print(name, "\n");
auto buffer = file::read({path, "program.rom"});
SufamiTurboCartridge information(buffer.data(), buffer.size());
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
}
if((path.iendswith(".gb/") || path.iendswith(".gbc/")) && file::exists({path, "program.rom"})) {
print(name, "\n");
auto buffer = file::read({path, "program.rom"});
GameBoyCartridge information(buffer.data(), buffer.size());
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
}
if(path.iendswith(".gba/") && file::exists({path, "program.rom"})) {
print(name, "\n");
auto buffer = file::read({path, "program.rom"});
GameBoyAdvanceCartridge information(buffer.data(), buffer.size());
file::write({path, "manifest.xml"}, (const uint8_t*)information.markup(), information.markup.length());
}
}
//$ purify synchronize
//this function recursively scans directories; as purify uses nested folders for different systems
void synchronize_manifests(string pathname) {
pathname.transform("\\", "/");
if(pathname.endswith("/") == false) pathname.append("/");
lstring folders = directory::folders(pathname);
for(auto &folder : folders) {
if(valid_extension(folder)) {
create_manifest({pathname, folder});
} else {
synchronize_manifests({pathname, folder});
}
}
}
void create_folder(string pathname) {
pathname.transform("\\", "/");
if(pathname.endswith("/") == false) pathname.append("/");
if(pathname == settings.path) {
print(
"You cannot use the same path for both the source and destination directories.\n"
"Please choose a different output path in settings.cfg.\n"
if(ananke.open("ananke") == false) {
MessageWindow::critical(Window::none(),
"Error: ananke was not found, but is required to use purify.\n\n"
"Please install ananke and then run purify again."
);
exit(0);
}
setFrameGeometry({64, 64, 720, 480});
setTitle("purify v02");
layout.setMargin(5);
pathLabel.setText("Path:");
browseButton.setText("Browse ...");
fileList.setCheckable(true);
selectAllButton.setText("Select All");
unselectAllButton.setText("Unselect All");
purifyButton.setText("Purify");
append(layout);
layout.append(pathLayout, {~0, 0}, 5);
pathLayout.append(pathLabel, {0, 0}, 5);
pathLayout.append(pathEdit, {~0, 0}, 5);
pathLayout.append(browseButton, {80, 0});
layout.append(fileList, {~0, ~0}, 5);
layout.append(progressBar, {~0, 0}, 5);
layout.append(controlLayout, {~0, 0});
controlLayout.append(selectAllButton, {100, 0}, 5);
controlLayout.append(unselectAllButton, {100, 0}, 5);
controlLayout.append(spacer, {~0, 0});
controlLayout.append(purifyButton, {80, 0});
setVisible();
onClose = &OS::quit;
pathEdit.onActivate = {&Application::scanPath, this};
browseButton.onActivate = [&] {
string path = DialogWindow::folderSelect(*this, userpath());
if(path.empty() == false) {
pathEdit.setText(path);
scanPath();
}
};
selectAllButton.onActivate = [&] {
for(unsigned n = 0; n < filenameList.size(); n++) fileList.setChecked(n, true);
};
unselectAllButton.onActivate = [&] {
for(unsigned n = 0; n < filenameList.size(); n++) fileList.setChecked(n, false);
};
purifyButton.onActivate = {&Application::purify, this};
}
void Application::scanPath() {
string path = pathEdit.text();
fileList.reset();
filenameList.reset();
scanPath(path, path);
selectAllButton.onActivate();
}
void Application::scanPath(const string &path, const string &basepath) {
lstring files = directory::icontents(path);
for(auto &file : files) {
if(
directory::exists({path, file})
&& !file.endswith(".fc/")
&& !file.endswith(".sfc/")
&& !file.endswith(".st/")
&& !file.endswith(".bs/")
&& !file.endswith(".gb/")
&& !file.endswith(".gbc/")
&& !file.endswith(".gba/")
) {
scanPath({path, file}, basepath);
} else if(
directory::exists({path, file})
) {
fileList.append(string{path, file}.ltrim<1>(basepath).rtrim<1>("/"));
filenameList.append({path, file});
fileList.setImage(filenameList.size() - 1, 0, {resource::game, sizeof resource::game});
} else if(
file.endswith(".fc") || file.endswith(".nes")
|| file.endswith(".sfc") || file.endswith(".smc")
|| file.endswith(".st") || file.endswith(".bs")
|| file.endswith(".gb")
|| file.endswith(".gbc")
|| file.endswith(".gba")
|| file.endswith(".bpa") || file.endswith(".zip")
) {
fileList.append(string{path, file}.ltrim<1>(basepath));
filenameList.append({path, file});
if(file.endswith(".bpa") || file.endswith(".zip")) {
fileList.setImage(filenameList.size() - 1, 0, {resource::archive, sizeof resource::archive});
} else {
fileList.setImage(filenameList.size() - 1, 0, {resource::file, sizeof resource::file});
}
}
}
}
struct PurifyContext {
lstring list;
unsigned position;
unsigned size;
void run() {
function<string (string)> sync = application->ananke.sym("ananke_sync");
function<string (string)> open = application->ananke.sym("ananke_open");
if(!open || !sync) {
position = size;
return;
}
lstring files = directory::contents(pathname);
for(auto &name : files) {
if(name.iendswith(".zip")) {
create_zip({pathname, name});
} else {
create_file({pathname, name});
}
}
}
struct Progress : Window {
VerticalLayout layout;
Label title;
HorizontalLayout fileLayout;
Label fileLabel;
Label fileName;
HorizontalLayout progressLayout;
ProgressBar progressBar;
Button stopButton;
bool quit;
void convert(const string &pathname) {
fileName.setText("Initializing ...");
progressBar.setPosition(0);
stopButton.setEnabled(true);
quit = false;
setVisible(true);
setModal(true);
lstring files = directory::contents(pathname);
for(unsigned n = 0; n < files.size() && quit == false; n++) {
auto &filename = files(n);
if(!filename.iendswith(".zip") && !valid_extension(filename)) continue;
OS::processEvents();
double position = (double)n / (double)files.size() * 100.0 + 0.5;
progressBar.setPosition((unsigned)position);
string name = filename;
name.rtrim<1>("/");
fileName.setText(notdir(name));
if(filename.iendswith(".zip")) {
create_zip({pathname, filename});
} else {
create_file({pathname, filename});
while(position < size) {
string filename = list[position];
if(directory::exists(filename)) sync(filename);
else if(file::exists(filename)) open(filename);
position++;
}
}
if(quit == false) {
fileName.setText("All games have been converted.");
progressBar.setPosition(100);
} else {
fileName.setText("Process aborted. Not all games have been converted.");
PurifyContext(const lstring &list) : list(list) {
position = 0;
size = list.size();
}
stopButton.setEnabled(false);
};
setModal(false);
void Application::purify() {
lstring purifyList;
for(unsigned n = 0; n < filenameList.size(); n++) {
if(fileList.checked(n)) purifyList.append(filenameList[n]);
}
Progress() {
setTitle("purify");
layout.setMargin(5);
title.setFont("Sans, 16, Bold");
title.setText("Conversion Progress");
fileLabel.setFont("Sans, 8, Bold");
fileLabel.setText("Filename:");
stopButton.setText("Stop");
append(layout);
layout.append(title, {~0, 0}, 5);
layout.append(fileLayout, {~0, 0}, 5);
fileLayout.append(fileLabel, {0, 0}, 5);
fileLayout.append(fileName, {~0, 0});
layout.append(progressLayout, {~0, 0});
progressLayout.append(progressBar, {~0, 0}, 5);
progressLayout.append(stopButton, {80, 0});
setGeometry({192, 192, 560, layout.minimumGeometry().height});
stopButton.onActivate = [&] { quit = true; };
if(purifyList.size() == 0) {
MessageWindow::information(*this, "Please select at least one file to purify.");
return;
}
} *progress = nullptr;
struct Application : Window {
VerticalLayout layout;
Label title;
HorizontalLayout purifyLayout;
Button playButton;
Button convertButton;
HorizontalLayout configLayout;
Button emulatorButton;
Button pathButton;
HorizontalLayout gridLayout;
VerticalLayout labelLayout;
HorizontalLayout emulatorLayout;
Label emulatorName;
Label emulatorValue;
HorizontalLayout pathLayout;
Label pathName;
Label pathValue;
VerticalLayout synchronizeLayout;
Button synchronizeButton;
Application() {
setTitle("purify v01");
setGeometry({128, 128, 600, 200});
layout.setMargin(5);
title.setFont("Sans, 16, Bold");
title.setText("Choose Action");
playButton.setImage({resource::play, sizeof resource::play});
playButton.setText("Play Game");
convertButton.setImage({resource::convert, sizeof resource::convert});
convertButton.setText("Convert Games");
emulatorButton.setImage({resource::emulator, sizeof resource::emulator});
emulatorButton.setText("Choose Emulator");
pathButton.setImage({resource::path, sizeof resource::path});
pathButton.setText("Choose Output Path");
emulatorName.setFont("Sans, 8, Bold");
emulatorName.setText("Emulator:");
emulatorValue.setText(settings.emulator);
pathName.setFont("Sans, 8, Bold");
pathName.setText("Output Path:");
pathValue.setText(settings.path);
synchronizeButton.setImage({resource::synchronize, sizeof resource::synchronize});
synchronizeButton.setText("Update Manifests");
Font font("Sans, 8, Bold");
unsigned width = max(font.geometry("Emulator:").width, font.geometry("Output Path:").width);
append(layout);
layout.append(title, {~0, 0}, 5);
layout.append(purifyLayout, {~0, ~0}, 5);
purifyLayout.append(playButton, {~0, ~0}, 5);
purifyLayout.append(convertButton, {~0, ~0});
layout.append(configLayout, {~0, ~0}, 5);
configLayout.append(emulatorButton, {~0, ~0}, 5);
configLayout.append(pathButton, {~0, ~0});
layout.append(gridLayout, {~0, 0});
gridLayout.append(labelLayout, {~0, 0}, 5);
labelLayout.append(emulatorLayout, {~0, 0}, 5);
emulatorLayout.append(emulatorName, {width, 0}, 5);
emulatorLayout.append(emulatorValue, {~0, 0});
labelLayout.append(pathLayout, {~0, 0});
pathLayout.append(pathName, {width, 0}, 5);
pathLayout.append(pathValue, {~0, 0});
gridLayout.append(synchronizeLayout, {0, 0});
synchronizeLayout.append(synchronizeButton, {0, 0});
onClose = &OS::quit;
playButton.onActivate = {&Application::playAction, this};
convertButton.onActivate = {&Application::convertAction, this};
emulatorButton.onActivate = {&Application::emulatorAction, this};
pathButton.onActivate = {&Application::pathAction, this};
synchronizeButton.onActivate = [&] {
if(MessageWindow::question(*this,
"This will update all manifest.xml files located in your output path.\n"
"This process may take a few minutes to complete.\n"
"The user interface will not be responsive during this time.\n\n"
"Would you like to proceed?"
) == MessageWindow::Response::No) return;
layout.setEnabled(false);
OS::processEvents();
synchronize_manifests(settings.path);
PurifyContext purifyContext(purifyList);
std::thread purifyThread([&] { purifyContext.run(); });
while(purifyContext.position < purifyContext.size) {
OS::processEvents();
unsigned position = ((unsigned)(double)purifyContext.position / (double)purifyContext.size * 100.0 + 0.5);
progressBar.setPosition(position);
}
purifyThread.join();
fileList.reset();
filenameList.reset();
progressBar.setPosition(0);
layout.setEnabled(true);
MessageWindow::information(*this, "Process completed. All identified manifests have been updated.");
};
setVisible();
}
void playAction() {
string filters = {settings.extensions.concatenate(","), ",.zip"};
filters.replace(".", "*.");
string filename = DialogWindow::fileOpen(*this, settings.recent, string{"Game Images (", filters, ")"});
if(!filename.empty()) {
setVisible(false);
settings.recent = dir(filename);
settings.play = true;
if(filename.iendswith(".zip")) {
create_zip(filename);
} else {
create_file(filename);
}
exit(0);
}
}
void convertAction() {
string pathname = DialogWindow::folderSelect(*this, settings.recent);
if(pathname.empty()) return;
if(pathname == settings.path) {
MessageWindow::critical(*this,
"You cannot use the same path for both the source and destination directories.\n\n"
"Please choose a different output path."
);
return;
}
settings.recent = pathname;
progress->convert(pathname);
}
void emulatorAction() {
string filter = {
"Emulators ", Intrinsics::platform() == Intrinsics::Platform::Windows
? "(*.exe)"
: "(*)"
};
string filename = DialogWindow::fileOpen(*this, settings.recent, filter);
if(!filename.empty()) {
settings.recent = dir(filename);
emulatorValue.setText(settings.emulator = filename);
}
}
void pathAction() {
string pathname = DialogWindow::folderSelect(*this, settings.recent);
if(!pathname.empty()) {
settings.recent = pathname;
pathValue.setText(settings.path = pathname);
}
}
} *application = nullptr;
}
int main(int argc, char **argv) {
#if defined(PLATFORM_WINDOWS)
utf8_args(argc, argv);
#endif
lstring args;
for(unsigned n = 1; n < argc; n++) {
string argument = argv[n];
argument.replace("~/", userpath());
args.append(argument);
}
if(args.size() == 1 && args[0] == "synchronize") {
synchronize_manifests(settings.path);
return 0;
}
if(args.size() == 1 && directory::exists(args[0])) {
create_folder(args[0]);
return 0;
}
if(args.size() == 1 && istrend(args[0], ".zip")) {
settings.play = true;
create_zip(args[0]);
return 0;
}
if(args.size() == 1 && file::exists(args[0]) && valid_extension(args[0])) {
settings.play = true;
create_file(args[0]);
return 0;
}
settings.ui = true;
progress = new Progress;
application = new Application;
new Application;
OS::main();
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

BIN
purify/resource/archive.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
purify/resource/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
purify/resource/game.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,4 @@
resource name=resource
binary id=game name=game.png
binary id=file name=file.png
binary id=archive name=archive.png

View File

@ -1,247 +1,120 @@
namespace resource {
const uint8_t play[1470] = {
const uint8_t game[1490] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,5,117,73,68,65,84,88,133,189,151,95,104,28,
199,29,199,63,179,187,247,87,90,89,177,122,190,107,108,85,137,26,53,142,173,66,74,193,134,22,218,218,14,174,75,98,
21,25,29,20,154,151,56,24,185,137,91,8,121,233,139,193,121,11,42,24,10,37,57,75,15,125,40,121,177,176,221,60,
53,208,66,31,74,177,67,193,170,147,56,86,28,169,142,49,178,106,199,145,238,180,187,183,187,183,59,51,125,144,111,209,
233,228,68,231,226,252,96,216,157,239,236,124,127,223,157,249,206,204,174,224,33,226,248,241,227,195,90,235,223,106,173,127,
14,116,3,174,16,226,93,33,196,155,149,74,229,163,78,184,68,167,201,143,29,59,246,203,140,37,254,248,194,51,91,82,
187,158,216,70,46,155,199,171,215,245,7,243,11,226,189,235,94,20,41,94,154,154,154,122,231,145,8,24,31,31,31,74,
33,63,250,245,143,31,79,247,233,37,244,205,247,33,88,65,102,122,48,158,252,33,139,97,150,202,197,47,26,74,24,195,
103,206,156,249,116,51,156,70,39,2,180,214,175,253,108,208,72,247,70,119,145,31,255,133,108,180,66,87,74,144,14,171,
132,87,222,165,79,126,206,254,126,149,214,90,191,182,89,206,142,4,0,35,187,6,182,17,204,93,68,8,129,97,128,101,
8,132,88,29,74,255,63,239,179,187,255,49,128,145,205,18,90,157,100,55,81,133,180,101,82,175,87,177,82,38,129,128,
134,80,52,164,34,86,154,40,116,200,24,26,224,27,143,68,128,196,184,229,174,212,190,173,204,28,65,228,35,149,70,8,
144,74,19,70,146,216,204,209,112,61,128,207,54,203,217,233,20,252,249,234,66,21,81,120,138,122,67,226,133,113,82,252,
134,68,244,126,139,143,239,4,8,33,206,63,42,1,111,255,125,193,10,150,205,109,24,59,190,135,219,80,184,65,140,215,
144,136,210,46,150,178,253,252,243,78,202,23,66,76,62,18,1,147,147,147,243,129,20,191,121,231,19,3,227,187,35,4,
185,109,184,17,120,153,34,193,119,158,231,236,124,138,134,100,188,82,169,124,182,89,206,142,60,0,32,132,56,219,208,98,
210,26,250,9,93,165,239,227,186,46,158,231,65,54,75,164,174,49,53,53,245,167,78,248,58,157,130,36,180,214,45,117,
165,212,67,241,60,180,128,245,9,191,78,1,229,76,38,211,6,106,173,177,44,139,163,71,143,218,157,144,181,157,5,71,
142,28,121,93,8,241,58,240,205,141,58,20,10,5,78,156,56,129,82,138,40,138,240,60,15,215,117,145,82,82,42,149,
168,84,42,84,171,213,141,186,126,168,181,126,249,252,249,243,255,122,160,128,114,185,156,214,90,135,19,19,19,244,247,247,
183,188,93,243,26,4,1,158,231,17,69,17,190,239,227,251,62,82,74,108,219,38,149,74,97,24,6,91,183,110,69,107,
221,82,174,93,187,198,233,211,167,63,61,119,238,220,211,64,98,160,150,85,160,181,62,178,119,239,94,182,111,223,206,194,
194,2,90,107,132,16,8,33,200,102,179,164,211,105,28,199,161,90,173,98,219,54,125,125,125,152,166,73,20,69,220,187,
119,143,185,185,57,10,133,2,185,92,14,215,117,137,162,40,225,24,26,26,98,112,112,112,104,116,116,244,185,11,23,46,
252,181,153,179,197,3,74,169,227,35,35,35,172,172,172,36,166,210,90,147,207,231,73,167,211,0,56,142,131,148,146,187,
119,239,50,59,59,203,204,204,12,51,51,51,204,207,207,19,134,33,75,75,75,152,166,73,79,79,79,210,71,41,133,239,
251,28,58,116,8,195,48,94,93,155,51,17,48,58,58,250,76,177,88,252,209,192,192,0,158,231,37,67,103,89,22,166,
105,38,245,56,142,209,90,163,148,74,176,230,189,82,138,32,8,146,41,203,231,243,73,162,122,189,206,206,157,59,217,178,
101,203,225,114,185,252,100,155,0,195,48,198,199,198,198,68,189,94,79,146,172,95,235,0,189,189,189,109,88,51,50,153,
12,195,195,195,27,182,41,165,136,227,152,3,7,14,24,74,169,87,154,184,0,40,151,203,57,33,196,237,201,201,201,222,
90,173,70,163,209,64,136,85,127,10,33,176,109,27,211,52,91,8,29,199,73,204,104,154,38,93,93,93,244,244,244,180,
136,118,28,167,101,68,44,203,66,107,205,201,147,39,171,192,227,211,211,211,190,5,160,181,254,197,193,131,7,123,133,16,
132,97,136,122,239,20,119,254,253,55,178,150,65,225,217,231,112,126,250,70,139,15,0,108,219,166,187,187,187,69,84,51,
145,82,10,207,243,8,195,176,165,45,138,34,108,219,102,207,158,61,189,151,46,93,122,17,152,50,238,55,190,188,127,255,
126,28,199,89,117,237,226,135,12,244,229,121,162,208,197,242,236,69,110,221,186,133,235,186,212,106,53,234,245,58,81,20,
33,165,108,73,24,199,49,97,24,226,56,14,75,75,75,132,97,216,54,141,90,107,124,223,103,223,190,125,0,191,7,176,
78,157,58,101,204,206,206,238,45,22,139,220,190,125,27,173,53,181,231,255,192,63,206,190,197,205,185,5,6,127,240,2,
135,138,197,196,128,113,28,227,251,126,219,91,63,232,218,188,111,214,163,40,98,199,142,29,100,50,153,220,225,195,135,243,
214,213,171,87,109,107,53,40,222,79,84,42,149,120,122,248,76,155,145,54,50,229,102,176,245,245,32,8,154,155,87,38,
217,136,92,215,229,242,229,203,45,138,215,47,179,175,194,54,131,27,134,65,177,88,108,142,134,50,30,164,114,109,231,141,
240,78,158,253,178,163,219,88,11,10,33,54,77,212,73,210,245,245,166,128,235,215,175,119,91,211,211,211,43,99,99,99,
255,189,113,227,70,105,247,238,221,109,59,91,179,104,173,19,231,175,197,149,82,72,41,219,176,181,248,250,246,197,197,69,
26,141,198,226,149,43,87,66,11,208,113,28,255,106,98,98,226,119,66,136,167,248,26,66,74,121,115,121,121,249,13,192,
89,123,28,155,192,99,172,254,237,102,120,136,239,197,175,8,197,234,49,28,3,53,96,25,136,31,244,115,106,242,127,124,
174,125,73,200,251,34,18,67,252,15,38,245,181,113,211,146,231,231,0,0,0,0,73,69,78,68,174,66,96,130,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,5,137,73,68,65,84,88,133,237,150,91,108,20,
231,21,199,127,231,155,155,151,181,124,89,3,181,113,193,183,13,16,81,82,21,225,52,50,1,132,91,53,20,212,132,155,
212,84,149,18,161,40,82,213,151,72,68,81,108,212,74,121,168,122,81,165,190,24,171,125,141,218,226,66,171,66,91,37,
65,145,82,225,2,9,6,148,132,40,73,9,55,219,4,112,146,58,49,23,239,125,102,190,62,204,236,120,236,176,182,1,
169,79,29,233,232,236,203,156,255,239,252,207,249,190,29,248,255,115,31,79,182,135,157,153,94,116,102,47,91,239,181,134,
186,31,113,173,216,159,173,249,10,90,243,215,76,47,91,254,103,0,217,30,118,106,225,143,183,219,215,219,250,225,103,201,
180,173,179,53,28,186,23,136,187,6,8,58,151,253,147,233,13,142,151,106,97,226,76,63,238,194,54,50,237,143,222,19,
132,220,173,56,134,57,144,95,243,3,43,111,8,147,31,253,3,180,7,98,80,189,226,123,152,159,95,102,193,197,227,69,
129,237,201,95,240,234,124,106,206,219,129,108,47,59,48,204,1,111,211,139,150,145,254,22,217,11,175,160,68,163,148,66,
137,38,123,225,21,252,133,105,114,233,245,129,19,123,249,238,124,234,206,203,129,108,47,59,180,48,224,111,124,193,150,150,
46,138,19,23,64,107,4,184,253,225,1,220,155,163,160,65,139,34,153,222,140,57,62,76,213,197,227,69,17,182,37,127,
206,107,179,213,158,211,129,64,92,6,178,15,108,180,189,134,86,74,19,23,17,64,68,16,17,204,68,93,224,130,161,80,
2,185,203,175,227,54,180,145,79,175,179,181,230,240,92,78,204,234,64,36,158,126,212,246,83,203,64,187,56,141,107,65,
160,52,126,142,252,245,147,248,185,207,167,94,208,58,72,8,137,214,110,140,241,17,170,46,189,57,171,19,21,29,200,246,
176,93,139,12,228,210,235,108,63,245,85,242,163,255,68,187,249,168,115,63,51,6,133,137,160,251,40,12,68,41,148,8,
249,43,71,241,26,150,145,111,127,196,22,164,226,78,220,17,32,219,195,118,95,228,79,217,116,151,237,213,53,147,191,50,
136,8,129,128,8,34,10,17,65,41,99,70,168,169,12,20,174,30,195,91,216,66,182,173,211,169,4,241,37,128,178,120,
174,227,17,219,175,91,66,225,234,49,20,160,68,161,172,5,80,158,125,205,82,204,228,226,105,226,18,101,21,65,20,175,
158,192,75,45,37,219,182,214,17,228,208,100,15,155,43,2,100,122,217,22,136,63,108,251,117,77,20,174,190,137,8,72,
185,168,83,27,117,111,47,92,73,242,107,79,97,47,94,61,189,243,16,72,194,113,136,64,113,108,8,175,174,153,108,235,
26,71,41,57,28,135,136,0,244,75,40,224,80,177,190,209,102,201,215,41,140,157,14,108,151,169,249,154,53,205,136,72,
232,66,0,34,165,201,169,217,71,89,97,88,85,88,245,29,40,195,10,156,248,236,29,212,3,143,81,168,89,236,136,76,
45,100,4,32,47,225,227,179,205,158,248,164,40,215,222,193,105,90,19,21,20,81,24,201,69,40,59,137,136,160,115,227,
228,222,221,71,238,221,126,252,204,245,72,220,176,18,209,8,140,154,165,44,88,190,29,179,182,5,49,76,18,45,221,56,
185,12,206,237,255,20,181,158,218,133,105,35,72,254,138,191,9,250,251,137,225,51,69,243,198,117,236,198,111,32,202,68,
148,194,94,180,122,234,4,220,188,140,200,116,203,205,234,38,18,15,61,131,149,90,142,89,221,136,149,90,142,8,36,58,
182,80,219,245,83,18,84,33,39,250,139,190,231,63,81,253,75,142,68,141,223,233,20,100,122,217,134,200,129,98,251,90,
219,79,53,225,223,184,68,98,245,110,148,225,128,8,197,243,127,70,231,198,131,113,68,173,88,56,171,118,163,37,236,73,
107,52,160,18,13,232,145,33,244,241,254,146,246,220,199,227,226,21,1,166,65,164,59,109,107,197,86,84,162,1,9,174,
64,40,78,226,126,252,6,160,49,106,59,240,198,223,195,88,210,133,170,237,8,133,53,104,144,170,122,244,232,16,254,177,
59,139,207,10,80,134,208,168,131,170,251,121,75,45,93,131,206,127,17,118,45,248,55,47,66,225,38,102,227,90,180,214,
81,169,224,183,6,39,20,255,215,190,138,226,115,2,64,120,47,24,230,65,99,211,30,83,39,171,241,174,31,199,168,107,
71,223,56,15,94,1,107,229,15,65,153,83,215,176,214,96,215,161,71,79,226,205,33,62,47,128,50,132,54,140,3,254,
242,78,75,234,27,17,237,66,241,22,214,146,46,164,174,131,80,25,173,65,236,26,252,209,33,188,193,190,146,214,238,19,
115,253,27,206,247,131,196,248,108,15,59,146,85,242,7,86,126,211,182,59,127,140,246,242,168,240,78,40,3,96,85,227,
141,156,194,27,236,43,77,228,220,93,205,191,225,53,192,3,252,138,133,103,17,85,128,13,84,1,206,175,223,98,244,201,
85,92,72,101,174,109,241,201,24,102,115,39,248,110,112,52,145,72,188,52,216,231,190,255,169,251,244,131,253,28,5,204,
80,163,124,220,191,4,82,9,192,2,156,153,241,219,51,92,249,118,27,151,155,115,195,223,145,69,29,134,170,95,134,248,
46,88,73,252,145,33,74,131,125,238,209,97,247,71,221,47,115,44,38,94,6,80,4,142,135,91,90,25,192,8,1,236,
88,68,16,47,159,229,90,103,147,30,110,187,117,170,91,26,90,149,212,183,226,143,156,164,56,216,231,30,254,183,187,103,
215,65,222,10,197,227,162,241,81,107,130,177,204,10,96,134,16,102,8,80,6,114,0,231,192,7,140,173,72,249,87,86,
76,158,218,160,111,127,170,74,111,255,197,251,253,89,247,39,207,252,157,211,49,209,184,160,31,134,23,203,21,1,252,24,
189,17,203,101,48,3,176,14,159,99,172,165,198,191,182,218,26,89,255,187,51,254,207,158,59,194,233,25,66,238,140,40,
197,34,26,193,108,167,160,188,7,118,44,199,195,138,1,150,187,44,11,20,103,68,33,150,167,45,226,124,142,97,121,28,
54,211,71,83,22,143,47,150,23,235,62,14,51,173,235,187,5,184,211,59,113,241,114,141,153,179,158,215,243,95,119,198,
63,107,9,247,71,127,0,0,0,0,73,69,78,68,174,66,96,130,
};
const uint8_t convert[1155] = {
const uint8_t file[844] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,4,58,73,68,65,84,88,133,237,150,205,111,19,
71,24,198,127,179,179,142,13,246,218,106,10,84,178,235,40,109,168,65,9,183,72,233,1,132,34,34,66,168,148,68,21,
73,15,136,22,42,40,253,27,122,203,173,7,114,234,173,103,206,61,115,203,63,64,47,112,43,92,138,20,33,20,36,99,
217,187,235,253,240,204,244,146,157,174,191,128,74,220,202,43,189,154,143,29,189,207,51,207,59,243,206,194,71,251,191,155,
152,52,185,187,187,123,214,24,243,123,161,80,88,149,82,58,255,53,168,49,198,246,149,82,42,77,211,63,194,48,252,225,
209,163,71,241,232,90,119,82,0,173,245,111,183,110,221,186,178,185,185,137,227,56,54,224,164,54,243,209,113,206,229,195,
135,15,191,59,56,56,248,19,120,240,78,2,119,238,220,41,249,190,255,245,250,250,58,157,78,135,32,8,16,66,32,132,
120,43,208,180,249,74,165,194,245,235,215,57,56,56,184,7,236,3,38,143,55,38,111,175,215,251,118,109,109,109,86,74,
73,28,199,20,139,69,0,132,16,184,174,59,228,142,227,88,114,66,8,164,148,118,14,160,84,42,17,199,49,181,90,141,
165,165,165,214,206,206,206,149,81,188,73,249,189,187,177,177,129,239,251,164,105,74,154,166,22,48,11,108,140,65,107,141,
214,26,99,12,82,74,92,215,181,109,230,113,28,147,36,9,65,16,112,245,234,85,140,49,63,191,149,192,141,27,55,190,
60,115,230,204,149,70,163,65,16,4,67,192,25,160,82,10,165,20,90,107,171,74,182,235,209,148,100,138,244,251,125,90,
173,22,149,74,101,123,107,107,235,179,169,4,132,16,247,118,118,118,68,24,134,86,246,60,104,230,198,152,33,201,129,33,
69,242,158,17,75,211,148,181,181,181,153,66,161,240,227,68,2,171,171,171,174,16,226,246,242,242,50,190,239,227,186,46,
198,152,49,112,0,199,113,144,82,90,130,121,192,108,60,4,226,56,132,97,200,202,202,10,192,253,189,189,61,103,140,192,
236,236,236,55,151,46,93,170,23,10,5,162,40,194,113,28,187,171,44,104,6,156,223,245,232,121,24,189,170,66,8,138,
197,34,105,154,226,121,30,23,46,92,248,226,201,147,39,235,99,4,132,16,63,93,187,118,141,110,183,107,3,100,36,178,
124,230,119,253,54,240,188,205,204,204,216,126,191,223,231,242,229,203,72,41,239,15,17,216,218,218,170,159,58,117,106,163,
217,108,18,4,1,198,24,130,32,192,113,28,202,229,178,61,136,121,192,81,117,38,201,94,46,151,145,82,226,251,62,0,
113,28,179,176,176,64,181,90,221,220,221,221,109,88,2,174,235,222,222,222,222,118,195,48,28,42,56,65,16,16,199,49,
39,79,158,228,196,137,19,184,174,107,213,201,192,243,114,75,41,41,22,139,84,42,21,42,149,10,253,126,223,42,154,125,
87,74,113,241,226,69,87,107,125,215,18,16,66,220,92,94,94,38,12,67,234,245,58,158,231,217,93,69,81,68,187,221,
166,215,235,161,148,178,0,158,231,81,173,86,173,215,106,53,74,165,18,0,189,94,143,118,187,77,28,199,54,142,231,121,
204,205,205,161,148,98,101,101,5,215,117,191,135,127,75,241,87,158,231,209,235,245,120,249,242,37,90,107,123,208,50,203,
110,65,20,69,99,121,206,171,144,181,163,111,136,239,251,4,65,128,214,154,211,167,79,163,181,62,155,39,80,76,146,100,
172,218,141,182,249,60,79,123,160,242,107,71,191,101,41,123,245,234,149,237,219,199,104,48,24,240,236,217,179,177,0,147,
138,203,164,185,247,93,11,80,175,215,45,89,171,115,198,8,224,241,227,199,60,125,250,148,231,207,159,115,120,120,200,209,
209,145,125,25,227,56,30,170,134,89,224,118,187,77,179,217,164,211,233,208,108,54,121,243,230,13,115,115,115,116,58,29,
230,231,231,109,59,138,101,9,228,229,146,82,78,204,243,52,249,39,125,155,150,14,192,86,212,169,10,20,139,197,177,67,
56,141,200,180,2,52,109,126,20,203,158,129,140,149,49,134,197,197,69,148,82,184,174,75,146,36,86,114,165,20,66,8,
59,126,31,50,163,115,66,8,6,131,1,128,182,4,140,49,127,191,120,241,98,254,220,185,115,99,229,117,82,229,155,228,
141,70,3,173,53,139,139,139,36,73,194,249,243,231,137,162,136,86,171,69,16,4,44,44,44,208,237,118,169,213,106,217,
85,255,11,40,185,0,97,24,254,178,191,191,255,192,113,156,207,223,169,251,7,48,165,212,225,209,209,209,175,192,76,246,
87,92,2,62,5,62,1,170,192,12,80,248,192,184,9,32,129,8,232,2,175,129,215,249,223,114,1,148,143,189,116,188,
248,67,218,224,216,35,192,63,38,244,209,62,26,255,0,149,96,129,13,164,124,187,62,0,0,0,0,73,69,78,68,174,
66,96,130,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,3,3,73,68,65,84,88,133,229,151,79,110,212,
48,24,197,127,182,227,56,81,39,116,36,132,0,169,167,226,2,101,209,93,239,192,150,37,171,46,43,245,4,92,164,183,
40,82,89,21,36,134,84,76,18,59,44,90,91,142,243,103,50,21,59,62,201,138,227,120,236,247,189,247,252,37,3,255,
123,136,116,224,234,234,234,67,150,101,55,64,165,148,66,74,137,115,14,107,109,104,93,215,133,107,220,95,26,3,118,93,
215,125,188,185,185,249,26,239,151,141,16,9,113,125,113,113,81,61,247,195,120,223,247,131,121,241,253,202,126,117,121,121,
121,13,44,3,176,214,110,1,110,111,111,17,66,32,165,12,87,41,37,74,41,148,82,8,33,80,74,177,221,110,209,90,
15,158,167,125,33,4,251,253,158,166,105,182,233,126,50,29,232,251,30,33,68,104,64,88,208,55,207,78,215,117,24,99,
70,191,247,109,77,140,24,112,206,133,13,98,6,210,38,165,164,40,138,48,127,42,60,16,159,200,20,168,17,3,206,185,
17,11,177,4,49,40,231,28,77,211,28,204,114,137,141,17,0,107,109,232,207,177,224,159,41,165,208,90,31,100,97,9,
200,36,3,233,230,233,189,181,150,60,207,41,203,146,186,174,233,186,46,204,91,202,124,21,0,207,192,156,9,1,140,49,
20,69,65,93,215,100,89,70,158,231,3,173,231,50,127,17,128,88,123,127,239,105,55,198,96,140,161,109,219,85,174,95,
13,96,202,132,41,35,62,218,182,5,240,213,110,113,211,163,25,72,179,23,66,208,52,13,90,235,80,92,252,243,199,199,
199,229,244,97,210,172,7,37,136,93,47,165,164,170,42,178,44,67,8,193,201,201,73,232,151,101,57,152,63,197,194,139,
37,136,77,168,181,198,90,27,42,97,215,117,97,222,210,113,92,205,128,215,50,205,222,103,16,75,226,156,11,12,120,64,
62,203,41,38,142,146,32,213,190,239,123,172,181,225,69,228,105,143,193,214,117,141,115,110,86,134,163,0,196,11,11,33,
200,178,140,170,170,6,115,235,186,14,114,120,79,40,165,70,155,28,205,64,74,181,7,32,165,28,248,195,24,67,158,231,
1,172,82,106,117,89,158,5,144,158,231,212,7,113,40,165,6,12,120,169,210,170,184,20,171,142,161,47,54,177,254,254,
236,123,205,227,58,177,219,237,86,3,152,250,34,26,80,37,132,96,179,217,80,150,229,200,96,155,205,6,173,245,96,190,
49,38,48,177,6,196,162,9,189,15,178,44,27,140,123,96,109,219,142,36,136,95,90,107,98,214,3,177,9,211,240,99,
190,8,77,197,161,162,52,11,32,149,96,238,37,3,79,18,164,0,60,232,135,135,135,17,107,171,1,132,135,82,6,157,
227,240,0,119,187,221,8,160,63,5,167,167,167,65,138,163,62,201,226,5,189,235,167,88,232,251,30,173,245,36,56,95,
71,252,247,229,82,164,167,160,146,82,254,234,251,254,213,217,217,89,24,156,211,185,40,138,197,197,211,242,155,231,249,14,
120,7,252,4,246,41,128,10,120,115,127,127,255,249,252,252,252,19,80,46,174,190,50,242,60,167,105,26,132,16,127,238,
238,238,190,0,239,1,5,124,7,92,156,90,14,188,5,94,3,91,192,60,79,252,23,241,135,167,100,107,224,7,240,13,
248,13,19,127,78,159,55,46,158,1,173,43,103,135,195,1,45,79,180,239,129,96,140,191,182,58,238,12,241,249,173,246,
0,0,0,0,73,69,78,68,174,66,96,130,
};
const uint8_t emulator[2544] = {
const uint8_t archive[1067] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,9,167,73,68,65,84,88,133,173,87,123,112,84,
229,21,255,125,223,119,239,190,55,187,121,237,230,69,222,36,36,16,65,2,98,170,242,72,64,130,188,5,166,78,59,50,
34,74,59,173,226,76,199,182,211,90,167,127,180,34,213,106,153,169,141,22,4,177,42,78,165,86,7,6,52,168,160,40,
168,16,76,12,144,16,242,220,108,222,187,217,205,190,239,222,103,255,200,66,99,88,157,202,244,204,156,185,179,223,253,206,
249,253,246,220,115,126,223,189,4,55,97,75,86,108,27,5,224,32,132,10,154,166,26,64,105,243,39,141,251,231,223,76,
46,238,102,130,24,99,182,159,238,124,24,38,147,209,16,9,71,240,247,23,14,148,221,76,30,0,160,223,55,160,182,118,
123,1,227,56,8,162,138,215,143,124,9,198,235,0,104,186,186,186,173,233,255,55,2,181,245,15,63,80,187,242,161,145,
197,203,183,223,58,253,158,204,180,185,105,233,105,234,216,120,16,49,65,196,232,120,8,41,54,187,164,16,174,98,250,222,
37,43,182,223,191,100,197,54,109,217,221,219,31,252,54,2,108,250,194,178,123,30,254,141,217,100,124,174,174,174,198,230,
238,31,218,54,163,100,174,208,215,213,252,121,93,221,214,244,194,242,234,71,52,77,123,121,246,156,74,78,227,77,100,220,
31,129,35,213,2,89,140,16,143,199,123,95,81,233,252,168,179,172,178,125,102,94,141,181,164,178,250,109,107,138,101,231,
218,245,245,92,87,183,235,238,252,130,91,212,190,238,230,207,166,227,145,169,63,234,86,110,127,140,48,250,252,211,187,126,
69,141,38,19,66,193,16,254,250,194,171,210,208,240,88,156,99,140,155,61,167,156,159,85,57,139,149,148,20,161,241,211,
203,24,246,76,160,32,55,29,203,22,149,161,173,189,19,77,231,91,227,3,253,253,154,166,170,134,133,139,170,149,59,238,
170,97,129,144,128,12,187,1,207,61,219,160,2,202,253,167,26,15,30,154,138,249,141,38,84,137,170,89,77,22,205,110,
179,162,165,99,16,102,131,14,79,60,241,8,223,210,124,137,47,41,45,132,217,108,129,123,196,135,115,173,189,240,248,66,
224,57,134,225,177,0,254,221,216,130,130,156,116,108,222,178,78,15,89,132,32,198,17,17,9,59,121,182,3,140,50,172,
91,81,5,29,175,83,227,82,220,241,157,143,160,175,171,229,92,225,204,249,171,37,81,206,174,154,93,70,124,193,40,38,
130,49,228,231,231,34,24,17,49,234,13,34,28,17,16,139,75,240,77,68,192,24,3,71,39,219,40,16,138,161,199,237,
129,164,16,248,67,34,58,123,60,160,148,98,241,109,51,241,254,241,143,164,177,49,207,145,147,239,191,252,139,233,4,166,
143,161,22,21,148,13,141,31,124,214,61,111,94,133,209,106,50,34,42,136,24,25,11,128,16,32,24,138,225,204,133,110,
16,2,232,24,3,33,4,132,32,113,37,160,132,36,246,18,240,28,67,85,121,14,70,134,220,104,107,187,50,174,68,185,
7,110,232,64,36,153,130,79,27,247,14,75,162,244,225,145,35,31,105,185,89,118,48,74,192,40,5,165,20,94,127,24,
60,99,208,49,6,147,65,135,84,155,25,70,131,30,58,110,114,141,103,12,60,55,233,6,29,135,202,178,92,156,60,121,
70,150,101,233,200,199,31,55,132,147,17,32,211,23,22,173,250,113,138,149,154,135,127,255,228,78,19,229,12,8,132,98,
215,55,74,178,130,193,145,9,20,230,165,195,106,214,193,59,30,64,70,186,13,145,168,132,75,29,67,240,250,194,160,83,
170,82,152,151,1,187,133,162,225,197,87,130,74,140,207,77,70,226,6,37,212,43,252,230,178,242,18,62,203,153,137,110,
151,39,145,144,96,212,19,196,140,156,84,84,150,58,177,255,149,195,130,219,229,2,161,52,164,169,170,53,191,176,16,91,
239,223,104,112,185,39,208,63,232,3,193,36,137,145,209,0,202,138,203,144,147,151,203,187,93,238,77,0,94,157,142,199,
0,96,213,170,71,245,57,133,149,149,69,165,183,46,134,138,215,54,221,187,146,89,82,108,16,226,50,40,33,152,8,198,
208,55,48,142,194,220,84,236,254,83,67,196,239,27,127,53,200,179,229,103,143,237,219,149,94,185,240,47,113,255,132,227,
252,185,214,202,141,235,106,117,62,127,20,154,6,148,23,59,160,105,26,8,8,170,102,205,224,207,126,241,213,134,57,243,
22,111,46,42,157,59,119,70,193,220,140,252,210,121,227,174,238,150,32,169,173,127,104,88,85,85,103,70,122,170,148,157,
227,36,165,197,249,220,250,245,117,164,127,192,15,73,86,64,0,184,135,253,176,167,152,240,225,7,159,196,219,47,95,62,
240,225,123,123,127,54,253,159,44,95,189,163,97,206,236,170,7,239,89,85,171,239,233,243,160,170,50,15,4,128,6,160,
164,48,19,193,80,24,221,61,110,180,95,233,213,190,110,109,147,93,253,131,226,199,39,14,88,56,77,83,29,47,53,252,
129,48,198,233,34,81,17,162,40,99,96,112,2,138,162,34,20,22,64,64,144,235,180,195,110,51,163,227,202,21,89,144,
132,221,201,154,73,136,11,187,219,219,218,182,110,219,186,65,223,126,101,24,94,111,24,217,206,20,128,16,184,220,227,24,
30,9,32,18,1,102,87,84,145,59,106,22,240,191,125,242,217,56,0,112,170,170,81,171,197,12,247,160,15,66,92,154,
236,74,66,224,241,134,208,213,59,217,3,6,61,135,186,197,21,144,196,184,193,159,26,27,73,70,192,159,26,27,225,3,
156,65,175,227,96,208,243,200,72,51,131,82,10,85,213,208,219,231,133,16,149,96,54,233,192,40,69,74,138,5,138,34,
153,129,196,24,158,62,125,14,28,149,144,151,101,135,213,98,64,154,221,4,74,8,120,70,193,49,10,69,209,32,8,18,
44,86,107,212,17,48,85,37,35,224,8,152,170,172,41,41,81,69,86,81,86,226,132,209,168,3,165,4,110,183,15,85,
21,185,152,85,158,1,66,163,184,120,249,34,14,28,124,75,212,18,19,200,81,198,30,123,227,205,163,27,101,85,169,18,
227,162,221,100,52,106,79,255,241,113,46,30,151,193,115,9,177,1,129,199,19,194,242,186,165,150,99,199,222,223,91,93,
189,227,246,11,23,246,74,215,192,171,171,119,240,60,79,246,214,175,168,181,72,162,130,84,187,233,122,12,35,4,161,112,
24,191,254,221,110,24,141,198,47,68,73,108,86,101,181,149,128,52,1,211,116,160,122,237,14,147,93,209,206,110,222,184,
102,110,78,78,62,6,6,253,215,199,144,227,24,110,91,80,132,134,151,94,143,117,246,244,94,85,69,249,9,73,147,58,
121,194,207,164,122,238,169,242,226,226,178,71,127,190,213,24,14,9,48,24,120,200,146,130,174,174,49,100,101,219,208,126,
245,10,222,126,247,189,99,141,71,95,92,147,116,12,175,217,240,213,11,210,140,162,91,68,65,144,238,94,190,236,118,222,
227,9,79,42,33,163,160,132,32,24,140,97,205,61,119,242,70,163,201,25,8,6,215,1,228,39,14,71,250,166,250,21,
75,102,220,183,101,53,223,211,237,65,102,186,21,148,82,248,198,35,16,227,10,138,138,50,113,232,240,209,152,103,220,183,
203,213,221,124,113,58,129,27,132,200,0,229,159,46,87,255,243,30,143,199,152,155,99,199,216,88,112,82,231,65,32,197,
21,92,186,56,136,57,179,42,200,93,53,213,86,157,142,131,36,42,8,4,98,232,238,244,192,106,209,131,177,73,117,143,
132,69,56,50,172,240,120,189,232,119,245,179,113,91,232,112,178,222,185,225,44,56,113,226,181,136,172,168,167,223,57,218,
168,206,200,75,131,142,113,147,58,159,208,120,74,40,188,99,33,116,117,140,161,253,210,48,186,59,199,160,41,26,74,75,
29,200,202,182,129,82,2,73,82,192,64,160,169,26,132,152,0,69,213,116,233,1,139,243,127,34,176,116,229,246,82,29,
207,45,255,225,189,171,233,96,191,47,1,204,129,231,88,210,67,71,199,113,80,21,21,113,65,2,37,147,7,151,42,105,
224,57,6,81,80,224,76,119,98,243,250,122,213,104,48,28,70,146,179,231,27,61,48,123,203,22,93,154,102,249,116,211,
250,122,103,94,86,62,153,240,71,97,54,235,81,82,234,0,207,49,200,162,2,74,233,228,123,64,98,68,25,163,80,101,
13,209,176,136,20,155,17,22,139,30,246,52,19,140,70,29,196,168,140,120,84,194,130,234,10,210,212,220,154,153,225,152,
25,234,237,110,254,242,91,43,224,12,217,14,218,82,172,165,43,235,238,36,62,111,24,78,103,10,178,178,205,56,126,226,
164,38,136,17,84,204,206,65,110,94,42,236,54,35,116,124,162,18,140,193,98,54,32,43,219,134,76,135,21,35,30,15,
118,62,190,75,254,219,190,127,200,153,185,102,152,44,122,40,178,134,13,107,86,232,101,85,221,243,157,21,40,40,158,103,
16,37,101,253,157,53,243,89,113,113,22,218,58,174,224,153,61,251,226,151,218,59,91,207,126,209,100,190,220,222,173,233,
13,28,55,179,52,23,25,153,54,68,130,113,152,76,58,20,148,166,227,194,215,23,113,240,208,59,226,155,255,58,46,142,
14,15,237,246,249,252,254,79,206,52,149,44,92,88,201,81,70,240,204,158,125,130,40,197,126,228,234,105,109,155,138,121,
195,51,89,186,124,219,227,6,163,254,41,71,102,6,27,29,243,122,6,250,175,238,184,220,122,178,205,100,178,167,148,87,
221,177,54,45,213,89,47,43,90,205,27,123,255,12,119,143,31,102,171,30,29,253,29,120,97,239,33,213,239,29,124,178,
183,243,252,169,80,200,23,4,32,45,92,180,110,169,45,35,107,143,36,74,70,77,145,126,121,250,228,235,13,0,68,0,
114,210,10,0,64,95,79,75,83,86,110,89,111,56,20,153,56,255,249,209,7,220,253,151,198,0,240,146,36,144,145,193,
206,158,222,174,230,143,102,85,44,218,252,131,219,111,53,168,34,133,201,162,195,217,115,95,169,45,45,95,237,111,105,58,
126,68,20,99,74,34,149,54,52,216,209,165,2,111,81,141,28,253,242,204,219,239,2,80,18,224,218,53,188,100,159,102,
242,103,167,14,29,6,112,4,128,14,128,62,65,84,77,184,166,170,74,255,208,208,104,85,65,118,33,12,38,30,93,61,
46,41,16,244,118,39,226,213,4,144,10,64,233,235,188,224,238,3,186,18,192,114,226,222,117,75,246,101,164,2,136,1,
136,78,185,70,0,132,19,30,140,69,195,45,238,161,81,45,51,219,10,80,21,174,129,65,206,239,27,104,2,16,0,16,
154,178,55,50,45,143,56,29,236,134,30,72,98,12,0,159,112,14,0,183,160,102,227,90,179,197,190,31,0,40,101,50,
160,70,79,53,30,168,192,127,75,124,205,165,100,160,223,151,192,183,25,157,18,175,77,241,239,101,255,1,34,122,56,10,
134,174,85,83,0,0,0,0,73,69,78,68,174,66,96,130,
};
const uint8_t path[1176] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
97,112,101,46,111,114,103,155,238,60,26,0,0,4,21,73,68,65,84,88,133,237,151,61,111,28,69,24,199,127,51,187,
119,123,175,246,57,145,48,9,5,86,148,194,31,0,9,5,33,83,110,149,130,26,137,2,90,58,62,64,36,62,64,74,
132,68,133,104,161,67,72,167,64,23,201,138,2,138,210,69,194,198,196,40,22,198,247,186,119,222,151,153,157,25,138,123,
201,58,119,142,19,114,82,40,248,75,143,118,118,118,247,121,126,243,60,51,179,187,34,12,67,94,167,228,107,141,254,95,
0,240,1,110,220,184,225,43,165,126,54,198,188,119,209,3,66,136,220,90,251,249,195,135,15,191,92,25,64,154,166,91,
149,74,229,157,155,55,111,250,73,146,224,156,3,192,90,11,48,63,159,182,253,59,119,238,220,222,222,222,254,254,209,163,
71,199,43,1,80,74,101,213,106,213,29,29,29,209,233,116,112,206,45,53,0,33,4,155,155,155,249,193,193,193,71,192,
237,149,0,104,173,211,44,203,196,165,75,27,24,99,176,214,158,9,92,204,132,16,130,106,181,90,63,60,60,252,98,123,
123,251,214,172,127,153,156,115,218,90,251,241,222,222,222,143,207,5,0,178,44,203,188,78,167,75,167,211,153,3,204,142,
179,32,197,64,91,91,91,245,70,163,65,179,217,68,8,129,16,2,0,41,229,188,29,69,17,247,238,221,187,5,60,31,
224,242,229,203,105,191,223,247,54,54,90,104,173,23,0,150,149,98,166,56,142,1,230,16,69,139,227,24,99,76,237,188,
224,115,128,221,221,93,117,237,218,53,217,235,245,230,25,0,150,130,204,178,81,212,108,196,197,224,82,202,25,64,229,66,
0,0,99,140,109,54,215,100,154,102,220,63,180,220,221,63,27,228,188,58,63,171,25,204,228,153,22,206,93,185,206,219,
31,232,226,61,158,39,190,93,255,253,155,79,206,0,56,231,116,183,219,245,187,221,46,143,59,13,62,253,240,93,174,191,
181,81,112,54,61,62,47,250,18,200,105,207,60,206,147,206,152,175,190,187,255,254,236,188,152,1,221,104,212,171,90,107,
212,31,146,102,181,204,254,223,99,162,56,159,140,108,233,104,207,35,17,11,77,1,52,42,62,42,211,8,65,103,118,121,
190,21,27,99,116,191,63,160,219,237,18,103,150,122,181,132,53,147,27,228,212,193,179,134,59,207,220,83,179,19,179,214,
145,100,134,52,203,193,137,249,6,86,44,65,86,169,4,180,90,45,50,35,168,5,37,172,83,120,114,53,175,139,113,154,
83,111,250,140,134,57,214,186,39,11,0,121,158,171,40,26,209,239,15,208,230,13,170,129,143,115,14,79,158,155,231,23,
147,131,81,170,209,185,37,240,37,113,170,173,49,249,209,2,128,49,38,43,151,75,84,155,27,172,213,202,56,64,10,177,
188,248,47,40,227,28,163,84,147,91,135,231,9,130,178,199,105,146,41,33,196,201,2,128,181,54,27,141,198,28,15,20,
235,245,75,232,220,190,210,232,51,109,137,51,3,78,224,79,253,148,61,201,232,84,229,130,167,147,176,152,129,212,247,125,
188,32,160,73,153,76,217,11,131,76,230,152,195,186,201,62,97,236,196,84,110,151,173,72,156,131,193,40,181,185,115,139,
0,214,218,100,60,30,211,25,248,84,90,101,162,68,147,233,233,75,8,200,141,69,27,135,206,45,185,153,110,203,47,153,
149,68,25,134,227,196,19,210,91,90,130,84,8,129,12,26,148,74,62,163,68,19,37,154,84,25,148,182,47,29,108,153,
226,44,231,52,209,62,231,148,32,142,227,83,122,81,133,74,13,30,159,196,12,99,189,212,209,191,145,0,134,177,66,25,
83,250,245,234,94,47,124,252,12,128,181,54,6,129,245,107,100,185,37,138,53,74,95,60,15,94,84,82,10,78,134,9,
82,200,56,220,221,157,59,62,51,7,146,36,33,74,12,87,175,4,84,3,143,160,188,186,111,86,41,4,105,170,144,82,
12,139,253,197,18,36,0,154,18,235,85,143,122,105,21,85,127,42,231,28,253,36,65,88,59,88,0,104,183,219,98,103,
103,199,68,177,98,112,234,248,243,100,136,236,190,226,14,184,68,199,199,67,155,36,195,7,187,237,118,45,12,195,120,14,
0,120,81,20,253,244,203,161,248,76,173,227,255,182,255,202,31,187,103,228,192,2,88,157,30,28,63,248,225,235,226,53,
49,251,53,107,183,219,205,96,109,237,205,90,208,218,116,56,41,133,92,201,18,200,141,49,42,75,107,185,16,121,62,58,
233,1,127,133,97,216,91,0,152,66,72,32,96,146,153,124,21,0,83,5,64,10,168,48,12,207,44,45,241,255,207,233,
235,6,248,7,188,50,165,151,203,8,55,43,0,0,0,0,73,69,78,68,174,66,96,130,
};
const uint8_t synchronize[912] = {
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,3,34,73,68,65,84,56,141,
109,82,77,108,84,101,20,61,247,123,239,149,249,173,51,109,7,91,168,182,10,134,18,166,161,162,51,141,127,209,152,70,
55,196,96,88,168,188,68,140,144,129,240,179,97,7,27,12,77,12,17,116,97,162,224,3,163,49,76,37,152,52,1,55,
252,4,33,146,212,194,171,113,209,208,146,212,210,198,1,70,59,180,29,232,204,180,243,222,251,190,235,162,243,146,89,112,
146,155,220,197,61,39,39,231,92,98,102,248,72,101,178,207,52,232,218,17,16,222,244,164,106,213,132,152,39,194,184,227,
202,19,0,46,216,150,233,214,221,30,4,112,130,124,129,116,38,187,69,211,196,64,79,215,42,125,93,103,179,209,214,20,
1,152,81,40,86,48,50,118,191,124,103,250,161,39,21,239,182,45,243,108,122,87,54,195,140,239,0,196,136,153,145,202,
100,215,27,186,24,217,218,215,29,138,199,130,208,137,208,28,13,32,28,208,17,13,25,8,26,2,51,197,69,28,207,14,
87,10,197,138,29,13,53,244,62,46,87,13,102,52,17,51,227,213,61,103,79,245,172,107,251,52,24,48,196,173,209,28,
170,174,7,67,215,228,154,213,177,197,190,244,243,225,222,245,173,164,9,130,16,132,95,174,77,168,183,94,108,23,7,190,
190,234,56,174,108,209,1,128,153,63,188,51,93,16,174,167,88,49,47,50,227,51,199,149,103,198,167,103,147,19,247,230,
143,94,239,104,238,218,183,117,83,40,17,11,225,227,119,187,68,45,6,2,160,4,0,72,169,194,85,199,243,146,47,60,
251,131,148,234,17,128,65,219,50,243,182,101,94,249,227,219,143,94,26,157,44,236,221,125,236,18,238,63,44,97,122,166,
12,169,24,0,3,0,11,44,175,170,233,169,72,127,99,36,240,85,103,251,202,110,0,83,117,105,235,186,38,118,110,123,
39,233,118,62,29,69,162,113,5,52,65,190,3,214,107,119,241,95,63,223,188,128,39,227,180,39,213,107,23,135,39,171,
87,237,169,37,223,188,227,170,0,0,246,91,168,2,48,106,4,174,155,223,0,236,4,224,62,65,88,183,45,243,158,47,
224,157,63,250,190,6,16,152,25,23,237,127,212,169,243,127,229,164,84,61,182,101,22,235,89,135,179,99,171,74,229,165,
142,223,71,198,111,0,136,233,0,64,4,16,17,238,254,87,134,33,24,39,7,255,36,0,95,212,147,83,153,108,152,136,
238,246,110,92,123,38,63,51,151,2,160,1,168,10,0,16,68,4,0,29,137,16,86,183,132,241,229,254,183,169,173,37,
114,236,149,61,63,223,74,101,178,233,154,198,246,21,134,174,231,242,179,7,242,133,226,235,154,70,174,109,153,174,88,118,
64,120,92,113,113,97,104,138,61,143,209,190,50,138,195,59,222,8,125,208,151,124,185,181,57,114,45,189,107,192,53,116,
241,141,84,50,62,247,104,1,77,141,65,210,132,24,4,0,255,145,168,255,199,161,165,137,220,220,228,165,225,201,206,204,
150,77,225,68,44,136,238,181,9,122,174,61,30,42,150,93,252,59,91,130,227,120,20,9,26,56,119,121,180,226,73,117,
4,0,150,31,73,49,77,228,230,78,223,60,185,45,249,160,176,240,73,255,247,55,230,143,15,220,44,13,223,206,35,63,
91,70,213,149,16,68,40,204,151,228,185,203,163,139,82,169,140,109,153,99,0,224,183,240,19,128,237,182,101,114,45,48,
3,192,123,13,134,182,87,49,111,144,82,197,13,93,155,33,194,80,213,145,135,108,203,252,219,15,247,127,46,85,118,106,
19,101,204,198,0,0,0,0,73,69,78,68,174,66,96,130,
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,3,226,73,68,65,84,88,133,237,150,95,136,212,
85,20,199,63,231,222,251,251,253,102,102,103,255,142,166,27,150,150,88,144,102,127,31,218,40,49,200,36,136,68,200,30,
20,218,212,13,89,84,8,138,30,138,222,10,122,232,205,212,44,89,55,145,192,146,32,168,232,37,42,208,212,84,212,165,
32,162,168,101,21,203,221,213,253,51,237,56,51,191,223,189,61,252,134,217,217,117,86,155,26,240,161,253,194,225,222,115,
239,253,157,251,189,231,158,223,57,23,102,49,139,255,59,228,90,147,189,47,164,151,26,63,120,51,12,243,107,156,184,60,
14,59,229,75,135,2,130,234,70,5,45,114,196,89,121,233,185,247,198,142,215,68,96,223,150,166,37,126,144,58,168,148,
190,115,249,163,107,82,205,109,237,68,81,136,32,24,63,0,229,163,76,2,209,1,202,76,138,232,4,202,11,80,58,32,
180,206,253,240,117,143,244,159,250,232,47,27,113,209,138,125,165,115,87,246,208,117,9,236,239,106,190,77,18,254,137,101,
43,58,91,231,45,186,67,141,12,156,117,103,15,127,38,78,0,231,112,81,30,163,4,163,193,211,130,209,130,167,193,76,
235,39,146,45,100,30,123,195,93,56,190,215,133,131,167,213,207,231,70,115,69,203,182,141,239,142,247,84,238,167,43,149,
3,93,201,5,36,147,199,239,93,221,221,182,248,129,213,122,252,66,31,167,191,249,88,10,133,43,216,40,196,218,8,37,
160,68,80,10,180,18,148,18,180,2,53,173,47,46,79,225,242,79,114,83,199,139,146,255,227,20,173,137,208,27,30,203,
175,122,250,65,51,240,233,201,98,223,85,30,120,191,171,97,94,50,149,60,185,108,229,166,246,246,37,29,218,141,253,138,
23,52,80,140,38,157,100,157,5,107,65,25,68,123,177,168,88,40,181,162,13,136,46,27,78,54,207,167,144,27,167,175,
119,45,162,52,191,93,24,155,112,214,110,234,220,147,61,88,38,208,179,49,61,55,104,76,29,107,91,176,116,161,56,114,
67,3,125,233,71,158,121,141,230,185,183,146,27,57,55,121,95,162,64,52,136,138,5,13,74,226,13,69,202,115,18,7,
40,0,65,67,134,200,193,151,59,214,227,108,33,151,12,252,68,54,151,15,113,172,239,220,147,61,36,123,55,55,181,25,
99,135,1,252,32,117,62,44,230,191,187,253,190,39,215,221,179,122,27,209,200,47,224,44,206,89,112,46,182,234,92,172,
151,250,255,100,204,207,220,69,118,228,34,95,125,240,242,69,92,120,38,178,246,9,0,177,108,85,198,68,111,3,160,100,
205,149,98,126,69,208,152,121,106,249,170,173,68,163,191,99,139,19,216,98,14,23,94,41,139,13,167,233,197,220,117,199,
242,127,158,165,113,206,34,238,94,185,161,69,180,30,182,74,47,22,56,234,20,59,167,252,5,7,182,207,57,252,248,230,
221,15,167,51,11,175,153,31,254,11,190,221,191,61,55,116,254,199,141,157,187,42,98,0,96,95,119,211,67,98,237,81,
165,117,81,68,121,162,52,206,70,53,152,118,51,206,136,196,182,28,46,111,163,40,0,120,126,79,86,166,16,112,32,31,
118,55,183,0,20,109,116,169,99,237,171,12,28,235,169,110,177,70,220,124,255,58,190,255,124,7,158,210,109,0,158,248,
197,103,119,13,102,1,76,153,37,56,118,143,94,6,232,221,146,38,145,74,147,76,165,235,66,32,72,54,2,176,161,100,
191,18,230,170,213,37,40,109,240,147,13,117,33,160,141,55,227,220,140,4,180,241,240,19,245,241,128,54,126,237,4,140,
246,8,18,245,241,128,249,247,30,184,145,4,60,143,160,78,49,96,252,242,21,248,64,225,90,4,2,160,21,226,138,103,
252,20,184,90,114,65,21,136,66,169,114,209,189,5,24,3,46,1,81,53,2,205,64,198,41,111,112,184,255,204,220,204,
194,142,210,112,69,146,113,149,9,199,149,117,87,117,77,220,14,245,247,97,197,27,2,230,151,246,140,74,36,166,16,208,
37,221,251,226,244,196,91,216,157,175,11,97,75,141,231,173,10,135,25,249,228,196,196,59,64,2,240,168,120,135,76,207,
249,173,64,166,212,6,196,119,86,15,20,0,5,100,137,79,126,30,40,86,35,0,144,4,90,74,155,207,24,164,53,34,
36,190,143,81,96,28,38,31,183,215,171,122,245,170,138,51,87,170,89,220,104,252,13,162,179,143,166,193,167,182,66,0,
0,0,0,73,69,78,68,174,66,96,130,
};
};

View File

@ -1,7 +1,5 @@
namespace resource {
extern const uint8_t play[1470];
extern const uint8_t convert[1155];
extern const uint8_t emulator[2544];
extern const uint8_t path[1176];
extern const uint8_t synchronize[912];
extern const uint8_t game[1490];
extern const uint8_t file[844];
extern const uint8_t archive[1067];
};

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<resource name="resource">
<binary id="play" name="input-gaming.png"/>
<binary id="convert" name="drive-harddisk.png"/>
<binary id="emulator" name="applications-system.png"/>
<binary id="path" name="folder.png"/>
<binary id="synchronize" name="view-refresh.png"/>
</resource>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 912 B