From b6575ca02ae4dde57157c72bf48900525163a540 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 17 Jan 2013 22:19:42 +1100 Subject: [PATCH] Update to purify v02 release. byuu says: purify has been rewritten. It now resembles the older snespurify, and lets you import multiple game files+archives and regenerate manifests for multiple game folders. It is also recursive. So you can now import all of your games for all systems at once, or you can update all of your bsnes v091 game folders to the new higan v092 format at once. Caveats: First, I am now using std::thread, so that the GUI doesn't freeze. Instead, you get a nice progress bar. Unfortunately, I was mislead and TDM/GCC 4.7 still does not have std::thread support. So ... sorry, but I can't compile purify for Windows. I am sick and tired of not being able to write multi-threaded code, so fuck it. If anyone can get it to build on Windows, whether that be by using Windows threads, hacking in std::thread support, skipping threading all together, whatever ... that'll be great. Otherwise, sorry, purify is Linux only until MinGW can get its god damned shit together and offers threading support. Second, there's no way to regenerate Famicom (NES) manifests, because we discard the iNES header. We are going to need a database for that. So, all I can suggest is that if you use bsnes/higan, keep all your iNES images around to re-import as new releases come out. Third, when you purify game folders, it will back up the ROM and RAM files only. Your save states, cheat codes, debug logs, etc will be wiped out. There's a whole lot of reasons for this, the most pertinent is that it's meant to clean up the folder to a pristine state. It also fixes the game folder name, etc. So ... sorry, but this is how it works. New releases rarely if ever allow old save states to work anyway. Lastly, I am not going to have purify contain infinite backward compatibility for updating manifests. You will want to keep up with purifying the collection, otherwise you'll have to grab older purify copies and convert your way along. Although hopefully the format won't be so volatile and this won't be necessary very often. --- purify/Makefile | 58 +- purify/nall/Makefile | 7 +- purify/nall/base64.hpp | 92 ++- purify/nall/beat/archive.hpp | 84 ++ purify/nall/beat/base.hpp | 92 +++ purify/nall/{bps => beat}/delta.hpp | 4 +- purify/nall/{bps => beat}/linear.hpp | 4 +- purify/nall/{bps => beat}/metadata.hpp | 4 +- purify/nall/beat/multi.hpp | 242 ++++++ purify/nall/{bps => beat}/patch.hpp | 4 +- purify/nall/crc16.hpp | 25 + purify/nall/directory.hpp | 115 ++- purify/nall/emulation/famicom.hpp | 182 ----- purify/nall/emulation/game-boy-advance.hpp | 68 -- purify/nall/emulation/game-boy.hpp | 122 --- purify/nall/emulation/satellaview.hpp | 26 - purify/nall/emulation/sufami-turbo.hpp | 33 - purify/nall/emulation/super-famicom.hpp | 803 -------------------- purify/nall/file.hpp | 21 + purify/nall/image.hpp | 33 + purify/nall/nall.hpp | 2 + purify/nall/property.hpp | 12 +- purify/nall/sort.hpp | 4 +- purify/nall/stream/zip.hpp | 4 +- purify/nall/string.hpp | 8 +- purify/nall/string/base.hpp | 19 +- purify/nall/string/bml.hpp | 151 ---- purify/nall/string/core.hpp | 23 +- purify/nall/string/datetime.hpp | 31 + purify/nall/string/format.hpp | 73 ++ purify/nall/string/markup/bml.hpp | 147 ++++ purify/nall/string/markup/document.hpp | 14 + purify/nall/string/markup/node.hpp | 146 ++++ purify/nall/string/{ => markup}/xml.hpp | 72 +- purify/nall/string/platform.hpp | 16 +- purify/nall/string/trim.hpp | 17 + purify/nall/string/utility.hpp | 53 +- purify/nall/string/wrapper.hpp | 6 + purify/nall/string/xml-legacy.hpp | 265 ------- purify/nall/unzip.hpp | 126 +++ purify/nall/vector.hpp | 10 +- purify/nall/zip.hpp | 179 ++--- purify/obj/.gitignore | 1 + purify/phoenix/core/core.cpp | 16 + purify/phoenix/core/core.hpp | 7 +- purify/phoenix/core/state.hpp | 7 + purify/phoenix/gtk/platform.hpp | 4 + purify/phoenix/gtk/widget/hex-edit.cpp | 4 + purify/phoenix/gtk/widget/list-view.cpp | 4 + purify/phoenix/gtk/widget/text-edit.cpp | 4 + purify/phoenix/gtk/widget/widget.cpp | 12 +- purify/phoenix/gtk/window.cpp | 10 + purify/phoenix/qt/platform.moc | 2 +- purify/phoenix/qt/platform.moc.hpp | 1 + purify/phoenix/qt/widget/widget.cpp | 6 +- purify/phoenix/qt/window.cpp | 9 + purify/phoenix/reference/platform.hpp | 3 + purify/phoenix/reference/widget/widget.cpp | 6 +- purify/phoenix/reference/window.cpp | 6 + purify/phoenix/windows/dialog-window.cpp | 14 +- purify/phoenix/windows/platform.hpp | 5 +- purify/phoenix/windows/widget/button.cpp | 19 +- purify/phoenix/windows/widget/list-view.cpp | 117 ++- purify/phoenix/windows/widget/widget.cpp | 6 +- purify/purify.cpp | 704 +++++------------ purify/resource/applications-system.png | Bin 2544 -> 0 bytes purify/resource/archive.png | Bin 0 -> 1067 bytes purify/resource/drive-harddisk.png | Bin 1155 -> 0 bytes purify/resource/file.png | Bin 0 -> 844 bytes purify/resource/folder.png | Bin 1176 -> 0 bytes purify/resource/game.png | Bin 0 -> 1490 bytes purify/resource/input-gaming.png | Bin 1470 -> 0 bytes purify/resource/resource.bml | 4 + purify/resource/resource.cpp | 343 +++------ purify/resource/resource.hpp | 8 +- purify/resource/resource.xml | 8 - purify/resource/view-refresh.png | Bin 912 -> 0 bytes 77 files changed, 1900 insertions(+), 2827 deletions(-) create mode 100644 purify/nall/beat/archive.hpp create mode 100644 purify/nall/beat/base.hpp rename purify/nall/{bps => beat}/delta.hpp (99%) rename purify/nall/{bps => beat}/linear.hpp (98%) rename purify/nall/{bps => beat}/metadata.hpp (97%) create mode 100644 purify/nall/beat/multi.hpp rename purify/nall/{bps => beat}/patch.hpp (99%) create mode 100644 purify/nall/crc16.hpp delete mode 100644 purify/nall/emulation/famicom.hpp delete mode 100644 purify/nall/emulation/game-boy-advance.hpp delete mode 100644 purify/nall/emulation/game-boy.hpp delete mode 100644 purify/nall/emulation/satellaview.hpp delete mode 100644 purify/nall/emulation/sufami-turbo.hpp delete mode 100644 purify/nall/emulation/super-famicom.hpp delete mode 100644 purify/nall/string/bml.hpp create mode 100644 purify/nall/string/datetime.hpp create mode 100644 purify/nall/string/format.hpp create mode 100644 purify/nall/string/markup/bml.hpp create mode 100644 purify/nall/string/markup/document.hpp create mode 100644 purify/nall/string/markup/node.hpp rename purify/nall/string/{ => markup}/xml.hpp (77%) delete mode 100644 purify/nall/string/xml-legacy.hpp create mode 100644 purify/nall/unzip.hpp create mode 100644 purify/obj/.gitignore delete mode 100644 purify/resource/applications-system.png create mode 100644 purify/resource/archive.png delete mode 100644 purify/resource/drive-harddisk.png create mode 100644 purify/resource/file.png delete mode 100644 purify/resource/folder.png create mode 100644 purify/resource/game.png delete mode 100644 purify/resource/input-gaming.png create mode 100644 purify/resource/resource.bml delete mode 100644 purify/resource/resource.xml delete mode 100644 purify/resource/view-refresh.png 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 565f406dd147d585f47374e20544996b3542dbd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2544 zcmVAH^(GUqo@b?!El-*1o0HEJDptg2<0pmCbqKem#dezS?F+s%WcnB#ryQC1VV*1`CE4JUiWW_dZ+1=Y8{5Aq? z`ogk%9{!ElWPGl!uEyr>AKEsfWUkQH)%HMg(tg?M(UX8V+vifanie=~8_4Qm=x9jfwf?L$S?&i;?Q-)l zj4=BA)4P5}jV2R9!4UrX!mHBI@K~HrH%KfJr4<4F|`Hd!2iKJ1_s5fF<@@J&R(7z0Nq+b9r(>ap%xXC4UHds);(`th0$sU z979!k4n~IDXluJNu0**9Zs0D zSyF5e;xZ^9m8-F#(v*l-x?c0@Jx$rM>|AgcGxz@^!>uPmA ze-xSqlovaoX&ML!s%FjL&wlaMhB@;VZ!RjGJ9kFG+>HFvdE>6`mLRFEe`r`$RcA(e zs+67MB&Egq-1_x(q<_EFE#vuFvjU3 zQ4yjjV_$JmeGo$0m6-{^J8Bm2zkTxAH~?^}s!^-O4ELZ9LR^~cN zfy5Y|K79@xm5`TZht*<0n%x9S2zW-pF$zV|AP5pHR%_G|FjXZZ;0TzitSKo`MOIN( z;)Kzt1EmCRw+~enxtLX+0Yam=c=;j@9DGaE5);T##vXg)&7&J-RjCr=qTOUPYES?1 zaV{R0!E*_*0fEO8!t%PMmSe}>-&3R$! z1fg&kzy95B7>&j+L`iH`Wwn)nGy&6epnB~NlU>oyZr-?NZca`<1_u4t$B^R~Zd*`< zhL>KCb@udLRYmy;Ns~Hx!q237?y2(P;<877zSS5G2{0IV$dZDtt}$d~+t7aX3f_MA zyUtE)P46oVn)eF*$qFY2kknUc->~`Mc2~R2^qo9N!7>r@fJ$Ld(qsbWv2G;@P zCk{vYrur32X56=BHQ&?iL1wxYlv4P{BM{>Xii$FE=8ibetMb&ia0gLm0BCvGj_EH_sE&t)YtLb3B zF9FPf=UCE1N5^qc6%}zo0wt3Nmnv!CIR;Ts;7oU7^ZI(#XfPZ`lC$Rg{g+@>?t54A~CaQmmyb>+U2zZx~Kz3bL}zIDX=!CWsMK z%+5h>UMlQ1BXoSC5{$uOHXtk8hD?VQBOVWSKE6x-+rEFunYm_|EP5!ih7D_0=w(&i z^PLqaD4u5!C1w4cHS<|lrsKlb7jf{=JK~%FIxdb55AF8({QghQG?gq|SjkaF@XVflf+)p)hj1EiGT| zZ)rMyL=B_C*suvkP1NQ>%8FZ1G*Bxa^l`~OZH`?0*C>KPPs+^ zgcBZ&MZ+!bp;0X}+X_$>u7N?$?;B_W5C9OGvNtj_3;>^S4gj~HX5(73 z#l9Z^C}lFBMo%>!sK9?qe9fg~px>9kw;7!huTAmqW&Z&pdN>M(u2oY20000_(TaVgb657zbqVb6YQ9Yp{)uHPI$P&0x6?Cz35>9(%)GIKJ0~gQwNc=SFiy%eIa0j`vjVrz@2MUC9L` zlgIB}-QBkMfz^8aLLaBU`B(~tE7&N++6X0}L{JK}5|k3O0yV8KE>Gd|PjXYoi<1B2 z6s?KS8y?HAdip^#@af57-&G!XzpG(hGL<~JY~`l*_9Y$Kjs1!jG*FBton)N_Ye5-A zd$FikD7B&uh%OsWHqWP!8>g>#Euu8PT^O5i)^6(DlRa1_(6cc;H)u>9Yg@2%p*V6w z1AXa7o_UStxwn!VIX8JRhas`u}9i&)h|J z`KdRmMbb1E6Hhm12HH(3p8@`Kql%qrYQ|6WC%77hWlg4X5eF z+;zwDf1agr&LZw!xuHe09=1v^7C7n@JE!~w2e!{S{P^bG52rH=ubChI3%=dIJvVgz z$Bmu4Dq;X&Uw6|(V(n3-bx}llqBPD%HJElY&WOae94>F8C;)hVPgbT7a3CLcH@5&3 zZFH$)?W+tN>#1(BM%;Y=I==sW2cFVx0G{~8XLk)|0Zf@CIChU-2I$?ACXq^$Or`5W zAV@X>u%TqX66UJaB`9t1lMQttpp7@<+>8XY@$eJ%@qjjd?F5YW2om-2fcf_xxRC@r zkDyL5VEpnG_(0)W1OlKH;)-Yd6u4S)K^2HXsdCZ11sDS^0Z}yq%|ON}Z*aK(%-qbv zj?!>Nl5v#@$CY(gbxqt3_4i@L8v^E+_KZp-rV!92-TUJ7#9P>%uM1&IjpwZjhZ*_g z#Kb#50`RDKhfL4;tw5&K2E=z(4-^0eWQk9H9w=5L0FVS)N}JCZq%|TWzK>C093~&P l*QaAzaJ#))HKre7BT#6JS6eMHWyX*b%%=D6-uD^gv+{!DxnvWg5 z&oj?E^UlIY`@fr*G`YLGd)659d!bNRl~OwYHK;Mh_LWj9P18TZF#O@sqet=RT6a(@CrIJgJw1166*CU39T`*v+@ZB|N29LE%kMF4~lxUM_oj^kKz zLI|Xkb`GFaDiOyqwOWm(rKQ%+&d!xlyh-`JyRor>@B5@_N}8q?FbiyqL2HfH8ep^8#lf@bLPyIMx#Lx1Vg|Xpi&B@6k2PmO1A6K zlw^wZ`+Zui7L`h6duwa!v#A7x5chU=c0?G4R`xt-=Ej&I$$=q7@}3b()0DNfwV6Vp z@Z&@RtE;Q75aRao@-n{f%Sb8j4-wef+Nz&Ab!wy8Y!UnK($)k+}+)6SOTu=-rnBccEd0nDmXz9 z5XUhmPoCt&i4(Z4Yt_lX^KwfmDHe-VDitb~3jKcHDw=&)N^$YxMOSNm*Afsy++1E> zCJaOB^*WyC*$}dCselwWDu{@B0KnfYzGRr%$7` zKAR^{Op;`*+KsmPJWqR|@@q%GWhs00>Xq%=$7nDZ@a);Mu>g~b%cQyYT?SfNO|#jg)9KJ`HhJ^r4RdpIbUGd8 z=jUxZie(Ap<&siP%=0w)Cw-fi4!~B_sR|T}#j!&;jmWeDG;Mwq%btOZl`)3J#YL1- zxUNf*Bvx{z6ha6)ejhTUb3zCPg8_iH1dK6%zj*Ot{@l59W94;H<(uSLftFs0Pt(b3Vb zz)bd6Tmn7?J^`x03{d#MxCtPE2z_zL)zfjBfvi<^wB;V{{WR> Vfeoa5yFLH_002ovPDHLkV1oKSJahm6 diff --git a/purify/resource/file.png b/purify/resource/file.png new file mode 100644 index 0000000000000000000000000000000000000000..5b7e64910e6b549d37a0f0c0063f8b73aa15fe5a GIT binary patch literal 844 zcmV-S1GD^zP)iTsi)!sWzk*lz?LPeE-Uo}T%@-s zQdt!whEz-vJ1kmTj`L?S6+1r3isN|f_r3T0B?JF^h;-oU>gq$5Wj8>jltN00a}H~5 zXkFKZYusNN19n~4eZ0B3`5Nz+jSvZOeQ|M7J@>AkgHN z>rh<$^T)@><@90FtOq5CT`#4isw%Q9LkPji$w}y!WjUBX#lnjvh)t-q##$RHVb^tG z-qLYg&4AZ3&E97aOX8dh40u33%PA&~k|5Q-?|ZDZ;YH+WkHOY;jdN~RhNA$)3kxC0 zvW%*#26OAW4sv);D5XZ59L+#VwFe-j9QeZ+L!RdWtWs)qS)K)up6ApDaVVt*40x&1 zs<;%Zj-f9$vHt$-d(GoIvD@ud1DN_E8c-#KV6)j!mgUG`o6QET^IgQ>!(=g`yQ?JBsgD3{P*Qjq^F>gQvT}u{?*yp zSs0wtxhRUo#nbYe=lKqJ2mA!~DS!%ibANyT`TYF+6HqR$zB2MWr)e655P$CO?!Ex; z0R?;qT%1}CyaiqZTfjU|{1@?ur(|p32k;H}4HJJ(pEoX^0j(=%hr1yxd$0C6kuV7bR+PdY00{*lQ*M=l z8i@j0IzB)oK0r!@M1^RfLrAAU7s-kOiqb{G#>6NV#`n5+-j|u38H#(!I(Lo}a#ASx zOOJMTcK3OH^E@-NyCMujUZ>=1js9N%@BwbzxZx|Mem2IuchLhvh}>HH`NM|~zg!tW znx z&Mh?nAq1P7oB6@P!AHRDl>lh1(=5xxrAr$aW3bkq30%yC5Q1v8T0cBI{9=21`_`;~ znVfUlTKn$b$Z5|!&XdlVufDtg z{_MP%0o`cSt6FQ+AChLZ%>LhFqBx(7Z0TsvBM}Qt0W>N;D$@u-XLfnM6&qu8zdxYc z?Gk5}dbNZ#lN;pJ!Lx=t&)ie96SLNlWQH`$!HM|>RxCkQsRXoIEixmh1tqLgcycwb zi*cG$Z~BaeIo7%-3jleZt0)@L?+?)C4XS~Ua}H0Qo97pkgHfu{xh3!=ajLB``OyM^ zF(xaQOH`X1tkudmKnk(E_%A5q9HUf|TZiWfg0jbHk|`m?$pV12HXDt`91j%h^-Jj7 zzS8J3ZHY4jPT)`dls(DlRON!Fldzb9)c1Y7AfQQ^OxX*AOy-!wy2(Ca?8H=Vm#s*g zgTW}ZxpRvE*4kt|9@824R9a=CM3d?1O9;q~MH`3CExDO)%P*OgL>Y$T#1qn6T7pyv zfeadyNs60>X%$s}D(4*iBtcj^SO93Z+hS*D$3(GWaO(K; zWQgp(;tse($HzmPB*O>0?RG5;!*~t=cu^Gn^vj|6Os(Vpw*Sf>yJzIU0$80M96$Ks z>*6&r`!#E~+s$BY?af-y+H^Q6g8x)+GM1_(Nr{gdepj+bg-;!Hp8!ZtGP zkKPl6+^slId#p6YR?a5#8>U?U>+ktE^J21=v zZ_|sGk^UCh3$LI=qm)94CVSx({Do<9>GgpVd-?8s>dwo7Y%e>sFyL=|!IqXTiW3(= zYXrFc;Q`#6qo6^HBAMQdIX+C)#p6X0wk3bgYqQm!(}6%-2C@%qw7rH_N)s0mKtvD` z%tT$_f}v0Xxy!HPHnfpXud}u0@LW#J>Odr(e$@6;%Rsw0jcY-mG=7f!^^^Fy&!=9{ zI!FkK)^3dP5vne|Ija*D9mw{uU5gCl((7zL^;Pnt|3tg_sB=Fv28D^B`zQ=VDU=BL zD@XAfmXU9N#1^FDPTUb7+rxHWE2!VhIA_@^fDPu;csSFgZ9>ZJM zLbk2bsRZ#QfU1YzVKe znQZGCM=3cRhll|5ODNz_vA)q}$=4~2oepf5sri~U&4@_oLI5JA$*I{#Sz%aJSoIw! z!wQ`E>>bntk5Q;u?1)S)WCS2D;p4N-p7^9F*WSTtS`!r%Q5eZZC_9OWpmXEo-rYy; z-G2Pc^+?>X643~Znx!mQz8$l+1u;w#E!{Y|49@NU6t$k(g=r&5{))c|`umCDGez^p zb?nA(Aj(9P!d|p`>IB@pjL3A$&FUr+4?TmGT8>%Wh?QE72qZeTP}}uW5>z24`is8j zKT-Wljzo>$=@evo*@no0V*6U#Pc`A+xI|+0Zj|AG2*vY%M(4(ZaYU_HIIDJ}CG1IS zG@ud<=#e4x@&1zb+{feM6#=F}q&sbE#a2|J0ST@^C{E&j{05*gYCG`8-@{nag{tic zg*9l9s(SS35dN_{i_Z#BhR~>ks{46MEnS1oe-U;q_%|;?;U;F|TC~iiKwpRe%jt0nV@AV)V&XTu5cE zZSlz%W^*U_Zg4b_NW3)I=f3}``}BkT90kl!qkP=I1X2mW0-Txg+cRAGb{m(QbHm?} zMIDB!UxxTDSV{bmAxeXN?$KfQN8K-TEGAwCPy~*LTUr^w2m$O^L=Z2Zpl)Mt?Ro5xYIjv@p6rsv!2)UUw6hoYYF#9e*gdg07*qoM6N<$f+&dEi2wiq literal 0 HcmV?d00001 diff --git a/purify/resource/input-gaming.png b/purify/resource/input-gaming.png deleted file mode 100644 index 26e2a98273c504480f20a50ea2d0d27ac27e5a5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1470 zcmV;v1ws0WP)5IX63n~~2B{@qEeJFXcu-!a0&_W+du|vVClQ>o!sg5zSYR8f8 zw7a*vw>vX^kZ;lHX#YOQ|`bOWvs;$0!c zo0UrCqfWR)r^$~UJNCR+=bu$QT;n8;?YQ#JJ_I)V{6E z96{tGl|~4VN)tws*v!#zJ%Gz-MD%`(kM9HsAHcOi=#)6+MSTKodrjZ9C=*`@jCF$ zjXD$x1x}nefl`Vnits!S*L9InQYw|GR4P=f)s4D-sI`9i{Q2|$dZ2)Ext!KohZ7SM z^!N9-yj}Ae1OcAs5k(Qc@8kPEQc7&wCY4HI7zSInZbfU|Qm$RQ#_7|iKRtKu+|xi} zpq5pjwLY?U?_RcV-_F9q0$OW?5C|d2X0xQzX&lF)TCHN+Hhq14n5Ib-Mcli0kGZ)y z3WWl>Tn^WDiJ~as7#bR4aBy&FbaeE&3l}c@wVAWU14^mmBO@cMtgIxa(prgCiOUu(ORRFO0ed=Qi>o5k}1pQ^GTw5z0S^^JK3^j z%i(gl{A8N|!!X9j#>PawUQd#&U+Vz9y}fN%GcuVB!^6WHwkf5EU0q%D^z^idb{wZ=L@dif zYt6}%C#%5Y)6>(w1)#P5#leFIdxa2$VTgL~6nFmpH?o#N;o0YKe)>j>4*+c2rn|el zMN~79QVP%W2*a?YEs7#++h)(6J-wGMU3w9iGVV9Nynp|G9LHI8?c#?F^yPV?(8coX zMQ+`?h3mT1YBlQhI#CoMrEEzU$1!0T;y4aVOG|`d*fxz?Ykc2l-@bhSzXz~RojPUA z&d%;F7K_}zeH*Pcwdeo9KhD0z&A9~zfA9i_ip9i$aUA3Oe%o6==-Rw*)}tt5$BrFj zGMU`r!-wu3L!R4)Jf9y^`?O9*ROY5)6>%{V`F3g zy>a74Y4`5kZ97}DXsr|Huf>&8NGaQ}wfOq>#l=M$jmF}YD_24b&~Y69YGPvIcS4A# z_!>e=d2@Mr`3=BXdmNjM 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 3fd71d6e5929ba0c40db1960e36e9acba9d7e525..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 912 zcmV;B18@9^P)oWowDaEkzU!j%l38$)o7}}cCnx7z zVrKYAWwOsS=-Lqw-t?qu)r6QQ!notg696vQmg&~r9t3cLe1UW(yG7H)Ku^~yeO+g> z(bg0Jm{BNJFfw+(d}sQhCl&9uE%R)8S2n|p?*PPznUTt5*BiPR+1l3~ipPS`iO?Jk zARN#U4H*a;0yD)$9M29{3dM>Y4K?&WE>{g^G!Zl7)jelV^{i|AG#D_%trsJC8h7YDw+>Nu`!(E)&&KfE(t5U!_KF)uRXGsl%@ z#;0eK6ZhtiUhin`+P8I6C=m!d1ufk}o{_gOutKfI-_b^R{JP z`QzJ2^LHMWS&9G(o-t)@yh1Uq9cxfG6X$C)H~ghbOC7?WrZ-yyL0>0QOs`0x)U>uAAQg z>;&LGL0Gpf^PVr@oj>}%1`wDT7wv!4sq=s3q*Oh&WftpMhqGg=O68@F-$%x80Ep=T zKm-sG?*3PXAs8nI|0Dok)RR-0Y?z4d_HJBzCO6*s&6^5?o^0No>h2ra)My_p{u2^&LcltM%9%NL@3OcL;J5G-fbF(raxE|ez