aboutsummaryrefslogtreecommitdiff
path: root/libs/archive
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
commit7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch)
tree27fb39be241fdb5ac2734c574de678977d1856d0 /libs/archive
Fluorine Manager: full Linux port of Mod Organizer 2
Complete native Linux port with FUSE-based virtual filesystem, Proton/umu-run integration, and Flatpak packaging. Key features: - FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak) - Proton/GE-Proton/umu-run launcher with env var forwarding - Flatpak support (sandbox-aware VFS, NXM handler, umu-run) - Wine prefix management UI - Case-insensitive path resolution for Linux filesystems - QSettings-safe INI handling (avoids Bethesda INI corruption) - Portable instance support with auto-generated launcher scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/archive')
-rw-r--r--libs/archive/.clang-format41
-rw-r--r--libs/archive/.git-blame-ignore-revs1
-rw-r--r--libs/archive/.gitattributes7
-rw-r--r--libs/archive/.github/workflows/build.yml39
-rw-r--r--libs/archive/.github/workflows/linting.yml16
-rw-r--r--libs/archive/.gitignore5
-rw-r--r--libs/archive/.pre-commit-config.yaml20
-rw-r--r--libs/archive/CMakeLists.txt37
-rw-r--r--libs/archive/CMakePresets.json74
-rw-r--r--libs/archive/LICENSE165
-rw-r--r--libs/archive/README.md239
-rw-r--r--libs/archive/cmake/config.cmake.in7
-rw-r--r--libs/archive/include/archive/archive.h280
-rw-r--r--libs/archive/src/CMakeLists.txt101
-rw-r--r--libs/archive/src/archive.cpp609
-rw-r--r--libs/archive/src/compat.h87
-rw-r--r--libs/archive/src/extractcallback.cpp364
-rw-r--r--libs/archive/src/extractcallback.h133
-rw-r--r--libs/archive/src/fileio.cpp449
-rw-r--r--libs/archive/src/fileio.h343
-rw-r--r--libs/archive/src/formatter.h121
-rw-r--r--libs/archive/src/inputstream.cpp70
-rw-r--r--libs/archive/src/inputstream.h54
-rw-r--r--libs/archive/src/instrument.h111
-rw-r--r--libs/archive/src/interfaceguids.cpp37
-rw-r--r--libs/archive/src/library.h164
-rw-r--r--libs/archive/src/multioutputstream.cpp142
-rw-r--r--libs/archive/src/multioutputstream.h106
-rw-r--r--libs/archive/src/opencallback.cpp168
-rw-r--r--libs/archive/src/opencallback.h75
-rw-r--r--libs/archive/src/propertyvariant.cpp214
-rw-r--r--libs/archive/src/propertyvariant.h56
-rw-r--r--libs/archive/src/unknown_impl.h191
-rw-r--r--libs/archive/src/version.rc28
-rw-r--r--libs/archive/thirdparty/C/7zTypes.h597
-rw-r--r--libs/archive/thirdparty/C/7zWindows.h101
-rw-r--r--libs/archive/thirdparty/C/Compiler.h236
-rw-r--r--libs/archive/thirdparty/C/Precomp.h127
-rw-r--r--libs/archive/thirdparty/CPP/7zip/Archive/IArchive.h754
-rw-r--r--libs/archive/thirdparty/CPP/7zip/ICoder.h477
-rw-r--r--libs/archive/thirdparty/CPP/7zip/IDecl.h76
-rw-r--r--libs/archive/thirdparty/CPP/7zip/IPassword.h54
-rw-r--r--libs/archive/thirdparty/CPP/7zip/IProgress.h20
-rw-r--r--libs/archive/thirdparty/CPP/7zip/IStream.h210
-rw-r--r--libs/archive/thirdparty/CPP/7zip/MyVersion.h2
-rw-r--r--libs/archive/thirdparty/CPP/7zip/PropID.h178
-rw-r--r--libs/archive/thirdparty/CPP/Common/Common.h28
-rw-r--r--libs/archive/thirdparty/CPP/Common/Common0.h330
-rw-r--r--libs/archive/thirdparty/CPP/Common/MyGuidDef.h63
-rw-r--r--libs/archive/thirdparty/CPP/Common/MyTypes.h38
-rw-r--r--libs/archive/thirdparty/CPP/Common/MyUnknown.h8
-rw-r--r--libs/archive/thirdparty/CPP/Common/MyWindows.cpp292
-rw-r--r--libs/archive/thirdparty/CPP/Common/MyWindows.h325
-rw-r--r--libs/archive/thirdparty/CPP/Common/NewHandler.h121
-rw-r--r--libs/archive/thirdparty/CPP/Common/StdAfx.h8
-rw-r--r--libs/archive/vcpkg.json10
56 files changed, 8609 insertions, 0 deletions
diff --git a/libs/archive/.clang-format b/libs/archive/.clang-format
new file mode 100644
index 0000000..6098e1f
--- /dev/null
+++ b/libs/archive/.clang-format
@@ -0,0 +1,41 @@
+---
+# We'll use defaults from the LLVM style, but with 4 columns indentation.
+BasedOnStyle: LLVM
+IndentWidth: 2
+---
+Language: Cpp
+DeriveLineEnding: false
+UseCRLF: true
+DerivePointerAlignment: false
+PointerAlignment: Left
+AlignConsecutiveAssignments: true
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: Empty
+AlwaysBreakTemplateDeclarations: Yes
+AccessModifierOffset: -2
+AlignTrailingComments: true
+SpacesBeforeTrailingComments: 2
+NamespaceIndentation: Inner
+MaxEmptyLinesToKeep: 1
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: false
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterStruct: true
+ AfterUnion: true
+ AfterExternBlock: true
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: true
+ColumnLimit: 88
+ForEachMacros: ['Q_FOREACH', 'foreach']
diff --git a/libs/archive/.git-blame-ignore-revs b/libs/archive/.git-blame-ignore-revs
new file mode 100644
index 0000000..be37b84
--- /dev/null
+++ b/libs/archive/.git-blame-ignore-revs
@@ -0,0 +1 @@
+bc77d2a270cc24fd69a8b46ccad4cca2b334a19d
diff --git a/libs/archive/.gitattributes b/libs/archive/.gitattributes
new file mode 100644
index 0000000..f869712
--- /dev/null
+++ b/libs/archive/.gitattributes
@@ -0,0 +1,7 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.cpp text eol=crlf
+*.h text eol=crlf
diff --git a/libs/archive/.github/workflows/build.yml b/libs/archive/.github/workflows/build.yml
new file mode 100644
index 0000000..f476ad1
--- /dev/null
+++ b/libs/archive/.github/workflows/build.yml
@@ -0,0 +1,39 @@
+name: Build Archive
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+env:
+ VCPKG_BINARY_SOURCES: clear;x-azblob,${{ vars.AZ_BLOB_VCPKG_URL }},${{ secrets.AZ_BLOB_SAS }},readwrite
+
+jobs:
+ build:
+ runs-on: windows-2022
+ steps:
+ # set VCPKG Root
+ - name: "Set environmental variables"
+ shell: bash
+ run: |
+ echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> $GITHUB_ENV
+
+ - uses: actions/checkout@v4
+
+ - name: Configure Archive build
+ shell: pwsh
+ run: |
+ cmake --preset vs2022-windows-shared "-DCMAKE_INSTALL_PREFIX=install"
+
+ - name: Build Archive
+ run: cmake --build vsbuild --config RelWithDebInfo
+
+ - name: Install Archive
+ run: cmake --install vsbuild --config RelWithDebInfo
+
+ - name: Upload Archive artifact
+ uses: actions/upload-artifact@master
+ with:
+ name: archive
+ path: ./install
diff --git a/libs/archive/.github/workflows/linting.yml b/libs/archive/.github/workflows/linting.yml
new file mode 100644
index 0000000..22eb21a
--- /dev/null
+++ b/libs/archive/.github/workflows/linting.yml
@@ -0,0 +1,16 @@
+name: Lint Archive
+
+on:
+ push:
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Check format
+ uses: ModOrganizer2/check-formatting-action@master
+ with:
+ check-path: "."
diff --git a/libs/archive/.gitignore b/libs/archive/.gitignore
new file mode 100644
index 0000000..cf71be7
--- /dev/null
+++ b/libs/archive/.gitignore
@@ -0,0 +1,5 @@
+edit
+CMakeLists.txt.user
+/msbuild.log
+/*std*.log
+/*build
diff --git a/libs/archive/.pre-commit-config.yaml b/libs/archive/.pre-commit-config.yaml
new file mode 100644
index 0000000..eb8969c
--- /dev/null
+++ b/libs/archive/.pre-commit-config.yaml
@@ -0,0 +1,20 @@
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v6.0.0
+ hooks:
+ - id: trailing-whitespace
+ - id: end-of-file-fixer
+ - id: check-merge-conflict
+ - id: check-case-conflict
+ - repo: https://github.com/pre-commit/mirrors-clang-format
+ rev: v21.1.8
+ hooks:
+ - id: clang-format
+ 'types_or': [c++, c]
+
+ci:
+ autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks."
+ autofix_prs: true
+ autoupdate_commit_msg: "[pre-commit.ci] Pre-commit autoupdate."
+ autoupdate_schedule: quarterly
+ submodules: false
diff --git a/libs/archive/CMakeLists.txt b/libs/archive/CMakeLists.txt
new file mode 100644
index 0000000..fa5d783
--- /dev/null
+++ b/libs/archive/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.16)
+
+include(CMakePackageConfigHelpers)
+
+project(archive)
+
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+
+add_subdirectory(src)
+
+configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.cmake.in
+ "${CMAKE_CURRENT_BINARY_DIR}/mo2-archive-config.cmake"
+ INSTALL_DESTINATION "lib/cmake/mo2-archive"
+ NO_SET_AND_CHECK_MACRO
+ NO_CHECK_REQUIRED_COMPONENTS_MACRO
+)
+
+file(READ "${CMAKE_CURRENT_SOURCE_DIR}/src/version.rc" archive_version)
+string(REGEX MATCH "#define VER_FILEVERSION[ \t]*([0-9]+),([0-9]+),([0-9]+)" _ ${archive_version})
+set(archive_version_major ${CMAKE_MATCH_1})
+set(archive_version_minor ${CMAKE_MATCH_2})
+set(archive_version_patch ${CMAKE_MATCH_3})
+
+message(STATUS "[MO2] Found version '${archive_version_major}.${archive_version_minor}.${archive_version_patch}'.")
+
+write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/mo2-archive-config-version.cmake"
+ VERSION "${archive_version_major}.${archive_version_minor}.${archive_version_patch}"
+ COMPATIBILITY AnyNewerVersion
+)
+
+install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/mo2-archive-config.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/mo2-archive-config-version.cmake
+ DESTINATION lib/cmake/mo2-archive
+)
diff --git a/libs/archive/CMakePresets.json b/libs/archive/CMakePresets.json
new file mode 100644
index 0000000..ed1f0cc
--- /dev/null
+++ b/libs/archive/CMakePresets.json
@@ -0,0 +1,74 @@
+{
+ "configurePresets": [
+ {
+ "errors": {
+ "deprecated": true
+ },
+ "hidden": true,
+ "name": "cmake-dev",
+ "warnings": {
+ "deprecated": true,
+ "dev": true
+ }
+ },
+ {
+ "cacheVariables": {
+ "VCPKG_MANIFEST_NO_DEFAULT_FEATURES": {
+ "type": "BOOL",
+ "value": "ON"
+ }
+ },
+ "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
+ "hidden": true,
+ "name": "vcpkg"
+ },
+ {
+ "binaryDir": "${sourceDir}/vsbuild",
+ "architecture": {
+ "strategy": "set",
+ "value": "x64"
+ },
+ "cacheVariables": {
+ "CMAKE_CXX_FLAGS": "/EHsc /MP /W4",
+ "VCPKG_TARGET_TRIPLET": {
+ "type": "STRING",
+ "value": "x64-windows-static-md"
+ },
+ "BUILD_SHARED_LIBS": {
+ "type": "BOOL",
+ "value": "OFF"
+ }
+ },
+ "generator": "Visual Studio 17 2022",
+ "inherits": ["cmake-dev", "vcpkg"],
+ "name": "vs2022-windows-static",
+ "toolset": "v143"
+ },
+ {
+ "cacheVariables": {
+ "BUILD_SHARED_LIBS": {
+ "type": "BOOL",
+ "value": "ON"
+ },
+ "VCPKG_TARGET_TRIPLET": {
+ "type": "STRING",
+ "value": "x64-windows"
+ }
+ },
+ "inherits": "vs2022-windows-static",
+ "name": "vs2022-windows-shared"
+ },
+ {
+ "inherits": "vs2022-windows-shared",
+ "name": "vs2022-windows"
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "vs2022-windows",
+ "resolvePackageReferences": "on",
+ "configurePreset": "vs2022-windows"
+ }
+ ],
+ "version": 4
+}
diff --git a/libs/archive/LICENSE b/libs/archive/LICENSE
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/libs/archive/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/libs/archive/README.md b/libs/archive/README.md
new file mode 100644
index 0000000..e389081
--- /dev/null
+++ b/libs/archive/README.md
@@ -0,0 +1,239 @@
+[![Build status](https://ci.appveyor.com/api/projects/status/hdthueiiuedeb38f?svg=true)](https://ci.appveyor.com/project/Modorganizer2/modorganizer-archive)
+
+# modorganizer-archive
+
+This module provides a wrapper round the 7zip `7z.dll` allowing easy(ish) access to the contents of an archive.
+
+## How to build?
+
+If you want to build this as par of ModOrganizer2, simply use ModOrganizer2 build system.
+
+If you want to build this as a standalone DLL, you can run the following (requires `cmake >= 3.16`):
+
+```batch
+mkdir build
+cd build
+cmake ..
+cmake --build . --config Release
+```
+
+This will download the two required dependencies (7z sources and [`fmtlib`](https://github.com/fmtlib/fmt)), and
+build the DLL under `build/src/Release`.
+
+In order to use the DLL, you need to have the `7z.dll` available in your path, otherwize `CreateArchive()` will
+always fail. You can get the `7z.dll` by installing `7z.exe` or by building 7z yourself (building `archive` does
+not build `7z`).
+
+## The `Archive` class
+
+The first thing to do is to create an archive wrapper:
+
+```cpp
+#include <archive.h>
+
+std::unique_ptr<Archive> CreateArchive();
+```
+
+This creates an archive handler.
+
+You can check the [src/archive.h](src/archive.h) header for more details but here are some
+of the available methods:
+
+```cpp
+/**
+ * @brief Check if this Archive wrapper is in a valid state.
+ *
+ * A non-valid Archive instance usually means that the 7z DLLs could not be loaded properly. Failures
+ * to open or extract archives do not invalidate the Archive, so this should only be used to check
+ * if the Archive object has been initialized properly.
+ *
+ * @return true if this instance is valid, false otherwise.
+ */
+bool Archive::isValid() const;
+```
+
+If this returns `false`, this probably means the system cannot find `7z.dll` or it is corrupt, very old (or possibly too new).
+**You should check this before calling `open`.**
+
+```cpp
+/**
+ * @brief Open the given archive.
+ *
+ * @param archivePath Path to the archive to open.
+ * @param passwordCallback Callback to use to ask user for password. This callback must remain
+ * valid until extraction is complete since some types of archives only requires password when
+ * extracting.
+ *
+ * @return true if the archive was open properly, false otherwise.
+ */
+bool Archive::open(std::wstring const &archiveName, ArchiveCallbacks::PasswordCallback passwordCallback)
+```
+
+This attempts to open the specified archive file. It should manage to open pretty much anything 7zip recognises. It returns `true` on success.
+If an error occurs, it returns `false` and `getLastError()` can be used to check the cause of the failure (see the `Archive.h` header for the
+list of possible errors):
+
+```cpp
+Archive::Error getLastError() const;
+```
+
+If `std::wstring passwordChangeCallback()` is not empty, it is called if when password is needed and should return the password to use.
+
+**Note:** this may be called during `extract` rather than during `open`, so should remain usable until the end of the extraction.
+If you do not supply this callback, archives with passwords will be unreadable.
+
+Once the archive is opned, you can retrieve the list of files inside using `getFileList()`:
+
+```cpp
+const std::vector<FileData*>& getFileList() const;
+```
+
+This will return a reference to a vector containing `FileData`. The vector contains non-const pointers so you can actually modify (not assign)
+the pointed `FileData` to indicate which files to extract and the path to extract them to.
+
+Once you have updated the `FileData` (see below) you want to extract, you can then perform the extraction using:
+
+```cpp
+/**
+ * @brief Extract the content of the archive.
+ *
+ * This function uses the filenames from FileData to obtain the extraction paths of file.
+ *
+ * @param outputDirectory Path to the directory where the archive should be extracted. If not empty,
+ * conflicting files will be replaced by the extracted ones.
+ * @param progressCallback Function called to notify extraction progress. This function is called
+ * when new data is written to the disk, not when the archive is read, so it may take a little
+ * time to start.
+ * @param fileChangeCallback Function called when the file currently being extracted changes.
+ * @param errorCallback Function called when an error occurs.
+ *
+ * @return true if the archive was extracted, false otherwise.
+ */
+bool extract(std::wstring const &outputDirectory, ArchiveCallbacks::ProgressCallback progressCallback,
+ ArchiveCallbacks::FileChangeCallback fileChangeCallback, ArchiveCallbacks::ErrorCallback errorCallback)
+```
+
+All callbacks are optional, you can pass an empty `std::function` instead (either `nullptr` or `{}`). The purpose of the callbacks:
+
+- `progressCallback(float)` is called during extraction to notify progress.
+- `fileChangeCallback(std::wstring const&)` is called when a file starts being extracted.
+- `errorCallback(std::wstring const&)` is called if an error occurred, with an appropriate error message. There is not much you can do
+ here beyond displaying the message. This will also result in a failure return from `extract`.
+
+Once `extract()` is done, you can call `getFileList()` again and perform a different extractions. `extract()` will clean the list of
+`FileData` (unless an error occurred).
+
+You can cancel the extraction at any time by calling:
+
+```cpp
+void Archive::cancel();
+```
+
+This will cause `extract` to return `false` and `getLastError` to return `ERROR_EXTRACT_CANCELLED`.
+
+Once you are done, do not forget to close the currently opened `Archive`:
+
+```cpp
+void Archive::close();
+```
+
+## The `FileData` class
+
+As you have seen above, the `getFileList` method returns a reference to a vector of entries about all the files in the archive.
+You can see the full declaration of `FileData` in [src/archive.h](src/archive.h). The following methods are the most important
+ones:
+
+```cpp
+void FileData::addOutputFileName(std::wstring const& filepath)
+```
+
+Adds a new output path for this file. The given `filepath` should be relative to the extraction folder specified in `Archive::extract`.
+Initially, the list of output paths is empty, so if you do not call `addOutputFileName`, the corresponding file will not be extracted.
+You can extract a file in the archive to as many files as you want.
+
+```cpp
+std::vector<std::wstring> FileData::getAndClearOutputFileNames()
+```
+
+Returns the list of output paths (relative to the extraction folder) for this file and clears it. This is normally only used inside
+`extract` but can be used to clear the output filenames after a failure.
+
+Depending on the type of archives, you may have entries corresponding to directories, in which case `FileData::isDirectory()` will
+return `true`.
+You can "extract" those like normal files, but directories will be automatically created for files if necessary anyway.
+
+## Full example
+
+Below is a full example on how to extract an archive to a given folder:
+
+```cpp
+#include <iostream>
+
+#include "archive.h"
+
+int main() {
+
+ // Path to the archive and to the output folder:
+ const std::wstring archivePath = L"archive.7z";
+ const std::wstring outputFolder = L"output";
+
+ auto archive = CreateArchive();
+
+ if (!archive->isValid()) {
+ std::wcerr << "Failed to load the archive module: " << archive->getLastError() << '\n';
+ return -1;
+ }
+
+ // You can set a log callback if you want:
+ archive->setLogCallback([](auto level, auto const& message) {
+ std::wcout << message << '\n';
+ });
+
+ // Open the archive:
+ if (!archive->open(archivePath, nullptr)) {
+ std::wcerr << "Failed to open the archive: " << archive->getLastError() << '\n';
+ return -1;
+ }
+
+ // Get the list of files:
+ auto const& files = archive->getFileList();
+
+ // Mark all files for extraction to their path in the archive:
+ for (auto *fileData: files) {
+ fileData->addOutputFileName(fileData->getFileName());
+ }
+
+ // Extract everything (without callbacks):
+ auto result = archive->extract(outputFolder, nullptr, nullptr, nullptr);
+
+ if (!result) {
+ std::wcerr << "Failed to extract the archive: " << archive->getLastError() << '\n';
+ return -1;
+ }
+
+ // Close the archive:
+ archive->close();
+
+ return 0;
+}
+```
+
+# Copyright
+
+Copyright (C) 2012 Sebastian Herbord, (C) 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+See [LICENSE](LICENSE) for more details.
diff --git a/libs/archive/cmake/config.cmake.in b/libs/archive/cmake/config.cmake.in
new file mode 100644
index 0000000..4bf70e6
--- /dev/null
+++ b/libs/archive/cmake/config.cmake.in
@@ -0,0 +1,7 @@
+@PACKAGE_INIT@
+
+if(WIN32)
+ find_package(7zip CONFIG REQUIRED)
+endif()
+
+include ( "${CMAKE_CURRENT_LIST_DIR}/mo2-archive-targets.cmake" )
diff --git a/libs/archive/include/archive/archive.h b/libs/archive/include/archive/archive.h
new file mode 100644
index 0000000..3985427
--- /dev/null
+++ b/libs/archive/include/archive/archive.h
@@ -0,0 +1,280 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef ARCHIVE_H
+#define ARCHIVE_H
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <string>
+
+#if defined(MO2_ARCHIVE_BUILD_STATIC)
+#define DLLEXPORT
+#elif defined(_WIN32)
+ #if defined(MO2_ARCHIVE_BUILD_EXPORT)
+ #define DLLEXPORT __declspec(dllexport)
+ #else
+ #define DLLEXPORT __declspec(dllimport)
+ #endif
+#else
+ #if defined(MO2_ARCHIVE_BUILD_EXPORT)
+ #define DLLEXPORT __attribute__((visibility("default")))
+ #else
+ #define DLLEXPORT
+ #endif
+#endif
+
+class FileData
+{
+public:
+ /**
+ * @return the path of this entry in the archive (usually relative, unless the archive
+ * contains absolute path).
+ */
+ virtual std::wstring getArchiveFilePath() const = 0;
+
+ /**
+ * @return the size of this entry in bytes (uncompressed).
+ */
+ virtual uint64_t getSize() const = 0;
+
+ /**
+ * @brief Add the given filepath to the list of files to create from this
+ * entry when extracting.
+ *
+ * @param filepath The filepath to add, relative to the output folder.
+ */
+ virtual void addOutputFilePath(std::wstring const& filepath) = 0;
+
+ /**
+ * @brief Retrieve the list of filepaths to extract this entry to.
+ *
+ * @return the list of paths this entry should be extracted to, relative to the
+ * output folder.
+ */
+ virtual const std::vector<std::wstring>& getOutputFilePaths() const = 0;
+
+ /**
+ * @brief Clear the list of output file paths for this entry.
+ */
+ virtual void clearOutputFilePaths() = 0;
+
+ /**
+ * @return the CRC of this file.
+ */
+ virtual uint64_t getCRC() const = 0;
+
+ /**
+ * @return true if this entry is a directory, false otherwize.
+ */
+ virtual bool isDirectory() const = 0;
+
+ virtual ~FileData() = default;
+};
+
+class Archive
+{
+public: // Declarations
+ enum class LogLevel
+ {
+ Debug,
+ Info,
+ Warning,
+ Error
+ };
+
+ enum class ProgressType
+ {
+
+ // Indicates the 7z progression in the archive (related to reading the archive. When
+ // extracting
+ // a lot of files, this may reach 100% way before the extraction is complete since
+ // most of the
+ // time will be spend writing data and not reading it (use EXTRACTION in this case).
+ // When
+ // extracting few small files, this may be useful for solid archives since most of
+ // the time
+ // will be spent in reading and decompressing the archive rather than in writing the
+ // actual files.
+ ARCHIVE,
+
+ // Progress about extraction. If this reach 100%, it means that the extraction of
+ // all files is
+ // complete. The EXTRACTION progress may not start immediately, and might be kind of
+ // chaotic when
+ // extracting few files from an archive, but is much more representative of the
+ // actual progress
+ // than ARCHIVE.
+ EXTRACTION
+ };
+
+ enum class FileChangeType
+ {
+ EXTRACTION_START,
+ EXTRACTION_END
+ };
+
+ static constexpr int MAX_PASSWORD_LENGTH = 256;
+
+ /**
+ * List of callbacks:
+ */
+ using LogCallback = std::function<void(LogLevel, std::wstring const& log)>;
+ using ProgressCallback = std::function<void(ProgressType, uint64_t, uint64_t)>;
+ using PasswordCallback = std::function<std::wstring()>;
+ using FileChangeCallback = std::function<void(FileChangeType, std::wstring const&)>;
+ using ErrorCallback = std::function<void(std::wstring const&)>;
+
+ /**
+ *
+ */
+ enum class Error
+ {
+ ERROR_NONE,
+ ERROR_EXTRACT_CANCELLED,
+ ERROR_LIBRARY_NOT_FOUND,
+ ERROR_LIBRARY_INVALID,
+ ERROR_ARCHIVE_NOT_FOUND,
+ ERROR_FAILED_TO_OPEN_ARCHIVE,
+ ERROR_INVALID_ARCHIVE_FORMAT,
+ ERROR_LIBRARY_ERROR,
+ ERROR_ARCHIVE_INVALID,
+ ERROR_OUT_OF_MEMORY
+ };
+
+public: // Special member functions:
+ virtual ~Archive() {}
+
+public:
+ /**
+ * @brief Check if this Archive wrapper is in a valid state.
+ *
+ * A non-valid Archive instance usually means that the 7z DLLs could not be loaded
+ * properly. Failures to open or extract archives do not invalidate the Archive, so
+ * this should only be used to check if the Archive object has been initialized
+ * properly.
+ *
+ * @return true if this instance is valid, false otherwise.
+ */
+ virtual bool isValid() const = 0;
+
+ /**
+ * @return retrieve the error-code of the last error that occurred.
+ */
+ virtual Error getLastError() const = 0;
+
+ /**
+ * @brief Set the callback used to log messages.
+ *
+ * To remove the callback, you can pass a default-constructed LogCallback object.
+ *
+ * @param logCallback The new callback to use for logging message.
+ */
+ virtual void setLogCallback(LogCallback logCallback) = 0;
+
+ /**
+ * @brief Open the given archive.
+ *
+ * @param archivePath Path to the archive to open.
+ * @param passwordCallback Callback to use to ask user for password. This callback
+ * must remain valid until extraction is complete since some types of archives only
+ * requires password when extracting.
+ *
+ * @return true if the archive was open properly, false otherwise.
+ */
+ virtual bool open(std::wstring const& archivePath,
+ PasswordCallback passwordCallback) = 0;
+
+ /**
+ * @brief Close the currently opened archive.
+ */
+ virtual void close() = 0;
+
+ /**
+ * @return the list of files in the currently opened archive.
+ */
+ virtual const std::vector<FileData*>& getFileList() const = 0;
+
+ /**
+ * @brief Extract the content of the archive.
+ *
+ * This function uses the filenames from FileData to obtain the extraction paths of
+ * file. All the callbacks are optional (you can specify default-constructed
+ * std::function). Overloads with one or two callbacks are also provided.
+ *
+ * @param outputDirectory Path to the directory where the archive should be extracted.
+ * If not empty, conflicting files will be replaced by the extracted ones.
+ * @param progressCallback Function called to notify extraction progress.
+ * @param fileChangeCallback Function called when the file currently being extracted
+ * changes.
+ * @param errorCallback Function called when an error occurs.
+ *
+ * @return true if the archive was extracted, false otherwise.
+ */
+ virtual bool extract(std::wstring const& outputDirectory,
+ ProgressCallback progressCallback,
+ FileChangeCallback fileChangeCallback,
+ ErrorCallback errorCallback) = 0;
+
+ /**
+ * @brief Cancel the current extraction process.
+ */
+ virtual void cancel() = 0;
+
+ // A bunch of useful overloads (with one or two callbacks):
+ bool extract(std::wstring const& outputDirectory, ErrorCallback errorCallback)
+ {
+ return extract(outputDirectory, {}, {}, errorCallback);
+ }
+ bool extract(std::wstring const& outputDirectory, ProgressCallback progressCallback)
+ {
+ return extract(outputDirectory, progressCallback, {}, {});
+ }
+ bool extract(std::wstring const& outputDirectory,
+ FileChangeCallback fileChangeCallback)
+ {
+ return extract(outputDirectory, {}, fileChangeCallback, {});
+ }
+ bool extract(std::wstring const& outputDirectory, ProgressCallback progressCallback,
+ ErrorCallback errorCallback)
+ {
+ return extract(outputDirectory, progressCallback, {}, errorCallback);
+ }
+ bool extract(std::wstring const& outputDirectory, ProgressCallback progressCallback,
+ FileChangeCallback fileChangeCallback)
+ {
+ return extract(outputDirectory, progressCallback, fileChangeCallback, {});
+ }
+ bool extract(std::wstring const& outputDirectory,
+ FileChangeCallback fileChangeCallback, ErrorCallback errorCallback)
+ {
+ return extract(outputDirectory, {}, fileChangeCallback, errorCallback);
+ }
+};
+
+/**
+ * @brief Factory function for archive-objects.
+ *
+ * @return a pointer to a new Archive object that can be used to manipulate archives.
+ */
+DLLEXPORT std::unique_ptr<Archive> CreateArchive();
+
+#endif // ARCHIVE_H
diff --git a/libs/archive/src/CMakeLists.txt b/libs/archive/src/CMakeLists.txt
new file mode 100644
index 0000000..75e9b53
--- /dev/null
+++ b/libs/archive/src/CMakeLists.txt
@@ -0,0 +1,101 @@
+cmake_minimum_required(VERSION 3.16)
+
+if(WIN32)
+ find_package(7zip CONFIG REQUIRED)
+endif()
+
+add_library(archive)
+
+set_target_properties(archive PROPERTIES CXX_STANDARD 20)
+
+# On Linux, we use the bundled 7zip SDK headers and dlopen the 7z.so at runtime
+# On Windows, we use the vcpkg 7zip package
+if(WIN32)
+ target_link_libraries(archive PRIVATE 7zip::7zip)
+else()
+ # Add 7zip SDK headers from thirdparty
+ target_include_directories(archive PRIVATE
+ ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/CPP
+ )
+ # We need to compile MyWindows.cpp for BSTR/Variant support on Linux
+ target_sources(archive PRIVATE
+ ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/CPP/Common/MyWindows.cpp
+ )
+ target_link_libraries(archive PRIVATE dl)
+ target_compile_definitions(archive PRIVATE -DARCHIVE_LINUX_PORT)
+endif()
+
+set(ARCHIVE_SOURCES
+ archive.cpp
+ compat.h
+ extractcallback.cpp
+ extractcallback.h
+ fileio.cpp
+ fileio.h
+ formatter.h
+ inputstream.cpp
+ inputstream.h
+ instrument.h
+ interfaceguids.cpp
+ library.h
+ multioutputstream.cpp
+ multioutputstream.h
+ opencallback.cpp
+ opencallback.h
+ propertyvariant.cpp
+ propertyvariant.h
+ unknown_impl.h
+)
+
+if(WIN32)
+ list(APPEND ARCHIVE_SOURCES version.rc)
+endif()
+
+target_sources(archive
+ PRIVATE
+ ${ARCHIVE_SOURCES}
+ PUBLIC
+ FILE_SET HEADERS
+ BASE_DIRS ${CMAKE_CURRENT_LIST_DIR}/../include
+ FILES ${CMAKE_CURRENT_LIST_DIR}/../include/archive/archive.h
+)
+
+target_include_directories(archive PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../include/archive)
+
+if (MSVC)
+ target_compile_options(archive
+ PRIVATE
+ "/MP"
+ "/W4"
+ "/external:anglebrackets"
+ "/external:W0"
+ )
+ target_link_options(archive
+ PRIVATE
+ $<$<CONFIG:RelWithDebInfo>:/LTCG /INCREMENTAL:NO /OPT:REF /OPT:ICF>
+ )
+
+ set_target_properties(archive PROPERTIES VS_STARTUP_PROJECT archive)
+endif()
+
+if(NOT MSVC)
+ target_compile_options(archive PRIVATE -Wall -Wextra -Wno-unused-parameter)
+endif()
+
+if (BUILD_STATIC)
+ target_compile_definitions(archive PUBLIC -DMO2_ARCHIVE_BUILD_STATIC)
+else()
+ target_compile_definitions(archive PRIVATE -DMO2_ARCHIVE_BUILD_EXPORT)
+endif()
+
+add_library(mo2::archive ALIAS archive)
+
+install(TARGETS archive EXPORT archiveTargets FILE_SET HEADERS)
+if (NOT BUILD_STATIC AND WIN32)
+ install(FILES $<TARGET_PDB_FILE:archive> DESTINATION pdb OPTIONAL)
+endif()
+install(EXPORT archiveTargets
+ FILE mo2-archive-targets.cmake
+ NAMESPACE mo2::
+ DESTINATION lib/cmake/mo2-archive
+)
diff --git a/libs/archive/src/archive.cpp b/libs/archive/src/archive.cpp
new file mode 100644
index 0000000..6d35366
--- /dev/null
+++ b/libs/archive/src/archive.cpp
@@ -0,0 +1,609 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "archive.h"
+#include "compat.h"
+
+#include "extractcallback.h"
+#include "inputstream.h"
+#include "library.h"
+#include "opencallback.h"
+#include "propertyvariant.h"
+
+#include <algorithm>
+#include <map>
+#include <sstream>
+#include <stddef.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace PropID = NArchive::NHandlerPropID;
+
+class FileDataImpl : public FileData
+{
+ friend class Archive;
+
+public:
+ FileDataImpl(std::wstring const& fileName, UInt64 size, UInt64 crc, bool isDirectory)
+ : m_FileName(fileName), m_Size(size), m_CRC(crc), m_IsDirectory(isDirectory)
+ {}
+
+ virtual std::wstring getArchiveFilePath() const override { return m_FileName; }
+ virtual uint64_t getSize() const override { return m_Size; }
+
+ virtual void addOutputFilePath(std::wstring const& fileName) override
+ {
+ m_OutputFilePaths.push_back(fileName);
+ }
+ virtual const std::vector<std::wstring>& getOutputFilePaths() const override
+ {
+ return m_OutputFilePaths;
+ }
+
+ virtual void clearOutputFilePaths() override { m_OutputFilePaths.clear(); }
+
+ bool isEmpty() const { return m_OutputFilePaths.empty(); }
+ virtual bool isDirectory() const override { return m_IsDirectory; }
+ virtual uint64_t getCRC() const override { return m_CRC; }
+
+private:
+ std::wstring m_FileName;
+ UInt64 m_Size;
+ UInt64 m_CRC;
+ std::vector<std::wstring> m_OutputFilePaths;
+ bool m_IsDirectory;
+};
+
+/// represents the connection to one archive and provides common functionality
+class ArchiveImpl : public Archive
+{
+
+ // Callback that does nothing but avoid having to check if the callback is present
+ // everytime.
+ static LogCallback DefaultLogCallback;
+
+public:
+ ArchiveImpl();
+ virtual ~ArchiveImpl();
+
+ virtual bool isValid() const { return m_Valid; }
+
+ virtual Error getLastError() const { return m_LastError; }
+ virtual void setLogCallback(LogCallback logCallback) override
+ {
+ // Wrap the callback so that we do not have to check if it is set everywhere:
+ m_LogCallback = logCallback ? logCallback : DefaultLogCallback;
+ }
+
+ virtual bool open(std::wstring const& archiveName,
+ PasswordCallback passwordCallback) override;
+ virtual void close() override;
+ const std::vector<FileData*>& getFileList() const override { return m_FileList; }
+ virtual bool extract(std::wstring const& outputDirectory,
+ ProgressCallback progressCallback,
+ FileChangeCallback fileChangeCallback,
+ ErrorCallback errorCallback) override;
+
+ virtual void cancel() override;
+
+private:
+ void clearFileList();
+ void resetFileList();
+
+ HRESULT loadFormats();
+
+private:
+ typedef UINT32(WINAPI* CreateObjectFunc)(const GUID* clsID, const GUID* interfaceID,
+ void** outObject);
+ CreateObjectFunc m_CreateObjectFunc;
+
+ // A note: In 7zip source code this not is what this typedef is called, the old
+ // GetHandlerPropertyFunc appears to be deprecated.
+ typedef UInt32(WINAPI* GetPropertyFunc)(UInt32 index, PROPID propID,
+ PROPVARIANT* value);
+ GetPropertyFunc m_GetHandlerPropertyFunc;
+
+ template <typename T>
+ T readHandlerProperty(UInt32 index, PROPID propID) const;
+
+ template <typename T>
+ T readProperty(UInt32 index, PROPID propID) const;
+
+ bool m_Valid;
+ Error m_LastError;
+
+ ALibrary m_Library;
+ std::wstring m_ArchiveName; // TBH I don't think this is required
+ CComPtr<IInArchive> m_ArchivePtr;
+ CArchiveExtractCallback* m_ExtractCallback;
+
+ LogCallback m_LogCallback;
+ PasswordCallback m_PasswordCallback;
+
+ std::vector<FileData*> m_FileList;
+
+ std::wstring m_Password;
+
+ struct ArchiveFormatInfo
+ {
+ CLSID m_ClassID;
+ std::wstring m_Name;
+ std::vector<std::string> m_Signatures;
+ std::wstring m_Extensions;
+ std::wstring m_AdditionalExtensions;
+ UInt32 m_SignatureOffset;
+ };
+
+ typedef std::vector<ArchiveFormatInfo> Formats;
+ Formats m_Formats;
+
+ typedef std::unordered_map<std::wstring, Formats> FormatMap;
+ FormatMap m_FormatMap;
+
+ // I don't think one signature could possibly describe two formats.
+ typedef std::map<std::string, ArchiveFormatInfo> SignatureMap;
+ SignatureMap m_SignatureMap;
+
+ std::size_t m_MaxSignatureLen = 0;
+};
+
+Archive::LogCallback ArchiveImpl::DefaultLogCallback([](LogLevel, std::wstring const&) {
+});
+
+template <typename T>
+T ArchiveImpl::readHandlerProperty(UInt32 index, PROPID propID) const
+{
+ PropertyVariant prop;
+ if (m_GetHandlerPropertyFunc(index, propID, &prop) != S_OK) {
+ throw std::runtime_error("Failed to read property");
+ }
+ return static_cast<T>(prop);
+}
+
+template <typename T>
+T ArchiveImpl::readProperty(UInt32 index, PROPID propID) const
+{
+ PropertyVariant prop;
+ if (m_ArchivePtr->GetProperty(index, propID, &prop) != S_OK) {
+ throw std::runtime_error("Failed to read property");
+ }
+ return static_cast<T>(prop);
+}
+
+// Seriously, there is one format returned in the list that has no registered
+// extension and no signature. WTF?
+HRESULT ArchiveImpl::loadFormats()
+{
+ typedef UInt32(WINAPI * GetNumberOfFormatsFunc)(UInt32 * numFormats);
+ GetNumberOfFormatsFunc getNumberOfFormats =
+ m_Library.resolve<GetNumberOfFormatsFunc>("GetNumberOfFormats");
+ if (getNumberOfFormats == nullptr) {
+ return E_FAIL;
+ }
+
+ UInt32 numFormats;
+ RINOK(getNumberOfFormats(&numFormats));
+
+ for (UInt32 i = 0; i < numFormats; ++i) {
+ ArchiveFormatInfo item;
+
+ item.m_Name = readHandlerProperty<std::wstring>(i, PropID::kName);
+
+ item.m_ClassID = readHandlerProperty<GUID>(i, PropID::kClassID);
+
+ // Should split up the extensions and map extension to type, and see what we get
+ // from that for preference then try all extensions anyway...
+ item.m_Extensions = readHandlerProperty<std::wstring>(i, PropID::kExtension);
+
+ // This is unnecessary currently for our purposes. Basically, for each
+ // extension, there's an 'addext' which, if set (to other than *) means that
+ // theres a double encoding going on. For instance, the bzip format is like this
+ // addext = "* * .tar .tar"
+ // ext = "bz2 bzip2 tbz2 tbz"
+ // which means that tbz2 and tbz should uncompress to a tar file which can be
+ // further processed as if it were a tar file. Having said which, we don't
+ // need to support this at all, so I'm storing it but ignoring it.
+ item.m_AdditionalExtensions =
+ readHandlerProperty<std::wstring>(i, PropID::kAddExtension);
+
+ std::string signature = readHandlerProperty<std::string>(i, PropID::kSignature);
+ if (!signature.empty()) {
+ item.m_Signatures.push_back(signature);
+ if (m_MaxSignatureLen < signature.size()) {
+ m_MaxSignatureLen = signature.size();
+ }
+ m_SignatureMap[signature] = item;
+ }
+
+ std::string multiSig = readHandlerProperty<std::string>(i, PropID::kMultiSignature);
+ const char* multiSigBytes = multiSig.c_str();
+ std::size_t size = multiSig.length();
+ while (size > 0) {
+ unsigned len = *multiSigBytes++;
+ size--;
+ if (len > size)
+ break;
+ std::string sig(multiSigBytes, multiSigBytes + len);
+ multiSigBytes = multiSigBytes + len;
+ size -= len;
+ item.m_Signatures.push_back(sig);
+ if (m_MaxSignatureLen < sig.size()) {
+ m_MaxSignatureLen = sig.size();
+ }
+ m_SignatureMap[sig] = item;
+ }
+
+ UInt32 offset = readHandlerProperty<UInt32>(i, PropID::kSignatureOffset);
+ item.m_SignatureOffset = offset;
+
+ // Now split the extension up from the space separated string and create
+ // a map from each extension to the possible formats
+ // We could make these pointers but it's not a massive overhead and nobody
+ // should be changing this
+ std::wistringstream s(item.m_Extensions);
+ std::wstring t;
+ while (s >> t) {
+ m_FormatMap[t].push_back(item);
+ }
+ m_Formats.push_back(item);
+ }
+ return S_OK;
+}
+
+ArchiveImpl::ArchiveImpl()
+ : m_Valid(false), m_LastError(Error::ERROR_NONE), m_Library("dlls/7zip.dll"),
+ m_PasswordCallback{}
+{
+ // Reset the log callback:
+ setLogCallback({});
+
+ if (!m_Library) {
+ m_LastError = Error::ERROR_LIBRARY_NOT_FOUND;
+ return;
+ }
+
+ m_CreateObjectFunc = m_Library.resolve<CreateObjectFunc>("CreateObject");
+ if (m_CreateObjectFunc == nullptr) {
+ m_LastError = Error::ERROR_LIBRARY_INVALID;
+ return;
+ }
+
+ m_GetHandlerPropertyFunc = m_Library.resolve<GetPropertyFunc>("GetHandlerProperty2");
+ if (m_GetHandlerPropertyFunc == nullptr) {
+ m_LastError = Error::ERROR_LIBRARY_INVALID;
+ return;
+ }
+
+ try {
+ if (loadFormats() != S_OK) {
+ m_LastError = Error::ERROR_LIBRARY_INVALID;
+ return;
+ }
+
+ m_Valid = true;
+ return;
+ } catch (std::exception const& e) {
+ m_LogCallback(LogLevel::Error, std::format(L"Caught exception {}.", e));
+ m_LastError = Error::ERROR_LIBRARY_INVALID;
+ }
+}
+
+ArchiveImpl::~ArchiveImpl()
+{
+ close();
+}
+
+bool ArchiveImpl::open(std::wstring const& archiveName,
+ PasswordCallback passwordCallback)
+{
+ m_ArchiveName = archiveName; // Just for debugging, not actually used...
+
+ Formats formatList = m_Formats;
+
+ // Convert to long path if it's not already:
+ std::filesystem::path filepath = IO::make_path(archiveName);
+
+ // If it doesn't exist or is a directory, error
+ if (!exists(filepath) || is_directory(filepath)) {
+ m_LastError = Error::ERROR_ARCHIVE_NOT_FOUND;
+ return false;
+ }
+
+ // in rars the password seems to be requested during extraction, not on open, so we
+ // need to hold on to the callback for now
+ m_PasswordCallback = passwordCallback;
+
+ CComPtr<InputStream> file(new InputStream);
+
+ if (!file->Open(filepath)) {
+ m_LastError = Error::ERROR_FAILED_TO_OPEN_ARCHIVE;
+ return false;
+ }
+
+ CComPtr<CArchiveOpenCallback> openCallbackPtr;
+ try {
+ openCallbackPtr =
+ new CArchiveOpenCallback(passwordCallback, m_LogCallback, filepath);
+ } catch (std::runtime_error const&) {
+ m_LastError = Error::ERROR_FAILED_TO_OPEN_ARCHIVE;
+ return false;
+ }
+
+ // Try to open the archive
+
+ bool sigMismatch = false;
+
+ {
+ // Get the first iterator that is strictly > the signature we're looking for.
+ for (auto signatureInfo : m_SignatureMap) {
+ // Read the signature of the file and look that up.
+ std::vector<char> buff;
+ buff.reserve(m_MaxSignatureLen);
+ UInt32 act;
+ file->Seek(0, STREAM_SEEK_SET, nullptr);
+ file->Read(buff.data(), static_cast<UInt32>(m_MaxSignatureLen), &act);
+ file->Seek(0, STREAM_SEEK_SET, nullptr);
+ std::string signature = std::string(buff.data(), act);
+ if (signatureInfo.first == std::string(buff.data(), signatureInfo.first.size())) {
+ if (m_CreateObjectFunc(&signatureInfo.second.m_ClassID, &IID_IInArchive,
+ (void**)&m_ArchivePtr) != S_OK) {
+ m_LastError = Error::ERROR_LIBRARY_ERROR;
+ return false;
+ }
+
+ if (m_ArchivePtr->Open(file, 0, openCallbackPtr) != S_OK) {
+ m_LogCallback(LogLevel::Debug,
+ std::format(L"Failed to open {} using {} (from signature).",
+ archiveName, signatureInfo.second.m_Name));
+ m_ArchivePtr.Release();
+ } else {
+ m_LogCallback(LogLevel::Debug,
+ std::format(L"Opened {} using {} (from signature).",
+ archiveName, signatureInfo.second.m_Name));
+
+ // Retrieve the extension (warning: .extension() contains the dot):
+ std::wstring ext =
+ ArchiveStrings::towlower(filepath.extension().wstring().substr(1));
+ std::wistringstream s(signatureInfo.second.m_Extensions);
+ std::wstring t;
+ bool found = false;
+ while (s >> t) {
+ if (t == ext) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ m_LogCallback(LogLevel::Warning,
+ L"The extension of this file did not match the expected "
+ L"extensions for this format.");
+ sigMismatch = true;
+ }
+ }
+ // Arguably we should give up here if it's not OK if 7zip can't even start
+ // to decode even though we've found the format from the signature.
+ // Sadly, the 7zip API documentation is pretty well non-existant.
+ break;
+ }
+ std::vector<ArchiveFormatInfo>::iterator iter = std::find_if(
+ formatList.begin(), formatList.end(), [=](ArchiveFormatInfo a) -> bool {
+ return a.m_Name == signatureInfo.second.m_Name;
+ });
+ if (iter != formatList.end())
+ formatList.erase(iter);
+ }
+ }
+
+ {
+ // determine archive type based on extension
+ Formats const* formats = nullptr;
+ std::wstring ext =
+ ArchiveStrings::towlower(filepath.extension().wstring().substr(1));
+ FormatMap::const_iterator map_iter = m_FormatMap.find(ext);
+ if (map_iter != m_FormatMap.end()) {
+ formats = &map_iter->second;
+ if (formats != nullptr) {
+ if (m_ArchivePtr == nullptr) {
+ // OK, we have some potential formats. If there is only one, try it now. If
+ // there are multiple formats, we'll try by signature lookup first.
+ for (ArchiveFormatInfo format : *formats) {
+ if (m_CreateObjectFunc(&format.m_ClassID, &IID_IInArchive,
+ (void**)&m_ArchivePtr) != S_OK) {
+ m_LastError = Error::ERROR_LIBRARY_ERROR;
+ return false;
+ }
+
+ if (m_ArchivePtr->Open(file, 0, openCallbackPtr) != S_OK) {
+ m_LogCallback(LogLevel::Debug,
+ std::format(L"Failed to open {} using {} (from signature).",
+ archiveName, format.m_Name));
+ m_ArchivePtr.Release();
+ } else {
+ m_LogCallback(LogLevel::Debug,
+ std::format(L"Opened {} using {} (from signature).",
+ archiveName, format.m_Name));
+ break;
+ }
+
+ std::vector<ArchiveFormatInfo>::iterator iter = std::find_if(
+ formatList.begin(), formatList.end(), [=](ArchiveFormatInfo a) -> bool {
+ return a.m_Name == format.m_Name;
+ });
+ if (iter != formatList.end())
+ formatList.erase(iter);
+ }
+ } else if (sigMismatch) {
+ std::vector<std::wstring> vformats;
+ for (ArchiveFormatInfo format : *formats) {
+ vformats.push_back(format.m_Name);
+ }
+ m_LogCallback(
+ LogLevel::Warning,
+ std::format(L"The format(s) expected for this extension are: {}.",
+ ArchiveStrings::join(vformats, L", ")));
+ }
+ }
+ }
+ }
+
+ if (m_ArchivePtr == nullptr) {
+ m_LogCallback(LogLevel::Warning, L"Trying to open an archive but could not "
+ L"recognize the extension or signature.");
+ m_LogCallback(
+ LogLevel::Debug,
+ L"Attempting to open the file with the remaining formats as a fallback...");
+ for (auto format : formatList) {
+ if (m_CreateObjectFunc(&format.m_ClassID, &IID_IInArchive,
+ (void**)&m_ArchivePtr) != S_OK) {
+ m_LastError = Error::ERROR_LIBRARY_ERROR;
+ return false;
+ }
+ if (m_ArchivePtr->Open(file, 0, openCallbackPtr) == S_OK) {
+ m_LogCallback(LogLevel::Debug,
+ std::format(L"Opened {} using {} (from signature).", archiveName,
+ format.m_Name));
+ m_LogCallback(LogLevel::Warning,
+ L"This archive likely has an incorrect extension.");
+ break;
+ } else
+ m_ArchivePtr.Release();
+ }
+ }
+
+ if (m_ArchivePtr == nullptr) {
+ m_LastError = Error::ERROR_INVALID_ARCHIVE_FORMAT;
+ return false;
+ }
+
+ m_Password = openCallbackPtr->GetPassword();
+ /*
+ UInt32 subFile = ULONG_MAX;
+ {
+ NCOM::CPropVariant prop;
+ if (m_ArchivePtr->GetArchiveProperty(kpidMainSubfile, &prop) != S_OK) {
+ throw std::runtime_error("failed to get property kpidMainSubfile");
+ }
+
+ if (prop.vt == VT_UI4) {
+ subFile = prop.ulVal;
+ }
+ }
+
+ if (subFile != ULONG_MAX) {
+ std::wstring subPath = GetArchiveItemPath(m_ArchivePtr, subFile);
+
+ CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
+ openCallbackPtr.QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void
+ **)&setSubArchiveName); if (setSubArchiveName) {
+ setSubArchiveName->SetSubArchiveName(subPath.c_str());
+ }
+ }*/
+
+ m_LastError = Error::ERROR_NONE;
+
+ resetFileList();
+ return true;
+}
+
+void ArchiveImpl::close()
+{
+ if (m_ArchivePtr != nullptr) {
+ m_ArchivePtr->Close();
+ }
+ clearFileList();
+ m_ArchivePtr.Release();
+ m_PasswordCallback = {};
+}
+
+void ArchiveImpl::clearFileList()
+{
+ for (std::vector<FileData*>::iterator iter = m_FileList.begin();
+ iter != m_FileList.end(); ++iter) {
+ delete *iter;
+ }
+ m_FileList.clear();
+}
+
+void ArchiveImpl::resetFileList()
+{
+ UInt32 numItems = 0;
+ clearFileList();
+
+ m_ArchivePtr->GetNumberOfItems(&numItems);
+
+ for (UInt32 i = 0; i < numItems; ++i) {
+ m_FileList.push_back(new FileDataImpl(
+ readProperty<std::wstring>(i, kpidPath), readProperty<UInt64>(i, kpidSize),
+ readProperty<UInt64>(i, kpidCRC), readProperty<bool>(i, kpidIsDir)));
+ }
+}
+
+bool ArchiveImpl::extract(std::wstring const& outputDirectory,
+ ProgressCallback progressCallback,
+ FileChangeCallback fileChangeCallback,
+ ErrorCallback errorCallback)
+
+{
+ // Retrieve the list of indices we want to extract:
+ std::vector<UInt32> indices;
+ UInt64 totalSize = 0;
+ for (std::size_t i = 0; i < m_FileList.size(); ++i) {
+ FileDataImpl* fileData = static_cast<FileDataImpl*>(m_FileList[i]);
+ if (!fileData->isEmpty()) {
+ indices.push_back(static_cast<UInt32>(i));
+ totalSize += fileData->getSize();
+ }
+ }
+
+ m_ExtractCallback = new CArchiveExtractCallback(
+ progressCallback, fileChangeCallback, errorCallback, m_PasswordCallback,
+ m_LogCallback, m_ArchivePtr, outputDirectory, &m_FileList[0], m_FileList.size(),
+ totalSize, &m_Password);
+ HRESULT result = m_ArchivePtr->Extract(
+ indices.data(), static_cast<UInt32>(indices.size()), false, m_ExtractCallback);
+ // Note: m_ExtractCallBack is deleted by Extract
+ switch (result) {
+ case S_OK: {
+ // nop
+ } break;
+ case E_ABORT: {
+ m_LastError = Error::ERROR_EXTRACT_CANCELLED;
+ } break;
+ case E_OUTOFMEMORY: {
+ m_LastError = Error::ERROR_OUT_OF_MEMORY;
+ } break;
+ default: {
+ m_LastError = Error::ERROR_LIBRARY_ERROR;
+ } break;
+ }
+
+ return result == S_OK;
+}
+
+void ArchiveImpl::cancel()
+{
+ m_ExtractCallback->SetCanceled(true);
+}
+
+std::unique_ptr<Archive> CreateArchive()
+{
+ return std::make_unique<ArchiveImpl>();
+}
diff --git a/libs/archive/src/compat.h b/libs/archive/src/compat.h
new file mode 100644
index 0000000..65c2ec5
--- /dev/null
+++ b/libs/archive/src/compat.h
@@ -0,0 +1,87 @@
+/*
+Mod Organizer archive handling - Linux compatibility header
+
+Copyright (C) 2024 MO2 Team. All rights reserved.
+
+This header provides Windows COM compatibility types for Linux builds.
+On Windows, the real Windows headers are used instead.
+*/
+
+#ifndef ARCHIVE_COMPAT_H
+#define ARCHIVE_COMPAT_H
+
+#ifdef _WIN32
+
+#include <Unknwn.h>
+#include <atlbase.h>
+#include <initguid.h>
+#include <guiddef.h>
+#include <PropIdl.h>
+
+#else // Linux
+
+#include <Common/MyWindows.h>
+#include <cstring>
+
+// PropVariantInit / PropVariantClear - map to VariantClear from 7zip SDK
+inline HRESULT PropVariantInit(PROPVARIANT* pvar) {
+ pvar->vt = VT_EMPTY;
+ return S_OK;
+}
+
+inline HRESULT PropVariantClear(PROPVARIANT* pvar) {
+ return VariantClear(pvar);
+}
+
+// A minimal CComPtr replacement for Linux
+template <class T>
+class CComPtr
+{
+public:
+ CComPtr() : p(nullptr) {}
+ CComPtr(T* lp) : p(lp) { if (p) p->AddRef(); }
+ CComPtr(const CComPtr<T>& other) : p(other.p) { if (p) p->AddRef(); }
+ ~CComPtr() { Release(); }
+
+ CComPtr& operator=(T* lp) {
+ if (lp) lp->AddRef();
+ Release();
+ p = lp;
+ return *this;
+ }
+
+ CComPtr& operator=(const CComPtr<T>& other) {
+ if (this != &other) {
+ if (other.p) other.p->AddRef();
+ Release();
+ p = other.p;
+ }
+ return *this;
+ }
+
+ void Release() {
+ if (p) {
+ p->Release();
+ p = nullptr;
+ }
+ }
+
+ T* Detach() {
+ T* pt = p;
+ p = nullptr;
+ return pt;
+ }
+
+ operator T*() const { return p; }
+ T* operator->() const { return p; }
+ T** operator&() { return &p; }
+ bool operator!() const { return p == nullptr; }
+ bool operator==(std::nullptr_t) const { return p == nullptr; }
+ bool operator!=(std::nullptr_t) const { return p != nullptr; }
+
+ T* p;
+};
+
+#endif // _WIN32
+
+#endif // ARCHIVE_COMPAT_H
diff --git a/libs/archive/src/extractcallback.cpp b/libs/archive/src/extractcallback.cpp
new file mode 100644
index 0000000..7629298
--- /dev/null
+++ b/libs/archive/src/extractcallback.cpp
@@ -0,0 +1,364 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "compat.h"
+
+#include <algorithm>
+#include <filesystem>
+#include <format>
+#include <stdexcept>
+#include <string>
+
+#include "archive.h"
+#include "extractcallback.h"
+#include "propertyvariant.h"
+
+std::wstring operationResultToString(Int32 operationResult)
+{
+ namespace R = NArchive::NExtract::NOperationResult;
+
+ switch (operationResult) {
+ case R::kOK:
+ return {};
+
+ case R::kUnsupportedMethod:
+ return L"Encoding method unsupported";
+
+ case R::kDataError:
+ return L"Data error";
+
+ case R::kCRCError:
+ return L"CRC error";
+
+ case R::kUnavailable:
+ return L"Unavailable";
+
+ case R::kUnexpectedEnd:
+ return L"Unexpected end of archive";
+
+ case R::kDataAfterEnd:
+ return L"Data after end of archive";
+
+ case R::kIsNotArc:
+ return L"Not an ARC";
+
+ case R::kHeadersError:
+ return L"Bad headers";
+
+ case R::kWrongPassword:
+ return L"Wrong password";
+
+ default:
+ return std::format(L"Unknown error {}", operationResult);
+ }
+}
+
+CArchiveExtractCallback::CArchiveExtractCallback(
+ Archive::ProgressCallback progressCallback,
+ Archive::FileChangeCallback fileChangeCallback,
+ Archive::ErrorCallback errorCallback, Archive::PasswordCallback passwordCallback,
+ Archive::LogCallback logCallback, IInArchive* archiveHandler,
+ std::wstring const& directoryPath, FileData* const* fileData, std::size_t nbFiles,
+ UInt64 totalFileSize, std::wstring* password)
+ : m_ArchiveHandler(archiveHandler), m_Total(0), m_DirectoryPath(),
+ m_Extracting(false), m_Canceled(false), m_Timers{}, m_ProcessedFileInfo{},
+ m_OutputFileStream{}, m_OutFileStreamCom{}, m_FileData(fileData),
+ m_NbFiles(nbFiles), m_TotalFileSize(totalFileSize), m_LastCallbackFileSize(0),
+ m_ExtractedFileSize(0), m_ProgressCallback(progressCallback),
+ m_FileChangeCallback(fileChangeCallback), m_ErrorCallback(errorCallback),
+ m_PasswordCallback(passwordCallback), m_LogCallback(logCallback),
+ m_Password(password)
+{
+ m_DirectoryPath = IO::make_path(directoryPath);
+}
+
+CArchiveExtractCallback::~CArchiveExtractCallback()
+{
+#ifdef INSTRUMENT_ARCHIVE
+ m_LogCallback(Archive::LogLevel::Debug, m_Timers.GetStream.toString(L"GetStream"));
+ m_LogCallback(Archive::LogLevel::Debug, m_Timers.SetOperationResult.SetMTime.toString(
+ L"SetOperationResult.SetMTime"));
+ m_LogCallback(Archive::LogLevel::Debug, m_Timers.SetOperationResult.Close.toString(
+ L"SetOperationResult.Close"));
+ m_LogCallback(Archive::LogLevel::Debug, m_Timers.SetOperationResult.Release.toString(
+ L"SetOperationResult.Release"));
+ m_LogCallback(Archive::LogLevel::Debug,
+ m_Timers.SetOperationResult.SetFileAttributesW.toString(
+ L"SetOperationResult.SetFileAttributesW"));
+#endif
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) throw()
+{
+ m_Total = size;
+ return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64* completed) throw()
+{
+ if (m_ProgressCallback) {
+ m_ProgressCallback(Archive::ProgressType::ARCHIVE, *completed, m_Total);
+ }
+ return m_Canceled ? E_ABORT : S_OK;
+}
+
+template <typename T>
+bool CArchiveExtractCallback::getOptionalProperty(UInt32 index, int property,
+ T* result) const
+{
+ PropertyVariant prop;
+ if (m_ArchiveHandler->GetProperty(index, property, &prop) != S_OK) {
+ m_LogCallback(Archive::LogLevel::Error,
+ std::format(L"Error getting property {}.", property));
+ return false;
+ }
+ if (prop.is_empty()) {
+ return false;
+ }
+ *result = static_cast<T>(prop);
+ return true;
+}
+
+template <typename T>
+bool CArchiveExtractCallback::getProperty(UInt32 index, int property, T* result) const
+{
+ PropertyVariant prop;
+ if (m_ArchiveHandler->GetProperty(index, property, &prop) != S_OK) {
+ m_LogCallback(Archive::LogLevel::Error,
+ std::format(L"Error getting property {}.", property));
+ return false;
+ }
+
+ *result = static_cast<T>(prop);
+ return true;
+}
+
+STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index,
+ ISequentialOutStream** outStream,
+ Int32 askExtractMode) throw()
+{
+ [[maybe_unused]] auto guard = m_Timers.GetStream.instrument();
+ namespace fs = std::filesystem;
+
+ *outStream = nullptr;
+ m_OutFileStreamCom.Release();
+
+ m_FullProcessedPaths.clear();
+ m_Extracting = false;
+
+ if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) {
+ return S_OK;
+ }
+
+ std::vector<std::wstring> filenames = m_FileData[index]->getOutputFilePaths();
+ m_FileData[index]->clearOutputFilePaths();
+ if (filenames.empty()) {
+ return S_OK;
+ }
+
+#ifndef _WIN32
+ // Archives from Windows contain backslash path separators which are valid
+ // filename characters on Linux - convert them to forward slashes.
+ for (auto& fn : filenames) {
+ std::replace(fn.begin(), fn.end(), L'\\', L'/');
+ }
+#endif
+
+ try {
+ m_ProcessedFileInfo.AttribDefined =
+ getOptionalProperty(index, kpidAttrib, &m_ProcessedFileInfo.Attrib);
+
+ if (!getProperty(index, kpidIsDir, &m_ProcessedFileInfo.isDir)) {
+ return E_ABORT;
+ }
+
+ // Why do we do this? And if we are doing this, shouldn't we copy the created
+ // and accessed times (kpidATime, kpidCTime) as well?
+ m_ProcessedFileInfo.MTimeDefined =
+ getOptionalProperty(index, kpidMTime, &m_ProcessedFileInfo.MTime);
+
+ if (m_ProcessedFileInfo.isDir) {
+ for (auto const& filename : filenames) {
+ auto fullpath = m_DirectoryPath / fs::path(filename).make_preferred();
+ std::error_code ec;
+ std::filesystem::create_directories(fullpath, ec);
+ if (ec) {
+ reportError(L"cannot created directory '{}': {}", fullpath, ec);
+ return E_ABORT;
+ }
+ m_FullProcessedPaths.push_back(fullpath);
+ }
+ } else {
+ for (auto const& filename : filenames) {
+ auto fullProcessedPath = m_DirectoryPath / fs::path(filename).make_preferred();
+ // If the filename contains a '/' we want to make the directory
+ auto directoryPath = fullProcessedPath.parent_path();
+ if (!fs::exists(directoryPath)) {
+ // Make the containing directory
+ std::error_code ec;
+ std::filesystem::create_directories(directoryPath, ec);
+ if (ec) {
+ reportError(L"cannot created directory '{}': {}", directoryPath, ec);
+ return E_ABORT;
+ }
+ // m_DirectoryPath.mkpath(filename.left(slashPos));
+ }
+ // If the file already exists, delete it
+ if (fs::exists(fullProcessedPath)) {
+ std::error_code ec;
+ if (!fs::remove(fullProcessedPath, ec)) {
+ reportError(L"cannot delete output file '{}': {}", fullProcessedPath, ec);
+ return E_ABORT;
+ }
+ }
+ m_FullProcessedPaths.push_back(fullProcessedPath);
+ }
+
+ m_OutputFileStream = new MultiOutputStream([this](UInt32 size, UInt64) {
+ m_ExtractedFileSize += size;
+ if (m_ProgressCallback) {
+ m_ProgressCallback(Archive::ProgressType::EXTRACTION, m_ExtractedFileSize,
+ m_TotalFileSize);
+ }
+ });
+ CComPtr<MultiOutputStream> outStreamCom(m_OutputFileStream);
+
+ if (!m_OutputFileStream->Open(m_FullProcessedPaths)) {
+ reportError(L"cannot open output file '{}': {}", m_FullProcessedPaths[0],
+ ::GetLastError());
+ return E_ABORT;
+ }
+
+ UInt64 fileSize;
+ auto fileSizeFound = getOptionalProperty(index, kpidSize, &fileSize);
+ if (fileSizeFound && m_OutputFileStream->SetSize(fileSize) != S_OK) {
+ m_LogCallback(Archive::LogLevel::Error,
+ std::format(L"SetSize() failed on {}.", m_FullProcessedPaths[0]));
+ }
+
+ // This is messy but I can't find another way of doing it. A simple
+ // assignment of m_outFileStream to *outStream doesn't increase the
+ // reference count.
+ m_OutFileStreamCom = outStreamCom;
+ *outStream = outStreamCom.Detach();
+ }
+
+ if (m_FileChangeCallback) {
+ m_FileChangeCallback(Archive::FileChangeType::EXTRACTION_START, filenames[0]);
+ }
+
+ return S_OK;
+ } catch (std::exception const& e) {
+ m_LogCallback(Archive::LogLevel::Error,
+ std::format(L"Caught exception {} in GetStream.", e));
+ }
+ return E_FAIL;
+}
+
+STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) throw()
+{
+ if (m_Canceled) {
+ return E_ABORT;
+ }
+ m_Extracting = askExtractMode == NArchive::NExtract::NAskMode::kExtract;
+ return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) throw()
+{
+ if (operationResult != NArchive::NExtract::NOperationResult::kOK) {
+ reportError(operationResultToString(operationResult));
+ }
+
+ if (m_OutFileStreamCom) {
+ if (m_ProcessedFileInfo.MTimeDefined) {
+ [[maybe_unused]] auto guard = m_Timers.SetOperationResult.SetMTime.instrument();
+ m_OutputFileStream->SetMTime(&m_ProcessedFileInfo.MTime);
+ }
+ [[maybe_unused]] auto guard = m_Timers.SetOperationResult.Close.instrument();
+ RINOK(m_OutputFileStream->Close())
+ }
+
+ {
+ [[maybe_unused]] auto guard = m_Timers.SetOperationResult.Release.instrument();
+ m_OutFileStreamCom.Release();
+ }
+
+ [[maybe_unused]] auto guard2 = m_Timers.SetOperationResult.SetFileAttributesW.instrument();
+ if (m_Extracting && m_ProcessedFileInfo.AttribDefined) {
+ // this is moderately annoying. I can't do this on the file handle because if
+ // the file in question is a directory there isn't a file handle.
+ // Also I'd like to convert the attributes to QT attributes but I'm not sure
+ // if that's possible. Hence the conversions and strange string.
+ for (auto& path : m_FullProcessedPaths) {
+#ifdef _WIN32
+ std::wstring const fn = L"\\\\?\\" + path.native();
+ // If the attributes are POSIX-based, fix that
+ if (m_ProcessedFileInfo.Attrib & 0xF0000000)
+ m_ProcessedFileInfo.Attrib &= 0x7FFF;
+
+ // Should probably log any errors here somehow
+ ::SetFileAttributesW(fn.c_str(), m_ProcessedFileInfo.Attrib);
+#else
+ // On Linux, we could set file permissions based on the attributes,
+ // but Windows file attributes don't map well to POSIX permissions.
+ // For now, we only handle the read-only attribute and only for files.
+ // Applying read-only to directories can break extraction when later
+ // files need to be created inside those directories.
+ if (!m_ProcessedFileInfo.isDir &&
+ (m_ProcessedFileInfo.Attrib & FILE_ATTRIBUTE_READONLY)) {
+ std::filesystem::permissions(path,
+ std::filesystem::perms::owner_write,
+ std::filesystem::perm_options::remove);
+ } else if (m_ProcessedFileInfo.isDir) {
+ // Keep extracted directories writable for the owner.
+ std::filesystem::permissions(path,
+ std::filesystem::perms::owner_write,
+ std::filesystem::perm_options::add);
+ }
+#endif
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR* passwordOut) throw()
+{
+ // if we've already got a password, don't ask again (and again...)
+ if (m_Password->empty() && m_PasswordCallback) {
+ *m_Password = m_PasswordCallback();
+ }
+
+ *passwordOut = ::SysAllocString(m_Password->c_str());
+ return *passwordOut != 0 ? S_OK : E_OUTOFMEMORY;
+}
+
+void CArchiveExtractCallback::SetCanceled(bool aCanceled)
+{
+ m_Canceled = aCanceled;
+}
+
+void CArchiveExtractCallback::reportError(std::wstring const& message)
+{
+ if (m_ErrorCallback) {
+ m_ErrorCallback(message);
+ }
+}
diff --git a/libs/archive/src/extractcallback.h b/libs/archive/src/extractcallback.h
new file mode 100644
index 0000000..f95fc86
--- /dev/null
+++ b/libs/archive/src/extractcallback.h
@@ -0,0 +1,133 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef EXTRACTCALLBACK_H
+#define EXTRACTCALLBACK_H
+
+#include <atomic>
+#include <chrono>
+#include <filesystem>
+#include <format>
+
+#include <7zip/Archive/IArchive.h>
+#include <7zip/IPassword.h>
+
+#include "compat.h"
+
+#include "archive.h"
+#include "formatter.h"
+#include "instrument.h"
+#include "multioutputstream.h"
+#include "unknown_impl.h"
+
+class FileData;
+
+class CArchiveExtractCallback : public IArchiveExtractCallback,
+ public ICryptoGetTextPassword
+{
+
+ // A note: It appears that the IArchiveExtractCallback interface includes the
+ // IProgress interface, swo we need to respond to it
+ UNKNOWN_3_INTERFACE(IArchiveExtractCallback, ICryptoGetTextPassword, IProgress);
+
+public:
+ CArchiveExtractCallback(Archive::ProgressCallback progressCallback,
+ Archive::FileChangeCallback fileChangeCallback,
+ Archive::ErrorCallback errorCallback,
+ Archive::PasswordCallback passwordCallback,
+ Archive::LogCallback logCallback, IInArchive* archiveHandler,
+ std::wstring const& directoryPath, FileData* const* fileData,
+ std::size_t nbFiles, UInt64 totalFileSize,
+ std::wstring* password);
+
+ virtual ~CArchiveExtractCallback();
+
+ void SetCanceled(bool aCanceled);
+
+ Z7_IFACE_COM7_IMP(IProgress)
+ Z7_IFACE_COM7_IMP(IArchiveExtractCallback)
+
+ // ICryptoGetTextPassword
+ STDMETHOD(CryptoGetTextPassword)(BSTR* aPassword) throw();
+
+private:
+ void reportError(const std::wstring& message);
+
+ template <class... Args>
+ void reportError(std::wformat_string<Args...> format, Args&&... args)
+ {
+ reportError(std::format(format, std::forward<Args>(args)...));
+ }
+
+ template <typename T>
+ bool getOptionalProperty(UInt32 index, int property, T* result) const;
+ template <typename T>
+ bool getProperty(UInt32 index, int property, T* result) const;
+
+private:
+ CComPtr<IInArchive> m_ArchiveHandler;
+
+ UInt64 m_Total;
+
+ std::filesystem::path m_DirectoryPath;
+ bool m_Extracting;
+ std::atomic<bool> m_Canceled;
+
+ struct
+ {
+ ArchiveTimers::Timer GetStream;
+ struct
+ {
+ ArchiveTimers::Timer SetMTime;
+ ArchiveTimers::Timer Close;
+ ArchiveTimers::Timer Release;
+ ArchiveTimers::Timer SetFileAttributesW;
+ } SetOperationResult;
+ } m_Timers;
+
+ struct CProcessedFileInfo
+ {
+ FILETIME MTime;
+ UInt32 Attrib;
+ bool isDir;
+ bool AttribDefined;
+ bool MTimeDefined;
+ } m_ProcessedFileInfo;
+
+ MultiOutputStream* m_OutputFileStream;
+ CComPtr<MultiOutputStream> m_OutFileStreamCom;
+
+ std::vector<std::filesystem::path> m_FullProcessedPaths;
+
+ FileData* const* m_FileData;
+ std::size_t m_NbFiles;
+ UInt64 m_TotalFileSize;
+ UInt64 m_LastCallbackFileSize;
+ UInt64 m_ExtractedFileSize;
+
+ Archive::ProgressCallback m_ProgressCallback;
+ Archive::FileChangeCallback m_FileChangeCallback;
+ Archive::ErrorCallback m_ErrorCallback;
+ Archive::PasswordCallback m_PasswordCallback;
+ Archive::LogCallback m_LogCallback;
+ std::wstring* m_Password;
+};
+
+#endif // EXTRACTCALLBACK_H
diff --git a/libs/archive/src/fileio.cpp b/libs/archive/src/fileio.cpp
new file mode 100644
index 0000000..83d0665
--- /dev/null
+++ b/libs/archive/src/fileio.cpp
@@ -0,0 +1,449 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "fileio.h"
+
+#ifdef _WIN32
+
+inline bool BOOLToBool(BOOL v)
+{
+ return (v != FALSE);
+}
+
+namespace IO
+{
+
+// FileBase
+
+bool FileBase::Close() noexcept
+{
+ if (m_Handle == INVALID_HANDLE_VALUE)
+ return true;
+ if (!::CloseHandle(m_Handle))
+ return false;
+ m_Handle = INVALID_HANDLE_VALUE;
+ return true;
+}
+
+bool FileBase::GetPosition(UInt64& position) noexcept
+{
+ return Seek(0, FILE_CURRENT, position);
+}
+
+bool FileBase::GetLength(UInt64& length) const noexcept
+{
+ DWORD sizeHigh;
+ DWORD sizeLow = ::GetFileSize(m_Handle, &sizeHigh);
+ if (sizeLow == 0xFFFFFFFF)
+ if (::GetLastError() != NO_ERROR)
+ return false;
+ length = (((UInt64)sizeHigh) << 32) + sizeLow;
+ return true;
+}
+
+bool FileBase::Seek(Int64 distanceToMove, DWORD moveMethod,
+ UInt64& newPosition) noexcept
+{
+ LONG high = (LONG)(distanceToMove >> 32);
+ DWORD low = ::SetFilePointer(m_Handle, (LONG)(distanceToMove & 0xFFFFFFFF), &high,
+ moveMethod);
+ if (low == 0xFFFFFFFF)
+ if (::GetLastError() != NO_ERROR)
+ return false;
+ newPosition = (((UInt64)(UInt32)high) << 32) + low;
+ return true;
+}
+bool FileBase::Seek(UInt64 position, UInt64& newPosition) noexcept
+{
+ return Seek(position, FILE_BEGIN, newPosition);
+}
+
+bool FileBase::SeekToBegin() noexcept
+{
+ UInt64 newPosition;
+ return Seek(0, newPosition);
+}
+
+bool FileBase::SeekToEnd(UInt64& newPosition) noexcept
+{
+ return Seek(0, FILE_END, newPosition);
+}
+
+bool FileBase::Create(std::filesystem::path const& path, DWORD desiredAccess,
+ DWORD shareMode, DWORD creationDisposition,
+ DWORD flagsAndAttributes) noexcept
+{
+ if (!Close()) {
+ return false;
+ }
+
+ m_Handle =
+ ::CreateFileW(path.c_str(), desiredAccess, shareMode, (LPSECURITY_ATTRIBUTES)NULL,
+ creationDisposition, flagsAndAttributes, (HANDLE)NULL);
+
+ return m_Handle != INVALID_HANDLE_VALUE;
+}
+
+bool FileBase::GetFileInformation(std::filesystem::path const& path,
+ FileInfo* info) noexcept
+{
+ // Use FileBase to open/close the file:
+ FileBase file;
+ if (!file.Create(path, 0, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS))
+ return false;
+
+ BY_HANDLE_FILE_INFORMATION finfo;
+ if (!BOOLToBool(GetFileInformationByHandle(file.m_Handle, &finfo))) {
+ return false;
+ }
+
+ *info = FileInfo(path, finfo);
+ return true;
+}
+
+// FileIn
+
+bool FileIn::Open(std::filesystem::path const& filepath, DWORD shareMode,
+ DWORD creationDisposition, DWORD flagsAndAttributes) noexcept
+{
+ bool res = Create(filepath.c_str(), GENERIC_READ, shareMode, creationDisposition,
+ flagsAndAttributes);
+ return res;
+}
+bool FileIn::OpenShared(std::filesystem::path const& filepath,
+ bool shareForWrite) noexcept
+{
+ return Open(filepath, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0),
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL);
+}
+bool FileIn::Open(std::filesystem::path const& filepath) noexcept
+{
+ return OpenShared(filepath, false);
+}
+bool FileIn::Read(void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ processedSize = 0;
+ do {
+ UInt32 processedLoc = 0;
+ bool res = ReadPart(data, size, processedLoc);
+ processedSize += processedLoc;
+ if (!res)
+ return false;
+ if (processedLoc == 0)
+ return true;
+ data = (void*)((unsigned char*)data + processedLoc);
+ size -= processedLoc;
+ } while (size > 0);
+ return true;
+}
+bool FileIn::Read1(void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ DWORD processedLoc = 0;
+ bool res = BOOLToBool(::ReadFile(m_Handle, data, size, &processedLoc, NULL));
+ processedSize = (UInt32)processedLoc;
+ return res;
+}
+bool FileIn::ReadPart(void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ if (size > kChunkSizeMax)
+ size = kChunkSizeMax;
+ return Read1(data, size, processedSize);
+}
+
+// FileOut
+
+bool FileOut::Open(std::filesystem::path const& fileName, DWORD shareMode,
+ DWORD creationDisposition, DWORD flagsAndAttributes) noexcept
+{
+ return Create(fileName, GENERIC_WRITE, shareMode, creationDisposition,
+ flagsAndAttributes);
+}
+
+bool FileOut::Open(std::filesystem::path const& fileName) noexcept
+{
+ return Open(fileName, FILE_SHARE_READ, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
+}
+
+bool FileOut::SetTime(const FILETIME* cTime, const FILETIME* aTime,
+ const FILETIME* mTime) noexcept
+{
+ return BOOLToBool(::SetFileTime(m_Handle, cTime, aTime, mTime));
+}
+bool FileOut::SetMTime(const FILETIME* mTime) noexcept
+{
+ return SetTime(NULL, NULL, mTime);
+}
+bool FileOut::Write(const void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ processedSize = 0;
+ do {
+ UInt32 processedLoc = 0;
+ bool res = WritePart(data, size, processedLoc);
+ processedSize += processedLoc;
+ if (!res)
+ return false;
+ if (processedLoc == 0)
+ return true;
+ data = (const void*)((const unsigned char*)data + processedLoc);
+ size -= processedLoc;
+ } while (size > 0);
+ return true;
+}
+
+bool FileOut::SetLength(UInt64 length) noexcept
+{
+ UInt64 newPosition;
+ if (!Seek(length, newPosition))
+ return false;
+ if (newPosition != length)
+ return false;
+ return SetEndOfFile();
+}
+bool FileOut::SetEndOfFile() noexcept
+{
+ return BOOLToBool(::SetEndOfFile(m_Handle));
+}
+
+bool FileOut::WritePart(const void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ if (size > kChunkSizeMax)
+ size = kChunkSizeMax;
+ DWORD processedLoc = 0;
+ bool res = BOOLToBool(::WriteFile(m_Handle, data, size, &processedLoc, NULL));
+ processedSize = (UInt32)processedLoc;
+ return res;
+}
+
+} // namespace IO
+
+#else // Linux
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <cstring>
+#include <cerrno>
+
+namespace IO
+{
+
+// FileBase
+
+bool FileBase::Close() noexcept
+{
+ if (m_Fd == -1)
+ return true;
+ if (::close(m_Fd) != 0)
+ return false;
+ m_Fd = -1;
+ return true;
+}
+
+bool FileBase::GetPosition(UInt64& position) noexcept
+{
+ return Seek(0, SEEK_CUR, position);
+}
+
+bool FileBase::GetLength(UInt64& length) const noexcept
+{
+ struct stat st;
+ if (fstat(m_Fd, &st) != 0)
+ return false;
+ length = (UInt64)st.st_size;
+ return true;
+}
+
+bool FileBase::Seek(Int64 distanceToMove, int whence, UInt64& newPosition) noexcept
+{
+ off_t result = ::lseek(m_Fd, (off_t)distanceToMove, whence);
+ if (result == (off_t)-1)
+ return false;
+ newPosition = (UInt64)result;
+ return true;
+}
+
+bool FileBase::Seek(UInt64 position, UInt64& newPosition) noexcept
+{
+ return Seek((Int64)position, SEEK_SET, newPosition);
+}
+
+bool FileBase::SeekToBegin() noexcept
+{
+ UInt64 newPosition;
+ return Seek(0, newPosition);
+}
+
+bool FileBase::SeekToEnd(UInt64& newPosition) noexcept
+{
+ return Seek(0, SEEK_END, newPosition);
+}
+
+bool FileBase::Create(std::filesystem::path const& path, int flags, int mode) noexcept
+{
+ if (!Close()) {
+ return false;
+ }
+
+ m_Fd = ::open(path.c_str(), flags, mode);
+ return m_Fd != -1;
+}
+
+bool FileBase::GetFileInformation(std::filesystem::path const& path,
+ FileInfo* info) noexcept
+{
+ struct stat st;
+ if (::stat(path.c_str(), &st) != 0) {
+ return false;
+ }
+
+ *info = FileInfo(path, st);
+ return true;
+}
+
+// FileIn
+
+bool FileIn::Open(std::filesystem::path const& filepath) noexcept
+{
+ return Create(filepath, O_RDONLY);
+}
+
+bool FileIn::Read(void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ processedSize = 0;
+ do {
+ UInt32 processedLoc = 0;
+ bool res = ReadPart(data, size, processedLoc);
+ processedSize += processedLoc;
+ if (!res)
+ return false;
+ if (processedLoc == 0)
+ return true;
+ data = (void*)((unsigned char*)data + processedLoc);
+ size -= processedLoc;
+ } while (size > 0);
+ return true;
+}
+
+bool FileIn::Read1(void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ ssize_t result = ::read(m_Fd, data, size);
+ if (result < 0) {
+ processedSize = 0;
+ return false;
+ }
+ processedSize = (UInt32)result;
+ return true;
+}
+
+bool FileIn::ReadPart(void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ if (size > kChunkSizeMax)
+ size = kChunkSizeMax;
+ return Read1(data, size, processedSize);
+}
+
+// FileOut
+
+bool FileOut::Open(std::filesystem::path const& fileName) noexcept
+{
+ return Create(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+}
+
+bool FileOut::SetTime(const FILETIME* /*cTime*/, const FILETIME* /*aTime*/,
+ const FILETIME* mTime) noexcept
+{
+ return SetMTime(mTime);
+}
+
+bool FileOut::SetMTime(const FILETIME* mTime) noexcept
+{
+ if (!mTime) return true;
+
+ // Convert FILETIME to timespec
+ // FILETIME is 100ns intervals since 1601-01-01
+ // Unix time is seconds since 1970-01-01
+ constexpr UInt64 EPOCH_DIFF = 116444736000000000ULL;
+ UInt64 ticks = ((UInt64)mTime->dwHighDateTime << 32) | mTime->dwLowDateTime;
+ if (ticks < EPOCH_DIFF) return false;
+ ticks -= EPOCH_DIFF;
+
+ struct timespec times[2];
+ // atime - keep current
+ times[0].tv_sec = 0;
+ times[0].tv_nsec = UTIME_OMIT;
+ // mtime
+ times[1].tv_sec = (time_t)(ticks / 10000000ULL);
+ times[1].tv_nsec = (long)((ticks % 10000000ULL) * 100);
+
+ return futimens(m_Fd, times) == 0;
+}
+
+bool FileOut::Write(const void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ processedSize = 0;
+ do {
+ UInt32 processedLoc = 0;
+ bool res = WritePart(data, size, processedLoc);
+ processedSize += processedLoc;
+ if (!res)
+ return false;
+ if (processedLoc == 0)
+ return true;
+ data = (const void*)((const unsigned char*)data + processedLoc);
+ size -= processedLoc;
+ } while (size > 0);
+ return true;
+}
+
+bool FileOut::SetLength(UInt64 length) noexcept
+{
+ UInt64 newPosition;
+ if (!Seek(length, newPosition))
+ return false;
+ if (newPosition != length)
+ return false;
+ return SetEndOfFile();
+}
+
+bool FileOut::SetEndOfFile() noexcept
+{
+ UInt64 pos;
+ if (!GetPosition(pos))
+ return false;
+ return ftruncate(m_Fd, (off_t)pos) == 0;
+}
+
+bool FileOut::WritePart(const void* data, UInt32 size, UInt32& processedSize) noexcept
+{
+ if (size > kChunkSizeMax)
+ size = kChunkSizeMax;
+ ssize_t result = ::write(m_Fd, data, size);
+ if (result < 0) {
+ processedSize = 0;
+ return false;
+ }
+ processedSize = (UInt32)result;
+ return true;
+}
+
+} // namespace IO
+
+#endif
diff --git a/libs/archive/src/fileio.h b/libs/archive/src/fileio.h
new file mode 100644
index 0000000..72b71c7
--- /dev/null
+++ b/libs/archive/src/fileio.h
@@ -0,0 +1,343 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef ARCHIVE_FILEIO_H
+#define ARCHIVE_FILEIO_H
+
+// This code is adapted from 7z client code.
+
+#ifdef _WIN32
+#include <Windows.h>
+#include "7zip//Archive/IArchive.h"
+#else
+#include <Common/MyWindows.h>
+#include <7zip/Archive/IArchive.h>
+#endif
+
+#include <filesystem>
+#include <string>
+
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+namespace IO
+{
+
+#ifdef _WIN32
+
+/**
+ * Small class that wraps windows BY_HANDLE_FILE_INFORMATION and returns
+ * type matching 7z types.
+ */
+class FileInfo
+{
+public:
+ FileInfo() : m_Valid{false} {};
+ FileInfo(std::filesystem::path const& path, BY_HANDLE_FILE_INFORMATION fileInfo)
+ : m_Valid{true}, m_Path(path), m_FileInfo{fileInfo}
+ {}
+
+ bool isValid() const { return m_Valid; }
+
+ const std::filesystem::path& path() const { return m_Path; }
+
+ UInt32 fileAttributes() const { return m_FileInfo.dwFileAttributes; }
+ FILETIME creationTime() const { return m_FileInfo.ftCreationTime; }
+ FILETIME lastAccessTime() const { return m_FileInfo.ftLastAccessTime; }
+ FILETIME lastWriteTime() const { return m_FileInfo.ftLastWriteTime; }
+ UInt32 volumeSerialNumber() const { return m_FileInfo.dwVolumeSerialNumber; }
+ UInt64 fileSize() const
+ {
+ return ((UInt64)m_FileInfo.nFileSizeHigh) << 32 | m_FileInfo.nFileSizeLow;
+ }
+ UInt32 numberOfLinks() const { return m_FileInfo.nNumberOfLinks; }
+ UInt64 fileInfex() const
+ {
+ return ((UInt64)m_FileInfo.nFileIndexHigh) << 32 | m_FileInfo.nFileIndexLow;
+ }
+
+ bool isArchived() const { return MatchesMask(FILE_ATTRIBUTE_ARCHIVE); }
+ bool isCompressed() const { return MatchesMask(FILE_ATTRIBUTE_COMPRESSED); }
+ bool isDir() const { return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); }
+ bool isEncrypted() const { return MatchesMask(FILE_ATTRIBUTE_ENCRYPTED); }
+ bool isHidden() const { return MatchesMask(FILE_ATTRIBUTE_HIDDEN); }
+ bool isNormal() const { return MatchesMask(FILE_ATTRIBUTE_NORMAL); }
+ bool isOffline() const { return MatchesMask(FILE_ATTRIBUTE_OFFLINE); }
+ bool isReadOnly() const { return MatchesMask(FILE_ATTRIBUTE_READONLY); }
+ bool iasReparsePoint() const { return MatchesMask(FILE_ATTRIBUTE_REPARSE_POINT); }
+ bool isSparse() const { return MatchesMask(FILE_ATTRIBUTE_SPARSE_FILE); }
+ bool isSystem() const { return MatchesMask(FILE_ATTRIBUTE_SYSTEM); }
+ bool isTemporary() const { return MatchesMask(FILE_ATTRIBUTE_TEMPORARY); }
+
+private:
+ bool MatchesMask(UINT32 mask) const
+ {
+ return ((m_FileInfo.dwFileAttributes & mask) != 0);
+ }
+
+ bool m_Valid;
+ std::filesystem::path m_Path;
+ BY_HANDLE_FILE_INFORMATION m_FileInfo;
+};
+
+#else // Linux
+
+/**
+ * FileInfo class for Linux - uses stat() to get file information.
+ * Returns 7zip-compatible types.
+ */
+class FileInfo
+{
+public:
+ FileInfo() : m_Valid{false}, m_Stat{} {};
+ FileInfo(std::filesystem::path const& path, struct stat const& st)
+ : m_Valid{true}, m_Path(path), m_Stat{st}
+ {}
+
+ bool isValid() const { return m_Valid; }
+
+ const std::filesystem::path& path() const { return m_Path; }
+
+ UInt32 fileAttributes() const {
+ UInt32 attr = 0;
+ if (S_ISDIR(m_Stat.st_mode)) attr |= FILE_ATTRIBUTE_DIRECTORY;
+ if (!(m_Stat.st_mode & S_IWUSR)) attr |= FILE_ATTRIBUTE_READONLY;
+ if (attr == 0) attr = FILE_ATTRIBUTE_NORMAL;
+ return attr;
+ }
+
+ // Convert timespec to FILETIME (100ns intervals since 1601-01-01)
+ static FILETIME timespecToFiletime(struct timespec const& ts) {
+ // Offset between 1601-01-01 and 1970-01-01 in 100ns intervals
+ constexpr UInt64 EPOCH_DIFF = 116444736000000000ULL;
+ UInt64 ticks = (UInt64)ts.tv_sec * 10000000ULL + (UInt64)ts.tv_nsec / 100ULL + EPOCH_DIFF;
+ FILETIME ft;
+ ft.dwLowDateTime = (DWORD)(ticks & 0xFFFFFFFF);
+ ft.dwHighDateTime = (DWORD)(ticks >> 32);
+ return ft;
+ }
+
+ FILETIME creationTime() const {
+ // Linux doesn't have creation time in all filesystems, use ctime (status change)
+ return timespecToFiletime(m_Stat.st_ctim);
+ }
+ FILETIME lastAccessTime() const {
+ return timespecToFiletime(m_Stat.st_atim);
+ }
+ FILETIME lastWriteTime() const {
+ return timespecToFiletime(m_Stat.st_mtim);
+ }
+ UInt32 volumeSerialNumber() const { return (UInt32)m_Stat.st_dev; }
+ UInt64 fileSize() const { return (UInt64)m_Stat.st_size; }
+ UInt32 numberOfLinks() const { return (UInt32)m_Stat.st_nlink; }
+ UInt64 fileInfex() const { return (UInt64)m_Stat.st_ino; }
+
+ bool isArchived() const { return false; }
+ bool isCompressed() const { return false; }
+ bool isDir() const { return S_ISDIR(m_Stat.st_mode); }
+ bool isEncrypted() const { return false; }
+ bool isHidden() const { return false; }
+ bool isNormal() const { return S_ISREG(m_Stat.st_mode); }
+ bool isOffline() const { return false; }
+ bool isReadOnly() const { return !(m_Stat.st_mode & S_IWUSR); }
+ bool iasReparsePoint() const { return S_ISLNK(m_Stat.st_mode); }
+ bool isSparse() const { return false; }
+ bool isSystem() const { return false; }
+ bool isTemporary() const { return false; }
+
+private:
+ bool m_Valid;
+ std::filesystem::path m_Path;
+ struct stat m_Stat;
+};
+
+#endif // _WIN32
+
+class FileBase
+{
+public: // Constructors, destructor, assignment.
+#ifdef _WIN32
+ FileBase() noexcept : m_Handle{INVALID_HANDLE_VALUE} {}
+
+ FileBase(FileBase&& other) noexcept : m_Handle{other.m_Handle}
+ {
+ other.m_Handle = INVALID_HANDLE_VALUE;
+ }
+#else
+ FileBase() noexcept : m_Fd{-1} {}
+
+ FileBase(FileBase&& other) noexcept : m_Fd{other.m_Fd}
+ {
+ other.m_Fd = -1;
+ }
+#endif
+
+ ~FileBase() noexcept { Close(); }
+
+ FileBase(FileBase const&) = delete;
+ FileBase& operator=(FileBase const&) = delete;
+ FileBase& operator=(FileBase&&) = delete;
+
+public: // Operations
+ bool Close() noexcept;
+
+ bool GetPosition(UInt64& position) noexcept;
+ bool GetLength(UInt64& length) const noexcept;
+
+#ifdef _WIN32
+ bool Seek(Int64 distanceToMove, DWORD moveMethod, UInt64& newPosition) noexcept;
+#else
+ bool Seek(Int64 distanceToMove, int whence, UInt64& newPosition) noexcept;
+#endif
+ bool Seek(UInt64 position, UInt64& newPosition) noexcept;
+ bool SeekToBegin() noexcept;
+ bool SeekToEnd(UInt64& newPosition) noexcept;
+
+ // Note: Only the static version (unlike in 7z) because I want FileInfo to hold the
+ // path to the file, and the non-static version is never used (except by the static
+ // version).
+ static bool GetFileInformation(std::filesystem::path const& path,
+ FileInfo* info) noexcept;
+
+protected:
+#ifdef _WIN32
+ bool Create(std::filesystem::path const& path, DWORD desiredAccess, DWORD shareMode,
+ DWORD creationDisposition, DWORD flagsAndAttributes) noexcept;
+#else
+ bool Create(std::filesystem::path const& path, int flags, int mode = 0644) noexcept;
+#endif
+
+protected:
+ static constexpr UInt32 kChunkSizeMax = (1 << 22);
+
+#ifdef _WIN32
+ HANDLE m_Handle;
+#else
+ int m_Fd;
+#endif
+};
+
+class FileIn : public FileBase
+{
+public:
+ using FileBase::FileBase;
+
+public: // Operations
+#ifdef _WIN32
+ bool Open(std::filesystem::path const& filepath, DWORD shareMode,
+ DWORD creationDisposition, DWORD flagsAndAttributes) noexcept;
+ bool OpenShared(std::filesystem::path const& filepath, bool shareForWrite) noexcept;
+#endif
+ bool Open(std::filesystem::path const& filepath) noexcept;
+
+ bool Read(void* data, UInt32 size, UInt32& processedSize) noexcept;
+
+protected:
+ bool Read1(void* data, UInt32 size, UInt32& processedSize) noexcept;
+ bool ReadPart(void* data, UInt32 size, UInt32& processedSize) noexcept;
+};
+
+class FileOut : public FileBase
+{
+public:
+ using FileBase::FileBase;
+
+public: // Operations:
+#ifdef _WIN32
+ bool Open(std::filesystem::path const& fileName, DWORD shareMode,
+ DWORD creationDisposition, DWORD flagsAndAttributes) noexcept;
+#endif
+ bool Open(std::filesystem::path const& fileName) noexcept;
+
+ bool SetTime(const FILETIME* cTime, const FILETIME* aTime,
+ const FILETIME* mTime) noexcept;
+ bool SetMTime(const FILETIME* mTime) noexcept;
+ bool Write(const void* data, UInt32 size, UInt32& processedSize) noexcept;
+
+ bool SetLength(UInt64 length) noexcept;
+ bool SetEndOfFile() noexcept;
+
+protected: // Protected Operations:
+ bool WritePart(const void* data, UInt32 size, UInt32& processedSize) noexcept;
+};
+
+/**
+ * @brief Convert the given wide-string to a path object.
+ *
+ * On Windows: adds the long-path prefix if not present.
+ * On Linux: simply converts wstring to path (no long-path prefix needed).
+ *
+ * @param path The string containing the path.
+ *
+ * @return the created path.
+ */
+inline std::filesystem::path make_path(std::wstring const& pathstr)
+{
+ namespace fs = std::filesystem;
+
+#ifdef _WIN32
+ constexpr const wchar_t* lprefix = L"\\\\?\\";
+ constexpr const wchar_t* unc_prefix = L"\\\\";
+ constexpr const wchar_t* unc_lprefix = L"\\\\?\\UNC\\";
+
+ // If path is already a long path, just return it:
+ if (pathstr.starts_with(lprefix)) {
+ return fs::path{pathstr}.make_preferred();
+ }
+
+ fs::path path{pathstr};
+
+ // Convert to an absolute path:
+ if (!path.is_absolute()) {
+ path = fs::absolute(path);
+ }
+
+ // backslashes
+ path = path.make_preferred();
+
+ // Get rid of duplicate separators and relative moves
+ path = path.lexically_normal();
+
+ const std::wstring pathstr_fixed = path.native();
+
+ // If this is a UNC, the prefix is different
+ if (pathstr_fixed.starts_with(unc_prefix)) {
+ return fs::path{unc_lprefix + pathstr_fixed.substr(2)};
+ }
+
+ // Add the long-path prefix
+ return fs::path{lprefix + pathstr_fixed};
+#else
+ // On Linux, simply convert wstring to path (no long-path prefix needed)
+ fs::path path{pathstr};
+
+ if (!path.is_absolute()) {
+ path = fs::absolute(path);
+ }
+
+ path = path.lexically_normal();
+ return path;
+#endif
+}
+
+} // namespace IO
+
+#endif
diff --git a/libs/archive/src/formatter.h b/libs/archive/src/formatter.h
new file mode 100644
index 0000000..0776bee
--- /dev/null
+++ b/libs/archive/src/formatter.h
@@ -0,0 +1,121 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef ARCHIVE_FORMAT_H
+#define ARCHIVE_FORMAT_H
+
+// This header specialize formatter() for some useful types. It should not be
+// exposed outside of the library. It also contains some useful methods for
+// string manipulation.
+
+#include <format>
+
+#include <cwctype>
+#include <filesystem>
+#include <stdexcept>
+#include <string>
+
+template <>
+struct std::formatter<std::string, wchar_t> : std::formatter<std::wstring, wchar_t>
+{
+ template <typename FormatContext>
+ auto format(std::string const& s, FormatContext& ctx) const
+ {
+ return std::formatter<std::wstring, wchar_t>::format(
+ std::wstring(s.begin(), s.end()), ctx);
+ }
+};
+
+template <>
+struct std::formatter<std::exception, wchar_t> : std::formatter<std::string, wchar_t>
+{
+ template <typename FormatContext>
+ auto format(std::exception const& ex, FormatContext& ctx) const
+ {
+ return std::formatter<std::string, wchar_t>::format(ex.what(), ctx);
+ }
+};
+
+template <>
+struct std::formatter<std::error_code, wchar_t> : std::formatter<std::string, wchar_t>
+{
+ template <typename FormatContext>
+ auto format(std::error_code const& ec, FormatContext& ctx) const
+ {
+ return std::formatter<std::string, wchar_t>::format(ec.message(), ctx);
+ }
+};
+
+template <>
+struct std::formatter<std::filesystem::path, wchar_t>
+ : std::formatter<std::wstring, wchar_t>
+{
+ template <typename FormatContext>
+ auto format(std::filesystem::path const& path, FormatContext& ctx) const
+ {
+ return std::formatter<std::wstring, wchar_t>::format(path.wstring(), ctx);
+ }
+};
+
+namespace ArchiveStrings
+{
+
+/**
+ * @brief Join the element of the given container using the given separator.
+ *
+ * @param c The container. Must be satisfy standard container requirements.
+ * @param sep The separator.
+ *
+ * @return a string containing the element joint, or an empty string if c
+ * is empty.
+ */
+template <class C>
+std::wstring join(C const& c, std::wstring const& sep)
+{
+ auto begin = std::begin(c), end = std::end(c);
+
+ if (begin == end) {
+ return {};
+ }
+ std::wstring r = *begin++;
+ for (; begin != end; ++begin) {
+ r += *begin + sep;
+ }
+
+ return r;
+}
+
+/**
+ * @brief Conver the given string to lowercase.
+ *
+ * @param s The string to convert.
+ *
+ * @return the converted string.
+ */
+inline std::wstring towlower(std::wstring s)
+{
+ std::transform(std::begin(s), std::end(s), std::begin(s), [](wchar_t c) {
+ return static_cast<wchar_t>(::towlower(c));
+ });
+ return s;
+}
+} // namespace ArchiveStrings
+
+#endif
diff --git a/libs/archive/src/inputstream.cpp b/libs/archive/src/inputstream.cpp
new file mode 100644
index 0000000..b44986a
--- /dev/null
+++ b/libs/archive/src/inputstream.cpp
@@ -0,0 +1,70 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "inputstream.h"
+#include "compat.h"
+
+static inline HRESULT ConvertBoolToHRESULT(bool result)
+{
+ if (result) {
+ return S_OK;
+ }
+ DWORD lastError = ::GetLastError();
+ if (lastError == 0) {
+ return E_FAIL;
+ }
+ return HRESULT_FROM_WIN32(lastError);
+}
+
+InputStream::InputStream() {}
+
+InputStream::~InputStream() {}
+
+bool InputStream::Open(std::filesystem::path const& filename)
+{
+ return m_File.Open(filename);
+}
+
+STDMETHODIMP InputStream::Read(void* data, UInt32 size, UInt32* processedSize) throw()
+{
+ UInt32 realProcessedSize;
+ bool result = m_File.Read(data, size, realProcessedSize);
+
+ if (processedSize != nullptr) {
+ *processedSize = realProcessedSize;
+ }
+
+ if (result) {
+ return S_OK;
+ }
+
+ return HRESULT_FROM_WIN32(::GetLastError());
+}
+
+STDMETHODIMP InputStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) throw()
+{
+ UInt64 realNewPosition = offset;
+ bool result = m_File.Seek(offset, seekOrigin, realNewPosition);
+
+ if (newPosition) {
+ *newPosition = realNewPosition;
+ }
+ return ConvertBoolToHRESULT(result);
+}
diff --git a/libs/archive/src/inputstream.h b/libs/archive/src/inputstream.h
new file mode 100644
index 0000000..b305137
--- /dev/null
+++ b/libs/archive/src/inputstream.h
@@ -0,0 +1,54 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef INPUTSTREAM_H
+#define INPUTSTREAM_H
+
+#include <7zip/IStream.h>
+
+#include <filesystem>
+
+#include "fileio.h"
+#include "unknown_impl.h"
+
+/** This class implements an input stream for opening archive files
+ *
+ * Note that the handling on errors could be better.
+ */
+class InputStream : public IInStream
+{
+
+ UNKNOWN_1_INTERFACE(IInStream);
+
+public:
+ InputStream();
+
+ virtual ~InputStream();
+
+ bool Open(std::filesystem::path const& filename);
+
+ STDMETHOD(Read)(void* data, UInt32 size, UInt32* processedSize) throw();
+ STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) throw();
+
+private:
+ IO::FileIn m_File;
+};
+
+#endif // INPUTSTREAM_H
diff --git a/libs/archive/src/instrument.h b/libs/archive/src/instrument.h
new file mode 100644
index 0000000..82dc3f7
--- /dev/null
+++ b/libs/archive/src/instrument.h
@@ -0,0 +1,111 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef ARCHIVE_INSTRUMENT_H
+#define ARCHIVE_INSTRUMENT_H
+
+#include <chrono>
+#include <format>
+
+namespace ArchiveTimers
+{
+
+/**
+ * If INSTRUMENT_ARCHIVE is not defined, we define a Timer() class that is empty and
+ * does nothing, and thus should be optimized out by the compiler.
+ */
+#ifdef INSTRUMENT_ARCHIVE
+
+/**
+ * Small class that can be used to instrument portion of code using a guard.
+ *
+ */
+class Timer
+{
+public:
+ using clock_t = std::chrono::system_clock;
+
+ struct TimerGuard
+ {
+
+ TimerGuard(TimerGuard const&) = delete;
+ TimerGuard(TimerGuard&&) = delete;
+ TimerGuard& operator=(TimerGuard const&) = delete;
+ TimerGuard& operator=(TimerGuard&&) = delete;
+
+ ~TimerGuard()
+ {
+ m_Timer.ncalls++;
+ m_Timer.time += clock_t::now() - m_Start;
+ }
+
+ private:
+ TimerGuard(Timer& timer) : m_Timer{timer}, m_Start{clock_t::now()} {}
+
+ Timer& m_Timer;
+ clock_t::time_point m_Start;
+
+ friend class Timer;
+ };
+
+ /**
+ *
+ */
+ Timer() = default;
+
+ /**
+ * @brief Instrument a portion of code.
+ *
+ * Instrumenting is done by calling `instrument()` and storing the result in a local
+ * variable. The scope of the local guard variable is the scope of the
+ * instrumentation.
+ *
+ * @return a guard to instrument the code.
+ */
+ TimerGuard instrument() { return {*this}; }
+
+ std::wstring toString(std::wstring const& name) const
+ {
+ auto ms = [](auto&& t) {
+ return std::chrono::duration<double, std::milli>(t);
+ };
+ return std::format(
+ L"Instrument '{}': {} calls, total of {}ms, {:.3f}ms per call on average.",
+ name, ncalls, ms(time).count(), ms(time).count() / ncalls);
+ }
+
+private:
+ std::size_t ncalls{0};
+ clock_t::duration time{0};
+};
+
+#else
+
+struct Timer
+{
+ // This is the only method needed:
+ Timer& instrument() { return *this; }
+};
+
+#endif
+
+} // namespace ArchiveTimers
+
+#endif
diff --git a/libs/archive/src/interfaceguids.cpp b/libs/archive/src/interfaceguids.cpp
new file mode 100644
index 0000000..ae6659b
--- /dev/null
+++ b/libs/archive/src/interfaceguids.cpp
@@ -0,0 +1,37 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+// This file instantiates the GUIDs needed for linking with the 7zip code
+#ifdef _WIN32
+#include <initguid.h>
+#else
+#define INITGUID
+#include <Common/MyWindows.h>
+#endif
+
+// extractcallback, opencallback
+#include <7zip/IPassword.h>
+
+// archive, opencallback
+// Note: In a sense, this is unnecessary as IArchive.h includes it
+#include <7zip/IStream.h>
+
+// archive, opencallback
+#include <7zip/Archive/IArchive.h>
diff --git a/libs/archive/src/library.h b/libs/archive/src/library.h
new file mode 100644
index 0000000..9fec56d
--- /dev/null
+++ b/libs/archive/src/library.h
@@ -0,0 +1,164 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef ARCHIVE_LIBRARY_H
+#define ARCHIVE_LIBRARY_H
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <Common/MyWindows.h>
+#include <cstdlib>
+#include <dlfcn.h>
+#include <string>
+#include <unistd.h>
+#include <vector>
+#endif
+
+/**
+ * Very small wrapper around shared libraries (DLL on Windows, .so on Linux).
+ */
+class ALibrary
+{
+public:
+#ifdef _WIN32
+ ALibrary(const char* path) : m_Module{nullptr}, m_LastError{ERROR_SUCCESS}
+ {
+ m_Module = LoadLibraryA(path);
+ if (m_Module == nullptr) {
+ updateLastError();
+ }
+ }
+
+ ~ALibrary()
+ {
+ if (m_Module) {
+ FreeLibrary(m_Module);
+ }
+ }
+
+ template <class T>
+ T resolve(const char* procName)
+ {
+ if (!m_Module) {
+ return nullptr;
+ }
+ auto proc = GetProcAddress(m_Module, procName);
+ if (!proc) {
+ updateLastError();
+ return nullptr;
+ }
+ return reinterpret_cast<T>(proc);
+ }
+
+ DWORD getLastError() const { return m_LastError; }
+ bool isOpen() const { return m_Module != nullptr; }
+ operator bool() const { return isOpen(); }
+
+private:
+ void updateLastError() { m_LastError = ::GetLastError(); }
+
+ HMODULE m_Module;
+ DWORD m_LastError;
+
+#else // Linux
+
+ ALibrary(const char* path) : m_Handle{nullptr}, m_LastError{0}
+ {
+ // Find the directory containing our own executable
+ std::string exeDir;
+ char selfPath[4096];
+ ssize_t len = readlink("/proc/self/exe", selfPath, sizeof(selfPath) - 1);
+ if (len > 0) {
+ selfPath[len] = '\0';
+ exeDir = std::string(selfPath);
+ auto slash = exeDir.rfind('/');
+ if (slash != std::string::npos)
+ exeDir = exeDir.substr(0, slash);
+ }
+
+ // AppImage: check MO2_DLLS_DIR env var first (writable dir next to AppImage)
+ std::string envDlls;
+ const char* envVal = std::getenv("MO2_DLLS_DIR");
+ if (envVal && envVal[0] != '\0') {
+ envDlls = envVal;
+ }
+
+ // Try bundled 7z.so locations first (env override, exe dir, dlls/ subdir)
+ std::vector<std::string> tryPaths;
+ if (!envDlls.empty()) {
+ tryPaths.push_back(envDlls + "/7z.so");
+ }
+ tryPaths.push_back(exeDir + "/7z.so");
+ tryPaths.push_back(exeDir + "/dlls/7z.so");
+ tryPaths.push_back("7z.so");
+ tryPaths.push_back("dlls/7z.so");
+ tryPaths.push_back("/usr/lib/p7zip/7z.so");
+ tryPaths.push_back("/usr/lib64/p7zip/7z.so");
+ tryPaths.push_back("/usr/libexec/p7zip/7z.so");
+
+ for (const auto& tryPath : tryPaths) {
+ m_Handle = dlopen(tryPath.c_str(), RTLD_LAZY);
+ if (m_Handle) break;
+ }
+
+ if (!m_Handle) {
+ // Last resort: try the original path as-is
+ m_Handle = dlopen(path, RTLD_LAZY);
+ }
+
+ if (!m_Handle) {
+ m_LastError = 1;
+ }
+ }
+
+ ~ALibrary()
+ {
+ if (m_Handle) {
+ dlclose(m_Handle);
+ }
+ }
+
+ template <class T>
+ T resolve(const char* procName)
+ {
+ if (!m_Handle) {
+ return nullptr;
+ }
+ void* sym = dlsym(m_Handle, procName);
+ if (!sym) {
+ m_LastError = 1;
+ return nullptr;
+ }
+ return reinterpret_cast<T>(sym);
+ }
+
+ DWORD getLastError() const { return m_LastError; }
+ bool isOpen() const { return m_Handle != nullptr; }
+ operator bool() const { return isOpen(); }
+
+private:
+ void* m_Handle;
+ DWORD m_LastError;
+
+#endif
+};
+
+#endif
diff --git a/libs/archive/src/multioutputstream.cpp b/libs/archive/src/multioutputstream.cpp
new file mode 100644
index 0000000..a2eca06
--- /dev/null
+++ b/libs/archive/src/multioutputstream.cpp
@@ -0,0 +1,142 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "multioutputstream.h"
+#include "compat.h"
+
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+static inline HRESULT ConvertBoolToHRESULT(bool result)
+{
+ if (result) {
+ return S_OK;
+ }
+ DWORD lastError = ::GetLastError();
+ if (lastError == 0) {
+ return E_FAIL;
+ }
+ return HRESULT_FROM_WIN32(lastError);
+}
+
+//////////////////////////
+// MultiOutputStream
+
+MultiOutputStream::MultiOutputStream(WriteCallback callback) : m_WriteCallback(callback)
+{}
+
+MultiOutputStream::~MultiOutputStream() {}
+
+HRESULT MultiOutputStream::Close()
+{
+ for (auto& file : m_Files) {
+ file.Close();
+ }
+ return S_OK;
+}
+
+bool MultiOutputStream::Open(std::vector<std::filesystem::path> const& filepaths)
+{
+ m_ProcessedSize = 0;
+ bool ok = true;
+ m_Files.clear();
+ for (auto& path : filepaths) {
+ m_Files.emplace_back();
+ if (!m_Files.back().Open(path)) {
+ ok = false;
+ }
+ }
+ return ok;
+}
+
+STDMETHODIMP MultiOutputStream::Write(const void* data, UInt32 size,
+ UInt32* processedSize) throw()
+{
+ bool update_processed(true);
+ for (auto& file : m_Files) {
+ UInt32 realProcessedSize;
+ if (!file.Write(data, size, realProcessedSize)) {
+ return ConvertBoolToHRESULT(false);
+ }
+ if (update_processed) {
+ m_ProcessedSize += realProcessedSize;
+ if (m_WriteCallback) {
+ m_WriteCallback(realProcessedSize, m_ProcessedSize);
+ }
+ update_processed = false;
+ }
+ if (processedSize != nullptr) {
+ *processedSize = realProcessedSize;
+ }
+ }
+ return S_OK;
+}
+
+STDMETHODIMP MultiOutputStream::Seek(Int64 offset, UInt32 seekOrigin,
+ UInt64* newPosition) throw()
+{
+ if (seekOrigin >= 3)
+ return STG_E_INVALIDFUNCTION;
+
+ bool result = true;
+ for (auto& file : m_Files) {
+ UInt64 realNewPosition;
+ result = file.Seek(offset, seekOrigin, realNewPosition);
+ if (newPosition)
+ *newPosition = realNewPosition;
+ }
+ return ConvertBoolToHRESULT(result);
+}
+
+STDMETHODIMP MultiOutputStream::SetSize(UInt64 newSize) throw()
+{
+ bool result = true;
+ for (auto& file : m_Files) {
+ UInt64 currentPos;
+#ifdef _WIN32
+ if (!file.Seek(0, FILE_CURRENT, currentPos))
+#else
+ if (!file.Seek(0, SEEK_CUR, currentPos))
+#endif
+ return E_FAIL;
+ bool cresult = file.SetLength(newSize);
+ UInt64 currentPos2;
+ result = result && cresult && file.Seek(currentPos, currentPos2);
+ }
+ return result ? S_OK : E_FAIL;
+}
+
+HRESULT MultiOutputStream::GetSize(UInt64* size)
+{
+ if (m_Files.empty()) {
+ return ConvertBoolToHRESULT(false);
+ }
+ return ConvertBoolToHRESULT(m_Files[0].GetLength(*size));
+}
+
+bool MultiOutputStream::SetMTime(FILETIME const* mTime)
+{
+ for (auto& file : m_Files) {
+ file.SetMTime(mTime);
+ }
+ return true;
+}
diff --git a/libs/archive/src/multioutputstream.h b/libs/archive/src/multioutputstream.h
new file mode 100644
index 0000000..624ab5b
--- /dev/null
+++ b/libs/archive/src/multioutputstream.h
@@ -0,0 +1,106 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MULTIOUTPUTSTREAM_H
+#define MULTIOUTPUTSTREAM_H
+
+#include <filesystem>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <7zip/IStream.h>
+
+#include "fileio.h"
+#include "unknown_impl.h"
+
+/** This class allows you to open and output to multiple file handles at a time.
+ * It implements the ISequentalOutputStream interface and has some extra functions
+ * which are used by the CArchiveExtractCallback class to basically open and
+ * set the timestamp on all the files.
+ *
+ * Note that the handling on errors could be better.
+ */
+class MultiOutputStream : public IOutStream
+{
+
+ UNKNOWN_1_INTERFACE(IOutStream);
+
+public:
+ // Callback for write. Args are: 1) number of bytes that have been
+ // written for this call, 2) number of bytes that have been written
+ // in total.
+ using WriteCallback = std::function<void(UInt32, UInt64)>;
+
+ MultiOutputStream(WriteCallback callback = {});
+
+ virtual ~MultiOutputStream();
+
+ /** Opens the supplied files.
+ *
+ * @returns true if all went OK, false if any file failed to open
+ */
+ bool Open(std::vector<std::filesystem::path> const& fileNames);
+
+ /** Closes all the files opened by the last open
+ *
+ * Note if there are any errors, the code will merely report the last one.
+ */
+ HRESULT Close();
+
+ /** Sets the modification time on the open files
+ *
+ * @returns true if all files had the time set succesfully, false otherwise
+ * note that this will give up as soon as it gets an error
+ */
+ bool SetMTime(FILETIME const* mTime);
+
+ // ISequentialOutStream interface
+
+ /** Write data to all the streams
+ *
+ * The processedSize returned will be the same as size, unless
+ * there was an error, in which case it might or might not be different.
+ * @warn If an error happens, the code will not attempt any further writing,
+ * so some files might not get written to at all
+ */
+ STDMETHOD(Write)(const void* data, UInt32 size, UInt32* processedSize) throw() override;
+
+ STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) throw() override;
+ STDMETHOD(SetSize)(UInt64 newSize) throw() override;
+ HRESULT GetSize(UInt64* size);
+
+private:
+ WriteCallback m_WriteCallback;
+
+ /** This is the amount of data written to *any one* file.
+ *
+ * If there are errors writing to one of the files, this might or might
+ * not match what was actually written to another of the files
+ */
+ UInt64 m_ProcessedSize;
+
+ /** All the files opened for this 'stream'
+ *
+ */
+ std::vector<IO::FileOut> m_Files;
+};
+
+#endif // MULTIOUTPUTSTREAM_H
diff --git a/libs/archive/src/opencallback.cpp b/libs/archive/src/opencallback.cpp
new file mode 100644
index 0000000..9dd0e24
--- /dev/null
+++ b/libs/archive/src/opencallback.cpp
@@ -0,0 +1,168 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "opencallback.h"
+#include "compat.h"
+
+#include "inputstream.h"
+#include "propertyvariant.h"
+
+#include <format>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "fileio.h"
+
+#define UNUSED(x)
+
+CArchiveOpenCallback::CArchiveOpenCallback(Archive::PasswordCallback passwordCallback,
+ Archive::LogCallback logCallback,
+ std::filesystem::path const& filepath)
+ : m_PasswordCallback(passwordCallback), m_LogCallback(logCallback),
+ m_Path(filepath), m_SubArchiveMode(false)
+{
+ if (!exists(filepath)) {
+ throw std::runtime_error("invalid archive path");
+ }
+
+ if (!IO::FileBase::GetFileInformation(filepath, &m_FileInfo)) {
+ throw std::runtime_error("failed to retrieve file information");
+ }
+}
+
+/* -------------------- IArchiveOpenCallback -------------------- */
+STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64* UNUSED(files),
+ const UInt64* UNUSED(bytes)) throw()
+{
+ return S_OK;
+}
+
+STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64* UNUSED(files),
+ const UInt64* UNUSED(bytes)) throw()
+{
+ return S_OK;
+}
+
+/* -------------------- ICryptoGetTextPassword -------------------- */
+/* This apparently implements ICryptoGetTextPassword but even with a passworded
+ * archive it doesn't seem to be called. There is also another API which isn't
+ * implemented.
+ */
+STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR* passwordOut) throw()
+{
+ if (!m_PasswordCallback) {
+ return E_ABORT;
+ }
+ m_Password = m_PasswordCallback();
+ *passwordOut = ::SysAllocString(m_Password.c_str());
+ return *passwordOut != 0 ? S_OK : E_OUTOFMEMORY;
+}
+
+/* -------------------- IArchiveOpenSetSubArchiveName -------------------- */
+/* I don't know what this does or how you call it. */
+STDMETHODIMP CArchiveOpenCallback::SetSubArchiveName(const wchar_t* name) throw()
+{
+ m_SubArchiveMode = true;
+ m_SubArchiveName = name;
+ return S_OK;
+}
+
+/* -------------------- IArchiveOpenVolumeCallback -------------------- */
+
+STDMETHODIMP CArchiveOpenCallback::GetProperty(PROPID propID,
+ PROPVARIANT* value) throw()
+{
+ // A scan of the source code indicates that the only things that ever call the
+ // IArchiveOpenVolumeCallback interface ask for the file name and size.
+ PropertyVariant& prop = *static_cast<PropertyVariant*>(value);
+
+ switch (propID) {
+ case kpidName: {
+ if (m_SubArchiveMode) {
+ prop = m_SubArchiveName;
+ } else {
+ // Note: Need to call .native(), otherwize we get a link error because we try to
+ // assign a fs::path to the variant.
+ prop = m_Path.filename().wstring();
+ }
+ } break;
+
+ case kpidIsDir:
+ prop = m_FileInfo.isDir();
+ break;
+ case kpidSize:
+ prop = m_FileInfo.fileSize();
+ break;
+ case kpidAttrib:
+ prop = m_FileInfo.fileAttributes();
+ break;
+ case kpidCTime:
+ prop = m_FileInfo.creationTime();
+ break;
+ case kpidATime:
+ prop = m_FileInfo.lastAccessTime();
+ break;
+ case kpidMTime:
+ prop = m_FileInfo.lastWriteTime();
+ break;
+
+ default:
+ m_LogCallback(Archive::LogLevel::Warning,
+ std::format(L"Unexpected property {}.", propID));
+ }
+ return S_OK;
+}
+
+STDMETHODIMP CArchiveOpenCallback::GetStream(const wchar_t* name,
+ IInStream** inStream) throw()
+{
+ *inStream = nullptr;
+
+ // this function will be called repeatedly for split archives, `name` will
+ // have increasing numbers in the extension and S_FALSE must be returned
+ // when a filename doesn't exist so the search stops
+
+ if (!name) {
+ return S_FALSE;
+ }
+
+ // `name` is just the filename, so build a path from the directory that
+ // contained the last file
+ const auto path = m_Path.parent_path() / name;
+
+ if (!exists(m_FileInfo.path()) || m_FileInfo.isDir()) {
+ return S_FALSE;
+ }
+
+ if (!IO::FileBase::GetFileInformation(path, &m_FileInfo)) {
+ return S_FALSE;
+ }
+
+ CComPtr<InputStream> inFile(new InputStream);
+
+ if (!inFile->Open(m_FileInfo.path())) {
+ return ::GetLastError();
+ }
+
+ *inStream = inFile.Detach();
+ return S_OK;
+}
diff --git a/libs/archive/src/opencallback.h b/libs/archive/src/opencallback.h
new file mode 100644
index 0000000..44367be
--- /dev/null
+++ b/libs/archive/src/opencallback.h
@@ -0,0 +1,75 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef OPENCALLBACK_H
+#define OPENCALLBACK_H
+
+#include <filesystem>
+#include <string>
+
+#include <7zip/Archive/IArchive.h>
+#include <7zip/IPassword.h>
+
+#include "archive.h"
+#include "fileio.h"
+#include "unknown_impl.h"
+
+class CArchiveOpenCallback : public IArchiveOpenCallback,
+ public IArchiveOpenVolumeCallback,
+ public ICryptoGetTextPassword,
+ public IArchiveOpenSetSubArchiveName
+{
+
+ UNKNOWN_4_INTERFACE(IArchiveOpenCallback, IArchiveOpenVolumeCallback,
+ ICryptoGetTextPassword, IArchiveOpenSetSubArchiveName);
+
+public:
+ CArchiveOpenCallback(Archive::PasswordCallback passwordCallback,
+ Archive::LogCallback logCallback,
+ std::filesystem::path const& filepath);
+
+ virtual ~CArchiveOpenCallback() {}
+
+ const std::wstring& GetPassword() const { return m_Password; }
+
+ Z7_IFACE_COM7_IMP(IArchiveOpenCallback)
+ Z7_IFACE_COM7_IMP(IArchiveOpenVolumeCallback)
+
+ // ICryptoGetTextPassword interface
+ STDMETHOD(CryptoGetTextPassword)(BSTR* password) throw();
+ // Not implemented STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR
+ // *password);
+
+ // IArchiveOpenSetSubArchiveName interface
+ STDMETHOD(SetSubArchiveName)(const wchar_t* name) throw();
+
+private:
+ Archive::PasswordCallback m_PasswordCallback;
+ Archive::LogCallback m_LogCallback;
+ std::wstring m_Password;
+
+ std::filesystem::path m_Path;
+ IO::FileInfo m_FileInfo;
+
+ bool m_SubArchiveMode;
+ std::wstring m_SubArchiveName;
+};
+
+#endif // OPENCALLBACK_H
diff --git a/libs/archive/src/propertyvariant.cpp b/libs/archive/src/propertyvariant.cpp
new file mode 100644
index 0000000..fe882b2
--- /dev/null
+++ b/libs/archive/src/propertyvariant.cpp
@@ -0,0 +1,214 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "propertyvariant.h"
+#include "compat.h"
+
+#ifdef _WIN32
+#include <guiddef.h>
+#endif
+
+#include <stdexcept>
+#include <stdint.h>
+#include <string>
+
+PropertyVariant::PropertyVariant()
+{
+ PropVariantInit(this);
+}
+
+PropertyVariant::~PropertyVariant()
+{
+ clear();
+}
+
+void PropertyVariant::clear()
+{
+ PropVariantClear(this);
+}
+
+// Arguably the behviours for empty here are wrong.
+template <>
+PropertyVariant::operator bool() const
+{
+ switch (vt) {
+ case VT_EMPTY:
+ return false;
+
+ case VT_BOOL:
+ return boolVal != VARIANT_FALSE;
+
+ default:
+ throw std::runtime_error("Property is not a bool");
+ }
+}
+
+template <>
+PropertyVariant::operator uint64_t() const
+{
+ switch (vt) {
+ case VT_EMPTY:
+ return 0;
+
+ case VT_UI1:
+ return bVal;
+
+ case VT_UI2:
+ return uiVal;
+
+ case VT_UI4:
+ return ulVal;
+
+ case VT_UI8:
+ return static_cast<uint64_t>(uhVal.QuadPart);
+
+ default:
+ throw std::runtime_error("Property is not an unsigned integer");
+ }
+}
+
+template <>
+PropertyVariant::operator uint32_t() const
+{
+ switch (vt) {
+ case VT_EMPTY:
+ return 0;
+
+ case VT_UI1:
+ return bVal;
+
+ case VT_UI2:
+ return uiVal;
+
+ case VT_UI4:
+ return ulVal;
+
+ default:
+ throw std::runtime_error("Property is not an unsigned integer");
+ }
+}
+
+template <>
+PropertyVariant::operator std::wstring() const
+{
+ switch (vt) {
+ case VT_EMPTY:
+ return L"";
+
+ case VT_BSTR:
+ return std::wstring(bstrVal, ::SysStringLen(bstrVal));
+
+ default:
+ throw std::runtime_error("Property is not a string");
+ }
+}
+
+// This is what he does, though it looks rather a strange use of the property
+template <>
+PropertyVariant::operator std::string() const
+{
+ switch (vt) {
+ case VT_EMPTY:
+ return "";
+
+ case VT_BSTR:
+ // If he can do a memcpy, I can do a reinterpret case
+ return std::string(reinterpret_cast<char const*>(bstrVal),
+ ::SysStringByteLen(bstrVal));
+
+ default:
+ throw std::runtime_error("Property is not a string");
+ }
+}
+
+// This is what he does, though it looks rather a strange use of the property
+template <>
+PropertyVariant::operator GUID() const
+{
+ switch (vt) {
+ case VT_BSTR:
+ // He did a cast too!
+ return *reinterpret_cast<const GUID*>(bstrVal);
+
+ default:
+ throw std::runtime_error("Property is not a GUID (string)");
+ }
+}
+
+template <>
+PropertyVariant::operator FILETIME() const
+{
+ switch (vt) {
+ case VT_FILETIME:
+ return filetime;
+
+ default:
+ throw std::runtime_error("Property is not a file time");
+ }
+}
+
+// Assignments
+template <>
+PropertyVariant& PropertyVariant::operator=(std::wstring const& str)
+{
+ clear();
+ vt = VT_BSTR;
+ bstrVal = ::SysAllocString(str.c_str());
+ if (bstrVal == NULL) {
+ throw std::bad_alloc();
+ }
+ return *this;
+}
+
+template <>
+PropertyVariant& PropertyVariant::operator=(bool const& n)
+{
+ clear();
+ vt = VT_BOOL;
+ boolVal = n ? VARIANT_TRUE : VARIANT_FALSE;
+ return *this;
+}
+
+template <>
+PropertyVariant& PropertyVariant::operator=(FILETIME const& n)
+{
+ clear();
+ vt = VT_FILETIME;
+ filetime = n;
+ return *this;
+}
+
+template <>
+PropertyVariant& PropertyVariant::operator=(uint32_t const& n)
+{
+ clear();
+ vt = VT_UI4;
+ ulVal = n;
+ return *this;
+}
+
+template <>
+PropertyVariant& PropertyVariant::operator=(uint64_t const& n)
+{
+ clear();
+ vt = VT_UI8;
+ uhVal.QuadPart = n;
+ return *this;
+}
diff --git a/libs/archive/src/propertyvariant.h b/libs/archive/src/propertyvariant.h
new file mode 100644
index 0000000..0748d94
--- /dev/null
+++ b/libs/archive/src/propertyvariant.h
@@ -0,0 +1,56 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef PROPERTYVARIANT_H
+#define PROPERTYVARIANT_H
+
+#ifdef _WIN32
+#include <PropIdl.h>
+#else
+#include <Common/MyWindows.h>
+#endif
+
+/** This class implements a wrapper round the PROPVARIANT structure which
+ * makes it a little friendler to use and a little more C++ish
+ *
+ * Sadly the inheritance needs to be public. Once you have a pointer to the
+ * base class you can do pretty much anything to it anyway.
+ */
+class PropertyVariant : public tagPROPVARIANT
+{
+public:
+ PropertyVariant();
+ ~PropertyVariant();
+
+ // clears the property should you wish to overwrite it with another one.
+ void clear();
+
+ bool is_empty() { return vt == VT_EMPTY; }
+
+ // It's too much like hard work listing all the valid ones. If it doesn't
+ // link, then you need to implement it...
+ template <typename T>
+ explicit operator T() const;
+
+ template <typename T>
+ PropertyVariant& operator=(T const&);
+};
+
+#endif // PROPVARIANT_H
diff --git a/libs/archive/src/unknown_impl.h b/libs/archive/src/unknown_impl.h
new file mode 100644
index 0000000..78e9f4a
--- /dev/null
+++ b/libs/archive/src/unknown_impl.h
@@ -0,0 +1,191 @@
+/*
+Mod Organizer archive handling
+
+Copyright (C) 2012 Sebastian Herbord, 2020 MO2 Team. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 3 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef UNKNOWN_IMPL_H
+#define UNKNOWN_IMPL_H
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#else
+#include <Common/MyWindows.h>
+#include <atomic>
+#endif
+
+/* This implements a common way of creating classes which implement one or more
+ * COM interfaces.
+ *
+ * Your implementaton should be something like this
+ *
+ * class MyImplementation :
+ * public interface1,
+ * public interface2 //etc to taste
+ * {
+ * UNKNOWN_1_INTERFACE(interface1);
+ * or UNKNOWN_2_INTERFACE(interface1, interface2); etc
+ *
+ * It would probably be possible to do something very magic with macros
+ * that allowed you to do
+ * class My_Implmentation : UNKNOWN_IMPL_n(interface1, ...)
+ *
+ * that did all the above for you, but I can't see how to maintain the braces
+ * in order to make the class declaration look nice.
+ *
+ * You might ask why I have e-implemented this from the 7-zip stuff (sort of).
+ *
+ * Well,
+ *
+ * Firstly, the 7zip macros are slightly buggy (they don't null the outpointer
+ * if the interface isn't found), and they aren't thread safe.
+ *
+ * Secondly, although 7-zip has a requirement to run on platforms other than
+ * windows, we don't. So I changed this to use the Com functionality supplied
+ * by windows, which (potentially) has better debugging facilities
+ *
+ * Thirdly, this removes a number of dependencies on 7-zip source which aren't
+ * really part of the API
+ */
+
+#ifdef _WIN32
+
+/* Do not use these macros, use the ones at the bottom */
+#define UNKNOWN__INTERFACE_BEGIN \
+public: \
+ STDMETHOD_(ULONG, AddRef)() \
+ { \
+ return ::InterlockedIncrement(&m_RefCount__); \
+ } \
+ \
+ STDMETHOD_(ULONG, Release)() \
+ { \
+ ULONG res = ::InterlockedDecrement(&m_RefCount__); \
+ if (res == 0) { \
+ delete this; \
+ } \
+ return res; \
+ } \
+ \
+ STDMETHOD(QueryInterface)(REFGUID iid, void** outObject) \
+ { \
+ if (iid == IID_IUnknown) {
+
+#define UNKNOWN__INTERFACE_UNKNOWN(interface) \
+ *outObject = static_cast<IUnknown*>(static_cast<interface*>(this)); \
+ }
+
+#define UNKNOWN__INTERFACE_NAME(interface) \
+ else if (iid == IID_##interface) \
+ { \
+ *outObject = static_cast<interface*>(this); \
+ }
+
+#define UNKNOWN__INTERFACE_END \
+ else \
+ { \
+ *outObject = nullptr; \
+ return E_NOINTERFACE; \
+ } \
+ AddRef(); \
+ return S_OK; \
+ } \
+ \
+private: \
+ ULONG m_RefCount__ = 0
+
+#else // Linux
+
+/* Do not use these macros, use the ones at the bottom */
+#define UNKNOWN__INTERFACE_BEGIN \
+public: \
+ STDMETHOD_(ULONG, AddRef)() \
+ { \
+ return ++m_RefCount__; \
+ } \
+ \
+ STDMETHOD_(ULONG, Release)() \
+ { \
+ ULONG res = --m_RefCount__; \
+ if (res == 0) { \
+ delete this; \
+ } \
+ return res; \
+ } \
+ \
+ STDMETHOD(QueryInterface)(REFGUID iid, void** outObject) \
+ { \
+ if (iid == IID_IUnknown) {
+
+#define UNKNOWN__INTERFACE_UNKNOWN(interface) \
+ *outObject = static_cast<IUnknown*>(static_cast<interface*>(this)); \
+ }
+
+#define UNKNOWN__INTERFACE_NAME(interface) \
+ else if (iid == IID_##interface) \
+ { \
+ *outObject = static_cast<interface*>(this); \
+ }
+
+#define UNKNOWN__INTERFACE_END \
+ else \
+ { \
+ *outObject = nullptr; \
+ return E_NOINTERFACE; \
+ } \
+ AddRef(); \
+ return S_OK; \
+ } \
+ \
+private: \
+ std::atomic<ULONG> m_RefCount__{0}
+
+#endif // _WIN32
+
+/* These are the macros you should be using */
+#define UNKNOWN_1_INTERFACE(interface) \
+ UNKNOWN__INTERFACE_BEGIN \
+ UNKNOWN__INTERFACE_UNKNOWN(interface) \
+ UNKNOWN__INTERFACE_NAME(interface) \
+ UNKNOWN__INTERFACE_END
+
+#define UNKNOWN_2_INTERFACE(interface1, interface2) \
+ UNKNOWN__INTERFACE_BEGIN \
+ UNKNOWN__INTERFACE_UNKNOWN(interface1) \
+ UNKNOWN__INTERFACE_NAME(interface1) \
+ UNKNOWN__INTERFACE_NAME(interface2) \
+ UNKNOWN__INTERFACE_END
+
+#define UNKNOWN_3_INTERFACE(interface1, interface2, interface3) \
+ UNKNOWN__INTERFACE_BEGIN \
+ UNKNOWN__INTERFACE_UNKNOWN(interface1) \
+ UNKNOWN__INTERFACE_NAME(interface1) \
+ UNKNOWN__INTERFACE_NAME(interface2) \
+ UNKNOWN__INTERFACE_NAME(interface3) \
+ UNKNOWN__INTERFACE_END
+
+#define UNKNOWN_4_INTERFACE(interface1, interface2, interface3, interface4) \
+ UNKNOWN__INTERFACE_BEGIN \
+ UNKNOWN__INTERFACE_UNKNOWN(interface1) \
+ UNKNOWN__INTERFACE_NAME(interface1) \
+ UNKNOWN__INTERFACE_NAME(interface2) \
+ UNKNOWN__INTERFACE_NAME(interface3) \
+ UNKNOWN__INTERFACE_NAME(interface4) \
+ UNKNOWN__INTERFACE_END
+
+#endif // UNKNOWN_IMPL_H
diff --git a/libs/archive/src/version.rc b/libs/archive/src/version.rc
new file mode 100644
index 0000000..0fb4cf0
--- /dev/null
+++ b/libs/archive/src/version.rc
@@ -0,0 +1,28 @@
+#include "Winver.h"
+
+#define VER_FILEVERSION 2,0,0
+#define VER_FILEVERSION_STR 2,0,0
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VER_FILEVERSION
+PRODUCTVERSION VER_FILEVERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEFLAGS (0)
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "Mod Organizer 2 Team"
+ VALUE "FileVersion", VER_FILEVERSION_STR
+ END
+END
+
+BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1200
+END
+END
diff --git a/libs/archive/thirdparty/C/7zTypes.h b/libs/archive/thirdparty/C/7zTypes.h
new file mode 100644
index 0000000..5b77420
--- /dev/null
+++ b/libs/archive/thirdparty/C/7zTypes.h
@@ -0,0 +1,597 @@
+/* 7zTypes.h -- Basic types
+2024-01-24 : Igor Pavlov : Public domain */
+
+#ifndef ZIP7_7Z_TYPES_H
+#define ZIP7_7Z_TYPES_H
+
+#ifdef _WIN32
+/* #include <windows.h> */
+#else
+#include <errno.h>
+#endif
+
+#include <stddef.h>
+
+#ifndef EXTERN_C_BEGIN
+#ifdef __cplusplus
+#define EXTERN_C_BEGIN extern "C" {
+#define EXTERN_C_END }
+#else
+#define EXTERN_C_BEGIN
+#define EXTERN_C_END
+#endif
+#endif
+
+EXTERN_C_BEGIN
+
+#define SZ_OK 0
+
+#define SZ_ERROR_DATA 1
+#define SZ_ERROR_MEM 2
+#define SZ_ERROR_CRC 3
+#define SZ_ERROR_UNSUPPORTED 4
+#define SZ_ERROR_PARAM 5
+#define SZ_ERROR_INPUT_EOF 6
+#define SZ_ERROR_OUTPUT_EOF 7
+#define SZ_ERROR_READ 8
+#define SZ_ERROR_WRITE 9
+#define SZ_ERROR_PROGRESS 10
+#define SZ_ERROR_FAIL 11
+#define SZ_ERROR_THREAD 12
+
+#define SZ_ERROR_ARCHIVE 16
+#define SZ_ERROR_NO_ARCHIVE 17
+
+typedef int SRes;
+
+
+#ifdef _MSC_VER
+ #if _MSC_VER > 1200
+ #define MY_ALIGN(n) __declspec(align(n))
+ #else
+ #define MY_ALIGN(n)
+ #endif
+#else
+ /*
+ // C11/C++11:
+ #include <stdalign.h>
+ #define MY_ALIGN(n) alignas(n)
+ */
+ #define MY_ALIGN(n) __attribute__ ((aligned(n)))
+#endif
+
+
+#ifdef _WIN32
+
+/* typedef DWORD WRes; */
+typedef unsigned WRes;
+#define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x)
+
+// #define MY_HRES_ERROR_INTERNAL_ERROR MY_SRes_HRESULT_FROM_WRes(ERROR_INTERNAL_ERROR)
+
+#else // _WIN32
+
+// #define ENV_HAVE_LSTAT
+typedef int WRes;
+
+// (FACILITY_ERRNO = 0x800) is 7zip's FACILITY constant to represent (errno) errors in HRESULT
+#define MY_FACILITY_ERRNO 0x800
+#define MY_FACILITY_WIN32 7
+#define MY_FACILITY_WRes MY_FACILITY_ERRNO
+
+#define MY_HRESULT_FROM_errno_CONST_ERROR(x) ((HRESULT)( \
+ ( (HRESULT)(x) & 0x0000FFFF) \
+ | (MY_FACILITY_WRes << 16) \
+ | (HRESULT)0x80000000 ))
+
+#define MY_SRes_HRESULT_FROM_WRes(x) \
+ ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : MY_HRESULT_FROM_errno_CONST_ERROR(x))
+
+// we call macro HRESULT_FROM_WIN32 for system errors (WRes) that are (errno)
+#define HRESULT_FROM_WIN32(x) MY_SRes_HRESULT_FROM_WRes(x)
+
+/*
+#define ERROR_FILE_NOT_FOUND 2L
+#define ERROR_ACCESS_DENIED 5L
+#define ERROR_NO_MORE_FILES 18L
+#define ERROR_LOCK_VIOLATION 33L
+#define ERROR_FILE_EXISTS 80L
+#define ERROR_DISK_FULL 112L
+#define ERROR_NEGATIVE_SEEK 131L
+#define ERROR_ALREADY_EXISTS 183L
+#define ERROR_DIRECTORY 267L
+#define ERROR_TOO_MANY_POSTS 298L
+
+#define ERROR_INTERNAL_ERROR 1359L
+#define ERROR_INVALID_REPARSE_DATA 4392L
+#define ERROR_REPARSE_TAG_INVALID 4393L
+#define ERROR_REPARSE_TAG_MISMATCH 4394L
+*/
+
+// we use errno equivalents for some WIN32 errors:
+
+#define ERROR_INVALID_PARAMETER EINVAL
+#define ERROR_INVALID_FUNCTION EINVAL
+#define ERROR_ALREADY_EXISTS EEXIST
+#define ERROR_FILE_EXISTS EEXIST
+#define ERROR_PATH_NOT_FOUND ENOENT
+#define ERROR_FILE_NOT_FOUND ENOENT
+#define ERROR_DISK_FULL ENOSPC
+// #define ERROR_INVALID_HANDLE EBADF
+
+// we use FACILITY_WIN32 for errors that has no errno equivalent
+// Too many posts were made to a semaphore.
+#define ERROR_TOO_MANY_POSTS ((HRESULT)0x8007012AL)
+#define ERROR_INVALID_REPARSE_DATA ((HRESULT)0x80071128L)
+#define ERROR_REPARSE_TAG_INVALID ((HRESULT)0x80071129L)
+
+// if (MY_FACILITY_WRes != FACILITY_WIN32),
+// we use FACILITY_WIN32 for COM errors:
+#define E_OUTOFMEMORY ((HRESULT)0x8007000EL)
+#define E_INVALIDARG ((HRESULT)0x80070057L)
+#define MY_E_ERROR_NEGATIVE_SEEK ((HRESULT)0x80070083L)
+
+/*
+// we can use FACILITY_ERRNO for some COM errors, that have errno equivalents:
+#define E_OUTOFMEMORY MY_HRESULT_FROM_errno_CONST_ERROR(ENOMEM)
+#define E_INVALIDARG MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL)
+#define MY_E_ERROR_NEGATIVE_SEEK MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL)
+*/
+
+#define TEXT(quote) quote
+
+#define FILE_ATTRIBUTE_READONLY 0x0001
+#define FILE_ATTRIBUTE_HIDDEN 0x0002
+#define FILE_ATTRIBUTE_SYSTEM 0x0004
+#define FILE_ATTRIBUTE_DIRECTORY 0x0010
+#define FILE_ATTRIBUTE_ARCHIVE 0x0020
+#define FILE_ATTRIBUTE_DEVICE 0x0040
+#define FILE_ATTRIBUTE_NORMAL 0x0080
+#define FILE_ATTRIBUTE_TEMPORARY 0x0100
+#define FILE_ATTRIBUTE_SPARSE_FILE 0x0200
+#define FILE_ATTRIBUTE_REPARSE_POINT 0x0400
+#define FILE_ATTRIBUTE_COMPRESSED 0x0800
+#define FILE_ATTRIBUTE_OFFLINE 0x1000
+#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x2000
+#define FILE_ATTRIBUTE_ENCRYPTED 0x4000
+
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
+
+#endif
+
+
+#ifndef RINOK
+#define RINOK(x) { const int _result_ = (x); if (_result_ != 0) return _result_; }
+#endif
+
+#ifndef RINOK_WRes
+#define RINOK_WRes(x) { const WRes _result_ = (x); if (_result_ != 0) return _result_; }
+#endif
+
+typedef unsigned char Byte;
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef Z7_DECL_Int32_AS_long
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+
+#ifndef _WIN32
+
+typedef int INT;
+typedef Int32 INT32;
+typedef unsigned int UINT;
+typedef UInt32 UINT32;
+typedef INT32 LONG; // LONG, ULONG and DWORD must be 32-bit for _WIN32 compatibility
+typedef UINT32 ULONG;
+
+#undef DWORD
+typedef UINT32 DWORD;
+
+#define VOID void
+
+#define HRESULT LONG
+
+typedef void *LPVOID;
+// typedef void VOID;
+// typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
+// gcc / clang on Unix : sizeof(long==sizeof(void*) in 32 or 64 bits)
+typedef long INT_PTR;
+typedef unsigned long UINT_PTR;
+typedef long LONG_PTR;
+typedef unsigned long DWORD_PTR;
+
+typedef size_t SIZE_T;
+
+#endif // _WIN32
+
+
+#define MY_HRES_ERROR_INTERNAL_ERROR ((HRESULT)0x8007054FL)
+
+
+#ifdef Z7_DECL_Int64_AS_long
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if (defined(_MSC_VER) || defined(__BORLANDC__)) && !defined(__clang__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#else
+#if defined(__clang__) || defined(__GNUC__)
+#include <stdint.h>
+typedef int64_t Int64;
+typedef uint64_t UInt64;
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+// #define UINT64_CONST(n) n ## ULL
+#endif
+#endif
+
+#endif
+
+#define UINT64_CONST(n) n
+
+
+#ifdef Z7_DECL_SizeT_AS_unsigned_int
+typedef unsigned int SizeT;
+#else
+typedef size_t SizeT;
+#endif
+
+/*
+#if (defined(_MSC_VER) && _MSC_VER <= 1200)
+typedef size_t MY_uintptr_t;
+#else
+#include <stdint.h>
+typedef uintptr_t MY_uintptr_t;
+#endif
+*/
+
+typedef int BoolInt;
+/* typedef BoolInt Bool; */
+#define True 1
+#define False 0
+
+
+#ifdef _WIN32
+#define Z7_STDCALL __stdcall
+#else
+#define Z7_STDCALL
+#endif
+
+#ifdef _MSC_VER
+
+#if _MSC_VER >= 1300
+#define Z7_NO_INLINE __declspec(noinline)
+#else
+#define Z7_NO_INLINE
+#endif
+
+#define Z7_FORCE_INLINE __forceinline
+
+#define Z7_CDECL __cdecl
+#define Z7_FASTCALL __fastcall
+
+#else // _MSC_VER
+
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) \
+ || (defined(__clang__) && (__clang_major__ >= 4)) \
+ || defined(__INTEL_COMPILER) \
+ || defined(__xlC__)
+#define Z7_NO_INLINE __attribute__((noinline))
+#define Z7_FORCE_INLINE __attribute__((always_inline)) inline
+#else
+#define Z7_NO_INLINE
+#define Z7_FORCE_INLINE
+#endif
+
+#define Z7_CDECL
+
+#if defined(_M_IX86) \
+ || defined(__i386__)
+// #define Z7_FASTCALL __attribute__((fastcall))
+// #define Z7_FASTCALL __attribute__((cdecl))
+#define Z7_FASTCALL
+#elif defined(MY_CPU_AMD64)
+// #define Z7_FASTCALL __attribute__((ms_abi))
+#define Z7_FASTCALL
+#else
+#define Z7_FASTCALL
+#endif
+
+#endif // _MSC_VER
+
+
+/* The following interfaces use first parameter as pointer to structure */
+
+// #define Z7_C_IFACE_CONST_QUAL
+#define Z7_C_IFACE_CONST_QUAL const
+
+#define Z7_C_IFACE_DECL(a) \
+ struct a ## _; \
+ typedef Z7_C_IFACE_CONST_QUAL struct a ## _ * a ## Ptr; \
+ typedef struct a ## _ a; \
+ struct a ## _
+
+
+Z7_C_IFACE_DECL (IByteIn)
+{
+ Byte (*Read)(IByteInPtr p); /* reads one byte, returns 0 in case of EOF or error */
+};
+#define IByteIn_Read(p) (p)->Read(p)
+
+
+Z7_C_IFACE_DECL (IByteOut)
+{
+ void (*Write)(IByteOutPtr p, Byte b);
+};
+#define IByteOut_Write(p, b) (p)->Write(p, b)
+
+
+Z7_C_IFACE_DECL (ISeqInStream)
+{
+ SRes (*Read)(ISeqInStreamPtr p, void *buf, size_t *size);
+ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
+ (output(*size) < input(*size)) is allowed */
+};
+#define ISeqInStream_Read(p, buf, size) (p)->Read(p, buf, size)
+
+/* try to read as much as avail in stream and limited by (*processedSize) */
+SRes SeqInStream_ReadMax(ISeqInStreamPtr stream, void *buf, size_t *processedSize);
+/* it can return SZ_ERROR_INPUT_EOF */
+// SRes SeqInStream_Read(ISeqInStreamPtr stream, void *buf, size_t size);
+// SRes SeqInStream_Read2(ISeqInStreamPtr stream, void *buf, size_t size, SRes errorType);
+SRes SeqInStream_ReadByte(ISeqInStreamPtr stream, Byte *buf);
+
+
+Z7_C_IFACE_DECL (ISeqOutStream)
+{
+ size_t (*Write)(ISeqOutStreamPtr p, const void *buf, size_t size);
+ /* Returns: result - the number of actually written bytes.
+ (result < size) means error */
+};
+#define ISeqOutStream_Write(p, buf, size) (p)->Write(p, buf, size)
+
+typedef enum
+{
+ SZ_SEEK_SET = 0,
+ SZ_SEEK_CUR = 1,
+ SZ_SEEK_END = 2
+} ESzSeek;
+
+
+Z7_C_IFACE_DECL (ISeekInStream)
+{
+ SRes (*Read)(ISeekInStreamPtr p, void *buf, size_t *size); /* same as ISeqInStream::Read */
+ SRes (*Seek)(ISeekInStreamPtr p, Int64 *pos, ESzSeek origin);
+};
+#define ISeekInStream_Read(p, buf, size) (p)->Read(p, buf, size)
+#define ISeekInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
+
+
+Z7_C_IFACE_DECL (ILookInStream)
+{
+ SRes (*Look)(ILookInStreamPtr p, const void **buf, size_t *size);
+ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
+ (output(*size) > input(*size)) is not allowed
+ (output(*size) < input(*size)) is allowed */
+ SRes (*Skip)(ILookInStreamPtr p, size_t offset);
+ /* offset must be <= output(*size) of Look */
+ SRes (*Read)(ILookInStreamPtr p, void *buf, size_t *size);
+ /* reads directly (without buffer). It's same as ISeqInStream::Read */
+ SRes (*Seek)(ILookInStreamPtr p, Int64 *pos, ESzSeek origin);
+};
+
+#define ILookInStream_Look(p, buf, size) (p)->Look(p, buf, size)
+#define ILookInStream_Skip(p, offset) (p)->Skip(p, offset)
+#define ILookInStream_Read(p, buf, size) (p)->Read(p, buf, size)
+#define ILookInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
+
+
+SRes LookInStream_LookRead(ILookInStreamPtr stream, void *buf, size_t *size);
+SRes LookInStream_SeekTo(ILookInStreamPtr stream, UInt64 offset);
+
+/* reads via ILookInStream::Read */
+SRes LookInStream_Read2(ILookInStreamPtr stream, void *buf, size_t size, SRes errorType);
+SRes LookInStream_Read(ILookInStreamPtr stream, void *buf, size_t size);
+
+
+typedef struct
+{
+ ILookInStream vt;
+ ISeekInStreamPtr realStream;
+
+ size_t pos;
+ size_t size; /* it's data size */
+
+ /* the following variables must be set outside */
+ Byte *buf;
+ size_t bufSize;
+} CLookToRead2;
+
+void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead);
+
+#define LookToRead2_INIT(p) { (p)->pos = (p)->size = 0; }
+
+
+typedef struct
+{
+ ISeqInStream vt;
+ ILookInStreamPtr realStream;
+} CSecToLook;
+
+void SecToLook_CreateVTable(CSecToLook *p);
+
+
+
+typedef struct
+{
+ ISeqInStream vt;
+ ILookInStreamPtr realStream;
+} CSecToRead;
+
+void SecToRead_CreateVTable(CSecToRead *p);
+
+
+Z7_C_IFACE_DECL (ICompressProgress)
+{
+ SRes (*Progress)(ICompressProgressPtr p, UInt64 inSize, UInt64 outSize);
+ /* Returns: result. (result != SZ_OK) means break.
+ Value (UInt64)(Int64)-1 for size means unknown value. */
+};
+
+#define ICompressProgress_Progress(p, inSize, outSize) (p)->Progress(p, inSize, outSize)
+
+
+
+typedef struct ISzAlloc ISzAlloc;
+typedef const ISzAlloc * ISzAllocPtr;
+
+struct ISzAlloc
+{
+ void *(*Alloc)(ISzAllocPtr p, size_t size);
+ void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */
+};
+
+#define ISzAlloc_Alloc(p, size) (p)->Alloc(p, size)
+#define ISzAlloc_Free(p, a) (p)->Free(p, a)
+
+/* deprecated */
+#define IAlloc_Alloc(p, size) ISzAlloc_Alloc(p, size)
+#define IAlloc_Free(p, a) ISzAlloc_Free(p, a)
+
+
+
+
+
+#ifndef MY_offsetof
+ #ifdef offsetof
+ #define MY_offsetof(type, m) offsetof(type, m)
+ /*
+ #define MY_offsetof(type, m) FIELD_OFFSET(type, m)
+ */
+ #else
+ #define MY_offsetof(type, m) ((size_t)&(((type *)0)->m))
+ #endif
+#endif
+
+
+
+#ifndef Z7_container_of
+
+/*
+#define Z7_container_of(ptr, type, m) container_of(ptr, type, m)
+#define Z7_container_of(ptr, type, m) CONTAINING_RECORD(ptr, type, m)
+#define Z7_container_of(ptr, type, m) ((type *)((char *)(ptr) - offsetof(type, m)))
+#define Z7_container_of(ptr, type, m) (&((type *)0)->m == (ptr), ((type *)(((char *)(ptr)) - MY_offsetof(type, m))))
+*/
+
+/*
+ GCC shows warning: "perhaps the 'offsetof' macro was used incorrectly"
+ GCC 3.4.4 : classes with constructor
+ GCC 4.8.1 : classes with non-public variable members"
+*/
+
+#define Z7_container_of(ptr, type, m) \
+ ((type *)(void *)((char *)(void *) \
+ (1 ? (ptr) : &((type *)NULL)->m) - MY_offsetof(type, m)))
+
+#define Z7_container_of_CONST(ptr, type, m) \
+ ((const type *)(const void *)((const char *)(const void *) \
+ (1 ? (ptr) : &((type *)NULL)->m) - MY_offsetof(type, m)))
+
+/*
+#define Z7_container_of_NON_CONST_FROM_CONST(ptr, type, m) \
+ ((type *)(void *)(const void *)((const char *)(const void *) \
+ (1 ? (ptr) : &((type *)NULL)->m) - MY_offsetof(type, m)))
+*/
+
+#endif
+
+#define Z7_CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(void *)(ptr))
+
+// #define Z7_CONTAINER_FROM_VTBL(ptr, type, m) Z7_CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
+#define Z7_CONTAINER_FROM_VTBL(ptr, type, m) Z7_container_of(ptr, type, m)
+// #define Z7_CONTAINER_FROM_VTBL(ptr, type, m) Z7_container_of_NON_CONST_FROM_CONST(ptr, type, m)
+
+#define Z7_CONTAINER_FROM_VTBL_CONST(ptr, type, m) Z7_container_of_CONST(ptr, type, m)
+
+#define Z7_CONTAINER_FROM_VTBL_CLS(ptr, type, m) Z7_CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
+/*
+#define Z7_CONTAINER_FROM_VTBL_CLS(ptr, type, m) Z7_CONTAINER_FROM_VTBL(ptr, type, m)
+*/
+#if defined (__clang__) || defined(__GNUC__)
+#define Z7_DIAGNOSTIC_IGNORE_BEGIN_CAST_QUAL \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#define Z7_DIAGNOSTIC_IGNORE_END_CAST_QUAL \
+ _Pragma("GCC diagnostic pop")
+#else
+#define Z7_DIAGNOSTIC_IGNORE_BEGIN_CAST_QUAL
+#define Z7_DIAGNOSTIC_IGNORE_END_CAST_QUAL
+#endif
+
+#define Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR(ptr, type, m, p) \
+ Z7_DIAGNOSTIC_IGNORE_BEGIN_CAST_QUAL \
+ type *p = Z7_CONTAINER_FROM_VTBL(ptr, type, m); \
+ Z7_DIAGNOSTIC_IGNORE_END_CAST_QUAL
+
+#define Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(type) \
+ Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR(pp, type, vt, p)
+
+
+// #define ZIP7_DECLARE_HANDLE(name) typedef void *name;
+#define Z7_DECLARE_HANDLE(name) struct name##_dummy{int unused;}; typedef struct name##_dummy *name;
+
+
+#define Z7_memset_0_ARRAY(a) memset((a), 0, sizeof(a))
+
+#ifndef Z7_ARRAY_SIZE
+#define Z7_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+
+#ifdef _WIN32
+
+#define CHAR_PATH_SEPARATOR '\\'
+#define WCHAR_PATH_SEPARATOR L'\\'
+#define STRING_PATH_SEPARATOR "\\"
+#define WSTRING_PATH_SEPARATOR L"\\"
+
+#else
+
+#define CHAR_PATH_SEPARATOR '/'
+#define WCHAR_PATH_SEPARATOR L'/'
+#define STRING_PATH_SEPARATOR "/"
+#define WSTRING_PATH_SEPARATOR L"/"
+
+#endif
+
+#define k_PropVar_TimePrec_0 0
+#define k_PropVar_TimePrec_Unix 1
+#define k_PropVar_TimePrec_DOS 2
+#define k_PropVar_TimePrec_HighPrec 3
+#define k_PropVar_TimePrec_Base 16
+#define k_PropVar_TimePrec_100ns (k_PropVar_TimePrec_Base + 7)
+#define k_PropVar_TimePrec_1ns (k_PropVar_TimePrec_Base + 9)
+
+EXTERN_C_END
+
+#endif
+
+/*
+#ifndef Z7_ST
+#ifdef _7ZIP_ST
+#define Z7_ST
+#endif
+#endif
+*/
diff --git a/libs/archive/thirdparty/C/7zWindows.h b/libs/archive/thirdparty/C/7zWindows.h
new file mode 100644
index 0000000..42c6db8
--- /dev/null
+++ b/libs/archive/thirdparty/C/7zWindows.h
@@ -0,0 +1,101 @@
+/* 7zWindows.h -- StdAfx
+2023-04-02 : Igor Pavlov : Public domain */
+
+#ifndef ZIP7_INC_7Z_WINDOWS_H
+#define ZIP7_INC_7Z_WINDOWS_H
+
+#ifdef _WIN32
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+#endif
+
+#if defined(_MSC_VER)
+
+#pragma warning(push)
+#pragma warning(disable : 4668) // '_WIN32_WINNT' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
+
+#if _MSC_VER == 1900
+// for old kit10 versions
+// #pragma warning(disable : 4255) // winuser.h(13979): warning C4255: 'GetThreadDpiAwarenessContext':
+#endif
+// win10 Windows Kit:
+#endif // _MSC_VER
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 && !defined(_WIN64)
+// for msvc6 without sdk2003
+#define RPC_NO_WINDOWS_H
+#endif
+
+#if defined(__MINGW32__) || defined(__MINGW64__)
+// #if defined(__GNUC__) && !defined(__clang__)
+#include <windows.h>
+#else
+#include <Windows.h>
+#endif
+// #include <basetsd.h>
+// #include <wtypes.h>
+
+// but if precompiled with clang-cl then we need
+// #include <windows.h>
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 && !defined(_WIN64)
+#ifndef _W64
+
+typedef long LONG_PTR, *PLONG_PTR;
+typedef unsigned long ULONG_PTR, *PULONG_PTR;
+typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
+
+#define Z7_OLD_WIN_SDK
+#endif // _W64
+#endif // _MSC_VER == 1200
+
+#ifdef Z7_OLD_WIN_SDK
+
+#ifndef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+#ifndef FILE_SPECIAL_ACCESS
+#define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS)
+#endif
+
+// ShlObj.h:
+// #define BIF_NEWDIALOGSTYLE 0x0040
+
+#pragma warning(disable : 4201)
+// #pragma warning(disable : 4115)
+
+#undef VARIANT_TRUE
+#define VARIANT_TRUE ((VARIANT_BOOL)-1)
+#endif
+
+#endif // Z7_OLD_WIN_SDK
+
+#ifdef UNDER_CE
+#undef VARIANT_TRUE
+#define VARIANT_TRUE ((VARIANT_BOOL)-1)
+#endif
+
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 1400 && _MSC_VER <= 1600
+ // BaseTsd.h(148) : 'HandleToULong' : unreferenced inline function has been removed
+ // string.h
+ // #pragma warning(disable : 4514)
+#endif
+#endif
+
+
+/* #include "7zTypes.h" */
+
+#endif
diff --git a/libs/archive/thirdparty/C/Compiler.h b/libs/archive/thirdparty/C/Compiler.h
new file mode 100644
index 0000000..2a9c2b7
--- /dev/null
+++ b/libs/archive/thirdparty/C/Compiler.h
@@ -0,0 +1,236 @@
+/* Compiler.h : Compiler specific defines and pragmas
+2024-01-22 : Igor Pavlov : Public domain */
+
+#ifndef ZIP7_INC_COMPILER_H
+#define ZIP7_INC_COMPILER_H
+
+#if defined(__clang__)
+# define Z7_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
+#endif
+#if defined(__clang__) && defined(__apple_build_version__)
+# define Z7_APPLE_CLANG_VERSION Z7_CLANG_VERSION
+#elif defined(__clang__)
+# define Z7_LLVM_CLANG_VERSION Z7_CLANG_VERSION
+#elif defined(__GNUC__)
+# define Z7_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#endif
+
+#ifdef _MSC_VER
+#if !defined(__clang__) && !defined(__GNUC__)
+#define Z7_MSC_VER_ORIGINAL _MSC_VER
+#endif
+#endif
+
+#if defined(__MINGW32__) || defined(__MINGW64__)
+#define Z7_MINGW
+#endif
+
+#if defined(__LCC__) && (defined(__MCST__) || defined(__e2k__))
+#define Z7_MCST_LCC
+#define Z7_MCST_LCC_VERSION (__LCC__ * 100 + __LCC_MINOR__)
+#endif
+
+/*
+#if defined(__AVX2__) \
+ || defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40900) \
+ || defined(Z7_APPLE_CLANG_VERSION) && (Z7_APPLE_CLANG_VERSION >= 40600) \
+ || defined(Z7_LLVM_CLANG_VERSION) && (Z7_LLVM_CLANG_VERSION >= 30100) \
+ || defined(Z7_MSC_VER_ORIGINAL) && (Z7_MSC_VER_ORIGINAL >= 1800) \
+ || defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1400)
+ #define Z7_COMPILER_AVX2_SUPPORTED
+ #endif
+#endif
+*/
+
+// #pragma GCC diagnostic ignored "-Wunknown-pragmas"
+
+#ifdef __clang__
+// padding size of '' with 4 bytes to alignment boundary
+#pragma GCC diagnostic ignored "-Wpadded"
+
+#if defined(Z7_LLVM_CLANG_VERSION) && (__clang_major__ == 13) \
+ && defined(__FreeBSD__)
+// freebsd:
+#pragma GCC diagnostic ignored "-Wexcess-padding"
+#endif
+
+#if __clang_major__ >= 16
+#pragma GCC diagnostic ignored "-Wunsafe-buffer-usage"
+#endif
+
+#if __clang_major__ == 13
+#if defined(__SIZEOF_POINTER__) && (__SIZEOF_POINTER__ == 16)
+// cheri
+#pragma GCC diagnostic ignored "-Wcapability-to-integer-cast"
+#endif
+#endif
+
+#if __clang_major__ == 13
+ // for <arm_neon.h>
+ #pragma GCC diagnostic ignored "-Wreserved-identifier"
+#endif
+
+#endif // __clang__
+
+#if defined(_WIN32) && defined(__clang__) && __clang_major__ >= 16
+// #pragma GCC diagnostic ignored "-Wcast-function-type-strict"
+#define Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION \
+ _Pragma("GCC diagnostic ignored \"-Wcast-function-type-strict\"")
+#else
+#define Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
+#endif
+
+typedef void (*Z7_void_Function)(void);
+#if defined(__clang__) || defined(__GNUC__)
+#define Z7_CAST_FUNC_C (Z7_void_Function)
+#elif defined(_MSC_VER) && _MSC_VER > 1920
+#define Z7_CAST_FUNC_C (void *)
+// #pragma warning(disable : 4191) // 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)()'
+#else
+#define Z7_CAST_FUNC_C
+#endif
+/*
+#if (defined(__GNUC__) && (__GNUC__ >= 8)) || defined(__clang__)
+ // #pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+*/
+#ifdef __GNUC__
+#if defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40000) && (Z7_GCC_VERSION < 70000)
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif
+#endif
+
+
+#ifdef _MSC_VER
+
+ #ifdef UNDER_CE
+ #define RPC_NO_WINDOWS_H
+ /* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */
+ #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
+ #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int
+ #endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+#pragma warning(disable : 4464) // relative include path contains '..'
+#endif
+
+// == 1200 : -O1 : for __forceinline
+// >= 1900 : -O1 : for printf
+#pragma warning(disable : 4710) // function not inlined
+
+#if _MSC_VER < 1900
+// winnt.h: 'Int64ShllMod32'
+#pragma warning(disable : 4514) // unreferenced inline function has been removed
+#endif
+
+#if _MSC_VER < 1300
+// #pragma warning(disable : 4702) // unreachable code
+// Bra.c : -O1:
+#pragma warning(disable : 4714) // function marked as __forceinline not inlined
+#endif
+
+/*
+#if _MSC_VER > 1400 && _MSC_VER <= 1900
+// strcat: This function or variable may be unsafe
+// sysinfoapi.h: kit10: GetVersion was declared deprecated
+#pragma warning(disable : 4996)
+#endif
+*/
+
+#if _MSC_VER > 1200
+// -Wall warnings
+
+#pragma warning(disable : 4711) // function selected for automatic inline expansion
+#pragma warning(disable : 4820) // '2' bytes padding added after data member
+
+#if _MSC_VER >= 1400 && _MSC_VER < 1920
+// 1400: string.h: _DBG_MEMCPY_INLINE_
+// 1600 - 191x : smmintrin.h __cplusplus'
+// is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
+#pragma warning(disable : 4668)
+
+// 1400 - 1600 : WinDef.h : 'FARPROC' :
+// 1900 - 191x : immintrin.h: _readfsbase_u32
+// no function prototype given : converting '()' to '(void)'
+#pragma warning(disable : 4255)
+#endif
+
+#if _MSC_VER >= 1914
+// Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
+#pragma warning(disable : 5045)
+#endif
+
+#endif // _MSC_VER > 1200
+#endif // _MSC_VER
+
+
+#if defined(__clang__) && (__clang_major__ >= 4)
+ #define Z7_PRAGMA_OPT_DISABLE_LOOP_UNROLL_VECTORIZE \
+ _Pragma("clang loop unroll(disable)") \
+ _Pragma("clang loop vectorize(disable)")
+ #define Z7_ATTRIB_NO_VECTORIZE
+#elif defined(__GNUC__) && (__GNUC__ >= 5) \
+ && (!defined(Z7_MCST_LCC_VERSION) || (Z7_MCST_LCC_VERSION >= 12610))
+ #define Z7_ATTRIB_NO_VECTORIZE __attribute__((optimize("no-tree-vectorize")))
+ // __attribute__((optimize("no-unroll-loops")));
+ #define Z7_PRAGMA_OPT_DISABLE_LOOP_UNROLL_VECTORIZE
+#elif defined(_MSC_VER) && (_MSC_VER >= 1920)
+ #define Z7_PRAGMA_OPT_DISABLE_LOOP_UNROLL_VECTORIZE \
+ _Pragma("loop( no_vector )")
+ #define Z7_ATTRIB_NO_VECTORIZE
+#else
+ #define Z7_PRAGMA_OPT_DISABLE_LOOP_UNROLL_VECTORIZE
+ #define Z7_ATTRIB_NO_VECTORIZE
+#endif
+
+#if defined(MY_CPU_X86_OR_AMD64) && ( \
+ defined(__clang__) && (__clang_major__ >= 4) \
+ || defined(__GNUC__) && (__GNUC__ >= 5))
+ #define Z7_ATTRIB_NO_SSE __attribute__((__target__("no-sse")))
+#else
+ #define Z7_ATTRIB_NO_SSE
+#endif
+
+#define Z7_ATTRIB_NO_VECTOR \
+ Z7_ATTRIB_NO_VECTORIZE \
+ Z7_ATTRIB_NO_SSE
+
+
+#if defined(__clang__) && (__clang_major__ >= 8) \
+ || defined(__GNUC__) && (__GNUC__ >= 1000) \
+ /* || defined(_MSC_VER) && (_MSC_VER >= 1920) */
+ // GCC is not good for __builtin_expect()
+ #define Z7_LIKELY(x) (__builtin_expect((x), 1))
+ #define Z7_UNLIKELY(x) (__builtin_expect((x), 0))
+ // #define Z7_unlikely [[unlikely]]
+ // #define Z7_likely [[likely]]
+#else
+ #define Z7_LIKELY(x) (x)
+ #define Z7_UNLIKELY(x) (x)
+ // #define Z7_likely
+#endif
+
+
+#if (defined(Z7_CLANG_VERSION) && (Z7_CLANG_VERSION >= 30600))
+
+#if (Z7_CLANG_VERSION < 130000)
+#define Z7_DIAGNOSTIC_IGNORE_BEGIN_RESERVED_MACRO_IDENTIFIER \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wreserved-id-macro\"")
+#else
+#define Z7_DIAGNOSTIC_IGNORE_BEGIN_RESERVED_MACRO_IDENTIFIER \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wreserved-macro-identifier\"")
+#endif
+
+#define Z7_DIAGNOSTIC_IGNORE_END_RESERVED_MACRO_IDENTIFIER \
+ _Pragma("GCC diagnostic pop")
+#else
+#define Z7_DIAGNOSTIC_IGNORE_BEGIN_RESERVED_MACRO_IDENTIFIER
+#define Z7_DIAGNOSTIC_IGNORE_END_RESERVED_MACRO_IDENTIFIER
+#endif
+
+#define UNUSED_VAR(x) (void)x;
+/* #define UNUSED_VAR(x) x=x; */
+
+#endif
diff --git a/libs/archive/thirdparty/C/Precomp.h b/libs/archive/thirdparty/C/Precomp.h
new file mode 100644
index 0000000..7747fdd
--- /dev/null
+++ b/libs/archive/thirdparty/C/Precomp.h
@@ -0,0 +1,127 @@
+/* Precomp.h -- precompilation file
+2024-01-25 : Igor Pavlov : Public domain */
+
+#ifndef ZIP7_INC_PRECOMP_H
+#define ZIP7_INC_PRECOMP_H
+
+/*
+ this file must be included before another *.h files and before <windows.h>.
+ this file is included from the following files:
+ C\*.c
+ C\Util\*\Precomp.h <- C\Util\*\*.c
+ CPP\Common\Common.h <- *\StdAfx.h <- *\*.cpp
+
+ this file can set the following macros:
+ Z7_LARGE_PAGES 1
+ Z7_LONG_PATH 1
+ Z7_WIN32_WINNT_MIN 0x0500 (or higher) : we require at least win2000+ for 7-Zip
+ _WIN32_WINNT 0x0500 (or higher)
+ WINVER _WIN32_WINNT
+ UNICODE 1
+ _UNICODE 1
+*/
+
+#include "Compiler.h"
+
+#ifdef _MSC_VER
+// #pragma warning(disable : 4206) // nonstandard extension used : translation unit is empty
+#if _MSC_VER >= 1912
+// #pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to 'extern "C"' function under - EHc.Undefined behavior may occur if this function throws an exception.
+#endif
+#endif
+
+/*
+// for debug:
+#define UNICODE 1
+#define _UNICODE 1
+#define _WIN32_WINNT 0x0500 // win2000
+#ifndef WINVER
+ #define WINVER _WIN32_WINNT
+#endif
+*/
+
+#ifdef _WIN32
+/*
+ this "Precomp.h" file must be included before <windows.h>,
+ if we want to define _WIN32_WINNT before <windows.h>.
+*/
+
+#ifndef Z7_LARGE_PAGES
+#ifndef Z7_NO_LARGE_PAGES
+#define Z7_LARGE_PAGES 1
+#endif
+#endif
+
+#ifndef Z7_LONG_PATH
+#ifndef Z7_NO_LONG_PATH
+#define Z7_LONG_PATH 1
+#endif
+#endif
+
+#ifndef Z7_DEVICE_FILE
+#ifndef Z7_NO_DEVICE_FILE
+// #define Z7_DEVICE_FILE 1
+#endif
+#endif
+
+// we don't change macros if included after <windows.h>
+#ifndef _WINDOWS_
+
+#ifndef Z7_WIN32_WINNT_MIN
+ #if defined(_M_ARM64) || defined(__aarch64__)
+ // #define Z7_WIN32_WINNT_MIN 0x0a00 // win10
+ #define Z7_WIN32_WINNT_MIN 0x0600 // vista
+ #elif defined(_M_ARM) && defined(_M_ARMT) && defined(_M_ARM_NT)
+ // #define Z7_WIN32_WINNT_MIN 0x0602 // win8
+ #define Z7_WIN32_WINNT_MIN 0x0600 // vista
+ #elif defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) || defined(_M_IA64)
+ #define Z7_WIN32_WINNT_MIN 0x0503 // win2003
+ // #elif defined(_M_IX86) || defined(__i386__)
+ // #define Z7_WIN32_WINNT_MIN 0x0500 // win2000
+ #else // x86 and another(old) systems
+ #define Z7_WIN32_WINNT_MIN 0x0500 // win2000
+ // #define Z7_WIN32_WINNT_MIN 0x0502 // win2003 // for debug
+ #endif
+#endif // Z7_WIN32_WINNT_MIN
+
+
+#ifndef Z7_DO_NOT_DEFINE_WIN32_WINNT
+#ifdef _WIN32_WINNT
+ // #error Stop_Compiling_Bad_WIN32_WINNT
+#else
+ #ifndef Z7_NO_DEFINE_WIN32_WINNT
+Z7_DIAGNOSTIC_IGNORE_BEGIN_RESERVED_MACRO_IDENTIFIER
+ #define _WIN32_WINNT Z7_WIN32_WINNT_MIN
+Z7_DIAGNOSTIC_IGNORE_END_RESERVED_MACRO_IDENTIFIER
+ #endif
+#endif // _WIN32_WINNT
+
+#ifndef WINVER
+ #define WINVER _WIN32_WINNT
+#endif
+#endif // Z7_DO_NOT_DEFINE_WIN32_WINNT
+
+
+#ifndef _MBCS
+#ifndef Z7_NO_UNICODE
+// UNICODE and _UNICODE are used by <windows.h> and by 7-zip code.
+
+#ifndef UNICODE
+#define UNICODE 1
+#endif
+
+#ifndef _UNICODE
+Z7_DIAGNOSTIC_IGNORE_BEGIN_RESERVED_MACRO_IDENTIFIER
+#define _UNICODE 1
+Z7_DIAGNOSTIC_IGNORE_END_RESERVED_MACRO_IDENTIFIER
+#endif
+
+#endif // Z7_NO_UNICODE
+#endif // _MBCS
+#endif // _WINDOWS_
+
+// #include "7zWindows.h"
+
+#endif // _WIN32
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/7zip/Archive/IArchive.h b/libs/archive/thirdparty/CPP/7zip/Archive/IArchive.h
new file mode 100644
index 0000000..a817015
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/Archive/IArchive.h
@@ -0,0 +1,754 @@
+// IArchive.h
+
+#ifndef ZIP7_INC_IARCHIVE_H
+#define ZIP7_INC_IARCHIVE_H
+
+#include "../IProgress.h"
+#include "../IStream.h"
+#include "../PropID.h"
+
+Z7_PURE_INTERFACES_BEGIN
+
+
+#define Z7_IFACE_CONSTR_ARCHIVE_SUB(i, base, n) \
+ Z7_DECL_IFACE_7ZIP_SUB(i, base, 6, n) \
+ { Z7_IFACE_COM7_PURE(i) };
+
+#define Z7_IFACE_CONSTR_ARCHIVE(i, n) \
+ Z7_IFACE_CONSTR_ARCHIVE_SUB(i, IUnknown, n)
+
+/*
+How the function in 7-Zip returns object for output parameter via pointer
+
+1) The caller sets the value of variable before function call:
+ PROPVARIANT : vt = VT_EMPTY
+ BSTR : NULL
+ IUnknown* and derived interfaces : NULL
+ another scalar types : any non-initialized value is allowed
+
+2) The callee in current 7-Zip code now can free input object for output parameter:
+ PROPVARIANT : the callee calls VariantClear(propvaiant_ptr) for input
+ value stored in variable
+ another types : the callee ignores stored value.
+
+3) The callee writes new value to variable for output parameter and
+ returns execution to caller.
+
+4) The caller must free or release object returned by the callee:
+ PROPVARIANT : VariantClear(&propvaiant)
+ BSTR : SysFreeString(bstr)
+ IUnknown* and derived interfaces : if (ptr) ptr->Relase()
+*/
+
+
+namespace NFileTimeType
+{
+ enum EEnum
+ {
+ kNotDefined = -1,
+ kWindows = 0,
+ kUnix,
+ kDOS,
+ k1ns
+ };
+}
+
+namespace NArcInfoFlags
+{
+ const UInt32 kKeepName = 1 << 0; // keep name of file in archive name
+ const UInt32 kAltStreams = 1 << 1; // the handler supports alt streams
+ const UInt32 kNtSecure = 1 << 2; // the handler supports NT security
+ const UInt32 kFindSignature = 1 << 3; // the handler can find start of archive
+ const UInt32 kMultiSignature = 1 << 4; // there are several signatures
+ const UInt32 kUseGlobalOffset = 1 << 5; // the seek position of stream must be set as global offset
+ const UInt32 kStartOpen = 1 << 6; // call handler for each start position
+ const UInt32 kPureStartOpen = 1 << 7; // call handler only for start of file
+ const UInt32 kBackwardOpen = 1 << 8; // archive can be open backward
+ const UInt32 kPreArc = 1 << 9; // such archive can be stored before real archive (like SFX stub)
+ const UInt32 kSymLinks = 1 << 10; // the handler supports symbolic links
+ const UInt32 kHardLinks = 1 << 11; // the handler supports hard links
+ const UInt32 kByExtOnlyOpen = 1 << 12; // call handler only if file extension matches
+ const UInt32 kHashHandler = 1 << 13; // the handler contains the hashes (checksums)
+ const UInt32 kCTime = 1 << 14;
+ const UInt32 kCTime_Default = 1 << 15;
+ const UInt32 kATime = 1 << 16;
+ const UInt32 kATime_Default = 1 << 17;
+ const UInt32 kMTime = 1 << 18;
+ const UInt32 kMTime_Default = 1 << 19;
+ // const UInt32 kTTime_Reserved = 1 << 20;
+ // const UInt32 kTTime_Reserved_Default = 1 << 21;
+}
+
+namespace NArcInfoTimeFlags
+{
+ const unsigned kTime_Prec_Mask_bit_index = 0;
+ const unsigned kTime_Prec_Mask_num_bits = 26;
+
+ const unsigned kTime_Prec_Default_bit_index = 27;
+ const unsigned kTime_Prec_Default_num_bits = 5;
+}
+
+#define TIME_PREC_TO_ARC_FLAGS_MASK(v) \
+ ((UInt32)1 << (NArcInfoTimeFlags::kTime_Prec_Mask_bit_index + (v)))
+
+#define TIME_PREC_TO_ARC_FLAGS_TIME_DEFAULT(v) \
+ ((UInt32)(v) << NArcInfoTimeFlags::kTime_Prec_Default_bit_index)
+
+namespace NArchive
+{
+ namespace NHandlerPropID
+ {
+ enum
+ {
+ kName = 0, // VT_BSTR
+ kClassID, // binary GUID in VT_BSTR
+ kExtension, // VT_BSTR
+ kAddExtension, // VT_BSTR
+ kUpdate, // VT_BOOL
+ kKeepName, // VT_BOOL
+ kSignature, // binary in VT_BSTR
+ kMultiSignature, // binary in VT_BSTR
+ kSignatureOffset, // VT_UI4
+ kAltStreams, // VT_BOOL
+ kNtSecure, // VT_BOOL
+ kFlags, // VT_UI4
+ kTimeFlags // VT_UI4
+ };
+ }
+
+ namespace NExtract
+ {
+ namespace NAskMode
+ {
+ enum
+ {
+ kExtract = 0,
+ kTest,
+ kSkip,
+ kReadExternal
+ };
+ }
+
+ namespace NOperationResult
+ {
+ enum
+ {
+ kOK = 0,
+ kUnsupportedMethod,
+ kDataError,
+ kCRCError,
+ kUnavailable,
+ kUnexpectedEnd,
+ kDataAfterEnd,
+ kIsNotArc,
+ kHeadersError,
+ kWrongPassword
+ // , kMemError
+ };
+ }
+ }
+
+ namespace NEventIndexType
+ {
+ enum
+ {
+ kNoIndex = 0,
+ kInArcIndex,
+ kBlockIndex,
+ kOutArcIndex
+ // kArcProp
+ };
+ }
+
+ namespace NUpdate
+ {
+ namespace NOperationResult
+ {
+ enum
+ {
+ kOK = 0
+ // kError = 1,
+ // kError_FileChanged
+ };
+ }
+ }
+}
+
+#define Z7_IFACEM_IArchiveOpenCallback(x) \
+ x(SetTotal(const UInt64 *files, const UInt64 *bytes)) \
+ x(SetCompleted(const UInt64 *files, const UInt64 *bytes)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveOpenCallback, 0x10)
+
+/*
+IArchiveExtractCallback::
+
+7-Zip doesn't call IArchiveExtractCallback functions
+ GetStream()
+ PrepareOperation()
+ SetOperationResult()
+from different threads simultaneously.
+But 7-Zip can call functions for IProgress or ICompressProgressInfo functions
+from another threads simultaneously with calls for IArchiveExtractCallback interface.
+
+IArchiveExtractCallback::GetStream()
+ UInt32 index - index of item in Archive
+ Int32 askExtractMode (Extract::NAskMode)
+ if (askMode != NExtract::NAskMode::kExtract)
+ {
+ then the callee doesn't write data to stream: (*outStream == NULL)
+ }
+
+ Out:
+ (*outStream == NULL) - for directories
+ (*outStream == NULL) - if link (hard link or symbolic link) was created
+ if (*outStream == NULL && askMode == NExtract::NAskMode::kExtract)
+ {
+ then the caller must skip extracting of that file.
+ }
+
+ returns:
+ S_OK : OK
+ S_FALSE : data error (for decoders)
+
+if (IProgress::SetTotal() was called)
+{
+ IProgress::SetCompleted(completeValue) uses
+ packSize - for some stream formats (xz, gz, bz2, lzma, z, ppmd).
+ unpackSize - for another formats.
+}
+else
+{
+ IProgress::SetCompleted(completeValue) uses packSize.
+}
+
+SetOperationResult()
+ 7-Zip calls SetOperationResult at the end of extracting,
+ so the callee can close the file, set attributes, timestamps and security information.
+
+ Int32 opRes (NExtract::NOperationResult)
+*/
+
+// INTERFACE_IProgress(x)
+
+#define Z7_IFACEM_IArchiveExtractCallback(x) \
+ x(GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)) \
+ x(PrepareOperation(Int32 askExtractMode)) \
+ x(SetOperationResult(Int32 opRes)) \
+
+Z7_IFACE_CONSTR_ARCHIVE_SUB(IArchiveExtractCallback, IProgress, 0x20)
+
+
+
+/*
+v23:
+IArchiveExtractCallbackMessage2 can be requested from IArchiveExtractCallback object
+ by Extract() or UpdateItems() functions to report about extracting errors
+ReportExtractResult()
+ UInt32 indexType (NEventIndexType)
+ UInt32 index
+ Int32 opRes (NExtract::NOperationResult)
+*/
+/*
+before v23:
+#define Z7_IFACEM_IArchiveExtractCallbackMessage(x) \
+ x(ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes))
+Z7_IFACE_CONSTR_ARCHIVE_SUB(IArchiveExtractCallbackMessage, IProgress, 0x21)
+*/
+#define Z7_IFACEM_IArchiveExtractCallbackMessage2(x) \
+ x(ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes))
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveExtractCallbackMessage2, 0x22)
+
+#define Z7_IFACEM_IArchiveOpenVolumeCallback(x) \
+ x(GetProperty(PROPID propID, PROPVARIANT *value)) \
+ x(GetStream(const wchar_t *name, IInStream **inStream))
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveOpenVolumeCallback, 0x30)
+
+
+#define Z7_IFACEM_IInArchiveGetStream(x) \
+ x(GetStream(UInt32 index, ISequentialInStream **stream))
+Z7_IFACE_CONSTR_ARCHIVE(IInArchiveGetStream, 0x40)
+
+#define Z7_IFACEM_IArchiveOpenSetSubArchiveName(x) \
+ x(SetSubArchiveName(const wchar_t *name))
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveOpenSetSubArchiveName, 0x50)
+
+
+/*
+IInArchive::Open
+ stream
+ if (kUseGlobalOffset), stream current position can be non 0.
+ if (!kUseGlobalOffset), stream current position is 0.
+ if (maxCheckStartPosition == NULL), the handler can try to search archive start in stream
+ if (*maxCheckStartPosition == 0), the handler must check only current position as archive start
+
+IInArchive::Extract:
+ indices must be sorted
+ numItems = (UInt32)(Int32)-1 = 0xFFFFFFFF means "all files"
+ testMode != 0 means "test files without writing to outStream"
+
+IInArchive::GetArchiveProperty:
+ kpidOffset - start offset of archive.
+ VT_EMPTY : means offset = 0.
+ VT_UI4, VT_UI8, VT_I8 : result offset; negative values is allowed
+ kpidPhySize - size of archive. VT_EMPTY means unknown size.
+ kpidPhySize is allowed to be larger than file size. In that case it must show
+ supposed size.
+
+ kpidIsDeleted:
+ kpidIsAltStream:
+ kpidIsAux:
+ kpidINode:
+ must return VARIANT_TRUE (VT_BOOL), if archive can support that property in GetProperty.
+
+
+Notes:
+ Don't call IInArchive functions for same IInArchive object from different threads simultaneously.
+ Some IInArchive handlers will work incorrectly in that case.
+*/
+
+#if defined(_MSC_VER) && !defined(__clang__)
+ #define MY_NO_THROW_DECL_ONLY Z7_COM7F_E
+#else
+ #define MY_NO_THROW_DECL_ONLY
+#endif
+
+#define Z7_IFACEM_IInArchive(x) \
+ x(Open(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback)) \
+ x(Close()) \
+ x(GetNumberOfItems(UInt32 *numItems)) \
+ x(GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) \
+ x(Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback)) \
+ x(GetArchiveProperty(PROPID propID, PROPVARIANT *value)) \
+ x(GetNumberOfProperties(UInt32 *numProps)) \
+ x(GetPropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType)) \
+ x(GetNumberOfArchiveProperties(UInt32 *numProps)) \
+ x(GetArchivePropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IInArchive, 0x60)
+
+namespace NParentType
+{
+ enum
+ {
+ kDir = 0,
+ kAltStream
+ };
+}
+
+namespace NPropDataType
+{
+ const UInt32 kMask_ZeroEnd = 1 << 4;
+ // const UInt32 kMask_BigEndian = 1 << 5;
+ const UInt32 kMask_Utf = 1 << 6;
+ const UInt32 kMask_Utf8 = kMask_Utf | 0;
+ const UInt32 kMask_Utf16 = kMask_Utf | 1;
+ // const UInt32 kMask_Utf32 = kMask_Utf | 2;
+
+ const UInt32 kNotDefined = 0;
+ const UInt32 kRaw = 1;
+
+ const UInt32 kUtf8z = kMask_Utf8 | kMask_ZeroEnd;
+ const UInt32 kUtf16z = kMask_Utf16 | kMask_ZeroEnd;
+}
+
+// UTF string (pointer to wchar_t) with zero end and little-endian.
+#define PROP_DATA_TYPE_wchar_t_PTR_Z_LE ((NPropDataType::kMask_Utf | NPropDataType::kMask_ZeroEnd) + (sizeof(wchar_t) >> 1))
+
+
+/*
+GetRawProp:
+ Result:
+ S_OK - even if property is not set
+*/
+
+#define Z7_IFACEM_IArchiveGetRawProps(x) \
+ x(GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)) \
+ x(GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)) \
+ x(GetNumRawProps(UInt32 *numProps)) \
+ x(GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID))
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveGetRawProps, 0x70)
+
+#define Z7_IFACEM_IArchiveGetRootProps(x) \
+ x(GetRootProp(PROPID propID, PROPVARIANT *value)) \
+ x(GetRootRawProp(PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveGetRootProps, 0x71)
+
+#define Z7_IFACEM_IArchiveOpenSeq(x) \
+ x(OpenSeq(ISequentialInStream *stream)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveOpenSeq, 0x61)
+
+/*
+ OpenForSize
+ Result:
+ S_FALSE - is not archive
+ ? - DATA error
+*/
+
+/*
+const UInt32 kOpenFlags_RealPhySize = 1 << 0;
+const UInt32 kOpenFlags_NoSeek = 1 << 1;
+// const UInt32 kOpenFlags_BeforeExtract = 1 << 2;
+*/
+
+/*
+Flags:
+ 0 - opens archive with IInStream, if IInStream interface is supported
+ - if phySize is not available, it doesn't try to make full parse to get phySize
+ kOpenFlags_NoSeek - ArcOpen2 function doesn't use IInStream interface, even if it's available
+ kOpenFlags_RealPhySize - the handler will try to get PhySize, even if it requires full decompression for file
+
+ if handler is not allowed to use IInStream and the flag kOpenFlags_RealPhySize is not specified,
+ the handler can return S_OK, but it doesn't check even Signature.
+ So next Extract can be called for that sequential stream.
+*/
+/*
+#define Z7_IFACEM_IArchiveOpen2(x) \
+ x(ArcOpen2(ISequentialInStream *stream, UInt32 flags, IArchiveOpenCallback *openCallback))
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveOpen2, 0x62)
+*/
+
+// ---------- UPDATE ----------
+
+/*
+GetUpdateItemInfo outs:
+*newData *newProps
+ 0 0 - Copy data and properties from archive
+ 0 1 - Copy data from archive, request new properties
+ 1 0 - that combination is unused now
+ 1 1 - Request new data and new properties. It can be used even for folders
+
+ indexInArchive = -1 if there is no item in archive, or if it doesn't matter.
+
+
+GetStream out:
+ Result:
+ S_OK:
+ (*inStream == NULL) - only for directories
+ - the bug was fixed in 9.33: (*Stream == NULL) was in case of anti-file
+ (*inStream != NULL) - for any file, even for empty file or anti-file
+ S_FALSE - skip that file (don't add item to archive) - (client code can't open stream of that file by some reason)
+ (*inStream == NULL)
+
+The order of calling for hard links:
+ - GetStream()
+ - GetProperty(kpidHardLink)
+
+SetOperationResult()
+ Int32 opRes (NExtract::NOperationResult::kOK)
+*/
+
+// INTERFACE_IProgress(x)
+#define Z7_IFACEM_IArchiveUpdateCallback(x) \
+ x(GetUpdateItemInfo(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)) \
+ x(GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) \
+ x(GetStream(UInt32 index, ISequentialInStream **inStream)) \
+ x(SetOperationResult(Int32 operationResult)) \
+
+Z7_IFACE_CONSTR_ARCHIVE_SUB(IArchiveUpdateCallback, IProgress, 0x80)
+
+// INTERFACE_IArchiveUpdateCallback(x)
+#define Z7_IFACEM_IArchiveUpdateCallback2(x) \
+ x(GetVolumeSize(UInt32 index, UInt64 *size)) \
+ x(GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)) \
+
+Z7_IFACE_CONSTR_ARCHIVE_SUB(IArchiveUpdateCallback2, IArchiveUpdateCallback, 0x82)
+
+namespace NUpdateNotifyOp
+{
+ enum
+ {
+ kAdd = 0,
+ kUpdate,
+ kAnalyze,
+ kReplicate,
+ kRepack,
+ kSkip,
+ kDelete,
+ kHeader,
+ kHashRead,
+ kInFileChanged
+ // , kOpFinished
+ // , kNumDefined
+ };
+}
+
+/*
+IArchiveUpdateCallbackFile::ReportOperation
+ UInt32 indexType (NEventIndexType)
+ UInt32 index
+ UInt32 notifyOp (NUpdateNotifyOp)
+*/
+
+#define Z7_IFACEM_IArchiveUpdateCallbackFile(x) \
+ x(GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 notifyOp)) \
+ x(ReportOperation(UInt32 indexType, UInt32 index, UInt32 notifyOp)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveUpdateCallbackFile, 0x83)
+
+
+#define Z7_IFACEM_IArchiveGetDiskProperty(x) \
+ x(GetDiskProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveGetDiskProperty, 0x84)
+
+/*
+#define Z7_IFACEM_IArchiveUpdateCallbackArcProp(x) \
+ x(ReportProp(UInt32 indexType, UInt32 index, PROPID propID, const PROPVARIANT *value)) \
+ x(ReportRawProp(UInt32 indexType, UInt32 index, PROPID propID, const void *data, UInt32 dataSize, UInt32 propType)) \
+ x(ReportFinished(UInt32 indexType, UInt32 index, Int32 opRes)) \
+ x(DoNeedArcProp(PROPID propID, Int32 *answer)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveUpdateCallbackArcProp, 0x85)
+*/
+
+/*
+UpdateItems()
+-------------
+
+ outStream: output stream. (the handler) MUST support the case when
+ Seek position in outStream is not ZERO.
+ but the caller calls with empty outStream and seek position is ZERO??
+
+ archives with stub:
+
+ If archive is open and the handler and (Offset > 0), then the handler
+ knows about stub size.
+ UpdateItems():
+ 1) the handler MUST copy that stub to outStream
+ 2) the caller MUST NOT copy the stub to outStream, if
+ "rsfx" property is set with SetProperties
+
+ the handler must support the case where
+ ISequentialOutStream *outStream
+*/
+
+
+#define Z7_IFACEM_IOutArchive(x) \
+ x(UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback)) \
+ x(GetFileTimeType(UInt32 *type))
+
+Z7_IFACE_CONSTR_ARCHIVE(IOutArchive, 0xA0)
+
+
+/*
+ISetProperties::SetProperties()
+ PROPVARIANT values[i].vt:
+ VT_EMPTY
+ VT_BOOL
+ VT_UI4 - if 32-bit number
+ VT_UI8 - if 64-bit number
+ VT_BSTR
+*/
+
+#define Z7_IFACEM_ISetProperties(x) \
+ x(SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
+
+Z7_IFACE_CONSTR_ARCHIVE(ISetProperties, 0x03)
+
+#define Z7_IFACEM_IArchiveKeepModeForNextOpen(x) \
+ x(KeepModeForNextOpen()) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveKeepModeForNextOpen, 0x04)
+
+/* Exe handler: the handler for executable format (PE, ELF, Mach-O).
+ SFX archive: executable stub + some tail data.
+ before 9.31: exe handler didn't parse SFX archives as executable format.
+ for 9.31+: exe handler parses SFX archives as executable format, only if AllowTail(1) was called */
+
+#define Z7_IFACEM_IArchiveAllowTail(x) \
+ x(AllowTail(Int32 allowTail)) \
+
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveAllowTail, 0x05)
+
+
+namespace NRequestMemoryUseFlags
+{
+ const UInt32 k_AllowedSize_WasForced = 1 << 0; // (*allowedSize) was forced by -mmemx or -smemx
+ const UInt32 k_DefaultLimit_Exceeded = 1 << 1; // default limit of archive format was exceeded
+ const UInt32 k_MLimit_Exceeded = 1 << 2; // -mmemx value was exceeded
+ const UInt32 k_SLimit_Exceeded = 1 << 3; // -smemx value was exceeded
+
+ const UInt32 k_NoErrorMessage = 1 << 10; // do not show error message, and show only request
+ const UInt32 k_IsReport = 1 << 11; // only report is required, without user request
+
+ const UInt32 k_SkipArc_IsExpected = 1 << 12; // NRequestMemoryAnswerFlags::k_SkipArc flag answer is expected
+ const UInt32 k_Report_SkipArc = 1 << 13; // report about SkipArc operation
+
+ // const UInt32 k_SkipBigFile_IsExpected = 1 << 14; // NRequestMemoryAnswerFlags::k_SkipBigFiles flag answer is expected (unused)
+ // const UInt32 k_Report_SkipBigFile = 1 << 15; // report about SkipFile operation (unused)
+
+ // const UInt32 k_SkipBigFiles_IsExpected = 1 << 16; // NRequestMemoryAnswerFlags::k_SkipBigFiles flag answer is expected (unused)
+ // const UInt32 k_Report_SkipBigFiles = 1 << 17; // report that all big files will be skipped (unused)
+}
+
+namespace NRequestMemoryAnswerFlags
+{
+ const UInt32 k_Allow = 1 << 0; // allow further archive extraction
+ const UInt32 k_Stop = 1 << 1; // for exit (and return_code == E_ABORT is used)
+ const UInt32 k_SkipArc = 1 << 2; // skip current archive extraction
+ // const UInt32 k_SkipBigFile = 1 << 4; // skip extracting of files that exceed limit (unused)
+ // const UInt32 k_SkipBigFiles = 1 << 5; // skip extracting of files that exceed limit (unused)
+ const UInt32 k_Limit_Exceeded = 1 << 10; // limit was exceeded
+}
+
+/*
+ *allowedSize is in/out:
+ in : default allowed memory usage size or forced size, if it was changed by switch -mmemx.
+ out : value specified by user or unchanged value.
+
+ *answerFlags is in/out:
+ *answerFlags must be set by caller before calling for default action,
+
+ indexType : must be set with NEventIndexType::* constant
+ (indexType == kNoIndex), if request for whole archive.
+ index : must be set for some (indexType) types (if
+ fileIndex , if (indexType == NEventIndexType::kInArcIndex)
+ 0, if if (indexType == kNoIndex)
+ path : NULL can be used for any indexType.
+*/
+#define Z7_IFACEM_IArchiveRequestMemoryUseCallback(x) \
+ x(RequestMemoryUse(UInt32 flags, UInt32 indexType, UInt32 index, const wchar_t *path, \
+ UInt64 requiredSize, UInt64 *allowedSize, UInt32 *answerFlags))
+Z7_IFACE_CONSTR_ARCHIVE(IArchiveRequestMemoryUseCallback, 0x09)
+
+
+struct CStatProp
+{
+ const char *Name;
+ UInt32 PropID;
+ VARTYPE vt;
+};
+
+namespace NWindows {
+namespace NCOM {
+// PropVariant.cpp
+BSTR AllocBstrFromAscii(const char *s) throw();
+}}
+
+
+#define IMP_IInArchive_GetProp_Base(fn, f, k) \
+ Z7_COM7F_IMF(CHandler::fn(UInt32 *numProps)) \
+ { *numProps = Z7_ARRAY_SIZE(k); return S_OK; } \
+ Z7_COM7F_IMF(CHandler::f(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType)) \
+ { if (index >= Z7_ARRAY_SIZE(k)) return E_INVALIDARG; \
+
+#define IMP_IInArchive_GetProp_NO_NAME(fn, f, k) \
+ IMP_IInArchive_GetProp_Base(fn, f, k) \
+ *propID = k[index]; \
+ *varType = k7z_PROPID_To_VARTYPE[(unsigned)*propID]; \
+ *name = NULL; return S_OK; } \
+
+#define IMP_IInArchive_GetProp_WITH_NAME(fn, f, k) \
+ IMP_IInArchive_GetProp_Base(fn, f, k) \
+ const CStatProp &prop = k[index]; \
+ *propID = (PROPID)prop.PropID; \
+ *varType = prop.vt; \
+ *name = NWindows::NCOM::AllocBstrFromAscii(prop.Name); return S_OK; } \
+
+
+#define IMP_IInArchive_Props \
+ IMP_IInArchive_GetProp_NO_NAME(GetNumberOfProperties, GetPropertyInfo, kProps)
+
+#define IMP_IInArchive_Props_WITH_NAME \
+ IMP_IInArchive_GetProp_WITH_NAME(GetNumberOfProperties, GetPropertyInfo, kProps)
+
+#define IMP_IInArchive_ArcProps \
+ IMP_IInArchive_GetProp_NO_NAME(GetNumberOfArchiveProperties, GetArchivePropertyInfo, kArcProps)
+
+#define IMP_IInArchive_ArcProps_WITH_NAME \
+ IMP_IInArchive_GetProp_WITH_NAME(GetNumberOfArchiveProperties, GetArchivePropertyInfo, kArcProps)
+
+#define IMP_IInArchive_ArcProps_NO_Table \
+ Z7_COM7F_IMF(CHandler::GetNumberOfArchiveProperties(UInt32 *numProps)) \
+ { *numProps = 0; return S_OK; } \
+ Z7_COM7F_IMF(CHandler::GetArchivePropertyInfo(UInt32, BSTR *, PROPID *, VARTYPE *)) \
+ { return E_NOTIMPL; } \
+
+#define IMP_IInArchive_ArcProps_NO \
+ IMP_IInArchive_ArcProps_NO_Table \
+ Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID, PROPVARIANT *value)) \
+ { value->vt = VT_EMPTY; return S_OK; }
+
+
+#define Z7_class_CHandler_final \
+ Z7_class_final(CHandler)
+
+
+#define Z7_CLASS_IMP_CHandler_IInArchive_0 \
+ Z7_CLASS_IMP_COM_1(CHandler, IInArchive)
+#define Z7_CLASS_IMP_CHandler_IInArchive_1(i1) \
+ Z7_CLASS_IMP_COM_2(CHandler, IInArchive, i1)
+#define Z7_CLASS_IMP_CHandler_IInArchive_2(i1, i2) \
+ Z7_CLASS_IMP_COM_3(CHandler, IInArchive, i1, i2)
+#define Z7_CLASS_IMP_CHandler_IInArchive_3(i1, i2, i3) \
+ Z7_CLASS_IMP_COM_4(CHandler, IInArchive, i1, i2, i3)
+#define Z7_CLASS_IMP_CHandler_IInArchive_4(i1, i2, i3, i4) \
+ Z7_CLASS_IMP_COM_5(CHandler, IInArchive, i1, i2, i3, i4)
+#define Z7_CLASS_IMP_CHandler_IInArchive_5(i1, i2, i3, i4, i5) \
+ Z7_CLASS_IMP_COM_6(CHandler, IInArchive, i1, i2, i3, i4, i5)
+
+
+
+#define k_IsArc_Res_NO 0
+#define k_IsArc_Res_YES 1
+#define k_IsArc_Res_NEED_MORE 2
+// #define k_IsArc_Res_YES_LOW_PROB 3
+
+#define API_FUNC_IsArc EXTERN_C UInt32 WINAPI
+#define API_FUNC_static_IsArc extern "C" { static UInt32 WINAPI
+
+extern "C"
+{
+ typedef HRESULT (WINAPI *Func_CreateObject)(const GUID *clsID, const GUID *iid, void **outObject);
+
+ typedef UInt32 (WINAPI *Func_IsArc)(const Byte *p, size_t size);
+ typedef HRESULT (WINAPI *Func_GetIsArc)(UInt32 formatIndex, Func_IsArc *isArc);
+
+ typedef HRESULT (WINAPI *Func_GetNumberOfFormats)(UInt32 *numFormats);
+ typedef HRESULT (WINAPI *Func_GetHandlerProperty)(PROPID propID, PROPVARIANT *value);
+ typedef HRESULT (WINAPI *Func_GetHandlerProperty2)(UInt32 index, PROPID propID, PROPVARIANT *value);
+
+ typedef HRESULT (WINAPI *Func_SetCaseSensitive)(Int32 caseSensitive);
+ typedef HRESULT (WINAPI *Func_SetLargePageMode)();
+ // typedef HRESULT (WINAPI *Func_SetClientVersion)(UInt32 version);
+
+ typedef IOutArchive * (*Func_CreateOutArchive)();
+ typedef IInArchive * (*Func_CreateInArchive)();
+}
+
+
+/*
+ if there is no time in archive, external MTime of archive
+ will be used instead of _item.Time from archive.
+ For 7-zip before 22.00 we need to return some supported value.
+ But (kpidTimeType > kDOS) is not allowed in 7-Zip before 22.00.
+ So we return highest precision value supported by old 7-Zip.
+ new 7-Zip 22.00 doesn't use that value in usual cases.
+*/
+
+
+#define DECLARE_AND_SET_CLIENT_VERSION_VAR
+#define GET_FileTimeType_NotDefined_for_GetFileTimeType \
+ NFileTimeType::kWindows
+
+/*
+extern UInt32 g_ClientVersion;
+
+#define GET_CLIENT_VERSION(major, minor) \
+ ((UInt32)(((UInt32)(major) << 16) | (UInt32)(minor)))
+
+#define DECLARE_AND_SET_CLIENT_VERSION_VAR \
+ UInt32 g_ClientVersion = GET_CLIENT_VERSION(MY_VER_MAJOR, MY_VER_MINOR);
+
+#define GET_FileTimeType_NotDefined_for_GetFileTimeType \
+ ((UInt32)(g_ClientVersion >= GET_CLIENT_VERSION(22, 0) ? \
+ (UInt32)(Int32)NFileTimeType::kNotDefined : \
+ NFileTimeType::kWindows))
+*/
+
+Z7_PURE_INTERFACES_END
+#endif
diff --git a/libs/archive/thirdparty/CPP/7zip/ICoder.h b/libs/archive/thirdparty/CPP/7zip/ICoder.h
new file mode 100644
index 0000000..aec2834
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/ICoder.h
@@ -0,0 +1,477 @@
+// ICoder.h
+
+#ifndef ZIP7_INC_ICODER_H
+#define ZIP7_INC_ICODER_H
+
+#include "IStream.h"
+
+Z7_PURE_INTERFACES_BEGIN
+
+#define Z7_IFACE_CONSTR_CODER(i, n) \
+ Z7_DECL_IFACE_7ZIP(i, 4, n) \
+ { Z7_IFACE_COM7_PURE(i) };
+
+#define Z7_IFACEM_ICompressProgressInfo(x) \
+ x(SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize))
+Z7_IFACE_CONSTR_CODER(ICompressProgressInfo, 0x04)
+ /*
+ SetRatioInfo()
+ (inSize) can be NULL, if unknown
+ (outSize) can be NULL, if unknown
+ returns:
+ S_OK
+ E_ABORT : Break by user
+ another error codes
+ */
+
+#define Z7_IFACEM_ICompressCoder(x) \
+ x(Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, \
+ const UInt64 *inSize, const UInt64 *outSize, \
+ ICompressProgressInfo *progress))
+Z7_IFACE_CONSTR_CODER(ICompressCoder, 0x05)
+
+#define Z7_IFACEM_ICompressCoder2(x) \
+ x(Code(ISequentialInStream * const *inStreams, const UInt64 *const *inSizes, UInt32 numInStreams, \
+ ISequentialOutStream *const *outStreams, const UInt64 *const *outSizes, UInt32 numOutStreams, \
+ ICompressProgressInfo *progress))
+Z7_IFACE_CONSTR_CODER(ICompressCoder2, 0x18)
+
+/*
+ ICompressCoder::Code
+ ICompressCoder2::Code
+
+ returns:
+ S_OK : OK
+ S_FALSE : data error (for decoders)
+ E_OUTOFMEMORY : memory allocation error
+ E_NOTIMPL : unsupported encoding method (for decoders)
+ another error code : some error. For example, it can be error code received from inStream or outStream function.
+
+ Parameters:
+ (inStream != NULL)
+ (outStream != NULL)
+
+ if (inSize != NULL)
+ {
+ Encoders in 7-Zip ignore (inSize).
+ Decoder can use (*inSize) to check that stream was decoded correctly.
+ Some decoders in 7-Zip check it, if (full_decoding mode was set via ICompressSetFinishMode)
+ }
+
+ If it's required to limit the reading from input stream (inStream), it can
+ be done with ISequentialInStream implementation.
+
+ if (outSize != NULL)
+ {
+ Encoders in 7-Zip ignore (outSize).
+ Decoder unpacks no more than (*outSize) bytes.
+ }
+
+ (progress == NULL) is allowed.
+
+
+ Decoding with Code() function
+ -----------------------------
+
+ You can request some interfaces before decoding
+ - ICompressSetDecoderProperties2
+ - ICompressSetFinishMode
+
+ If you need to decode full stream:
+ {
+ 1) try to set full_decoding mode with ICompressSetFinishMode::SetFinishMode(1);
+ 2) call the Code() function with specified (inSize) and (outSize), if these sizes are known.
+ }
+
+ If you need to decode only part of stream:
+ {
+ 1) try to set partial_decoding mode with ICompressSetFinishMode::SetFinishMode(0);
+ 2) Call the Code() function with specified (inSize = NULL) and specified (outSize).
+ }
+
+ Encoding with Code() function
+ -----------------------------
+
+ You can request some interfaces :
+ - ICompressSetCoderProperties - use it before encoding to set properties
+ - ICompressWriteCoderProperties - use it before or after encoding to request encoded properties.
+
+ ICompressCoder2 is used when (numInStreams != 1 || numOutStreams != 1)
+ The rules are similar to ICompressCoder rules
+*/
+
+
+namespace NCoderPropID
+{
+ enum EEnum
+ {
+ kDefaultProp = 0,
+ kDictionarySize, // VT_UI4
+ kUsedMemorySize, // VT_UI4
+ kOrder, // VT_UI4
+ kBlockSize, // VT_UI4 or VT_UI8
+ kPosStateBits, // VT_UI4
+ kLitContextBits, // VT_UI4
+ kLitPosBits, // VT_UI4
+ kNumFastBytes, // VT_UI4
+ kMatchFinder, // VT_BSTR
+ kMatchFinderCycles, // VT_UI4
+ kNumPasses, // VT_UI4
+ kAlgorithm, // VT_UI4
+ kNumThreads, // VT_UI4
+ kEndMarker, // VT_BOOL
+ kLevel, // VT_UI4
+ kReduceSize, // VT_UI8 : it's estimated size of largest data stream that will be compressed
+ // encoder can use this value to reduce dictionary size and allocate data buffers
+
+ kExpectedDataSize, // VT_UI8 : for ICompressSetCoderPropertiesOpt :
+ // it's estimated size of current data stream
+ // real data size can differ from that size
+ // encoder can use this value to optimize encoder initialization
+
+ kBlockSize2, // VT_UI4 or VT_UI8
+ kCheckSize, // VT_UI4 : size of digest in bytes
+ kFilter, // VT_BSTR
+ kMemUse, // VT_UI8
+ kAffinity, // VT_UI8
+ kBranchOffset, // VT_UI4
+ kHashBits, // VT_UI4
+ /*
+ // kHash3Bits, // VT_UI4
+ // kHash2Bits, // VT_UI4
+ // kChainBits, // VT_UI4
+ kChainSize, // VT_UI4
+ kNativeLevel, // VT_UI4
+ kFast, // VT_UI4
+ kMinMatch, // VT_UI4 The minimum slen is 3 and the maximum is 7.
+ kOverlapLog, // VT_UI4 The minimum ovlog is 0 and the maximum is 9. (default: 6)
+ kRowMatchFinder, // VT_BOOL
+ kLdmEnable, // VT_BOOL
+ // kLdmWindowSizeLog, // VT_UI4
+ kLdmWindowSize, // VT_UI4
+ kLdmHashLog, // VT_UI4 The minimum ldmhlog is 6 and the maximum is 26 (default: 20).
+ kLdmMinMatchLength, // VT_UI4 The minimum ldmslen is 4 and the maximum is 4096 (default: 64).
+ kLdmBucketSizeLog, // VT_UI4 The minimum ldmblog is 0 and the maximum is 8 (default: 3).
+ kLdmHashRateLog, // VT_UI4 The default value is wlog - ldmhlog.
+ kWriteUnpackSizeFlag, // VT_BOOL
+ kUsePledged, // VT_BOOL
+ kUseSizeHintPledgedForSmall, // VT_BOOL
+ kUseSizeHintForEach, // VT_BOOL
+ kUseSizeHintGlobal, // VT_BOOL
+ kParamSelectMode, // VT_UI4
+ // kSearchLog, // VT_UI4 The minimum slog is 1 and the maximum is 26
+ // kTargetLen, // VT_UI4 The minimum tlen is 0 and the maximum is 999.
+ */
+ k_NUM_DEFINED
+ };
+}
+
+#define Z7_IFACEM_ICompressSetCoderPropertiesOpt(x) \
+ x(SetCoderPropertiesOpt(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps))
+Z7_IFACE_CONSTR_CODER(ICompressSetCoderPropertiesOpt, 0x1F)
+
+
+#define Z7_IFACEM_ICompressSetCoderProperties(x) \
+ x(SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps))
+Z7_IFACE_CONSTR_CODER(ICompressSetCoderProperties, 0x20)
+
+/*
+#define Z7_IFACEM_ICompressSetDecoderProperties(x) \
+ x(SetDecoderProperties(ISequentialInStream *inStream))
+Z7_IFACE_CONSTR_CODER(ICompressSetDecoderProperties, 0x21)
+*/
+
+#define Z7_IFACEM_ICompressSetDecoderProperties2(x) \
+ x(SetDecoderProperties2(const Byte *data, UInt32 size))
+Z7_IFACE_CONSTR_CODER(ICompressSetDecoderProperties2, 0x22)
+ /* returns:
+ S_OK
+ E_NOTIMP : unsupported properties
+ E_INVALIDARG : incorrect (or unsupported) properties
+ E_OUTOFMEMORY : memory allocation error
+ */
+
+
+#define Z7_IFACEM_ICompressWriteCoderProperties(x) \
+ x(WriteCoderProperties(ISequentialOutStream *outStream))
+Z7_IFACE_CONSTR_CODER(ICompressWriteCoderProperties, 0x23)
+
+#define Z7_IFACEM_ICompressGetInStreamProcessedSize(x) \
+ x(GetInStreamProcessedSize(UInt64 *value))
+Z7_IFACE_CONSTR_CODER(ICompressGetInStreamProcessedSize, 0x24)
+
+#define Z7_IFACEM_ICompressSetCoderMt(x) \
+ x(SetNumberOfThreads(UInt32 numThreads))
+Z7_IFACE_CONSTR_CODER(ICompressSetCoderMt, 0x25)
+
+#define Z7_IFACEM_ICompressSetFinishMode(x) \
+ x(SetFinishMode(UInt32 finishMode))
+Z7_IFACE_CONSTR_CODER(ICompressSetFinishMode, 0x26)
+ /* finishMode:
+ 0 : partial decoding is allowed. It's default mode for ICompressCoder::Code(), if (outSize) is defined.
+ 1 : full decoding. The stream must be finished at the end of decoding. */
+
+#define Z7_IFACEM_ICompressGetInStreamProcessedSize2(x) \
+ x(GetInStreamProcessedSize2(UInt32 streamIndex, UInt64 *value))
+Z7_IFACE_CONSTR_CODER(ICompressGetInStreamProcessedSize2, 0x27)
+
+#define Z7_IFACEM_ICompressSetMemLimit(x) \
+ x(SetMemLimit(UInt64 memUsage))
+Z7_IFACE_CONSTR_CODER(ICompressSetMemLimit, 0x28)
+
+
+/*
+ ICompressReadUnusedFromInBuf is supported by ICoder object
+ call ReadUnusedFromInBuf() after ICoder::Code(inStream, ...).
+ ICoder::Code(inStream, ...) decodes data, and the ICoder object is allowed
+ to read from inStream to internal buffers more data than minimal data required for decoding.
+ So we can call ReadUnusedFromInBuf() from same ICoder object to read unused input
+ data from the internal buffer.
+ in ReadUnusedFromInBuf(): the Coder is not allowed to use (ISequentialInStream *inStream) object, that was sent to ICoder::Code().
+*/
+#define Z7_IFACEM_ICompressReadUnusedFromInBuf(x) \
+ x(ReadUnusedFromInBuf(void *data, UInt32 size, UInt32 *processedSize))
+Z7_IFACE_CONSTR_CODER(ICompressReadUnusedFromInBuf, 0x29)
+
+
+#define Z7_IFACEM_ICompressGetSubStreamSize(x) \
+ x(GetSubStreamSize(UInt64 subStream, UInt64 *value))
+Z7_IFACE_CONSTR_CODER(ICompressGetSubStreamSize, 0x30)
+ /* returns:
+ S_OK : (*value) contains the size or estimated size (can be incorrect size)
+ S_FALSE : size is undefined
+ E_NOTIMP : the feature is not implemented
+ Let's (read_size) is size of data that was already read by ISequentialInStream::Read().
+ The caller should call GetSubStreamSize() after each Read() and check sizes:
+ if (start_of_subStream + *value < read_size)
+ {
+ // (*value) is correct, and it's allowed to call GetSubStreamSize() for next subStream:
+ start_of_subStream += *value;
+ subStream++;
+ }
+ */
+
+#define Z7_IFACEM_ICompressSetInStream(x) \
+ x(SetInStream(ISequentialInStream *inStream)) \
+ x(ReleaseInStream())
+Z7_IFACE_CONSTR_CODER(ICompressSetInStream, 0x31)
+
+#define Z7_IFACEM_ICompressSetOutStream(x) \
+ x(SetOutStream(ISequentialOutStream *outStream)) \
+ x(ReleaseOutStream())
+Z7_IFACE_CONSTR_CODER(ICompressSetOutStream, 0x32)
+
+/*
+#define Z7_IFACEM_ICompressSetInStreamSize(x) \
+ x(SetInStreamSize(const UInt64 *inSize)) \
+Z7_IFACE_CONSTR_CODER(ICompressSetInStreamSize, 0x33)
+*/
+
+#define Z7_IFACEM_ICompressSetOutStreamSize(x) \
+ x(SetOutStreamSize(const UInt64 *outSize))
+Z7_IFACE_CONSTR_CODER(ICompressSetOutStreamSize, 0x34)
+ /* That function initializes decoder structures.
+ Call this function only for stream version of decoder.
+ if (outSize == NULL), then output size is unknown
+ if (outSize != NULL), then the decoder must stop decoding after (*outSize) bytes. */
+
+#define Z7_IFACEM_ICompressSetBufSize(x) \
+ x(SetInBufSize(UInt32 streamIndex, UInt32 size)) \
+ x(SetOutBufSize(UInt32 streamIndex, UInt32 size))
+
+Z7_IFACE_CONSTR_CODER(ICompressSetBufSize, 0x35)
+
+#define Z7_IFACEM_ICompressInitEncoder(x) \
+ x(InitEncoder())
+Z7_IFACE_CONSTR_CODER(ICompressInitEncoder, 0x36)
+ /* That function initializes encoder structures.
+ Call this function only for stream version of encoder. */
+
+#define Z7_IFACEM_ICompressSetInStream2(x) \
+ x(SetInStream2(UInt32 streamIndex, ISequentialInStream *inStream)) \
+ x(ReleaseInStream2(UInt32 streamIndex))
+Z7_IFACE_CONSTR_CODER(ICompressSetInStream2, 0x37)
+
+/*
+#define Z7_IFACEM_ICompressSetOutStream2(x) \
+ x(SetOutStream2(UInt32 streamIndex, ISequentialOutStream *outStream))
+ x(ReleaseOutStream2(UInt32 streamIndex))
+Z7_IFACE_CONSTR_CODER(ICompressSetOutStream2, 0x38)
+
+#define Z7_IFACEM_ICompressSetInStreamSize2(x) \
+ x(SetInStreamSize2(UInt32 streamIndex, const UInt64 *inSize))
+Z7_IFACE_CONSTR_CODER(ICompressSetInStreamSize2, 0x39)
+*/
+
+/*
+#define Z7_IFACEM_ICompressInSubStreams(x) \
+ x(GetNextInSubStream(UInt64 *streamIndexRes, ISequentialInStream **stream))
+Z7_IFACE_CONSTR_CODER(ICompressInSubStreams, 0x3A)
+
+#define Z7_IFACEM_ICompressOutSubStreams(x) \
+ x(GetNextOutSubStream(UInt64 *streamIndexRes, ISequentialOutStream **stream))
+Z7_IFACE_CONSTR_CODER(ICompressOutSubStreams, 0x3B)
+*/
+
+/*
+ ICompressFilter
+ Filter(Byte *data, UInt32 size)
+ (size)
+ converts as most as possible bytes required for fast processing.
+ Some filters have (smallest_fast_block).
+ For example, (smallest_fast_block == 16) for AES CBC/CTR filters.
+ If data stream is not finished, caller must call Filter() for larger block:
+ where (size >= smallest_fast_block).
+ if (size >= smallest_fast_block)
+ {
+ The filter can leave some bytes at the end of data without conversion:
+ if there are data alignment reasons or speed reasons.
+ The caller can read additional data from stream and call Filter() again.
+ }
+ If data stream was finished, caller can call Filter() for (size < smallest_fast_block)
+
+ (data) parameter:
+ Some filters require alignment for any Filter() call:
+ 1) (stream_offset % alignment_size) == (data % alignment_size)
+ 2) (alignment_size == 2^N)
+ where (stream_offset) - is the number of bytes that were already filtered before.
+ The callers of Filter() are required to meet these requirements.
+ (alignment_size) can be different:
+ 16 : for AES filters
+ 4 or 2 : for some branch convert filters
+ 1 : for another filters
+ (alignment_size >= 16) is enough for all current filters of 7-Zip.
+ But the caller can use larger (alignment_size).
+ Recommended alignment for (data) of Filter() call is (alignment_size == 64).
+ Also it's recommended to use aligned value for (size):
+ (size % alignment_size == 0),
+ if it's not last call of Filter() for current stream.
+
+ returns: (outSize):
+ if (outSize == 0) : Filter have not converted anything.
+ So the caller can stop processing, if data stream was finished.
+ if (outSize <= size) : Filter have converted outSize bytes
+ if (outSize > size) : Filter have not converted anything.
+ and it needs at least outSize bytes to convert one block
+ (it's for crypto block algorithms).
+*/
+
+#define Z7_IFACEM_ICompressFilter(x) \
+ x(Init()) \
+ x##2(UInt32, Filter(Byte *data, UInt32 size))
+Z7_IFACE_CONSTR_CODER(ICompressFilter, 0x40)
+
+
+#define Z7_IFACEM_ICompressCodecsInfo(x) \
+ x(GetNumMethods(UInt32 *numMethods)) \
+ x(GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) \
+ x(CreateDecoder(UInt32 index, const GUID *iid, void* *coder)) \
+ x(CreateEncoder(UInt32 index, const GUID *iid, void* *coder))
+Z7_IFACE_CONSTR_CODER(ICompressCodecsInfo, 0x60)
+
+#define Z7_IFACEM_ISetCompressCodecsInfo(x) \
+ x(SetCompressCodecsInfo(ICompressCodecsInfo *compressCodecsInfo))
+Z7_IFACE_CONSTR_CODER(ISetCompressCodecsInfo, 0x61)
+
+#define Z7_IFACEM_ICryptoProperties(x) \
+ x(SetKey(const Byte *data, UInt32 size)) \
+ x(SetInitVector(const Byte *data, UInt32 size))
+Z7_IFACE_CONSTR_CODER(ICryptoProperties, 0x80)
+
+/*
+ x(ResetSalt())
+Z7_IFACE_CONSTR_CODER(ICryptoResetSalt, 0x88)
+*/
+
+#define Z7_IFACEM_ICryptoResetInitVector(x) \
+ x(ResetInitVector())
+Z7_IFACE_CONSTR_CODER(ICryptoResetInitVector, 0x8C)
+ /* Call ResetInitVector() only for encoding.
+ Call ResetInitVector() before encoding and before WriteCoderProperties().
+ Crypto encoder can create random IV in that function. */
+
+#define Z7_IFACEM_ICryptoSetPassword(x) \
+ x(CryptoSetPassword(const Byte *data, UInt32 size))
+Z7_IFACE_CONSTR_CODER(ICryptoSetPassword, 0x90)
+
+#define Z7_IFACEM_ICryptoSetCRC(x) \
+ x(CryptoSetCRC(UInt32 crc))
+Z7_IFACE_CONSTR_CODER(ICryptoSetCRC, 0xA0)
+
+
+namespace NMethodPropID
+{
+ enum EEnum
+ {
+ kID,
+ kName,
+ kDecoder,
+ kEncoder,
+ kPackStreams,
+ kUnpackStreams,
+ kDescription,
+ kDecoderIsAssigned,
+ kEncoderIsAssigned,
+ kDigestSize,
+ kIsFilter
+ };
+}
+
+namespace NModuleInterfaceType
+{
+ /*
+ virtual destructor in IUnknown:
+ - no : 7-Zip (Windows)
+ - no : 7-Zip (Linux) (v23) in default mode
+ - yes : p7zip
+ - yes : 7-Zip (Linux) before v23
+ - yes : 7-Zip (Linux) (v23), if Z7_USE_VIRTUAL_DESTRUCTOR_IN_IUNKNOWN is defined
+ */
+ const UInt32 k_IUnknown_VirtDestructor_No = 0;
+ const UInt32 k_IUnknown_VirtDestructor_Yes = 1;
+ const UInt32 k_IUnknown_VirtDestructor_ThisModule =
+ #if !defined(_WIN32) && defined(Z7_USE_VIRTUAL_DESTRUCTOR_IN_IUNKNOWN)
+ k_IUnknown_VirtDestructor_Yes;
+ #else
+ k_IUnknown_VirtDestructor_No;
+ #endif
+}
+
+namespace NModulePropID
+{
+ enum EEnum
+ {
+ kInterfaceType, // VT_UI4
+ kVersion // VT_UI4
+ };
+}
+
+
+#define Z7_IFACEM_IHasher(x) \
+ x##2(void, Init()) \
+ x##2(void, Update(const void *data, UInt32 size)) \
+ x##2(void, Final(Byte *digest)) \
+ x##2(UInt32, GetDigestSize())
+Z7_IFACE_CONSTR_CODER(IHasher, 0xC0)
+
+#define Z7_IFACEM_IHashers(x) \
+ x##2(UInt32, GetNumHashers()) \
+ x(GetHasherProp(UInt32 index, PROPID propID, PROPVARIANT *value)) \
+ x(CreateHasher(UInt32 index, IHasher **hasher))
+Z7_IFACE_CONSTR_CODER(IHashers, 0xC1)
+
+extern "C"
+{
+ typedef HRESULT (WINAPI *Func_GetNumberOfMethods)(UInt32 *numMethods);
+ typedef HRESULT (WINAPI *Func_GetMethodProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
+ typedef HRESULT (WINAPI *Func_CreateDecoder)(UInt32 index, const GUID *iid, void **outObject);
+ typedef HRESULT (WINAPI *Func_CreateEncoder)(UInt32 index, const GUID *iid, void **outObject);
+
+ typedef HRESULT (WINAPI *Func_GetHashers)(IHashers **hashers);
+
+ typedef HRESULT (WINAPI *Func_SetCodecs)(ICompressCodecsInfo *compressCodecsInfo);
+ typedef HRESULT (WINAPI *Func_GetModuleProp)(PROPID propID, PROPVARIANT *value);
+}
+
+Z7_PURE_INTERFACES_END
+#endif
diff --git a/libs/archive/thirdparty/CPP/7zip/IDecl.h b/libs/archive/thirdparty/CPP/7zip/IDecl.h
new file mode 100644
index 0000000..4dbf1eb
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/IDecl.h
@@ -0,0 +1,76 @@
+// IDecl.h
+
+#ifndef ZIP7_INC_IDECL_H
+#define ZIP7_INC_IDECL_H
+
+#include "../Common/Common0.h"
+#include "../Common/MyUnknown.h"
+
+#define k_7zip_GUID_Data1 0x23170F69
+#define k_7zip_GUID_Data2 0x40C1
+
+#define k_7zip_GUID_Data3_Common 0x278A
+
+#define k_7zip_GUID_Data3_Decoder 0x2790
+#define k_7zip_GUID_Data3_Encoder 0x2791
+#define k_7zip_GUID_Data3_Hasher 0x2792
+
+#define Z7_DECL_IFACE_7ZIP_SUB(i, _base, groupId, subId) \
+ Z7_DEFINE_GUID(IID_ ## i, \
+ k_7zip_GUID_Data1, \
+ k_7zip_GUID_Data2, \
+ k_7zip_GUID_Data3_Common, \
+ 0, 0, 0, (groupId), 0, (subId), 0, 0); \
+ struct Z7_DECLSPEC_NOVTABLE i: public _base
+
+#define Z7_DECL_IFACE_7ZIP(i, groupId, subId) \
+ Z7_DECL_IFACE_7ZIP_SUB(i, IUnknown, groupId, subId)
+
+
+#ifdef COM_DECLSPEC_NOTHROW
+#define Z7_COMWF_B COM_DECLSPEC_NOTHROW STDMETHODIMP
+#define Z7_COMWF_B_(t) COM_DECLSPEC_NOTHROW STDMETHODIMP_(t)
+#else
+#define Z7_COMWF_B STDMETHODIMP
+#define Z7_COMWF_B_(t) STDMETHODIMP_(t)
+#endif
+
+#if defined(_MSC_VER) && !defined(COM_DECLSPEC_NOTHROW)
+#define Z7_COM7F_B __declspec(nothrow) STDMETHODIMP
+#define Z7_COM7F_B_(t) __declspec(nothrow) STDMETHODIMP_(t)
+#else
+#define Z7_COM7F_B Z7_COMWF_B
+#define Z7_COM7F_B_(t) Z7_COMWF_B_(t)
+#endif
+
+// #define Z7_COM7F_E Z7_noexcept
+#define Z7_COM7F_E throw()
+#define Z7_COM7F_EO Z7_COM7F_E Z7_override
+#define Z7_COM7F_EOF Z7_COM7F_EO Z7_final
+#define Z7_COM7F_IMF(f) Z7_COM7F_B f Z7_COM7F_E
+#define Z7_COM7F_IMF2(t, f) Z7_COM7F_B_(t) f Z7_COM7F_E
+
+#define Z7_COM7F_PURE(f) virtual Z7_COM7F_IMF(f) =0;
+#define Z7_COM7F_PURE2(t, f) virtual Z7_COM7F_IMF2(t, f) =0;
+#define Z7_COM7F_IMP(f) Z7_COM7F_IMF(f) Z7_override Z7_final;
+#define Z7_COM7F_IMP2(t, f) Z7_COM7F_IMF2(t, f) Z7_override Z7_final;
+#define Z7_COM7F_IMP_NONFINAL(f) Z7_COM7F_IMF(f) Z7_override;
+#define Z7_COM7F_IMP_NONFINAL2(t, f) Z7_COM7F_IMF2(t, f) Z7_override;
+
+#define Z7_IFACE_PURE(name) Z7_IFACEN_ ## name(=0;)
+#define Z7_IFACE_IMP(name) Z7_IFACEN_ ## name(Z7_override Z7_final;)
+
+#define Z7_IFACE_COM7_PURE(name) Z7_IFACEM_ ## name(Z7_COM7F_PURE)
+#define Z7_IFACE_COM7_IMP(name) Z7_IFACEM_ ## name(Z7_COM7F_IMP)
+#define Z7_IFACE_COM7_IMP_NONFINAL(name) Z7_IFACEM_ ## name(Z7_COM7F_IMP_NONFINAL)
+
+
+#define Z7_IFACE_DECL_PURE(name) \
+ DECLARE_INTERFACE(name) \
+ { Z7_IFACE_PURE(name) };
+
+#define Z7_IFACE_DECL_PURE_(name, baseiface) \
+ DECLARE_INTERFACE_(name, baseiface) \
+ { Z7_IFACE_PURE(name) };
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/7zip/IPassword.h b/libs/archive/thirdparty/CPP/7zip/IPassword.h
new file mode 100644
index 0000000..689f08c
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/IPassword.h
@@ -0,0 +1,54 @@
+// IPassword.h
+
+#ifndef ZIP7_INC_IPASSWORD_H
+#define ZIP7_INC_IPASSWORD_H
+
+#include "../Common/MyTypes.h"
+
+#include "IDecl.h"
+
+Z7_PURE_INTERFACES_BEGIN
+
+#define Z7_IFACE_CONSTR_PASSWORD(i, n) \
+ Z7_DECL_IFACE_7ZIP(i, 5, n) \
+ { Z7_IFACE_COM7_PURE(i) };
+
+/*
+How to use output parameter (BSTR *password):
+
+in: The caller is required to set BSTR value as NULL (no string).
+ The callee (in 7-Zip code) ignores the input value stored in BSTR variable,
+
+out: The callee rewrites BSTR variable (*password) with new allocated string pointer.
+ The caller must free BSTR string with function SysFreeString();
+*/
+
+#define Z7_IFACEM_ICryptoGetTextPassword(x) \
+ x(CryptoGetTextPassword(BSTR *password))
+Z7_IFACE_CONSTR_PASSWORD(ICryptoGetTextPassword, 0x10)
+
+
+/*
+CryptoGetTextPassword2()
+in:
+ The caller is required to set BSTR value as NULL (no string).
+ The caller is not required to set (*passwordIsDefined) value.
+
+out:
+ Return code: != S_OK : error code
+ Return code: S_OK : success
+
+ if (*passwordIsDefined == 1), the variable (*password) contains password string
+
+ if (*passwordIsDefined == 0), the password is not defined,
+ but the callee still could set (*password) to some allocated string, for example, as empty string.
+
+ The caller must free BSTR string with function SysFreeString()
+*/
+
+#define Z7_IFACEM_ICryptoGetTextPassword2(x) \
+ x(CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password))
+Z7_IFACE_CONSTR_PASSWORD(ICryptoGetTextPassword2, 0x11)
+
+Z7_PURE_INTERFACES_END
+#endif
diff --git a/libs/archive/thirdparty/CPP/7zip/IProgress.h b/libs/archive/thirdparty/CPP/7zip/IProgress.h
new file mode 100644
index 0000000..6714983
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/IProgress.h
@@ -0,0 +1,20 @@
+// IProgress.h
+
+#ifndef ZIP7_INC_IPROGRESS_H
+#define ZIP7_INC_IPROGRESS_H
+
+#include "../Common/MyTypes.h"
+
+#include "IDecl.h"
+
+Z7_PURE_INTERFACES_BEGIN
+
+#define Z7_IFACEM_IProgress(x) \
+ x(SetTotal(UInt64 total)) \
+ x(SetCompleted(const UInt64 *completeValue)) \
+
+Z7_DECL_IFACE_7ZIP(IProgress, 0, 5)
+ { Z7_IFACE_COM7_PURE(IProgress) };
+
+Z7_PURE_INTERFACES_END
+#endif
diff --git a/libs/archive/thirdparty/CPP/7zip/IStream.h b/libs/archive/thirdparty/CPP/7zip/IStream.h
new file mode 100644
index 0000000..0c44a91
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/IStream.h
@@ -0,0 +1,210 @@
+// IStream.h
+
+#ifndef ZIP7_INC_ISTREAM_H
+#define ZIP7_INC_ISTREAM_H
+
+#include "../Common/Common0.h"
+#include "../Common/MyTypes.h"
+#include "../Common/MyWindows.h"
+
+#include "IDecl.h"
+
+Z7_PURE_INTERFACES_BEGIN
+
+#define Z7_IFACE_CONSTR_STREAM_SUB(i, base, n) \
+ Z7_DECL_IFACE_7ZIP_SUB(i, base, 3, n) \
+ { Z7_IFACE_COM7_PURE(i) };
+
+#define Z7_IFACE_CONSTR_STREAM(i, n) \
+ Z7_IFACE_CONSTR_STREAM_SUB(i, IUnknown, n)
+
+
+/*
+ISequentialInStream::Read()
+ The requirement for caller: (processedSize != NULL).
+ The callee can allow (processedSize == NULL) for compatibility reasons.
+
+ if (size == 0), this function returns S_OK and (*processedSize) is set to 0.
+
+ if (size != 0)
+ {
+ Partial read is allowed: (*processedSize <= avail_size && *processedSize <= size),
+ where (avail_size) is the size of remaining bytes in stream.
+ If (avail_size != 0), this function must read at least 1 byte: (*processedSize > 0).
+ You must call Read() in loop, if you need to read exact amount of data.
+ }
+
+ If seek pointer before Read() call was changed to position past the end of stream:
+ if (seek_pointer >= stream_size), this function returns S_OK and (*processedSize) is set to 0.
+
+ ERROR CASES:
+ If the function returns error code, then (*processedSize) is size of
+ data written to (data) buffer (it can be data before error or data with errors).
+ The recommended way for callee to work with reading errors:
+ 1) write part of data before error to (data) buffer and return S_OK.
+ 2) return error code for further calls of Read().
+*/
+#define Z7_IFACEM_ISequentialInStream(x) \
+ x(Read(void *data, UInt32 size, UInt32 *processedSize))
+Z7_IFACE_CONSTR_STREAM(ISequentialInStream, 0x01)
+
+
+/*
+ISequentialOutStream::Write()
+ The requirement for caller: (processedSize != NULL).
+ The callee can allow (processedSize == NULL) for compatibility reasons.
+
+ if (size != 0)
+ {
+ Partial write is allowed: (*processedSize <= size),
+ but this function must write at least 1 byte: (*processedSize > 0).
+ You must call Write() in loop, if you need to write exact amount of data.
+ }
+
+ ERROR CASES:
+ If the function returns error code, then (*processedSize) is size of
+ data written from (data) buffer.
+*/
+#define Z7_IFACEM_ISequentialOutStream(x) \
+ x(Write(const void *data, UInt32 size, UInt32 *processedSize))
+Z7_IFACE_CONSTR_STREAM(ISequentialOutStream, 0x02)
+
+
+#ifdef _WIN32
+
+#ifdef __HRESULT_FROM_WIN32
+#define HRESULT_WIN32_ERROR_NEGATIVE_SEEK __HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK)
+#else
+#define HRESULT_WIN32_ERROR_NEGATIVE_SEEK HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK)
+#endif
+
+#else
+
+#define HRESULT_WIN32_ERROR_NEGATIVE_SEEK MY_E_ERROR_NEGATIVE_SEEK
+
+#endif
+
+
+/*
+IInStream::Seek() / IOutStream::Seek()
+ If you seek to position before the beginning of the stream,
+ Seek() function returns error code:
+ Recommended error code is __HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK).
+ or STG_E_INVALIDFUNCTION
+ It is allowed to seek past the end of the stream.
+ if Seek() returns error, then the value of *newPosition is undefined.
+*/
+
+#define Z7_IFACEM_IInStream(x) \
+ x(Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
+Z7_IFACE_CONSTR_STREAM_SUB(IInStream, ISequentialInStream, 0x03)
+
+#define Z7_IFACEM_IOutStream(x) \
+ x(Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)) \
+ x(SetSize(UInt64 newSize))
+Z7_IFACE_CONSTR_STREAM_SUB(IOutStream, ISequentialOutStream, 0x04)
+
+#define Z7_IFACEM_IStreamGetSize(x) \
+ x(GetSize(UInt64 *size))
+Z7_IFACE_CONSTR_STREAM(IStreamGetSize, 0x06)
+
+#define Z7_IFACEM_IOutStreamFinish(x) \
+ x(OutStreamFinish())
+Z7_IFACE_CONSTR_STREAM(IOutStreamFinish, 0x07)
+
+#define Z7_IFACEM_IStreamGetProps(x) \
+ x(GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib))
+Z7_IFACE_CONSTR_STREAM(IStreamGetProps, 0x08)
+
+
+struct CStreamFileProps
+{
+ UInt64 Size;
+ UInt64 VolID;
+ UInt64 FileID_Low;
+ UInt64 FileID_High;
+ UInt32 NumLinks;
+ UInt32 Attrib;
+ FILETIME CTime;
+ FILETIME ATime;
+ FILETIME MTime;
+};
+
+
+#define Z7_IFACEM_IStreamGetProps2(x) \
+ x(GetProps2(CStreamFileProps *props))
+Z7_IFACE_CONSTR_STREAM(IStreamGetProps2, 0x09)
+
+#define Z7_IFACEM_IStreamGetProp(x) \
+ x(GetProperty(PROPID propID, PROPVARIANT *value)) \
+ x(ReloadProps())
+Z7_IFACE_CONSTR_STREAM(IStreamGetProp, 0x0a)
+
+
+/*
+IStreamSetRestriction::SetRestriction(UInt64 begin, UInt64 end)
+
+ It sets region of data in output stream that is restricted.
+ For restricted region it's expected (or allowed)
+ that the caller can write to same region with different calls of Write()/SetSize().
+ Another regions of output stream will be supposed as non-restricted:
+ - The callee usually doesn't flush the data in restricted region.
+ - The callee usually can flush data from non-restricted region after writing.
+
+Actual restiction rules depend also from current stream position.
+It's recommended to call SetRestriction() just before the Write() call.
+So the callee can optimize writing and flushing, if that Write()
+operation is not restricted.
+
+Note: Each new call of SetRestriction() sets new restictions,
+so previous restrction calls has no effect anymore.
+
+inputs:
+
+ (begin > end) is not allowed, and returns E_FAIL;
+
+ if (begin == end)
+ {
+ No restriction.
+ The caller will call Write() in sequential order.
+ After SetRestriction(begin, begin), but before next call of SetRestriction()
+ {
+ Additional condition:
+ it's expected that current stream seek position is equal to stream size.
+ The callee can make final flushing for any data before current stream seek position.
+ For each Write(size) call:
+ The callee can make final flushing for that new written data.
+ }
+ The pair of values (begin == 0 && end == 0) is recommended to remove write restriction.
+ }
+
+ if (begin < end)
+ {
+ it means that callee must NOT flush any data in region [begin, end).
+ The caller is allowed to Seek() to that region and rewrite the
+ data in that restriction region.
+ if (end == (UInt64)(Int64)-1)
+ {
+ there is no upper bound for restricted region.
+ So non-restricted region will be [0, begin) in that case
+ }
+ }
+
+ returns:
+ - if (begin > end) it return ERROR code (E_FAIL)
+ - S_OK : if no errors.
+ - Also the call of SetRestriction() can initiate the flushing of already written data.
+ So it can return the result of that flushing.
+
+ Note: IOutStream::SetSize() also can change the data.
+ So it's not expected the call
+ IOutStream::SetSize() to region that was written before as unrestricted.
+*/
+
+#define Z7_IFACEM_IStreamSetRestriction(x) \
+ x(SetRestriction(UInt64 begin, UInt64 end)) \
+
+Z7_IFACE_CONSTR_STREAM(IStreamSetRestriction, 0x10)
+
+Z7_PURE_INTERFACES_END
+#endif
diff --git a/libs/archive/thirdparty/CPP/7zip/MyVersion.h b/libs/archive/thirdparty/CPP/7zip/MyVersion.h
new file mode 100644
index 0000000..8f52a12
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/MyVersion.h
@@ -0,0 +1,2 @@
+#define USE_COPYRIGHT_CR
+#include "../../C/7zVersion.h"
diff --git a/libs/archive/thirdparty/CPP/7zip/PropID.h b/libs/archive/thirdparty/CPP/7zip/PropID.h
new file mode 100644
index 0000000..e074794
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/7zip/PropID.h
@@ -0,0 +1,178 @@
+// PropID.h
+
+#ifndef ZIP7_INC_7ZIP_PROP_ID_H
+#define ZIP7_INC_7ZIP_PROP_ID_H
+
+#include "../Common/MyTypes.h"
+
+enum
+{
+ kpidNoProperty = 0,
+ kpidMainSubfile,
+ kpidHandlerItemIndex,
+ kpidPath,
+ kpidName,
+ kpidExtension,
+ kpidIsDir,
+ kpidSize,
+ kpidPackSize,
+ kpidAttrib,
+ kpidCTime,
+ kpidATime,
+ kpidMTime,
+ kpidSolid,
+ kpidCommented,
+ kpidEncrypted,
+ kpidSplitBefore,
+ kpidSplitAfter,
+ kpidDictionarySize,
+ kpidCRC,
+ kpidType,
+ kpidIsAnti,
+ kpidMethod,
+ kpidHostOS,
+ kpidFileSystem,
+ kpidUser,
+ kpidGroup,
+ kpidBlock,
+ kpidComment,
+ kpidPosition,
+ kpidPrefix,
+ kpidNumSubDirs,
+ kpidNumSubFiles,
+ kpidUnpackVer,
+ kpidVolume,
+ kpidIsVolume,
+ kpidOffset,
+ kpidLinks,
+ kpidNumBlocks,
+ kpidNumVolumes,
+ kpidTimeType,
+ kpidBit64,
+ kpidBigEndian,
+ kpidCpu,
+ kpidPhySize,
+ kpidHeadersSize,
+ kpidChecksum,
+ kpidCharacts,
+ kpidVa,
+ kpidId,
+ kpidShortName,
+ kpidCreatorApp,
+ kpidSectorSize,
+ kpidPosixAttrib,
+ kpidSymLink,
+ kpidError,
+ kpidTotalSize,
+ kpidFreeSpace,
+ kpidClusterSize,
+ kpidVolumeName,
+ kpidLocalName,
+ kpidProvider,
+ kpidNtSecure,
+ kpidIsAltStream,
+ kpidIsAux,
+ kpidIsDeleted,
+ kpidIsTree,
+ kpidSha1,
+ kpidSha256,
+ kpidErrorType,
+ kpidNumErrors,
+ kpidErrorFlags,
+ kpidWarningFlags,
+ kpidWarning,
+ kpidNumStreams,
+ kpidNumAltStreams,
+ kpidAltStreamsSize,
+ kpidVirtualSize,
+ kpidUnpackSize,
+ kpidTotalPhySize,
+ kpidVolumeIndex,
+ kpidSubType,
+ kpidShortComment,
+ kpidCodePage,
+ kpidIsNotArcType,
+ kpidPhySizeCantBeDetected,
+ kpidZerosTailIsAllowed,
+ kpidTailSize,
+ kpidEmbeddedStubSize,
+ kpidNtReparse,
+ kpidHardLink,
+ kpidINode,
+ kpidStreamId,
+ kpidReadOnly,
+ kpidOutName,
+ kpidCopyLink,
+ kpidArcFileName,
+ kpidIsHash,
+ kpidChangeTime,
+ kpidUserId,
+ kpidGroupId,
+ kpidDeviceMajor,
+ kpidDeviceMinor,
+ kpidDevMajor,
+ kpidDevMinor,
+
+ kpid_NUM_DEFINED,
+
+ kpidUserDefined = 0x10000
+};
+
+extern const Byte k7z_PROPID_To_VARTYPE[kpid_NUM_DEFINED]; // VARTYPE
+
+const UInt32 kpv_ErrorFlags_IsNotArc = 1 << 0;
+const UInt32 kpv_ErrorFlags_HeadersError = 1 << 1;
+const UInt32 kpv_ErrorFlags_EncryptedHeadersError = 1 << 2;
+const UInt32 kpv_ErrorFlags_UnavailableStart = 1 << 3;
+const UInt32 kpv_ErrorFlags_UnconfirmedStart = 1 << 4;
+const UInt32 kpv_ErrorFlags_UnexpectedEnd = 1 << 5;
+const UInt32 kpv_ErrorFlags_DataAfterEnd = 1 << 6;
+const UInt32 kpv_ErrorFlags_UnsupportedMethod = 1 << 7;
+const UInt32 kpv_ErrorFlags_UnsupportedFeature = 1 << 8;
+const UInt32 kpv_ErrorFlags_DataError = 1 << 9;
+const UInt32 kpv_ErrorFlags_CrcError = 1 << 10;
+// const UInt32 kpv_ErrorFlags_Unsupported = 1 << 11;
+
+/*
+linux ctime :
+ file metadata was last changed.
+ changing the file modification time
+ counts as a metadata change, so will also have the side effect of updating the ctime.
+
+PROPVARIANT for timestamps in 7-Zip:
+{
+ vt = VT_FILETIME
+ wReserved1: set precision level
+ 0 : base value (backward compatibility value)
+ only filetime is used (7 digits precision).
+ wReserved2 and wReserved3 can contain random data
+ 1 : Unix (1 sec)
+ 2 : DOS (2 sec)
+ 3 : High Precision (1 ns)
+ 16 - 3 : (reserved) = 1 day
+ 16 - 2 : (reserved) = 1 hour
+ 16 - 1 : (reserved) = 1 minute
+ 16 + 0 : 1 sec (0 digits after point)
+ 16 + (1,2,3,4,5,6,7,8,9) : set subsecond precision level :
+ (number of decimal digits after point)
+ 16 + 9 : 1 ns (9 digits after point)
+ wReserved2 = ns % 100 : if (8 or 9 digits pecision)
+ = 0 : if not (8 or 9 digits pecision)
+ wReserved3 = 0;
+ filetime
+}
+
+NOTE: TAR-PAX archives created by GNU TAR don't keep
+ whole information about original level of precision,
+ and timestamp are stored in reduced form, where tail zero
+ digits after point are removed.
+ So 7-Zip can return different precision levels for different items for such TAR archives.
+*/
+
+/*
+TimePrec returned by IOutArchive::GetFileTimeType()
+is used only for updating, when we compare MTime timestamp
+from archive with timestamp from directory.
+*/
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/Common.h b/libs/archive/thirdparty/CPP/Common/Common.h
new file mode 100644
index 0000000..cde0c38
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/Common.h
@@ -0,0 +1,28 @@
+// Common.h
+
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+#pragma warning(disable : 4464) // relative include path contains '..'
+#endif
+
+#ifndef ZIP7_INC_COMMON_H
+#define ZIP7_INC_COMMON_H
+
+#include "../../C/Precomp.h"
+#include "Common0.h"
+#include "MyWindows.h"
+
+/*
+This file is included to all cpp files in 7-Zip.
+Each folder contains StdAfx.h file that includes "Common.h".
+So 7-Zip includes "Common.h" in both modes:
+ with precompiled StdAfx.h
+and
+ without precompiled StdAfx.h
+
+include "Common.h" before other h files of 7-zip,
+ if you need predefined macros.
+do not include "Common.h", if you need only interfaces,
+ and you don't need predefined macros.
+*/
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/Common0.h b/libs/archive/thirdparty/CPP/Common/Common0.h
new file mode 100644
index 0000000..55606cd
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/Common0.h
@@ -0,0 +1,330 @@
+// Common0.h
+
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+#pragma warning(disable : 4464) // relative include path contains '..'
+#endif
+
+#ifndef ZIP7_INC_COMMON0_H
+#define ZIP7_INC_COMMON0_H
+
+#include "../../C/Compiler.h"
+
+/*
+This file contains compiler related things for cpp files.
+This file is included to all cpp files in 7-Zip via "Common.h".
+Also this file is included in "IDecl.h" (that is included in interface files).
+So external modules can use 7-Zip interfaces without
+predefined macros defined in "Common.h".
+*/
+
+#ifdef _MSC_VER
+ #pragma warning(disable : 4710) // function not inlined
+ // 'CUncopyable::CUncopyable':
+ #pragma warning(disable : 4514) // unreferenced inline function has been removed
+ #if _MSC_VER < 1300
+ #pragma warning(disable : 4702) // unreachable code
+ #pragma warning(disable : 4714) // function marked as __forceinline not inlined
+ #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information
+ #endif
+ #if _MSC_VER < 1400
+ #pragma warning(disable : 4511) // copy constructor could not be generated // #pragma warning(disable : 4512) // assignment operator could not be generated
+ #pragma warning(disable : 4512) // assignment operator could not be generated
+ #endif
+ #if _MSC_VER > 1400 && _MSC_VER <= 1900
+ // #pragma warning(disable : 4996)
+ // strcat: This function or variable may be unsafe
+ // GetVersion was declared deprecated
+ #endif
+
+#if _MSC_VER > 1200
+// -Wall warnings
+
+#if _MSC_VER <= 1600
+#pragma warning(disable : 4917) // 'OLE_HANDLE' : a GUID can only be associated with a class, interface or namespace
+#endif
+
+// #pragma warning(disable : 4061) // enumerator '' in switch of enum '' is not explicitly handled by a case label
+// #pragma warning(disable : 4266) // no override available for virtual member function from base ''; function is hidden
+#pragma warning(disable : 4625) // copy constructor was implicitly defined as deleted
+#pragma warning(disable : 4626) // assignment operator was implicitly defined as deleted
+#if _MSC_VER >= 1600 && _MSC_VER < 1920
+#pragma warning(disable : 4571) // Informational: catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught
+#endif
+#if _MSC_VER >= 1600
+#pragma warning(disable : 4365) // 'initializing' : conversion from 'int' to 'unsigned int', signed / unsigned mismatch
+#endif
+#if _MSC_VER < 1800
+// we disable the warning, if we don't use 'final' in class
+#pragma warning(disable : 4265) // class has virtual functions, but destructor is not virtual
+#endif
+
+#if _MSC_VER >= 1900
+#pragma warning(disable : 5026) // move constructor was implicitly defined as deleted
+#pragma warning(disable : 5027) // move assignment operator was implicitly defined as deleted
+#endif
+#if _MSC_VER >= 1912
+#pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to 'extern "C"' function under - EHc.Undefined behavior may occur if this function throws an exception.
+#endif
+#if _MSC_VER >= 1925
+// #pragma warning(disable : 5204) // 'ISequentialInStream' : class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly
+#endif
+#if _MSC_VER >= 1934
+// #pragma warning(disable : 5264) // const variable is not used
+#endif
+
+#endif // _MSC_VER > 1200
+#endif // _MSC_VER
+
+
+#if defined(_MSC_VER) // && !defined(__clang__)
+#define Z7_DECLSPEC_NOTHROW __declspec(nothrow)
+#elif defined(__clang__) || defined(__GNUC__)
+#define Z7_DECLSPEC_NOTHROW __attribute__((nothrow))
+#else
+#define Z7_DECLSPEC_NOTHROW
+#endif
+
+/*
+#if defined (_MSC_VER) && _MSC_VER >= 1900 \
+ || defined(__clang__) && __clang_major__ >= 6 \
+ || defined(__GNUC__) && __GNUC__ >= 6
+ #define Z7_noexcept noexcept
+#else
+ #define Z7_noexcept throw()
+#endif
+*/
+
+
+#if defined(__clang__)
+
+#if /* defined(_WIN32) && */ __clang_major__ >= 16
+#pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
+#endif
+
+#if __clang_major__ >= 4 && __clang_major__ < 12 && !defined(_WIN32)
+/*
+if compiled with new GCC libstdc++, GCC libstdc++ can use:
+13.2.0/include/c++/
+ <new> : #define _NEW
+ <stdlib.h> : #define _GLIBCXX_STDLIB_H 1
+*/
+#pragma GCC diagnostic ignored "-Wreserved-id-macro"
+#endif
+
+// noexcept, final, = delete
+#pragma GCC diagnostic ignored "-Wc++98-compat"
+#if __clang_major__ >= 4
+// throw() dynamic exception specifications are deprecated
+#pragma GCC diagnostic ignored "-Wdeprecated-dynamic-exception-spec"
+#endif
+
+#if __clang_major__ <= 6 // check it
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#pragma GCC diagnostic ignored "-Wglobal-constructors"
+#pragma GCC diagnostic ignored "-Wexit-time-destructors"
+
+#if defined(Z7_LLVM_CLANG_VERSION) && __clang_major__ >= 18 // 18.1.0RC
+#pragma GCC diagnostic ignored "-Wswitch-default"
+#endif
+// #pragma GCC diagnostic ignored "-Wunused-private-field"
+// #pragma GCC diagnostic ignored "-Wnonportable-system-include-path"
+// #pragma GCC diagnostic ignored "-Wsuggest-override"
+// #pragma GCC diagnostic ignored "-Wsign-conversion"
+// #pragma GCC diagnostic ignored "-Winconsistent-missing-override"
+// #pragma GCC diagnostic ignored "-Wsuggest-destructor-override"
+// #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+// #pragma GCC diagnostic ignored "-Wdeprecated-copy-with-user-provided-dtor"
+// #pragma GCC diagnostic ignored "-Wdeprecated-copy-dtor"
+// #ifndef _WIN32
+// #pragma GCC diagnostic ignored "-Wweak-vtables"
+// #endif
+/*
+#if defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40400) \
+ || defined(Z7_CLANG_VERSION) && (Z7_CLANG_VERSION >= 30000)
+// enumeration values not explicitly handled in switch
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+#endif
+*/
+#endif // __clang__
+
+
+#ifdef __GNUC__
+// #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
+#endif
+
+
+/* There is BUG in MSVC 6.0 compiler for operator new[]:
+ It doesn't check overflow, when it calculates size in bytes for allocated array.
+ So we can use Z7_ARRAY_NEW macro instead of new[] operator. */
+
+#if defined(_MSC_VER) && (_MSC_VER == 1200) && !defined(_WIN64)
+ #define Z7_ARRAY_NEW(p, T, size) p = new T[((size) > 0xFFFFFFFFu / sizeof(T)) ? 0xFFFFFFFFu / sizeof(T) : (size)];
+#else
+ #define Z7_ARRAY_NEW(p, T, size) p = new T[size];
+#endif
+
+#if (defined(__GNUC__) && (__GNUC__ >= 8))
+ #define Z7_ATTR_NORETURN __attribute__((noreturn))
+#elif (defined(__clang__) && (__clang_major__ >= 3))
+ #if __has_feature(cxx_attributes)
+ #define Z7_ATTR_NORETURN [[noreturn]]
+ #else
+ #define Z7_ATTR_NORETURN __attribute__((noreturn))
+ #endif
+#elif (defined(_MSC_VER) && (_MSC_VER >= 1900))
+ #define Z7_ATTR_NORETURN [[noreturn]]
+#else
+ #define Z7_ATTR_NORETURN
+#endif
+
+
+// final in "GCC 4.7.0"
+// In C++98 and C++03 code the alternative spelling __final can be used instead (this is a GCC extension.)
+
+#if defined (__cplusplus) && __cplusplus >= 201103L \
+ || defined(_MSC_VER) && _MSC_VER >= 1800 \
+ || defined(__clang__) && __clang_major__ >= 4 \
+ /* || defined(__GNUC__) && __GNUC__ >= 9 */
+ #define Z7_final final
+ #if defined(__clang__) && __cplusplus < 201103L
+ #pragma GCC diagnostic ignored "-Wc++11-extensions"
+ #endif
+#elif defined (__cplusplus) && __cplusplus >= 199711L \
+ && defined(__GNUC__) && __GNUC__ >= 4 && !defined(__clang__)
+ #define Z7_final __final
+#else
+ #define Z7_final
+ #if defined(__clang__) && __clang_major__ >= 4 \
+ || defined(__GNUC__) && __GNUC__ >= 4
+ #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+ #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
+ #endif
+#endif
+
+#define Z7_class_final(c) class c Z7_final
+
+
+#if defined (__cplusplus) && __cplusplus >= 201103L \
+ || (defined(_MSC_VER) && _MSC_VER >= 1800)
+ #define Z7_CPP_IS_SUPPORTED_default
+ #define Z7_eq_delete = delete
+ // #define Z7_DECL_DEFAULT_COPY_CONSTRUCTOR_IF_SUPPORTED(c) c(const c& k) = default;
+#else
+ #define Z7_eq_delete
+ // #define Z7_DECL_DEFAULT_COPY_CONSTRUCTOR_IF_SUPPORTED(c)
+#endif
+
+
+#if defined(__cplusplus) && (__cplusplus >= 201103L) \
+ || defined(_MSC_VER) && (_MSC_VER >= 1400) /* && (_MSC_VER != 1600) */ \
+ || defined(__clang__) && __clang_major__ >= 4
+ #if defined(_MSC_VER) && (_MSC_VER == 1600) /* && (_MSC_VER != 1600) */
+ #pragma warning(disable : 4481) // nonstandard extension used: override specifier 'override'
+ #define Z7_DESTRUCTOR_override
+ #else
+ #define Z7_DESTRUCTOR_override override
+ #endif
+ #define Z7_override override
+#else
+ #define Z7_override
+ #define Z7_DESTRUCTOR_override
+#endif
+
+
+
+#define Z7_CLASS_NO_COPY(cls) \
+ private: \
+ cls(const cls &) Z7_eq_delete; \
+ cls &operator=(const cls &) Z7_eq_delete;
+
+class CUncopyable
+{
+protected:
+ CUncopyable() {} // allow constructor
+ // ~CUncopyable() {}
+ Z7_CLASS_NO_COPY(CUncopyable)
+};
+
+#define MY_UNCOPYABLE :private CUncopyable
+// #define MY_UNCOPYABLE
+
+
+// typedef void (*Z7_void_Function)(void);
+
+#if defined(__clang__) || defined(__GNUC__)
+#define Z7_CAST_FUNC(t, e) reinterpret_cast<t>(reinterpret_cast<Z7_void_Function>(e))
+#else
+#define Z7_CAST_FUNC(t, e) reinterpret_cast<t>(reinterpret_cast<void*>(e))
+// #define Z7_CAST_FUNC(t, e) reinterpret_cast<t>(e)
+#endif
+
+#define Z7_GET_PROC_ADDRESS(func_type, hmodule, func_name) \
+ Z7_CAST_FUNC(func_type, GetProcAddress(hmodule, func_name))
+
+// || defined(__clang__)
+// || defined(__GNUC__)
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define Z7_DECLSPEC_NOVTABLE __declspec(novtable)
+#else
+#define Z7_DECLSPEC_NOVTABLE
+#endif
+
+#ifdef __clang__
+#define Z7_PURE_INTERFACES_BEGIN \
+_Pragma("GCC diagnostic push") \
+_Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"")
+_Pragma("GCC diagnostic ignored \"-Wweak-vtables\"")
+#define Z7_PURE_INTERFACES_END \
+_Pragma("GCC diagnostic pop")
+#else
+#define Z7_PURE_INTERFACES_BEGIN
+#define Z7_PURE_INTERFACES_END
+#endif
+
+// NewHandler.h and NewHandler.cpp redefine operator new() to throw exceptions, if compiled with old MSVC compilers
+#include "NewHandler.h"
+
+/*
+// #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) Z7_ARRAY_SIZE(a)
+#endif
+*/
+
+#endif // ZIP7_INC_COMMON0_H
+
+
+
+// #define Z7_REDEFINE_NULL
+
+#if defined(Z7_REDEFINE_NULL) /* && (!defined(__clang__) || defined(_MSC_VER)) */
+
+// NULL is defined in <stddef.h>
+#include <stddef.h>
+#undef NULL
+
+#ifdef __cplusplus
+ #if defined (__cplusplus) && __cplusplus >= 201103L \
+ || (defined(_MSC_VER) && _MSC_VER >= 1800)
+ #define NULL nullptr
+ #else
+ #define NULL 0
+ #endif
+#else
+ #define NULL ((void *)0)
+#endif
+
+#else // Z7_REDEFINE_NULL
+
+#if defined(__clang__) && __clang_major__ >= 5
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif
+
+#endif // Z7_REDEFINE_NULL
+
+// for precompiler:
+// #include "MyWindows.h"
diff --git a/libs/archive/thirdparty/CPP/Common/MyGuidDef.h b/libs/archive/thirdparty/CPP/Common/MyGuidDef.h
new file mode 100644
index 0000000..3aa5266
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/MyGuidDef.h
@@ -0,0 +1,63 @@
+// Common/MyGuidDef.h
+
+// #pragma message "Common/MyGuidDef.h"
+
+#ifndef GUID_DEFINED
+#define GUID_DEFINED
+
+// #pragma message "GUID_DEFINED"
+
+#include "MyTypes.h"
+
+typedef struct {
+ UInt32 Data1;
+ UInt16 Data2;
+ UInt16 Data3;
+ Byte Data4[8];
+} GUID;
+
+#ifdef __cplusplus
+#define REFGUID const GUID &
+#else
+#define REFGUID const GUID *
+#endif
+
+// typedef GUID IID;
+typedef GUID CLSID;
+
+#define REFCLSID REFGUID
+#define REFIID REFGUID
+
+#ifdef __cplusplus
+inline int operator==(REFGUID g1, REFGUID g2)
+{
+ for (unsigned i = 0; i < sizeof(g1); i++)
+ if (((const Byte *)&g1)[i] != ((const Byte *)&g2)[i])
+ return 0;
+ return 1;
+}
+inline int operator!=(REFGUID g1, REFGUID g2) { return !(g1 == g2); }
+#endif
+
+#endif // GUID_DEFINED
+
+#ifndef EXTERN_C
+#ifdef __cplusplus
+ #define EXTERN_C extern "C"
+#else
+ #define EXTERN_C extern
+#endif
+#endif
+
+#ifdef DEFINE_GUID
+#undef DEFINE_GUID
+#endif
+
+#ifdef INITGUID
+ #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID name; \
+ EXTERN_C const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+#else
+ #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID name
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/MyTypes.h b/libs/archive/thirdparty/CPP/Common/MyTypes.h
new file mode 100644
index 0000000..eadc9a4
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/MyTypes.h
@@ -0,0 +1,38 @@
+// Common/MyTypes.h
+
+#ifndef ZIP7_INC_COMMON_MY_TYPES_H
+#define ZIP7_INC_COMMON_MY_TYPES_H
+
+#include "Common0.h"
+#include "../../C/7zTypes.h"
+
+// typedef int HRes;
+// typedef HRESULT HRes;
+
+struct CBoolPair
+{
+ bool Val;
+ bool Def;
+
+ CBoolPair(): Val(false), Def(false) {}
+
+ void Init()
+ {
+ Val = false;
+ Def = false;
+ }
+
+ void SetTrueTrue()
+ {
+ Val = true;
+ Def = true;
+ }
+
+ void SetVal_as_Defined(bool val)
+ {
+ Val = val;
+ Def = true;
+ }
+};
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/MyUnknown.h b/libs/archive/thirdparty/CPP/Common/MyUnknown.h
new file mode 100644
index 0000000..75ee96f
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/MyUnknown.h
@@ -0,0 +1,8 @@
+// MyUnknown.h
+
+#ifndef ZIP7_INC_MY_UNKNOWN_H
+#define ZIP7_INC_MY_UNKNOWN_H
+
+#include "MyWindows.h"
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/MyWindows.cpp b/libs/archive/thirdparty/CPP/Common/MyWindows.cpp
new file mode 100644
index 0000000..a1dfbef
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/MyWindows.cpp
@@ -0,0 +1,292 @@
+// MyWindows.cpp
+
+#include "StdAfx.h"
+
+#ifndef _WIN32
+
+#include <stdlib.h>
+#include <time.h>
+#ifdef __GNUC__
+#include <sys/time.h>
+#endif
+
+#include "MyWindows.h"
+
+static inline void *AllocateForBSTR(size_t cb) { return ::malloc(cb); }
+static inline void FreeForBSTR(void *pv) { ::free(pv);}
+
+/* Win32 uses DWORD (32-bit) type to store size of string before (OLECHAR *) string.
+ We must select CBstrSizeType for another systems (not Win32):
+
+ if (CBstrSizeType is UINT32),
+ then we support only strings smaller than 4 GB.
+ Win32 version always has that limitation.
+
+ if (CBstrSizeType is UINT),
+ (UINT can be 16/32/64-bit)
+ We can support strings larger than 4 GB (if UINT is 64-bit),
+ but sizeof(UINT) can be different in parts compiled by
+ different compilers/settings,
+ and we can't send such BSTR strings between such parts.
+*/
+
+typedef UINT32 CBstrSizeType;
+// typedef UINT CBstrSizeType;
+
+#define k_BstrSize_Max 0xFFFFFFFF
+// #define k_BstrSize_Max UINT_MAX
+// #define k_BstrSize_Max ((UINT)(INT)-1)
+
+BSTR SysAllocStringByteLen(LPCSTR s, UINT len)
+{
+ /* Original SysAllocStringByteLen in Win32 maybe fills only unaligned null OLECHAR at the end.
+ We provide also aligned null OLECHAR at the end. */
+
+ if (len >= (k_BstrSize_Max - (UINT)sizeof(OLECHAR) - (UINT)sizeof(OLECHAR) - (UINT)sizeof(CBstrSizeType)))
+ return NULL;
+
+ UINT size = (len + (UINT)sizeof(OLECHAR) + (UINT)sizeof(OLECHAR) - 1) & ~((UINT)sizeof(OLECHAR) - 1);
+ void *p = AllocateForBSTR(size + (UINT)sizeof(CBstrSizeType));
+ if (!p)
+ return NULL;
+ *(CBstrSizeType *)p = (CBstrSizeType)len;
+ BSTR bstr = (BSTR)((CBstrSizeType *)p + 1);
+ if (s)
+ memcpy(bstr, s, len);
+ for (; len < size; len++)
+ ((Byte *)bstr)[len] = 0;
+ return bstr;
+}
+
+BSTR SysAllocStringLen(const OLECHAR *s, UINT len)
+{
+ if (len >= (k_BstrSize_Max - (UINT)sizeof(OLECHAR) - (UINT)sizeof(CBstrSizeType)) / (UINT)sizeof(OLECHAR))
+ return NULL;
+
+ UINT size = len * (UINT)sizeof(OLECHAR);
+ void *p = AllocateForBSTR(size + (UINT)sizeof(CBstrSizeType) + (UINT)sizeof(OLECHAR));
+ if (!p)
+ return NULL;
+ *(CBstrSizeType *)p = (CBstrSizeType)size;
+ BSTR bstr = (BSTR)((CBstrSizeType *)p + 1);
+ if (s)
+ memcpy(bstr, s, size);
+ bstr[len] = 0;
+ return bstr;
+}
+
+BSTR SysAllocString(const OLECHAR *s)
+{
+ if (!s)
+ return NULL;
+ const OLECHAR *s2 = s;
+ while (*s2 != 0)
+ s2++;
+ return SysAllocStringLen(s, (UINT)(s2 - s));
+}
+
+void SysFreeString(BSTR bstr)
+{
+ if (bstr)
+ FreeForBSTR((CBstrSizeType *)(void *)bstr - 1);
+}
+
+UINT SysStringByteLen(BSTR bstr)
+{
+ if (!bstr)
+ return 0;
+ return *((CBstrSizeType *)(void *)bstr - 1);
+}
+
+UINT SysStringLen(BSTR bstr)
+{
+ if (!bstr)
+ return 0;
+ return *((CBstrSizeType *)(void *)bstr - 1) / (UINT)sizeof(OLECHAR);
+}
+
+
+HRESULT VariantClear(VARIANTARG *prop)
+{
+ if (prop->vt == VT_BSTR)
+ SysFreeString(prop->bstrVal);
+ prop->vt = VT_EMPTY;
+ return S_OK;
+}
+
+HRESULT VariantCopy(VARIANTARG *dest, const VARIANTARG *src)
+{
+ HRESULT res = ::VariantClear(dest);
+ if (res != S_OK)
+ return res;
+ if (src->vt == VT_BSTR)
+ {
+ dest->bstrVal = SysAllocStringByteLen((LPCSTR)src->bstrVal,
+ SysStringByteLen(src->bstrVal));
+ if (!dest->bstrVal)
+ return E_OUTOFMEMORY;
+ dest->vt = VT_BSTR;
+ }
+ else
+ *dest = *src;
+ return S_OK;
+}
+
+LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2)
+{
+ if (ft1->dwHighDateTime < ft2->dwHighDateTime) return -1;
+ if (ft1->dwHighDateTime > ft2->dwHighDateTime) return 1;
+ if (ft1->dwLowDateTime < ft2->dwLowDateTime) return -1;
+ if (ft1->dwLowDateTime > ft2->dwLowDateTime) return 1;
+ return 0;
+}
+
+DWORD GetLastError()
+{
+ return (DWORD)errno;
+}
+
+void SetLastError(DWORD dw)
+{
+ errno = (int)dw;
+}
+
+
+static LONG TIME_GetBias()
+{
+ time_t utc = time(NULL);
+ struct tm *ptm = localtime(&utc);
+ int localdaylight = ptm->tm_isdst; /* daylight for local timezone */
+ ptm = gmtime(&utc);
+ ptm->tm_isdst = localdaylight; /* use local daylight, not that of Greenwich */
+ LONG bias = (int)(mktime(ptm)-utc);
+ return bias;
+}
+
+#define TICKS_PER_SEC 10000000
+/*
+#define SECS_PER_DAY (24 * 60 * 60)
+#define SECS_1601_TO_1970 ((369 * 365 + 89) * (UInt64)SECS_PER_DAY)
+#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKS_PER_SEC)
+*/
+
+#define GET_TIME_64(pft) ((pft)->dwLowDateTime | ((UInt64)(pft)->dwHighDateTime << 32))
+
+#define SET_FILETIME(ft, v64) \
+ (ft)->dwLowDateTime = (DWORD)v64; \
+ (ft)->dwHighDateTime = (DWORD)(v64 >> 32);
+
+
+BOOL WINAPI FileTimeToLocalFileTime(const FILETIME *fileTime, FILETIME *localFileTime)
+{
+ UInt64 v = GET_TIME_64(fileTime);
+ v = (UInt64)((Int64)v - (Int64)TIME_GetBias() * TICKS_PER_SEC);
+ SET_FILETIME(localFileTime, v)
+ return TRUE;
+}
+
+BOOL WINAPI LocalFileTimeToFileTime(const FILETIME *localFileTime, FILETIME *fileTime)
+{
+ UInt64 v = GET_TIME_64(localFileTime);
+ v = (UInt64)((Int64)v + (Int64)TIME_GetBias() * TICKS_PER_SEC);
+ SET_FILETIME(fileTime, v)
+ return TRUE;
+}
+
+/*
+VOID WINAPI GetSystemTimeAsFileTime(FILETIME *ft)
+{
+ UInt64 t = 0;
+ timeval tv;
+ if (gettimeofday(&tv, NULL) == 0)
+ {
+ t = tv.tv_sec * (UInt64)TICKS_PER_SEC + TICKS_1601_TO_1970;
+ t += tv.tv_usec * 10;
+ }
+ SET_FILETIME(ft, t)
+}
+*/
+
+DWORD WINAPI GetTickCount(VOID)
+{
+ #ifndef _WIN32
+ // gettimeofday() doesn't work in some MINGWs by unknown reason
+ timeval tv;
+ if (gettimeofday(&tv, NULL) == 0)
+ {
+ // tv_sec and tv_usec are (long)
+ return (DWORD)((UInt64)(Int64)tv.tv_sec * (UInt64)1000 + (UInt64)(Int64)tv.tv_usec / 1000);
+ }
+ #endif
+ return (DWORD)time(NULL) * 1000;
+}
+
+
+#define PERIOD_4 (4 * 365 + 1)
+#define PERIOD_100 (PERIOD_4 * 25 - 1)
+#define PERIOD_400 (PERIOD_100 * 4 + 1)
+
+BOOL WINAPI FileTimeToSystemTime(const FILETIME *ft, SYSTEMTIME *st)
+{
+ UInt32 v;
+ UInt64 v64 = GET_TIME_64(ft);
+ v64 /= 10000;
+ st->wMilliseconds = (WORD)(v64 % 1000); v64 /= 1000;
+ st->wSecond = (WORD)(v64 % 60); v64 /= 60;
+ st->wMinute = (WORD)(v64 % 60); v64 /= 60;
+ v = (UInt32)v64;
+ st->wHour = (WORD)(v % 24); v /= 24;
+
+ // 1601-01-01 was Monday
+ st->wDayOfWeek = (WORD)((v + 1) % 7);
+
+ UInt32 leaps, year, day, mon;
+ leaps = (3 * ((4 * v + (365 - 31 - 28) * 4 + 3) / PERIOD_400) + 3) / 4;
+ v += 28188 + leaps;
+ // leaps - the number of exceptions from PERIOD_4 rules starting from 1600-03-01
+ // (1959 / 64) - converts day from 03-01 to month
+ year = (20 * v - 2442) / (5 * PERIOD_4);
+ day = v - (year * PERIOD_4) / 4;
+ mon = (64 * day) / 1959;
+ st->wDay = (WORD)(day - (1959 * mon) / 64);
+ mon -= 1;
+ year += 1524;
+ if (mon > 12)
+ {
+ mon -= 12;
+ year++;
+ }
+ st->wMonth = (WORD)mon;
+ st->wYear = (WORD)year;
+
+ /*
+ unsigned year, mon;
+ unsigned char ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ unsigned t;
+
+ year = (WORD)(1601 + v / PERIOD_400 * 400);
+ v %= PERIOD_400;
+
+ t = v / PERIOD_100; if (t == 4) t = 3; year += t * 100; v -= t * PERIOD_100;
+ t = v / PERIOD_4; if (t == 25) t = 24; year += t * 4; v -= t * PERIOD_4;
+ t = v / 365; if (t == 4) t = 3; year += t; v -= t * 365;
+
+ st->wYear = (WORD)year;
+
+ if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+ ms[1] = 29;
+ for (mon = 0;; mon++)
+ {
+ unsigned d = ms[mon];
+ if (v < d)
+ break;
+ v -= d;
+ }
+ st->wDay = (WORD)(v + 1);
+ st->wMonth = (WORD)(mon + 1);
+ */
+
+ return TRUE;
+}
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/MyWindows.h b/libs/archive/thirdparty/CPP/Common/MyWindows.h
new file mode 100644
index 0000000..da5370b
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/MyWindows.h
@@ -0,0 +1,325 @@
+// MyWindows.h
+
+#ifdef Z7_DEFINE_GUID
+#undef Z7_DEFINE_GUID
+#endif
+
+#ifdef INITGUID
+ #define Z7_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID name; \
+ EXTERN_C const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+#else
+ #define Z7_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID name
+#endif
+
+
+#ifndef ZIP7_INC_MY_WINDOWS_H
+#define ZIP7_INC_MY_WINDOWS_H
+
+#ifdef _WIN32
+
+#include "../../C/7zWindows.h"
+
+#else // _WIN32
+
+#include <stddef.h> // for wchar_t
+#include <string.h>
+// #include <stdint.h> // for uintptr_t
+
+#include "../../C/7zTypes.h"
+#include "MyGuidDef.h"
+
+// WINAPI is __stdcall in Windows-MSVC in windef.h
+#define WINAPI
+
+typedef char CHAR;
+typedef unsigned char UCHAR;
+
+#undef BYTE
+typedef unsigned char BYTE;
+
+typedef short SHORT;
+typedef unsigned short USHORT;
+
+#undef WORD
+typedef unsigned short WORD;
+typedef short VARIANT_BOOL;
+
+#define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
+#define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
+
+// MS uses long for BOOL, but long is 32-bit in MS. So we use int.
+// typedef long BOOL;
+typedef int BOOL;
+
+#ifndef FALSE
+ #define FALSE 0
+ #define TRUE 1
+#endif
+
+// typedef size_t ULONG_PTR;
+// typedef size_t DWORD_PTR;
+// typedef uintptr_t UINT_PTR;
+// typedef ptrdiff_t UINT_PTR;
+
+typedef Int64 LONGLONG;
+typedef UInt64 ULONGLONG;
+
+typedef struct { LONGLONG QuadPart; } LARGE_INTEGER;
+typedef struct { ULONGLONG QuadPart; } ULARGE_INTEGER;
+
+typedef const CHAR *LPCSTR;
+typedef CHAR TCHAR;
+typedef const TCHAR *LPCTSTR;
+typedef wchar_t WCHAR;
+typedef WCHAR OLECHAR;
+typedef const WCHAR *LPCWSTR;
+typedef OLECHAR *BSTR;
+typedef const OLECHAR *LPCOLESTR;
+typedef OLECHAR *LPOLESTR;
+
+typedef struct
+{
+ DWORD dwLowDateTime;
+ DWORD dwHighDateTime;
+} FILETIME;
+
+#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
+#define FAILED(hr) ((HRESULT)(hr) < 0)
+typedef ULONG PROPID;
+typedef LONG SCODE;
+
+
+#define S_OK ((HRESULT)0x00000000L)
+#define S_FALSE ((HRESULT)0x00000001L)
+#define E_NOTIMPL ((HRESULT)0x80004001L)
+#define E_NOINTERFACE ((HRESULT)0x80004002L)
+#define E_ABORT ((HRESULT)0x80004004L)
+#define E_FAIL ((HRESULT)0x80004005L)
+#define STG_E_INVALIDFUNCTION ((HRESULT)0x80030001L)
+#define CLASS_E_CLASSNOTAVAILABLE ((HRESULT)0x80040111L)
+
+
+#ifdef _MSC_VER
+#define STDMETHODCALLTYPE __stdcall
+#define STDAPICALLTYPE __stdcall
+#else
+// do we need __export here?
+#define STDMETHODCALLTYPE
+#define STDAPICALLTYPE
+#endif
+
+#define STDAPI EXTERN_C HRESULT STDAPICALLTYPE
+
+#ifndef DECLSPEC_NOTHROW
+#define DECLSPEC_NOTHROW Z7_DECLSPEC_NOTHROW
+#endif
+
+#ifndef DECLSPEC_NOVTABLE
+#define DECLSPEC_NOVTABLE Z7_DECLSPEC_NOVTABLE
+#endif
+
+#ifndef COM_DECLSPEC_NOTHROW
+#ifdef COM_STDMETHOD_CAN_THROW
+ #define COM_DECLSPEC_NOTHROW
+#else
+ #define COM_DECLSPEC_NOTHROW DECLSPEC_NOTHROW
+#endif
+#endif
+
+#define DECLARE_INTERFACE(iface) struct DECLSPEC_NOVTABLE iface
+#define DECLARE_INTERFACE_(iface, baseiface) struct DECLSPEC_NOVTABLE iface : public baseiface
+
+#define STDMETHOD_(t, f) virtual COM_DECLSPEC_NOTHROW t STDMETHODCALLTYPE f
+#define STDMETHOD(f) STDMETHOD_(HRESULT, f)
+#define STDMETHODIMP_(t) COM_DECLSPEC_NOTHROW t STDMETHODCALLTYPE
+#define STDMETHODIMP STDMETHODIMP_(HRESULT)
+
+
+#define PURE = 0
+
+// #define MIDL_INTERFACE(x) struct
+
+
+#ifdef __cplusplus
+
+/*
+ p7zip and 7-Zip before v23 used virtual destructor in IUnknown,
+ if _WIN32 is not defined.
+ It used virtual destructor, because some compilers don't like virtual
+ interfaces without virtual destructor.
+ IUnknown in Windows (_WIN32) doesn't use virtual destructor in IUnknown.
+ We still can define Z7_USE_VIRTUAL_DESTRUCTOR_IN_IUNKNOWN here,
+ if we want to be compatible with old plugin interface of p7zip and 7-Zip before v23.
+
+v23:
+ In new 7-Zip v23 we try to be more compatible with original IUnknown from _WIN32.
+ So we do not define Z7_USE_VIRTUAL_DESTRUCTOR_IN_IUNKNOWN here,
+*/
+// #define Z7_USE_VIRTUAL_DESTRUCTOR_IN_IUNKNOWN
+
+#ifdef Z7_USE_VIRTUAL_DESTRUCTOR_IN_IUNKNOWN
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Winconsistent-missing-destructor-override"
+#endif
+#endif
+
+Z7_PURE_INTERFACES_BEGIN
+
+DEFINE_GUID(IID_IUnknown,
+0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+struct IUnknown
+{
+ STDMETHOD(QueryInterface) (REFIID iid, void **outObject) =0;
+ STDMETHOD_(ULONG, AddRef)() =0;
+ STDMETHOD_(ULONG, Release)() =0;
+ #ifdef Z7_USE_VIRTUAL_DESTRUCTOR_IN_IUNKNOWN
+ virtual ~IUnknown() {}
+ #endif
+};
+
+typedef IUnknown *LPUNKNOWN;
+
+Z7_PURE_INTERFACES_END
+
+#endif // __cplusplus
+
+#define VARIANT_TRUE ((VARIANT_BOOL)-1)
+#define VARIANT_FALSE ((VARIANT_BOOL)0)
+
+enum VARENUM
+{
+ VT_EMPTY = 0,
+ VT_NULL = 1,
+ VT_I2 = 2,
+ VT_I4 = 3,
+ VT_R4 = 4,
+ VT_R8 = 5,
+ VT_CY = 6,
+ VT_DATE = 7,
+ VT_BSTR = 8,
+ VT_DISPATCH = 9,
+ VT_ERROR = 10,
+ VT_BOOL = 11,
+ VT_VARIANT = 12,
+ VT_UNKNOWN = 13,
+ VT_DECIMAL = 14,
+
+ VT_I1 = 16,
+ VT_UI1 = 17,
+ VT_UI2 = 18,
+ VT_UI4 = 19,
+ VT_I8 = 20,
+ VT_UI8 = 21,
+ VT_INT = 22,
+ VT_UINT = 23,
+ VT_VOID = 24,
+ VT_HRESULT = 25,
+ VT_FILETIME = 64
+};
+
+typedef unsigned short VARTYPE;
+typedef WORD PROPVAR_PAD1;
+typedef WORD PROPVAR_PAD2;
+typedef WORD PROPVAR_PAD3;
+
+typedef struct tagPROPVARIANT
+{
+ VARTYPE vt;
+ PROPVAR_PAD1 wReserved1;
+ PROPVAR_PAD2 wReserved2;
+ PROPVAR_PAD3 wReserved3;
+ union
+ {
+ CHAR cVal;
+ UCHAR bVal;
+ SHORT iVal;
+ USHORT uiVal;
+ LONG lVal;
+ ULONG ulVal;
+ INT intVal;
+ UINT uintVal;
+ LARGE_INTEGER hVal;
+ ULARGE_INTEGER uhVal;
+ VARIANT_BOOL boolVal;
+ SCODE scode;
+ FILETIME filetime;
+ BSTR bstrVal;
+ };
+} PROPVARIANT;
+
+typedef PROPVARIANT tagVARIANT;
+typedef tagVARIANT VARIANT;
+typedef VARIANT VARIANTARG;
+
+EXTERN_C HRESULT VariantClear(VARIANTARG *prop);
+EXTERN_C HRESULT VariantCopy(VARIANTARG *dest, const VARIANTARG *src);
+
+typedef struct tagSTATPROPSTG
+{
+ LPOLESTR lpwstrName;
+ PROPID propid;
+ VARTYPE vt;
+} STATPROPSTG;
+
+EXTERN_C BSTR SysAllocStringByteLen(LPCSTR psz, UINT len);
+EXTERN_C BSTR SysAllocStringLen(const OLECHAR *sz, UINT len);
+EXTERN_C BSTR SysAllocString(const OLECHAR *sz);
+EXTERN_C void SysFreeString(BSTR bstr);
+EXTERN_C UINT SysStringByteLen(BSTR bstr);
+EXTERN_C UINT SysStringLen(BSTR bstr);
+
+EXTERN_C DWORD GetLastError();
+EXTERN_C void SetLastError(DWORD dwCode);
+EXTERN_C LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2);
+
+EXTERN_C DWORD GetCurrentThreadId();
+EXTERN_C DWORD GetCurrentProcessId();
+
+#define MAX_PATH 1024
+
+#define CP_ACP 0
+#define CP_OEMCP 1
+#define CP_UTF8 65001
+
+typedef enum tagSTREAM_SEEK
+{
+ STREAM_SEEK_SET = 0,
+ STREAM_SEEK_CUR = 1,
+ STREAM_SEEK_END = 2
+} STREAM_SEEK;
+
+
+
+typedef struct
+{
+ WORD wYear;
+ WORD wMonth;
+ WORD wDayOfWeek;
+ WORD wDay;
+ WORD wHour;
+ WORD wMinute;
+ WORD wSecond;
+ WORD wMilliseconds;
+} SYSTEMTIME;
+
+BOOL WINAPI FileTimeToLocalFileTime(const FILETIME *fileTime, FILETIME *localFileTime);
+BOOL WINAPI LocalFileTimeToFileTime(const FILETIME *localFileTime, FILETIME *fileTime);
+BOOL WINAPI FileTimeToSystemTime(const FILETIME *fileTime, SYSTEMTIME *systemTime);
+// VOID WINAPI GetSystemTimeAsFileTime(FILETIME *systemTimeAsFileTime);
+
+DWORD GetTickCount();
+
+
+/*
+#define CREATE_NEW 1
+#define CREATE_ALWAYS 2
+#define OPEN_EXISTING 3
+#define OPEN_ALWAYS 4
+#define TRUNCATE_EXISTING 5
+*/
+
+#endif // _WIN32
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/NewHandler.h b/libs/archive/thirdparty/CPP/Common/NewHandler.h
new file mode 100644
index 0000000..5ba64b7
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/NewHandler.h
@@ -0,0 +1,121 @@
+// Common/NewHandler.h
+
+#ifndef ZIP7_INC_COMMON_NEW_HANDLER_H
+#define ZIP7_INC_COMMON_NEW_HANDLER_H
+
+/*
+NewHandler.h and NewHandler.cpp allows to solve problem with compilers that
+don't throw exception in operator new().
+
+This file must be included before any code that uses operators new() or delete()
+and you must compile and link "NewHandler.cpp", if you use some old MSVC compiler.
+
+DOCs:
+ Since ISO C++98, operator new throws std::bad_alloc when memory allocation fails.
+ MSVC 6.0 returned a null pointer on an allocation failure.
+ Beginning in VS2002, operator new conforms to the standard and throws on failure.
+
+ By default, the compiler also generates defensive null checks to prevent
+ these older-style allocators from causing an immediate crash on failure.
+ The /Zc:throwingNew option tells the compiler to leave out these null checks,
+ on the assumption that all linked memory allocators conform to the standard.
+
+The operator new() in some MSVC versions doesn't throw exception std::bad_alloc.
+MSVC 6.0 (_MSC_VER == 1200) doesn't throw exception.
+The code produced by some another MSVC compilers also can be linked
+to library that doesn't throw exception.
+We suppose that code compiled with VS2015+ (_MSC_VER >= 1900) throws exception std::bad_alloc.
+For older _MSC_VER versions we redefine operator new() and operator delete().
+Our version of operator new() throws CNewException() exception on failure.
+
+It's still allowed to use redefined version of operator new() from "NewHandler.cpp"
+with any compiler. 7-Zip's code can work with std::bad_alloc and CNewException() exceptions.
+But if you use some additional code (outside of 7-Zip's code), you must check
+that redefined version of operator new() is not problem for your code.
+*/
+
+#include <stddef.h>
+
+#ifdef _WIN32
+// We can compile my_new and my_delete with _fastcall
+/*
+void * my_new(size_t size);
+void my_delete(void *p) throw();
+// void * my_Realloc(void *p, size_t newSize, size_t oldSize);
+*/
+#endif
+
+
+#if defined(_MSC_VER) && (_MSC_VER < 1600)
+ // If you want to use default operator new(), you can disable the following line
+ #define Z7_REDEFINE_OPERATOR_NEW
+#endif
+
+
+#ifdef Z7_REDEFINE_OPERATOR_NEW
+
+// std::bad_alloc can require additional DLL dependency.
+// So we don't define CNewException as std::bad_alloc here.
+
+class CNewException {};
+
+void *
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator new(size_t size);
+
+/*
+#if 0 && defined(_MSC_VER) && _MSC_VER == 1600
+ #define Z7_OPERATOR_DELETE_SPEC_THROW0
+#else
+ #define Z7_OPERATOR_DELETE_SPEC_THROW0 throw()
+#endif
+*/
+#if defined(_MSC_VER) && _MSC_VER == 1600
+#pragma warning(push)
+#pragma warning(disable : 4986) // 'operator delete': exception specification does not match previous declaration
+#endif
+
+void
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator delete(void *p) throw();
+
+void
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator delete(void *p, size_t n) throw();
+
+#if defined(_MSC_VER) && _MSC_VER == 1600
+#pragma warning(pop)
+#endif
+
+
+#else
+
+#include <new>
+
+#define CNewException std::bad_alloc
+
+#endif
+
+/*
+#ifdef _WIN32
+void *
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator new[](size_t size);
+
+void
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator delete[](void *p) throw();
+#endif
+*/
+
+#endif
diff --git a/libs/archive/thirdparty/CPP/Common/StdAfx.h b/libs/archive/thirdparty/CPP/Common/StdAfx.h
new file mode 100644
index 0000000..a5228b0
--- /dev/null
+++ b/libs/archive/thirdparty/CPP/Common/StdAfx.h
@@ -0,0 +1,8 @@
+// StdAfx.h
+
+#ifndef ZIP7_INC_STDAFX_H
+#define ZIP7_INC_STDAFX_H
+
+#include "Common.h"
+
+#endif
diff --git a/libs/archive/vcpkg.json b/libs/archive/vcpkg.json
new file mode 100644
index 0000000..5367449
--- /dev/null
+++ b/libs/archive/vcpkg.json
@@ -0,0 +1,10 @@
+{
+ "dependencies": ["7zip"],
+ "vcpkg-configuration": {
+ "default-registry": {
+ "kind": "git",
+ "repository": "https://github.com/Microsoft/vcpkg",
+ "baseline": "294f76666c3000630d828703e675814c05a4fd43"
+ }
+ }
+}