| @@ -14,6 +14,7 @@ | |||||
| /licenses | /licenses | ||||
| .DS_Store | .DS_Store | ||||
| /autosave.vcv | /autosave.vcv | ||||
| /autosave | |||||
| /settings.json | /settings.json | ||||
| /screenshots | /screenshots | ||||
| /Fundamental.zip | /Fundamental.zip | ||||
| @@ -33,7 +33,7 @@ build/dep/osdialog/osdialog_gtk3.c.o: FLAGS += $(shell pkg-config --cflags gtk+- | |||||
| FLAGS += -fno-gnu-unique | FLAGS += -fno-gnu-unique | ||||
| LDFLAGS += -Wl,--whole-archive | LDFLAGS += -Wl,--whole-archive | ||||
| LDFLAGS += dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libarchive.a dep/lib/libzstd.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a -lstdc++fs | |||||
| LDFLAGS += dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libarchive.a dep/lib/libzstd.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a -lstdc++fs | |||||
| LDFLAGS += -Wl,--no-whole-archive | LDFLAGS += -Wl,--no-whole-archive | ||||
| LDFLAGS += -lpthread -lGL -ldl -lX11 -lasound -ljack | LDFLAGS += -lpthread -lGL -ldl -lX11 -lasound -ljack | ||||
| LDFLAGS += $(shell pkg-config --libs gtk+-3.0) | LDFLAGS += $(shell pkg-config --libs gtk+-3.0) | ||||
| @@ -49,7 +49,7 @@ ifdef ARCH_MAC | |||||
| LDFLAGS += -lpthread -ldl | LDFLAGS += -lpthread -ldl | ||||
| LDFLAGS += -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -framework CoreAudio -framework CoreMIDI | LDFLAGS += -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -framework CoreAudio -framework CoreMIDI | ||||
| LDFLAGS += -Wl,-all_load | LDFLAGS += -Wl,-all_load | ||||
| LDFLAGS += dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libarchive.a dep/lib/libzstd.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a | |||||
| LDFLAGS += dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libarchive.a dep/lib/libzstd.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a | |||||
| STANDALONE_TARGET := Rack | STANDALONE_TARGET := Rack | ||||
| STANDALONE_LDFLAGS += -stdlib=libc++ | STANDALONE_LDFLAGS += -stdlib=libc++ | ||||
| @@ -65,7 +65,7 @@ ifdef ARCH_WIN | |||||
| LDFLAGS += -Wl,--export-all-symbols | LDFLAGS += -Wl,--export-all-symbols | ||||
| LDFLAGS += -Wl,--out-implib,$(TARGET).a | LDFLAGS += -Wl,--out-implib,$(TARGET).a | ||||
| LDFLAGS += -Wl,-Bstatic -Wl,--whole-archive | LDFLAGS += -Wl,-Bstatic -Wl,--whole-archive | ||||
| LDFLAGS += dep/lib/libglew32.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/libarchive.a dep/lib/libzstd.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/librtaudio.a dep/lib/librtmidi.a -lstdc++fs | |||||
| LDFLAGS += dep/lib/libglew32.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/libarchive.a dep/lib/libzstd.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/librtaudio.a dep/lib/librtmidi.a -lstdc++fs | |||||
| LDFLAGS += -Wl,-Bdynamic -Wl,--no-whole-archive | LDFLAGS += -Wl,-Bdynamic -Wl,--no-whole-archive | ||||
| LDFLAGS += -lpthread -lopengl32 -lgdi32 -lws2_32 -lcomdlg32 -lole32 -ldsound -lwinmm -lksuser -lshlwapi -lmfplat -lmfuuid -lwmcodecdspuuid -ldbghelp | LDFLAGS += -lpthread -lopengl32 -lgdi32 -lws2_32 -lcomdlg32 -lole32 -ldsound -lwinmm -lksuser -lshlwapi -lmfplat -lmfuuid -lwmcodecdspuuid -ldbghelp | ||||
| @@ -14,8 +14,6 @@ ifdef ARCH_LIN | |||||
| libcurl = lib/libcurl.a | libcurl = lib/libcurl.a | ||||
| zstd = lib/libzstd.a | zstd = lib/libzstd.a | ||||
| libarchive = lib/libarchive.a | libarchive = lib/libarchive.a | ||||
| libzip = lib/libzip.a | |||||
| zlib = lib/libz.a | |||||
| libspeexdsp = lib/libspeexdsp.a | libspeexdsp = lib/libspeexdsp.a | ||||
| libsamplerate = lib/libsamplerate.a | libsamplerate = lib/libsamplerate.a | ||||
| rtmidi = lib/librtmidi.a | rtmidi = lib/librtmidi.a | ||||
| @@ -30,8 +28,6 @@ ifdef ARCH_MAC | |||||
| libcurl = lib/libcurl.a | libcurl = lib/libcurl.a | ||||
| zstd = lib/libzstd.a | zstd = lib/libzstd.a | ||||
| libarchive = lib/libarchive.a | libarchive = lib/libarchive.a | ||||
| libzip = lib/libzip.a | |||||
| zlib = lib/libz.a | |||||
| libspeexdsp = lib/libspeexdsp.a | libspeexdsp = lib/libspeexdsp.a | ||||
| libsamplerate = lib/libsamplerate.a | libsamplerate = lib/libsamplerate.a | ||||
| rtmidi = lib/librtmidi.a | rtmidi = lib/librtmidi.a | ||||
| @@ -46,8 +42,6 @@ ifdef ARCH_WIN | |||||
| libcurl = lib/libcurl.a | libcurl = lib/libcurl.a | ||||
| zstd = lib/libzstd.a | zstd = lib/libzstd.a | ||||
| libarchive = lib/libarchive.a | libarchive = lib/libarchive.a | ||||
| libzip = lib/libzip.a | |||||
| zlib = lib/libz.a | |||||
| libspeexdsp = lib/libspeexdsp.a | libspeexdsp = lib/libspeexdsp.a | ||||
| libsamplerate = lib/libsamplerate.a | libsamplerate = lib/libsamplerate.a | ||||
| rtmidi = lib/librtmidi.a | rtmidi = lib/librtmidi.a | ||||
| @@ -64,9 +58,7 @@ DEPS += $(glew) | |||||
| DEPS += $(glfw) | DEPS += $(glfw) | ||||
| DEPS += $(jansson) | DEPS += $(jansson) | ||||
| DEPS += $(libcurl) | DEPS += $(libcurl) | ||||
| DEPS += $(zstd) | |||||
| DEPS += $(libarchive) | DEPS += $(libarchive) | ||||
| DEPS += $(libzip) | |||||
| DEPS += $(libspeexdsp) | DEPS += $(libspeexdsp) | ||||
| DEPS += $(libsamplerate) | DEPS += $(libsamplerate) | ||||
| DEPS += $(rtmidi) | DEPS += $(rtmidi) | ||||
| @@ -165,35 +157,6 @@ $(libarchive): | $(zstd) libarchive-3.4.3 | |||||
| $(MAKE) -C libarchive-3.4.3 | $(MAKE) -C libarchive-3.4.3 | ||||
| $(MAKE) -C libarchive-3.4.3 install | $(MAKE) -C libarchive-3.4.3 install | ||||
| libzip-1.5.2: | |||||
| $(WGET) "https://libzip.org/download/libzip-1.5.2.tar.gz" | |||||
| $(SHA256) libzip-1.5.2.tar.gz be694a4abb2ffe5ec02074146757c8b56084dbcebf329123c84b205417435e15 | |||||
| $(UNTAR) libzip-1.5.2.tar.gz | |||||
| rm libzip-1.5.2.tar.gz | |||||
| $(libzip): | $(zlib) libzip-1.5.2 | |||||
| cd libzip-1.5.2 && mkdir -p build | |||||
| cd libzip-1.5.2/build && $(CMAKE) .. -DCMAKE_FIND_ROOT_PATH="$(DEP_PATH)" -DENABLE_COMMONCRYPTO=OFF -DENABLE_GNUTLS=OFF -DENABLE_MBEDTLS=OFF -DENABLE_OPENSSL=OFF -DENABLE_WINDOWS_CRYPTO=OFF -DENABLE_BZIP2=OFF -DBUILD_TOOLS=OFF -DBUILD_REGRESS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_DOC=OFF -DBUILD_SHARED_LIBS=OFF | |||||
| $(MAKE) -C libzip-1.5.2/build | |||||
| $(MAKE) -C libzip-1.5.2/build install | |||||
| zlib-1.2.11: | |||||
| $(WGET) "https://www.zlib.net/zlib-1.2.11.tar.gz" | |||||
| $(SHA256) zlib-1.2.11.tar.gz c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 | |||||
| $(UNTAR) zlib-1.2.11.tar.gz | |||||
| rm zlib-1.2.11.tar.gz | |||||
| $(zlib): | zlib-1.2.11 | |||||
| ifdef ARCH_WIN | |||||
| $(MAKE) -C zlib-1.2.11 -f win32/Makefile.gcc | |||||
| $(MAKE) -C zlib-1.2.11 -f win32/Makefile.gcc BINARY_PATH="$(DEP_PATH)/bin" INCLUDE_PATH="$(DEP_PATH)/include" LIBRARY_PATH="$(DEP_PATH)/lib" install | |||||
| else | |||||
| # Don't use $(CONFIGURE) because this is a handwritten configure script | |||||
| cd zlib-1.2.11 && ./configure --prefix="$(DEP_PATH)" | |||||
| $(MAKE) -C zlib-1.2.11 | |||||
| $(MAKE) -C zlib-1.2.11 install | |||||
| endif | |||||
| speexdsp-SpeexDSP-1.2rc3: | speexdsp-SpeexDSP-1.2rc3: | ||||
| $(WGET) "https://vcvrack.com/downloads/dep/speexdsp-SpeexDSP-1.2rc3.tgz" | $(WGET) "https://vcvrack.com/downloads/dep/speexdsp-SpeexDSP-1.2rc3.tgz" | ||||
| $(SHA256) speexdsp-SpeexDSP-1.2rc3.tgz c8dded1454747f65956f981c95e7f89a06abdaa2a53e8aeaa66bab2a3d59cebd | $(SHA256) speexdsp-SpeexDSP-1.2rc3.tgz c8dded1454747f65956f981c95e7f89a06abdaa2a53e8aeaa66bab2a3d59cebd | ||||
| @@ -283,7 +246,7 @@ $(pffft): | pffft | |||||
| # Helpers | # Helpers | ||||
| src: glew-2.1.0 glfw jansson-2.12 speexdsp-SpeexDSP-1.2rc3 openssl-1.1.1d curl-7.66.0 zstd-1.4.5 libarchive-3.4.3 libzip-1.5.2 zlib-1.2.11 rtmidi-4.0.0 rtaudio nanovg nanosvg oui-blendish osdialog pffft | |||||
| src: glew-2.1.0 glfw jansson-2.12 speexdsp-SpeexDSP-1.2rc3 openssl-1.1.1d curl-7.66.0 zstd-1.4.5 libarchive-3.4.3 rtmidi-4.0.0 rtaudio nanovg nanosvg oui-blendish osdialog pffft | |||||
| clean: | clean: | ||||
| git clean -fdx | git clean -fdx | ||||
| @@ -69,18 +69,6 @@ Throws std::runtime_error if string is invalid. | |||||
| */ | */ | ||||
| std::vector<uint8_t> fromBase64(const std::string& str); | std::vector<uint8_t> fromBase64(const std::string& str); | ||||
| /** Compress bytes with zlib. | |||||
| */ | |||||
| std::vector<uint8_t> compress(const uint8_t* data, size_t dataLen); | |||||
| std::vector<uint8_t> compress(const std::vector<uint8_t>& data); | |||||
| /** Uncompress bytes with zlib. | |||||
| Before calling this function, set `dataLen` to the capacity of `data`. | |||||
| After returning, `dataLen` is set to the actual number of bytes written. | |||||
| */ | |||||
| void uncompress(const uint8_t* compressed, size_t compressedLen, uint8_t* data, size_t* dataLen); | |||||
| std::vector<uint8_t> uncompress(const std::vector<uint8_t>& compressed); | |||||
| struct CaseInsensitiveCompare { | struct CaseInsensitiveCompare { | ||||
| bool operator()(const std::string& a, const std::string& b) const; | bool operator()(const std::string& a, const std::string& b) const; | ||||
| }; | }; | ||||
| @@ -70,7 +70,7 @@ The launched process will continue running if the current process is closed. | |||||
| void runProcessDetached(const std::string& path); | void runProcessDetached(const std::string& path); | ||||
| std::string getOperatingSystemInfo(); | std::string getOperatingSystemInfo(); | ||||
| /** Compresses the contents of a folder (recursively) to an archive. | /** Compresses the contents of a folder (recursively) to an archive. | ||||
| Currently supports the "ustar zstd" format (.tar.zstd) | |||||
| Currently supports the "ustar zstd" format (.tar.zst) | |||||
| An equivalent shell command is | An equivalent shell command is | ||||
| tar -cf archivePath --zstd -C folderPath . | tar -cf archivePath --zstd -C folderPath . | ||||
| @@ -13,6 +13,8 @@ | |||||
| #include <history.hpp> | #include <history.hpp> | ||||
| #include <settings.hpp> | #include <settings.hpp> | ||||
| #include <fstream> | |||||
| namespace rack { | namespace rack { | ||||
| @@ -23,9 +25,11 @@ static const char PATCH_FILTERS[] = "VCV Rack patch (.vcv):vcv"; | |||||
| PatchManager::PatchManager() { | PatchManager::PatchManager() { | ||||
| } | } | ||||
| PatchManager::~PatchManager() { | PatchManager::~PatchManager() { | ||||
| } | } | ||||
| void PatchManager::reset() { | void PatchManager::reset() { | ||||
| if (APP->history) { | if (APP->history) { | ||||
| APP->history->clear(); | APP->history->clear(); | ||||
| @@ -35,6 +39,7 @@ void PatchManager::reset() { | |||||
| } | } | ||||
| } | } | ||||
| void PatchManager::clear() { | void PatchManager::clear() { | ||||
| if (APP->scene) { | if (APP->scene) { | ||||
| APP->scene->rack->clear(); | APP->scene->rack->clear(); | ||||
| @@ -43,6 +48,7 @@ void PatchManager::clear() { | |||||
| reset(); | reset(); | ||||
| } | } | ||||
| static bool promptClear(std::string text) { | static bool promptClear(std::string text) { | ||||
| if (APP->history->isSaved()) | if (APP->history->isSaved()) | ||||
| return true; | return true; | ||||
| @@ -51,6 +57,7 @@ static bool promptClear(std::string text) { | |||||
| return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str()); | return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str()); | ||||
| } | } | ||||
| void PatchManager::save(std::string path) { | void PatchManager::save(std::string path) { | ||||
| INFO("Saving patch %s", path.c_str()); | INFO("Saving patch %s", path.c_str()); | ||||
| saveAutosave(); | saveAutosave(); | ||||
| @@ -58,6 +65,7 @@ void PatchManager::save(std::string path) { | |||||
| system::archiveFolder(filesystem::u8path(path), asset::autosavePath); | system::archiveFolder(filesystem::u8path(path), asset::autosavePath); | ||||
| } | } | ||||
| void PatchManager::saveDialog() { | void PatchManager::saveDialog() { | ||||
| if (path == "") { | if (path == "") { | ||||
| saveAsDialog(); | saveAsDialog(); | ||||
| @@ -75,6 +83,7 @@ void PatchManager::saveDialog() { | |||||
| APP->history->setSaved(); | APP->history->setSaved(); | ||||
| } | } | ||||
| void PatchManager::saveAsDialog() { | void PatchManager::saveAsDialog() { | ||||
| std::string dir; | std::string dir; | ||||
| std::string filename; | std::string filename; | ||||
| @@ -120,6 +129,7 @@ void PatchManager::saveAsDialog() { | |||||
| pushRecentPath(path); | pushRecentPath(path); | ||||
| } | } | ||||
| void PatchManager::saveTemplateDialog() { | void PatchManager::saveTemplateDialog() { | ||||
| // Even if <user>/template.vcv doesn't exist, this message is still valid because it overrides the <system>/template.vcv patch. | // Even if <user>/template.vcv doesn't exist, this message is still valid because it overrides the <system>/template.vcv patch. | ||||
| if (!osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Overwrite template patch?")) | if (!osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Overwrite template patch?")) | ||||
| @@ -134,6 +144,7 @@ void PatchManager::saveTemplateDialog() { | |||||
| } | } | ||||
| } | } | ||||
| void PatchManager::saveAutosave() { | void PatchManager::saveAutosave() { | ||||
| INFO("Saving autosave"); | INFO("Saving autosave"); | ||||
| json_t* rootJ = toJson(); | json_t* rootJ = toJson(); | ||||
| @@ -158,16 +169,40 @@ void PatchManager::saveAutosave() { | |||||
| system::moveFile(tmpPath, patchPath); | system::moveFile(tmpPath, patchPath); | ||||
| } | } | ||||
| static bool isPatchLegacyPre2(std::string path) { | |||||
| FILE* f = std::fopen(path.c_str(), "rb"); | |||||
| if (!f) | |||||
| return false; | |||||
| DEFER({std::fclose(f);}); | |||||
| // Read first byte and check if it's a "{" character. | |||||
| // TODO Is it possible for .tar.zst files to start with the same character? | |||||
| char buf[1] = {}; | |||||
| std::fread(buf, 1, sizeof(buf), f); | |||||
| return std::memcmp(buf, "{", 1) == 0; | |||||
| } | |||||
| void PatchManager::load(std::string path) { | void PatchManager::load(std::string path) { | ||||
| INFO("Loading patch %s", path.c_str()); | INFO("Loading patch %s", path.c_str()); | ||||
| filesystem::remove_all(asset::autosavePath); | filesystem::remove_all(asset::autosavePath); | ||||
| filesystem::create_directories(asset::autosavePath); | filesystem::create_directories(asset::autosavePath); | ||||
| system::unarchiveToFolder(filesystem::u8path(path), asset::autosavePath); | |||||
| if (isPatchLegacyPre2(path)) { | |||||
| // Move the .vcv file directly to "patch.json". | |||||
| filesystem::path autosavePath = filesystem::u8path(asset::autosavePath); | |||||
| filesystem::copy(filesystem::u8path(path), autosavePath / "patch.json"); | |||||
| } | |||||
| else { | |||||
| // Extract the .vcv file as a .tar.zst archive. | |||||
| system::unarchiveToFolder(filesystem::u8path(path), asset::autosavePath); | |||||
| } | |||||
| loadAutosave(); | loadAutosave(); | ||||
| } | } | ||||
| void PatchManager::loadTemplate() { | void PatchManager::loadTemplate() { | ||||
| this->path = ""; | this->path = ""; | ||||
| APP->history->setSaved(); | APP->history->setSaved(); | ||||
| @@ -191,6 +226,7 @@ void PatchManager::loadTemplate() { | |||||
| clear(); | clear(); | ||||
| } | } | ||||
| void PatchManager::loadTemplateDialog() { | void PatchManager::loadTemplateDialog() { | ||||
| if (!promptClear("The current patch is unsaved. Clear it and start a new patch?")) { | if (!promptClear("The current patch is unsaved. Clear it and start a new patch?")) { | ||||
| return; | return; | ||||
| @@ -198,6 +234,7 @@ void PatchManager::loadTemplateDialog() { | |||||
| loadTemplate(); | loadTemplate(); | ||||
| } | } | ||||
| void PatchManager::loadAutosave() { | void PatchManager::loadAutosave() { | ||||
| INFO("Loading autosave"); | INFO("Loading autosave"); | ||||
| std::string patchPath = asset::autosavePath + "/patch.json"; | std::string patchPath = asset::autosavePath + "/patch.json"; | ||||
| @@ -225,6 +262,7 @@ void PatchManager::loadAutosave() { | |||||
| fromJson(rootJ); | fromJson(rootJ); | ||||
| } | } | ||||
| void PatchManager::loadAction(std::string path) { | void PatchManager::loadAction(std::string path) { | ||||
| try { | try { | ||||
| load(path); | load(path); | ||||
| @@ -239,6 +277,7 @@ void PatchManager::loadAction(std::string path) { | |||||
| pushRecentPath(path); | pushRecentPath(path); | ||||
| } | } | ||||
| void PatchManager::loadDialog() { | void PatchManager::loadDialog() { | ||||
| if (!promptClear("The current patch is unsaved. Clear it and open a new patch?")) | if (!promptClear("The current patch is unsaved. Clear it and open a new patch?")) | ||||
| return; | return; | ||||
| @@ -268,6 +307,7 @@ void PatchManager::loadDialog() { | |||||
| loadAction(path); | loadAction(path); | ||||
| } | } | ||||
| void PatchManager::loadPathDialog(std::string path) { | void PatchManager::loadPathDialog(std::string path) { | ||||
| if (!promptClear("The current patch is unsaved. Clear it and open the new patch?")) | if (!promptClear("The current patch is unsaved. Clear it and open the new patch?")) | ||||
| return; | return; | ||||
| @@ -275,6 +315,7 @@ void PatchManager::loadPathDialog(std::string path) { | |||||
| loadAction(path); | loadAction(path); | ||||
| } | } | ||||
| void PatchManager::revertDialog() { | void PatchManager::revertDialog() { | ||||
| if (path == "") | if (path == "") | ||||
| return; | return; | ||||
| @@ -292,6 +333,7 @@ void PatchManager::revertDialog() { | |||||
| APP->history->setSaved(); | APP->history->setSaved(); | ||||
| } | } | ||||
| void PatchManager::pushRecentPath(std::string path) { | void PatchManager::pushRecentPath(std::string path) { | ||||
| auto& recent = settings::recentPatchPaths; | auto& recent = settings::recentPatchPaths; | ||||
| // Remove path from recent patches (if exists) | // Remove path from recent patches (if exists) | ||||
| @@ -302,10 +344,12 @@ void PatchManager::pushRecentPath(std::string path) { | |||||
| recent.resize(std::min((int) recent.size(), 10)); | recent.resize(std::min((int) recent.size(), 10)); | ||||
| } | } | ||||
| void PatchManager::disconnectDialog() { | void PatchManager::disconnectDialog() { | ||||
| APP->scene->rack->clearCablesAction(); | APP->scene->rack->clearCablesAction(); | ||||
| } | } | ||||
| json_t* PatchManager::toJson() { | json_t* PatchManager::toJson() { | ||||
| // root | // root | ||||
| json_t* rootJ = json_object(); | json_t* rootJ = json_object(); | ||||
| @@ -326,6 +370,7 @@ json_t* PatchManager::toJson() { | |||||
| return rootJ; | return rootJ; | ||||
| } | } | ||||
| void PatchManager::fromJson(json_t* rootJ) { | void PatchManager::fromJson(json_t* rootJ) { | ||||
| clear(); | clear(); | ||||
| legacy = 0; | legacy = 0; | ||||
| @@ -365,10 +410,12 @@ void PatchManager::fromJson(json_t* rootJ) { | |||||
| warningLog = ""; | warningLog = ""; | ||||
| } | } | ||||
| bool PatchManager::isLegacy(int level) { | bool PatchManager::isLegacy(int level) { | ||||
| return legacy && legacy <= level; | return legacy && legacy <= level; | ||||
| } | } | ||||
| void PatchManager::log(std::string msg) { | void PatchManager::log(std::string msg) { | ||||
| warningLog += msg; | warningLog += msg; | ||||
| warningLog += "\n"; | warningLog += "\n"; | ||||
| @@ -1,7 +1,6 @@ | |||||
| #include <cctype> // for tolower and toupper | #include <cctype> // for tolower and toupper | ||||
| #include <algorithm> // for transform | #include <algorithm> // for transform | ||||
| #include <libgen.h> // for dirname and basename | #include <libgen.h> // for dirname and basename | ||||
| #include <zlib.h> | |||||
| #if defined ARCH_WIN | #if defined ARCH_WIN | ||||
| #include <windows.h> // for MultiByteToWideChar | #include <windows.h> // for MultiByteToWideChar | ||||
| @@ -229,63 +228,13 @@ std::vector<uint8_t> fromBase64(const std::string& str) { | |||||
| } | } | ||||
| std::vector<uint8_t> compress(const uint8_t* data, size_t dataLen) { | |||||
| std::vector<uint8_t> compressed; | |||||
| uLongf outCap = ::compressBound(dataLen); | |||||
| compressed.resize(outCap); | |||||
| int err = ::compress2(compressed.data(), &outCap, data, dataLen, Z_BEST_COMPRESSION); | |||||
| if (err) | |||||
| throw std::runtime_error("Zlib error"); | |||||
| compressed.resize(outCap); | |||||
| return compressed; | |||||
| } | |||||
| std::vector<uint8_t> compress(const std::vector<uint8_t>& data) { | |||||
| return compress(data.data(), data.size()); | |||||
| } | |||||
| void uncompress(const uint8_t* compressed, size_t compressedLen, uint8_t* data, size_t* dataLen) { | |||||
| uLongf dataLenF = *dataLen; | |||||
| int err = ::uncompress(data, &dataLenF, compressed, compressedLen); | |||||
| (void) err; | |||||
| *dataLen = dataLenF; | |||||
| } | |||||
| std::vector<uint8_t> uncompress(const std::vector<uint8_t>& compressed) { | |||||
| // We don't know the uncompressed size, so we can't use the easy compress/uncompress API. | |||||
| std::vector<uint8_t> data; | |||||
| z_stream zs; | |||||
| std::memset(&zs, 0, sizeof(zs)); | |||||
| zs.next_in = (Bytef*) &compressed[0]; | |||||
| zs.avail_in = compressed.size(); | |||||
| inflateInit(&zs); | |||||
| while (true) { | |||||
| uint8_t buffer[16384]; | |||||
| zs.next_out = (Bytef*) buffer; | |||||
| zs.avail_out = sizeof(buffer); | |||||
| int err = inflate(&zs, Z_NO_FLUSH); | |||||
| if (err < 0) | |||||
| throw Exception(string::f("zlib error %d", err)); | |||||
| data.insert(data.end(), buffer, zs.next_out); | |||||
| if (err == Z_STREAM_END) | |||||
| break; | |||||
| } | |||||
| inflateEnd(&zs); | |||||
| return data; | |||||
| } | |||||
| bool CaseInsensitiveCompare::operator()(const std::string& a, const std::string& b) const { | bool CaseInsensitiveCompare::operator()(const std::string& a, const std::string& b) const { | ||||
| // TODO Make more efficient by iterating characters | |||||
| return lowercase(a) < lowercase(b); | |||||
| if (a.size() != b.size()) | |||||
| return false; | |||||
| auto f = [](unsigned char a, unsigned char b) { | |||||
| return std::tolower(a) == std::tolower(b); | |||||
| }; | |||||
| return std::equal(a.begin(), a.end(), b.begin(), f); | |||||
| } | } | ||||
| @@ -26,8 +26,6 @@ | |||||
| #include <dbghelp.h> | #include <dbghelp.h> | ||||
| #endif | #endif | ||||
| #define ZIP_STATIC | |||||
| #include <zip.h> | |||||
| #include <archive.h> | #include <archive.h> | ||||
| #include <archive_entry.h> | #include <archive_entry.h> | ||||
| @@ -362,74 +360,6 @@ std::string getOperatingSystemInfo() { | |||||
| } | } | ||||
| int unzipToFolder(const std::string& zipPath, const std::string& dir) { | |||||
| int err; | |||||
| // Open ZIP file | |||||
| zip_t* za = zip_open(zipPath.c_str(), 0, &err); | |||||
| if (!za) { | |||||
| WARN("Could not open ZIP file %s: error %d", zipPath.c_str(), err); | |||||
| return err; | |||||
| } | |||||
| DEFER({ | |||||
| zip_close(za); | |||||
| }); | |||||
| // Iterate ZIP entries | |||||
| for (int i = 0; i < zip_get_num_entries(za, 0); i++) { | |||||
| zip_stat_t zs; | |||||
| err = zip_stat_index(za, i, 0, &zs); | |||||
| if (err) { | |||||
| WARN("zip_stat_index() failed: error %d", err); | |||||
| return err; | |||||
| } | |||||
| std::string path = dir + "/" + zs.name; | |||||
| if (path[path.size() - 1] == '/') { | |||||
| // Create directory | |||||
| system::createDirectory(path); | |||||
| // HACK | |||||
| // Create and delete file to update the directory's mtime. | |||||
| std::string tmpPath = path + "/.tmp"; | |||||
| FILE* tmpFile = fopen(tmpPath.c_str(), "w"); | |||||
| fclose(tmpFile); | |||||
| std::remove(tmpPath.c_str()); | |||||
| } | |||||
| else { | |||||
| // Open ZIP entry | |||||
| zip_file_t* zf = zip_fopen_index(za, i, 0); | |||||
| if (!zf) { | |||||
| WARN("zip_fopen_index() failed"); | |||||
| return -1; | |||||
| } | |||||
| DEFER({ | |||||
| zip_fclose(zf); | |||||
| }); | |||||
| // Create file | |||||
| FILE* outFile = fopen(path.c_str(), "wb"); | |||||
| if (!outFile) { | |||||
| WARN("Could not create file %s", path.c_str()); | |||||
| return -1; | |||||
| } | |||||
| DEFER({ | |||||
| fclose(outFile); | |||||
| }); | |||||
| // Read buffer and copy to file | |||||
| while (true) { | |||||
| char buffer[1 << 15]; | |||||
| int len = zip_fread(zf, buffer, sizeof(buffer)); | |||||
| if (len <= 0) | |||||
| break; | |||||
| fwrite(buffer, 1, len, outFile); | |||||
| } | |||||
| } | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| /** Behaves like `std::filesystem::relative()`. | /** Behaves like `std::filesystem::relative()`. | ||||
| Limitation: `p` must be a descendant of `base`. Doesn't support adding `../` to the return path. | Limitation: `p` must be a descendant of `base`. Doesn't support adding `../` to the return path. | ||||
| */ | */ | ||||
| @@ -448,7 +378,7 @@ static filesystem::path getRelativePath(filesystem::path p, filesystem::path bas | |||||
| } | } | ||||
| void archiveFolder(const filesystem::path& archivePath, const filesystem::path& folderPath){ | |||||
| void archiveFolder(const filesystem::path& archivePath, const filesystem::path& folderPath) { | |||||
| // Based on minitar.c create() in libarchive examples | // Based on minitar.c create() in libarchive examples | ||||
| int r; | int r; | ||||
| @@ -539,7 +469,6 @@ void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::pa | |||||
| // archive_read_support_filter_all(a); | // archive_read_support_filter_all(a); | ||||
| archive_read_support_format_tar(a); | archive_read_support_format_tar(a); | ||||
| // archive_read_support_format_all(a); | // archive_read_support_format_all(a); | ||||
| DEBUG("opening %s %s", archivePath.generic_string().c_str(), archivePath.string().c_str()); | |||||
| #if defined ARCH_WIN | #if defined ARCH_WIN | ||||
| r = archive_read_open_filename_w(a, archivePath.generic_wstring().c_str(), 1 << 14); | r = archive_read_open_filename_w(a, archivePath.generic_wstring().c_str(), 1 << 14); | ||||
| #else | #else | ||||
| @@ -554,7 +483,6 @@ void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::pa | |||||
| DEFER({archive_write_free(disk);}); | DEFER({archive_write_free(disk);}); | ||||
| int flags = ARCHIVE_EXTRACT_TIME; | int flags = ARCHIVE_EXTRACT_TIME; | ||||
| archive_write_disk_set_options(disk, flags); | archive_write_disk_set_options(disk, flags); | ||||
| // archive_write_disk_set_standard_lookup(disk); | |||||
| DEFER({archive_write_close(disk);}); | DEFER({archive_write_close(disk);}); | ||||
| // Iterate archive | // Iterate archive | ||||