diff --git a/purify/Makefile b/purify/Makefile index effed65e..58fd35be 100644 --- a/purify/Makefile +++ b/purify/Makefile @@ -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: diff --git a/purify/nall/Makefile b/purify/nall/Makefile index bbc4b029..9929d101 100644 --- a/purify/nall/Makefile +++ b/purify/nall/Makefile @@ -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 diff --git a/purify/nall/base64.hpp b/purify/nall/base64.hpp index a0afd8b1..daf3fa60 100644 --- a/purify/nall/base64.hpp +++ b/purify/nall/base64.hpp @@ -1,33 +1,38 @@ #ifndef NALL_BASE64_HPP #define NALL_BASE64_HPP -#include #include +#include 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; - case 1: { - uint8_t prev = dec(output[o]); - output[o++] = enc(prev + (input[i] >> 4)); - output[o] = enc((input[i] & 15) << 2); - } break; + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + break; + } + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + break; + } + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + break; + } - case 2: { - uint8_t prev = dec(output[o]); - output[o++] = enc(prev + (input[i] >> 6)); - output[o++] = enc(input[i] & 63); - } 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; - case 1: { - output[o++] |= x >> 4; - output[o] = (x & 15) << 4; - } break; + case 0: { + output[o] = x << 2; + break; + } - case 2: { - output[o++] |= x >> 2; - output[o] = (x & 3) << 6; - } break; + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + break; + } + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + break; + } + + case 3: { + output[o++] |= x; + break; + } - case 3: { - output[o++] |= x; - } 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]; } diff --git a/purify/nall/beat/archive.hpp b/purify/nall/beat/archive.hpp new file mode 100644 index 00000000..ef7294cf --- /dev/null +++ b/purify/nall/beat/archive.hpp @@ -0,0 +1,84 @@ +#ifndef NALL_BEAT_ARCHIVE_HPP +#define NALL_BEAT_ARCHIVE_HPP + +#include + +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 diff --git a/purify/nall/beat/base.hpp b/purify/nall/beat/base.hpp new file mode 100644 index 00000000..8e0001be --- /dev/null +++ b/purify/nall/beat/base.hpp @@ -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 diff --git a/purify/nall/bps/delta.hpp b/purify/nall/beat/delta.hpp similarity index 99% rename from purify/nall/bps/delta.hpp rename to purify/nall/beat/delta.hpp index 6cee56a3..ce120537 100644 --- a/purify/nall/bps/delta.hpp +++ b/purify/nall/beat/delta.hpp @@ -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 #include diff --git a/purify/nall/bps/linear.hpp b/purify/nall/beat/linear.hpp similarity index 98% rename from purify/nall/bps/linear.hpp rename to purify/nall/beat/linear.hpp index df840283..078aae34 100644 --- a/purify/nall/bps/linear.hpp +++ b/purify/nall/beat/linear.hpp @@ -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 #include diff --git a/purify/nall/bps/metadata.hpp b/purify/nall/beat/metadata.hpp similarity index 97% rename from purify/nall/bps/metadata.hpp rename to purify/nall/beat/metadata.hpp index 46759e6f..58e6ab0a 100644 --- a/purify/nall/bps/metadata.hpp +++ b/purify/nall/beat/metadata.hpp @@ -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 #include diff --git a/purify/nall/beat/multi.hpp b/purify/nall/beat/multi.hpp new file mode 100644 index 00000000..cddf5d6b --- /dev/null +++ b/purify/nall/beat/multi.hpp @@ -0,0 +1,242 @@ +#ifndef NALL_BEAT_MULTI_HPP +#define NALL_BEAT_MULTI_HPP + +#include +#include +#include + +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 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 diff --git a/purify/nall/bps/patch.hpp b/purify/nall/beat/patch.hpp similarity index 99% rename from purify/nall/bps/patch.hpp rename to purify/nall/beat/patch.hpp index 85c4dcae..8c6de75b 100644 --- a/purify/nall/bps/patch.hpp +++ b/purify/nall/beat/patch.hpp @@ -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 #include diff --git a/purify/nall/crc16.hpp b/purify/nall/crc16.hpp new file mode 100644 index 00000000..cd6e72fd --- /dev/null +++ b/purify/nall/crc16.hpp @@ -0,0 +1,25 @@ +#ifndef NALL_CRC16_HPP +#define NALL_CRC16_HPP + +#include + +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 diff --git a/purify/nall/directory.hpp b/purify/nall/directory.hpp index 4dba2b70..5617ee4e 100644 --- a/purify/nall/directory.hpp +++ b/purify/nall/directory.hpp @@ -1,6 +1,7 @@ #ifndef NALL_DIRECTORY_HPP #define NALL_DIRECTORY_HPP +#include #include #include #include @@ -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 } diff --git a/purify/nall/emulation/famicom.hpp b/purify/nall/emulation/famicom.hpp deleted file mode 100644 index 84275d69..00000000 --- a/purify/nall/emulation/famicom.hpp +++ /dev/null @@ -1,182 +0,0 @@ -#ifndef NALL_EMULATION_FAMICOM_HPP -#define NALL_EMULATION_FAMICOM_HPP - -#include -#include - -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("\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("\n"); - - switch(mapper) { - default: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 1: - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - - case 2: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 3: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 4: - //MMC3 - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - //MMC6 - //markup.append(" \n"); - //markup.append(" \n"); - //prgram = 1024; - break; - - case 5: - markup.append(" \n"); - markup.append(" \n"); - prgram = 65536; - break; - - case 7: - markup.append(" \n"); - break; - - case 9: - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - - case 10: - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - - case 16: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 21: - case 23: - case 25: - //VRC4 - markup.append(" \n"); - markup.append(" \n"); - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - - case 22: - //VRC2 - markup.append(" \n"); - markup.append(" \n"); - markup.append(" \n"); - markup.append(" \n"); - break; - - case 24: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 26: - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - - case 34: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 66: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 69: - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - - case 73: - markup.append(" \n"); - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - - case 75: - markup.append(" \n"); - markup.append(" \n"); - break; - - case 85: - markup.append(" \n"); - markup.append(" \n"); - prgram = 8192; - break; - } - - markup.append(" \n"); - if(prgrom) markup.append(" \n"); - if(prgram) markup.append(" \n"); - markup.append(" \n"); - - markup.append(" \n"); - if(chrrom) markup.append(" \n"); - if(chrram) markup.append(" \n"); - markup.append(" \n"); - - markup.append("\n"); - markup.transform("'", "\""); -} - -} - -#endif diff --git a/purify/nall/emulation/game-boy-advance.hpp b/purify/nall/emulation/game-boy-advance.hpp deleted file mode 100644 index e16d0949..00000000 --- a/purify/nall/emulation/game-boy-advance.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef NALL_EMULATION_GAME_BOY_ADVANCE_HPP -#define NALL_EMULATION_GAME_BOY_ADVANCE_HPP - -#include -#include -#include - -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 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("\n"); - markup.append("\n"); - markup.append(" \n"); - if(0); - else if(identifiers.beginswith("SRAM_V" )) markup.append(" \n"); - else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" \n"); - else if(identifiers.beginswith("EEPROM_V" )) markup.append(" \n"); - else if(identifiers.beginswith("FLASH_V" )) markup.append(" \n"); - else if(identifiers.beginswith("FLASH512_V")) markup.append(" \n"); - else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" \n"); - if(identifiers.empty() == false) markup.append(" \n"); - - markup.append("\n"); - markup.transform("'", "\""); -} - -} - -#endif diff --git a/purify/nall/emulation/game-boy.hpp b/purify/nall/emulation/game-boy.hpp deleted file mode 100644 index ef1f3da9..00000000 --- a/purify/nall/emulation/game-boy.hpp +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef NALL_EMULATION_GAME_BOY_HPP -#define NALL_EMULATION_GAME_BOY_HPP - -#include -#include - -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 = "\n"; - markup.append("\n"); - markup.append(" \n"); - markup.append(" \n"); - if(info.ramsize > 0) markup.append(" \n"); - markup.append("\n"); - markup.transform("'", "\""); -} - -} - -#endif diff --git a/purify/nall/emulation/satellaview.hpp b/purify/nall/emulation/satellaview.hpp deleted file mode 100644 index ddae080c..00000000 --- a/purify/nall/emulation/satellaview.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef NALL_EMULATION_SATELLAVIEW_HPP -#define NALL_EMULATION_SATELLAVIEW_HPP - -#include -#include - -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("\n"); - markup.append("\n"); - markup.append(" \n"); - markup.append("\n"); - markup.transform("'", "\""); -} - -} - -#endif diff --git a/purify/nall/emulation/sufami-turbo.hpp b/purify/nall/emulation/sufami-turbo.hpp deleted file mode 100644 index 0256360c..00000000 --- a/purify/nall/emulation/sufami-turbo.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef NALL_EMULATION_SUFAMI_TURBO_HPP -#define NALL_EMULATION_SUFAMI_TURBO_HPP - -#include -#include - -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("\n"); - markup.append("\n"); - markup.append(" \n"); - markup.append(" \n"); - markup.append("\n"); - markup.transform("'", "\""); -} - -} - -#endif diff --git a/purify/nall/emulation/super-famicom.hpp b/purify/nall/emulation/super-famicom.hpp deleted file mode 100644 index 3115d7ea..00000000 --- a/purify/nall/emulation/super-famicom.hpp +++ /dev/null @@ -1,803 +0,0 @@ -#ifndef NALL_EMULATION_SUPER_FAMICOM_HPP -#define NALL_EMULATION_SUPER_FAMICOM_HPP - -#include -#include - -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("\n"); - - const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff"; - markup.append("\n"); - - if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - else if(has_cx4) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - else if(has_spc7110) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - else if(has_sdd1) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - else if(mapper == LoROM) { - markup.append( - " \n" - " \n" - " \n" - ); - if(ram_size > 0) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - } - - else if(mapper == HiROM) { - markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - if(ram_size > 0) markup.append( - " \n" - " \n" - " \n" - " \n" - ); - } - - else if(mapper == ExLoROM) { - markup.append( - " \n" - " \n" - " \n" - " \n" - ); - if(ram_size > 0) markup.append( - " \n" - " \n" - " \n" - " \n" - ); - } - - else if(mapper == ExHiROM) { - markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - if(ram_size > 0) markup.append( - " \n" - " \n" - " \n" - " \n" - ); - } - - else if(mapper == SuperFXROM) { - markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - if(ram_size > 0) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - markup.append( - " \n" - ); - } - - else if(mapper == SA1ROM) { - markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - if(ram_size > 0) markup.append( - " \n" - " \n" - " \n" - " \n" - ); - markup.append( - " \n" - ); - } - - else if(mapper == BSCLoROM) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - else if(mapper == BSCHiROM) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - else if(mapper == BSXROM) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - else if(mapper == STROM) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_spc7110rtc) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_srtc) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_obc1) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_dsp1) { - //91e87d11e1c30d172556bed2211cce2efa94ba595f58c5d264809ef4d363a97b dsp1.rom - markup.append( - " \n" - " \n" - ); - if(dsp1_mapper == DSP1LoROM1MB) markup.append( - " \n" - " \n" - " \n" - " \n" - ); - if(dsp1_mapper == DSP1LoROM2MB) markup.append( - " \n" - " \n" - " \n" - " \n" - ); - if(dsp1_mapper == DSP1HiROM) markup.append( - " \n" - " \n" - " \n" - " \n" - ); - markup.append( - " \n" - ); - } - - if(has_dsp2) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_dsp3) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_dsp4) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_st010) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_st011) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - if(has_st018) markup.append( - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - markup.append("\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 diff --git a/purify/nall/file.hpp b/purify/nall/file.hpp index 8a751041..be9fc086 100644 --- a/purify/nall/file.hpp +++ b/purify/nall/file.hpp @@ -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 &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 diff --git a/purify/nall/image.hpp b/purify/nall/image.hpp index 552a3e30..e334b6e0 100644 --- a/purify/nall/image.hpp +++ b/purify/nall/image.hpp @@ -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; diff --git a/purify/nall/nall.hpp b/purify/nall/nall.hpp index 9a8f1dfa..06040fac 100644 --- a/purify/nall/nall.hpp +++ b/purify/nall/nall.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/purify/nall/property.hpp b/purify/nall/property.hpp index 665afcad..1ddb5c9b 100644 --- a/purify/nall/property.hpp +++ b/purify/nall/property.hpp @@ -5,12 +5,6 @@ //example: property::readonly 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::readonly x; @@ -50,8 +44,6 @@ namespace nall { template struct property { - template struct traits { typedef T type; }; - template 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::type; + friend C; }; template struct writeonly { @@ -73,7 +65,7 @@ namespace nall { T* operator->() { return &value; } operator T&() { return value; } T value; - friend class traits::type; + friend C; }; template struct readwrite { diff --git a/purify/nall/sort.hpp b/purify/nall/sort.hpp index 36d91865..8326ab1d 100644 --- a/purify/nall/sort.hpp +++ b/purify/nall/sort.hpp @@ -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++]); diff --git a/purify/nall/stream/zip.hpp b/purify/nall/stream/zip.hpp index 975cdeff..94aa3992 100644 --- a/purify/nall/stream/zip.hpp +++ b/purify/nall/stream/zip.hpp @@ -1,7 +1,7 @@ #ifndef NALL_STREAM_ZIP_HPP #define NALL_STREAM_ZIP_HPP -#include +#include 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; diff --git a/purify/nall/string.hpp b/purify/nall/string.hpp index 8c4f6b2c..82b7cde4 100644 --- a/purify/nall/string.hpp +++ b/purify/nall/string.hpp @@ -22,14 +22,15 @@ #define NALL_STRING_INTERNAL_HPP #include -#include #include #include #include #include #include #include +#include #include +#include #include #include #include @@ -44,7 +45,10 @@ #include #include #include -#include +#include +#include +#include +#include #undef NALL_STRING_INTERNAL_HPP #endif diff --git a/purify/nall/string/base.hpp b/purify/nall/string/base.hpp index f70c1247..abf21735 100644 --- a/purify/nall/string/base.hpp +++ b/purify/nall/string/base.hpp @@ -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 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 inline string& ltrim(const char *key = " "); template inline string& rtrim(const char *key = " "); template inline string& trim(const char *key = " ", const char *rkey = 0); + inline string& strip(); inline optional position(const char *key) const; inline optional iposition(const char *key) const; @@ -115,6 +122,7 @@ namespace nall { inline optional find(const char*) const; inline string concatenate(const char*) const; inline void append() {} + inline void isort(); template inline void append(const string&, Args&&...); template 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 inline string format(const string &value); + template inline string hex(uintmax_t value); + template inline string octal(uintmax_t value); + template 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 inline char* ltrim(char *str, const char *key = " "); template inline char* rtrim(char *str, const char *key = " "); template inline char* trim(char *str, const char *key = " ", const char *rkey = 0); + inline char* strip(char *s); //utility.hpp template 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 inline string integer(intmax_t value); template inline string linteger(intmax_t value); template inline string decimal(uintmax_t value); template inline string ldecimal(uintmax_t value); - template inline string hex(uintmax_t value); - template inline string binary(uintmax_t value); inline unsigned fp(char *str, long double value); inline string fp(long double value); diff --git a/purify/nall/string/bml.hpp b/purify/nall/string/bml.hpp deleted file mode 100644 index de1f07a0..00000000 --- a/purify/nall/string/bml.hpp +++ /dev/null @@ -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 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 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 diff --git a/purify/nall/string/core.hpp b/purify/nall/string/core.hpp index d5f75f3a..64c9250d 100644 --- a/purify/nall/string/core.hpp +++ b/purify/nall/string/core.hpp @@ -12,11 +12,18 @@ static void istring(string &output, const T &value, Args&&... args) { } void string::reserve(unsigned size_) { - if(size_ > size) { - size = size_; - data = (char*)realloc(data, size + 1); - data[size] = 0; - } + 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 void lstring::append(const string &data, Args&&... ar append(std::forward(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; diff --git a/purify/nall/string/datetime.hpp b/purify/nall/string/datetime.hpp new file mode 100644 index 00000000..5382fdfd --- /dev/null +++ b/purify/nall/string/datetime.hpp @@ -0,0 +1,31 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +string string::date() { + time_t timestamp = ::time(0); + tm *info = localtime(×tamp); + 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(×tamp); + 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 diff --git a/purify/nall/string/format.hpp b/purify/nall/string/format.hpp new file mode 100644 index 00000000..599021d1 --- /dev/null +++ b/purify/nall/string/format.hpp @@ -0,0 +1,73 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { + +template 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 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(buffer); +} + +template 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(buffer); +} + +template 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(buffer); +} + +} + +#endif diff --git a/purify/nall/string/markup/bml.hpp b/purify/nall/string/markup/bml.hpp new file mode 100644 index 00000000..338ca406 --- /dev/null +++ b/purify/nall/string/markup/bml.hpp @@ -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 diff --git a/purify/nall/string/markup/document.hpp b/purify/nall/string/markup/document.hpp new file mode 100644 index 00000000..34465a4f --- /dev/null +++ b/purify/nall/string/markup/document.hpp @@ -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 diff --git a/purify/nall/string/markup/node.hpp b/purify/nall/string/markup/node.hpp new file mode 100644 index 00000000..77822373 --- /dev/null +++ b/purify/nall/string/markup/node.hpp @@ -0,0 +1,146 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//note: specific markups inherit from Markup::Node +//vector will slice any data; so derived nodes must not contain data nor virtual functions +//vector 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 find(const string &query) const { + vector 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 children; +}; + +} +} + +#endif diff --git a/purify/nall/string/xml.hpp b/purify/nall/string/markup/xml.hpp similarity index 77% rename from purify/nall/string/xml.hpp rename to purify/nall/string/markup/xml.hpp index 937436c8..d3a3e15a 100644 --- a/purify/nall/string/xml.hpp +++ b/purify/nall/string/markup/xml.hpp @@ -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 children; - - inline bool exists() const { - return !name.empty(); +struct Node : Markup::Node { +protected: + inline string escape() const { + string result = data; + result.replace("&", "&"); + result.replace("<", "<"); + result.replace(">", ">"); + if(attribute == false) return result; + result.replace("\'", "'"); + result.replace("\"", """); + 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 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 { diff --git a/purify/nall/string/platform.hpp b/purify/nall/string/platform.hpp index 469d79b3..5c44a313 100644 --- a/purify/nall/string/platform.hpp +++ b/purify/nall/string/platform.hpp @@ -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 diff --git a/purify/nall/string/trim.hpp b/purify/nall/string/trim.hpp index ba049d71..3e0c914f 100644 --- a/purify/nall/string/trim.hpp +++ b/purify/nall/string/trim.hpp @@ -33,6 +33,23 @@ template char* trim(char *str, const char *key, const char *rkey return ltrim(rtrim(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 diff --git a/purify/nall/string/utility.hpp b/purify/nall/string/utility.hpp index b2f93d2c..657383f8 100644 --- a/purify/nall/string/utility.hpp +++ b/purify/nall/string/utility.hpp @@ -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 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 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 string ldecimal(uintmax_t value) { return (const char*)result; } -template 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 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. diff --git a/purify/nall/string/wrapper.hpp b/purify/nall/string/wrapper.hpp index 9845e0b7..08aa98b3 100644 --- a/purify/nall/string/wrapper.hpp +++ b/purify/nall/string/wrapper.hpp @@ -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 string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; } template string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; } template string& string::trim(const char *key, const char *rkey) { nall::trim (data, key, rkey); return *this; } +string& string::strip() { nall::strip(data); return *this; } optional string::position(const char *key) const { return strpos(data, key); } optional string::iposition(const char *key) const { return istrpos(data, key); } diff --git a/purify/nall/string/xml-legacy.hpp b/purify/nall/string/xml-legacy.hpp deleted file mode 100644 index 069639b0..00000000 --- a/purify/nall/string/xml-legacy.hpp +++ /dev/null @@ -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 attribute; - linear_vector 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, "<")) { data[offset++] = '<'; source += 4; continue; } - if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } - if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } - if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } - if(strbegin(source, """)) { 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, "<")) { data[offset++] = '<'; source += 4; continue; } - if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } - if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } - if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } - if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } - } - - if(strbegin(source, "")) { - source += pos() + 3; - continue; - } else { - return ""; - } - } - - if(strbegin(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 diff --git a/purify/nall/unzip.hpp b/purify/nall/unzip.hpp new file mode 100644 index 00000000..5a7935f6 --- /dev/null +++ b/purify/nall/unzip.hpp @@ -0,0 +1,126 @@ +#ifndef NALL_UNZIP_HPP +#define NALL_UNZIP_HPP + +#include +#include +#include +#include + +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 extract(File &file) { + vector 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; +}; + +} + +#endif diff --git a/purify/nall/vector.hpp b/purify/nall/vector.hpp index 13200ea6..6818c69d 100644 --- a/purify/nall/vector.hpp +++ b/purify/nall/vector.hpp @@ -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 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() { diff --git a/purify/nall/zip.hpp b/purify/nall/zip.hpp index 779b067e..0a73ccdd 100644 --- a/purify/nall/zip.hpp +++ b/purify/nall/zip.hpp @@ -1,124 +1,93 @@ -#ifndef NALL_UNZIP_HPP -#define NALL_UNZIP_HPP +#ifndef NALL_ZIP_HPP +#define NALL_ZIP_HPP -#include -#include +//creates uncompressed ZIP archives + +#include #include -#include 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(¤tTime); + 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 extract(File &file) { - vector 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 fp; + uint16_t dosTime, dosDate; + struct entry_t { + string filename; + uint32_t checksum; + uint32_t size; + uint32_t offset; + }; + vector directory; }; } diff --git a/purify/obj/.gitignore b/purify/obj/.gitignore new file mode 100644 index 00000000..5761abcf --- /dev/null +++ b/purify/obj/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/purify/phoenix/core/core.cpp b/purify/phoenix/core/core.cpp index a6a1ce38..a2588770 100644 --- a/purify/phoenix/core/core.cpp +++ b/purify/phoenix/core/core.cpp @@ -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; } diff --git a/purify/phoenix/core/core.hpp b/purify/phoenix/core/core.hpp index 1e88ca5d..1b329f85 100644 --- a/purify/phoenix/core/core.hpp +++ b/purify/phoenix/core/core.hpp @@ -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, 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, Sizable { struct Widget : private nall::base_from_member, Sizable { bool enabled(); + bool focused(); nall::string font(); Geometry geometry(); Geometry minimumGeometry(); diff --git a/purify/phoenix/core/state.hpp b/purify/phoenix/core/state.hpp index 2d46ca2f..a4bff751 100644 --- a/purify/phoenix/core/state.hpp +++ b/purify/phoenix/core/state.hpp @@ -1,3 +1,10 @@ +struct OS::State { + string name; + + State() { + } +} osState; + struct Timer::State { bool enabled; unsigned milliseconds; diff --git a/purify/phoenix/gtk/platform.hpp b/purify/phoenix/gtk/platform.hpp index 344e8239..01c4a223 100644 --- a/purify/phoenix/gtk/platform.hpp +++ b/purify/phoenix/gtk/platform.hpp @@ -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); diff --git a/purify/phoenix/gtk/widget/hex-edit.cpp b/purify/phoenix/gtk/widget/hex-edit.cpp index 9c0dbdef..ec10cd7c 100644 --- a/purify/phoenix/gtk/widget/hex-edit.cpp +++ b/purify/phoenix/gtk/widget/hex-edit.cpp @@ -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(); diff --git a/purify/phoenix/gtk/widget/list-view.cpp b/purify/phoenix/gtk/widget/list-view.cpp index 5572c1f7..10e1d439 100644 --- a/purify/phoenix/gtk/widget/list-view.cpp +++ b/purify/phoenix/gtk/widget/list-view.cpp @@ -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; diff --git a/purify/phoenix/gtk/widget/text-edit.cpp b/purify/phoenix/gtk/widget/text-edit.cpp index 08812fb3..2d2b740b 100644 --- a/purify/phoenix/gtk/widget/text-edit.cpp +++ b/purify/phoenix/gtk/widget/text-edit.cpp @@ -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; diff --git a/purify/phoenix/gtk/widget/widget.cpp b/purify/phoenix/gtk/widget/widget.cpp index dceb8266..aa4eacfa 100644 --- a/purify/phoenix/gtk/widget/widget.cpp +++ b/purify/phoenix/gtk/widget/widget.cpp @@ -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; diff --git a/purify/phoenix/gtk/window.cpp b/purify/phoenix/gtk/window.cpp index ae1521a3..4518c38a 100644 --- a/purify/phoenix/gtk/window.cpp +++ b/purify/phoenix/gtk/window.cpp @@ -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 { diff --git a/purify/phoenix/qt/platform.moc b/purify/phoenix/qt/platform.moc index 3b2db923..2a53a326 100644 --- a/purify/phoenix/qt/platform.moc +++ b/purify/phoenix/qt/platform.moc @@ -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! diff --git a/purify/phoenix/qt/platform.moc.hpp b/purify/phoenix/qt/platform.moc.hpp index 9e75fdc8..6a3909bf 100644 --- a/purify/phoenix/qt/platform.moc.hpp +++ b/purify/phoenix/qt/platform.moc.hpp @@ -266,6 +266,7 @@ struct pWidget : public pSizable { Widget &widget; QWidget *qtWidget; + bool focused(); virtual Geometry minimumGeometry(); void setEnabled(bool enabled); void setFocused(); diff --git a/purify/phoenix/qt/widget/widget.cpp b/purify/phoenix/qt/widget/widget.cpp index 0bc4901c..27d23354 100644 --- a/purify/phoenix/qt/widget/widget.cpp +++ b/purify/phoenix/qt/widget/widget.cpp @@ -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) { diff --git a/purify/phoenix/qt/window.cpp b/purify/phoenix/qt/window.cpp index c6cb35d6..dac311ee 100644 --- a/purify/phoenix/qt/window.cpp +++ b/purify/phoenix/qt/window.cpp @@ -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); diff --git a/purify/phoenix/reference/platform.hpp b/purify/phoenix/reference/platform.hpp index 5ffb5b60..cd9e4152 100644 --- a/purify/phoenix/reference/platform.hpp +++ b/purify/phoenix/reference/platform.hpp @@ -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(); diff --git a/purify/phoenix/reference/widget/widget.cpp b/purify/phoenix/reference/widget/widget.cpp index 49a6c79e..d9c86478 100644 --- a/purify/phoenix/reference/widget/widget.cpp +++ b/purify/phoenix/reference/widget/widget.cpp @@ -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) { diff --git a/purify/phoenix/reference/window.cpp b/purify/phoenix/reference/window.cpp index 128a9c2d..aca2cc2d 100644 --- a/purify/phoenix/reference/window.cpp +++ b/purify/phoenix/reference/window.cpp @@ -1,3 +1,9 @@ +Window& pWindow::none() { + static Window *window = nullptr; + if(window == nullptr) window = new Window; + return *window; +} + void pWindow::append(Layout &layout) { } diff --git a/purify/phoenix/windows/dialog-window.cpp b/purify/phoenix/windows/dialog-window.cpp index 3005c020..5ef21153 100644 --- a/purify/phoenix/windows/dialog-window.cpp +++ b/purify/phoenix/windows/dialog-window.cpp @@ -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); diff --git a/purify/phoenix/windows/platform.hpp b/purify/phoenix/windows/platform.hpp index 96811796..d14685b2 100644 --- a/purify/phoenix/windows/platform.hpp +++ b/purify/phoenix/windows/platform.hpp @@ -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> imageMap; + vector 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 { diff --git a/purify/phoenix/windows/widget/button.cpp b/purify/phoenix/windows/widget/button.cpp index 41e7e283..12cacbe5 100644 --- a/purify/phoenix/windows/widget/button.cpp +++ b/purify/phoenix/windows/widget/button.cpp @@ -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) { - SetWindowText(hwnd, utf16_t(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(); } diff --git a/purify/phoenix/windows/widget/list-view.cpp b/purify/phoenix/windows/widget/list-view.cpp index 11ae023f..675691e6 100644 --- a/purify/phoenix/windows/widget/list-view.cpp +++ b/purify/phoenix/windows/widget/list-view.cpp @@ -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); - bool found = false; - for(auto &row : listView.state.image) { - for(auto &column : row) { - if(column.empty() == false) { - found = true; - break; + 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(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); + //build image list + for(auto &imageItem : images) ImageList_Append(imageList, imageItem); + if(images.size() <= 1) return; - 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); - - 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)); } } } diff --git a/purify/phoenix/windows/widget/widget.cpp b/purify/phoenix/windows/widget/widget.cpp index b0e60c7d..d8c25c9b 100644 --- a/purify/phoenix/windows/widget/widget.cpp +++ b/purify/phoenix/windows/widget/widget.cpp @@ -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) { diff --git a/purify/purify.cpp b/purify/purify.cpp index 5d05c1d6..e8aebb80 100644 --- a/purify/purify.cpp +++ b/purify/purify.cpp @@ -1,571 +1,209 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include using namespace nall; #include using namespace phoenix; -#include "resource/resource.hpp" +#include + #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; - - string emulator; - string path; - string recent; - - Settings() { - ui = false; - play = false; - extensions = {".fc", ".nes", ".sfc", ".smc", ".swc", ".fig", ".bs", ".st", ".gb", ".gbc", ".sgb", ".gba"}; - - 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"}); - } - - ~Settings() { - save({configpath(), "purify/settings.cfg"}); - } -} settings; - -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("") && 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 = "("/"); - 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" - ); - 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}); - } - } - - if(quit == false) { - fileName.setText("All games have been converted."); - progressBar.setPosition(100); - } else { - fileName.setText("Process aborted. Not all games have been converted."); - } - stopButton.setEnabled(false); - - setModal(false); - } - - 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; }; - } -} *progress = nullptr; - struct Application : Window { + library ananke; + 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; + HorizontalLayout pathLayout; + Label pathLabel; + LineEdit pathEdit; + Button browseButton; + ListView fileList; + ProgressBar progressBar; + HorizontalLayout controlLayout; + Button selectAllButton; + Button unselectAllButton; + Widget spacer; + Button purifyButton; - 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"); + lstring filenameList; - Font font("Sans, 8, Bold"); - unsigned width = max(font.geometry("Emulator:").width, font.geometry("Output Path:").width); + Application(); + void scanPath(); + void scanPath(const string &path, const string &basepath); + void purify(); +} *application = nullptr; - 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}); +Application::Application() { + application = this; - 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); - layout.setEnabled(true); - MessageWindow::information(*this, "Process completed. All identified manifests have been updated."); - }; - - setVisible(); + 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); } - 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); + setFrameGeometry({64, 64, 720, 480}); + setTitle("purify v02"); - settings.play = true; - if(filename.iendswith(".zip")) { - create_zip(filename); + 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 { - create_file(filename); + fileList.setImage(filenameList.size() - 1, 0, {resource::file, sizeof resource::file}); } - exit(0); } } +} - void convertAction() { - string pathname = DialogWindow::folderSelect(*this, settings.recent); - if(pathname.empty()) return; +struct PurifyContext { + lstring list; + unsigned position; + unsigned size; - 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." - ); + void run() { + function sync = application->ananke.sym("ananke_sync"); + function open = application->ananke.sym("ananke_open"); + + if(!open || !sync) { + position = size; 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); + while(position < size) { + string filename = list[position]; + if(directory::exists(filename)) sync(filename); + else if(file::exists(filename)) open(filename); + position++; } } - void pathAction() { - string pathname = DialogWindow::folderSelect(*this, settings.recent); - if(!pathname.empty()) { - settings.recent = pathname; - pathValue.setText(settings.path = pathname); - } + PurifyContext(const lstring &list) : list(list) { + position = 0; + size = list.size(); } -} *application = nullptr; +}; + +void Application::purify() { + lstring purifyList; + for(unsigned n = 0; n < filenameList.size(); n++) { + if(fileList.checked(n)) purifyList.append(filenameList[n]); + } + + if(purifyList.size() == 0) { + MessageWindow::information(*this, "Please select at least one file to purify."); + return; + } + + layout.setEnabled(false); + OS::processEvents(); + + 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); +} 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; } diff --git a/purify/resource/applications-system.png b/purify/resource/applications-system.png deleted file mode 100644 index 565f406d..00000000 Binary files a/purify/resource/applications-system.png and /dev/null differ diff --git a/purify/resource/archive.png b/purify/resource/archive.png new file mode 100644 index 00000000..4b55b504 Binary files /dev/null and b/purify/resource/archive.png differ diff --git a/purify/resource/drive-harddisk.png b/purify/resource/drive-harddisk.png deleted file mode 100644 index b34d8b77..00000000 Binary files a/purify/resource/drive-harddisk.png and /dev/null differ diff --git a/purify/resource/file.png b/purify/resource/file.png new file mode 100644 index 00000000..5b7e6491 Binary files /dev/null and b/purify/resource/file.png differ diff --git a/purify/resource/folder.png b/purify/resource/folder.png deleted file mode 100644 index 472484f1..00000000 Binary files a/purify/resource/folder.png and /dev/null differ diff --git a/purify/resource/game.png b/purify/resource/game.png new file mode 100644 index 00000000..1990dbb8 Binary files /dev/null and b/purify/resource/game.png differ diff --git a/purify/resource/input-gaming.png b/purify/resource/input-gaming.png deleted file mode 100644 index 26e2a982..00000000 Binary files a/purify/resource/input-gaming.png and /dev/null differ diff --git a/purify/resource/resource.bml b/purify/resource/resource.bml new file mode 100644 index 00000000..3ad4331b --- /dev/null +++ b/purify/resource/resource.bml @@ -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 diff --git a/purify/resource/resource.cpp b/purify/resource/resource.cpp index 5d691900..274c56de 100644 --- a/purify/resource/resource.cpp +++ b/purify/resource/resource.cpp @@ -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, }; }; diff --git a/purify/resource/resource.hpp b/purify/resource/resource.hpp index e3f2102b..7e49e44f 100644 --- a/purify/resource/resource.hpp +++ b/purify/resource/resource.hpp @@ -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]; }; diff --git a/purify/resource/resource.xml b/purify/resource/resource.xml deleted file mode 100644 index 586b9516..00000000 --- a/purify/resource/resource.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/purify/resource/view-refresh.png b/purify/resource/view-refresh.png deleted file mode 100644 index 3fd71d6e..00000000 Binary files a/purify/resource/view-refresh.png and /dev/null differ