@@ -14,6 +14,7 @@ | |||
/licenses | |||
.DS_Store | |||
/autosave.vcv | |||
/autosave | |||
/settings.json | |||
/screenshots | |||
/Fundamental.zip |
@@ -33,7 +33,7 @@ build/dep/osdialog/osdialog_gtk3.c.o: FLAGS += $(shell pkg-config --cflags gtk+- | |||
FLAGS += -fno-gnu-unique | |||
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 += -lpthread -lGL -ldl -lX11 -lasound -ljack | |||
LDFLAGS += $(shell pkg-config --libs gtk+-3.0) | |||
@@ -49,7 +49,7 @@ ifdef ARCH_MAC | |||
LDFLAGS += -lpthread -ldl | |||
LDFLAGS += -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -framework CoreAudio -framework CoreMIDI | |||
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_LDFLAGS += -stdlib=libc++ | |||
@@ -65,7 +65,7 @@ ifdef ARCH_WIN | |||
LDFLAGS += -Wl,--export-all-symbols | |||
LDFLAGS += -Wl,--out-implib,$(TARGET).a | |||
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 += -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 | |||
zstd = lib/libzstd.a | |||
libarchive = lib/libarchive.a | |||
libzip = lib/libzip.a | |||
zlib = lib/libz.a | |||
libspeexdsp = lib/libspeexdsp.a | |||
libsamplerate = lib/libsamplerate.a | |||
rtmidi = lib/librtmidi.a | |||
@@ -30,8 +28,6 @@ ifdef ARCH_MAC | |||
libcurl = lib/libcurl.a | |||
zstd = lib/libzstd.a | |||
libarchive = lib/libarchive.a | |||
libzip = lib/libzip.a | |||
zlib = lib/libz.a | |||
libspeexdsp = lib/libspeexdsp.a | |||
libsamplerate = lib/libsamplerate.a | |||
rtmidi = lib/librtmidi.a | |||
@@ -46,8 +42,6 @@ ifdef ARCH_WIN | |||
libcurl = lib/libcurl.a | |||
zstd = lib/libzstd.a | |||
libarchive = lib/libarchive.a | |||
libzip = lib/libzip.a | |||
zlib = lib/libz.a | |||
libspeexdsp = lib/libspeexdsp.a | |||
libsamplerate = lib/libsamplerate.a | |||
rtmidi = lib/librtmidi.a | |||
@@ -64,9 +58,7 @@ DEPS += $(glew) | |||
DEPS += $(glfw) | |||
DEPS += $(jansson) | |||
DEPS += $(libcurl) | |||
DEPS += $(zstd) | |||
DEPS += $(libarchive) | |||
DEPS += $(libzip) | |||
DEPS += $(libspeexdsp) | |||
DEPS += $(libsamplerate) | |||
DEPS += $(rtmidi) | |||
@@ -165,35 +157,6 @@ $(libarchive): | $(zstd) libarchive-3.4.3 | |||
$(MAKE) -C libarchive-3.4.3 | |||
$(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: | |||
$(WGET) "https://vcvrack.com/downloads/dep/speexdsp-SpeexDSP-1.2rc3.tgz" | |||
$(SHA256) speexdsp-SpeexDSP-1.2rc3.tgz c8dded1454747f65956f981c95e7f89a06abdaa2a53e8aeaa66bab2a3d59cebd | |||
@@ -283,7 +246,7 @@ $(pffft): | pffft | |||
# 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: | |||
git clean -fdx | |||
@@ -69,18 +69,6 @@ Throws std::runtime_error if string is invalid. | |||
*/ | |||
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 { | |||
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); | |||
std::string getOperatingSystemInfo(); | |||
/** 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 | |||
tar -cf archivePath --zstd -C folderPath . | |||
@@ -13,6 +13,8 @@ | |||
#include <history.hpp> | |||
#include <settings.hpp> | |||
#include <fstream> | |||
namespace rack { | |||
@@ -23,9 +25,11 @@ static const char PATCH_FILTERS[] = "VCV Rack patch (.vcv):vcv"; | |||
PatchManager::PatchManager() { | |||
} | |||
PatchManager::~PatchManager() { | |||
} | |||
void PatchManager::reset() { | |||
if (APP->history) { | |||
APP->history->clear(); | |||
@@ -35,6 +39,7 @@ void PatchManager::reset() { | |||
} | |||
} | |||
void PatchManager::clear() { | |||
if (APP->scene) { | |||
APP->scene->rack->clear(); | |||
@@ -43,6 +48,7 @@ void PatchManager::clear() { | |||
reset(); | |||
} | |||
static bool promptClear(std::string text) { | |||
if (APP->history->isSaved()) | |||
return true; | |||
@@ -51,6 +57,7 @@ static bool promptClear(std::string text) { | |||
return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str()); | |||
} | |||
void PatchManager::save(std::string path) { | |||
INFO("Saving patch %s", path.c_str()); | |||
saveAutosave(); | |||
@@ -58,6 +65,7 @@ void PatchManager::save(std::string path) { | |||
system::archiveFolder(filesystem::u8path(path), asset::autosavePath); | |||
} | |||
void PatchManager::saveDialog() { | |||
if (path == "") { | |||
saveAsDialog(); | |||
@@ -75,6 +83,7 @@ void PatchManager::saveDialog() { | |||
APP->history->setSaved(); | |||
} | |||
void PatchManager::saveAsDialog() { | |||
std::string dir; | |||
std::string filename; | |||
@@ -120,6 +129,7 @@ void PatchManager::saveAsDialog() { | |||
pushRecentPath(path); | |||
} | |||
void PatchManager::saveTemplateDialog() { | |||
// 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?")) | |||
@@ -134,6 +144,7 @@ void PatchManager::saveTemplateDialog() { | |||
} | |||
} | |||
void PatchManager::saveAutosave() { | |||
INFO("Saving autosave"); | |||
json_t* rootJ = toJson(); | |||
@@ -158,16 +169,40 @@ void PatchManager::saveAutosave() { | |||
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) { | |||
INFO("Loading patch %s", path.c_str()); | |||
filesystem::remove_all(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(); | |||
} | |||
void PatchManager::loadTemplate() { | |||
this->path = ""; | |||
APP->history->setSaved(); | |||
@@ -191,6 +226,7 @@ void PatchManager::loadTemplate() { | |||
clear(); | |||
} | |||
void PatchManager::loadTemplateDialog() { | |||
if (!promptClear("The current patch is unsaved. Clear it and start a new patch?")) { | |||
return; | |||
@@ -198,6 +234,7 @@ void PatchManager::loadTemplateDialog() { | |||
loadTemplate(); | |||
} | |||
void PatchManager::loadAutosave() { | |||
INFO("Loading autosave"); | |||
std::string patchPath = asset::autosavePath + "/patch.json"; | |||
@@ -225,6 +262,7 @@ void PatchManager::loadAutosave() { | |||
fromJson(rootJ); | |||
} | |||
void PatchManager::loadAction(std::string path) { | |||
try { | |||
load(path); | |||
@@ -239,6 +277,7 @@ void PatchManager::loadAction(std::string path) { | |||
pushRecentPath(path); | |||
} | |||
void PatchManager::loadDialog() { | |||
if (!promptClear("The current patch is unsaved. Clear it and open a new patch?")) | |||
return; | |||
@@ -268,6 +307,7 @@ void PatchManager::loadDialog() { | |||
loadAction(path); | |||
} | |||
void PatchManager::loadPathDialog(std::string path) { | |||
if (!promptClear("The current patch is unsaved. Clear it and open the new patch?")) | |||
return; | |||
@@ -275,6 +315,7 @@ void PatchManager::loadPathDialog(std::string path) { | |||
loadAction(path); | |||
} | |||
void PatchManager::revertDialog() { | |||
if (path == "") | |||
return; | |||
@@ -292,6 +333,7 @@ void PatchManager::revertDialog() { | |||
APP->history->setSaved(); | |||
} | |||
void PatchManager::pushRecentPath(std::string path) { | |||
auto& recent = settings::recentPatchPaths; | |||
// 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)); | |||
} | |||
void PatchManager::disconnectDialog() { | |||
APP->scene->rack->clearCablesAction(); | |||
} | |||
json_t* PatchManager::toJson() { | |||
// root | |||
json_t* rootJ = json_object(); | |||
@@ -326,6 +370,7 @@ json_t* PatchManager::toJson() { | |||
return rootJ; | |||
} | |||
void PatchManager::fromJson(json_t* rootJ) { | |||
clear(); | |||
legacy = 0; | |||
@@ -365,10 +410,12 @@ void PatchManager::fromJson(json_t* rootJ) { | |||
warningLog = ""; | |||
} | |||
bool PatchManager::isLegacy(int level) { | |||
return legacy && legacy <= level; | |||
} | |||
void PatchManager::log(std::string msg) { | |||
warningLog += msg; | |||
warningLog += "\n"; | |||
@@ -1,7 +1,6 @@ | |||
#include <cctype> // for tolower and toupper | |||
#include <algorithm> // for transform | |||
#include <libgen.h> // for dirname and basename | |||
#include <zlib.h> | |||
#if defined ARCH_WIN | |||
#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 { | |||
// 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> | |||
#endif | |||
#define ZIP_STATIC | |||
#include <zip.h> | |||
#include <archive.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()`. | |||
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 | |||
int r; | |||
@@ -539,7 +469,6 @@ void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::pa | |||
// archive_read_support_filter_all(a); | |||
archive_read_support_format_tar(a); | |||
// archive_read_support_format_all(a); | |||
DEBUG("opening %s %s", archivePath.generic_string().c_str(), archivePath.string().c_str()); | |||
#if defined ARCH_WIN | |||
r = archive_read_open_filename_w(a, archivePath.generic_wstring().c_str(), 1 << 14); | |||
#else | |||
@@ -554,7 +483,6 @@ void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::pa | |||
DEFER({archive_write_free(disk);}); | |||
int flags = ARCHIVE_EXTRACT_TIME; | |||
archive_write_disk_set_options(disk, flags); | |||
// archive_write_disk_set_standard_lookup(disk); | |||
DEFER({archive_write_close(disk);}); | |||
// Iterate archive | |||