@@ -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 | ||||