├── .clang-format ├── .github └── workflows │ ├── linux.yml │ ├── macos.yml │ ├── windows.yml │ └── winxp.yml ├── .gitignore ├── .gn ├── BUILD.gn ├── LICENSE ├── README.md ├── SRCDEP.yaml ├── fetch_deps ├── fetch_deps.bat ├── include └── zlibwrap │ ├── zlibwrap.h │ └── zlibwrapd.h ├── sample ├── BUILD.gn ├── test.py ├── unzip.cc └── zip.cc ├── src ├── BUILD.gn ├── encoding.h ├── encoding_win.cc ├── unzip_posix.cc ├── unzip_win.cc ├── zip.h ├── zip_posix.cc ├── zip_win.cc ├── zlibwrap.rc └── zlibwrapd.cc └── thirdparty └── BUILD.gn /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | ColumnLimit: 120 4 | 5 | # disable any short body on single line 6 | AllowShortBlocksOnASingleLine: Never 7 | AllowShortCaseLabelsOnASingleLine: false 8 | AllowShortEnumsOnASingleLine: false 9 | AllowShortFunctionsOnASingleLine: false 10 | AllowShortIfStatementsOnASingleLine: Never 11 | AllowShortLambdasOnASingleLine: None 12 | AllowShortLoopsOnASingleLine: false 13 | 14 | # diable template related thins on single line with class 15 | AlwaysBreakTemplateDeclarations: Yes 16 | 17 | # others 18 | BinPackParameters: false 19 | ShortNamespaceLines: 0 20 | SortIncludes: CaseInsensitive 21 | AlignArrayOfStructures: Left 22 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: published 8 | 9 | jobs: 10 | 11 | build: 12 | name: ${{ matrix.os }} ${{ matrix.arch }} ${{ matrix.config }} 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ ubuntu-latest ] 17 | arch: [ x64 ] 18 | config: [ debug, release ] 19 | 20 | steps: 21 | 22 | - name: Setup python environment 23 | run: | 24 | pip install PyYAML 25 | 26 | - name: Check out code 27 | uses: actions/checkout@v3 28 | 29 | - name: Fetch dependencies 30 | run: | 31 | ./fetch_deps 32 | 33 | - name: Fetch gn and ninja 34 | run: | 35 | python build/fetch_binaries.py 36 | 37 | - name: Compile 38 | run: | 39 | build/bin/gn gen out/${{ matrix.config }}_${{ matrix.arch }} --args="target_cpu=\"${{ matrix.arch }}\" is_debug=${{ matrix.config == 'debug' }}" 40 | build/bin/ninja -C out/${{ matrix.config }}_${{ matrix.arch }} 41 | 42 | - name: Test 43 | run: | 44 | python sample/test.py out/${{ matrix.config }}_${{ matrix.arch }} 45 | 46 | - name: Make package 47 | if: runner.os == 'Linux' 48 | run: | 49 | tar zcvf out/${{ matrix.config }}_${{ matrix.arch }}/zlibwrap_linux_${{ matrix.arch }}.tar.gz \ 50 | -C out/${{ matrix.config }}_${{ matrix.arch }} \ 51 | libzlibwrap.a \ 52 | libzlibwrapd.so \ 53 | zip \ 54 | unzip 55 | 56 | - name: Upload Release on push tag 57 | if: startsWith(github.ref, 'refs/tags/v') && matrix.config == 'release' 58 | uses: svenstaro/upload-release-action@v2 59 | with: 60 | repo_token: ${{ secrets.GITHUB_TOKEN }} 61 | file: out/${{ matrix.config }}_${{ matrix.arch }}/zlibwrap_linux_${{ matrix.arch }}.tar.gz 62 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: published 8 | 9 | jobs: 10 | 11 | build: 12 | name: ${{ matrix.os }} ${{ matrix.arch }} ${{ matrix.config }} 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ macOS-latest ] 17 | arch: [ x64 ] 18 | config: [ debug, release ] 19 | 20 | steps: 21 | 22 | - name: Setup python environment 23 | run: | 24 | pip install PyYAML 25 | 26 | - name: Check out code 27 | uses: actions/checkout@v3 28 | 29 | - name: Fetch dependencies 30 | run: | 31 | ./fetch_deps 32 | 33 | - name: Fetch gn and ninja 34 | run: | 35 | python build/fetch_binaries.py 36 | 37 | - name: Compile 38 | run: | 39 | build/bin/gn gen out/${{ matrix.config }}_${{ matrix.arch }} --args="target_cpu=\"${{ matrix.arch }}\" is_debug=${{ matrix.config == 'debug' }}" 40 | build/bin/ninja -C out/${{ matrix.config }}_${{ matrix.arch }} 41 | 42 | - name: Test 43 | run: | 44 | python sample/test.py out/${{ matrix.config }}_${{ matrix.arch }} 45 | 46 | - name: Make package 47 | run: | 48 | tar zcvf out/${{ matrix.config }}_${{ matrix.arch }}/zlibwrap_darwin_${{ matrix.arch }}.tar.gz \ 49 | -C out/${{ matrix.config }}_${{ matrix.arch }} \ 50 | libzlibwrap.a \ 51 | libzlibwrapd.dylib \ 52 | libzlibwrapd.dylib.dSYM \ 53 | zip \ 54 | zip.dSYM \ 55 | unzip \ 56 | unzip.dSYM 57 | 58 | - name: Upload Release on push tag 59 | if: startsWith(github.ref, 'refs/tags/v') && matrix.config == 'release' 60 | uses: svenstaro/upload-release-action@v2 61 | with: 62 | repo_token: ${{ secrets.GITHUB_TOKEN }} 63 | file: out/${{ matrix.config }}_${{ matrix.arch }}/zlibwrap_darwin_${{ matrix.arch }}.tar.gz 64 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: published 8 | 9 | jobs: 10 | 11 | build: 12 | name: ${{ matrix.os }} ${{ matrix.arch }} ${{ matrix.config }} 13 | runs-on: ${{ matrix.os }} 14 | defaults: 15 | run: 16 | shell: cmd 17 | strategy: 18 | matrix: 19 | os: [ windows-latest ] 20 | arch: [ x64, x86 ] 21 | config: [ debug, release ] 22 | 23 | steps: 24 | 25 | - name: Setup python environment 26 | run: | 27 | pip install PyYAML 28 | 29 | - name: Check out code 30 | uses: actions/checkout@v3 31 | 32 | - name: Fetch dependencies 33 | run: | 34 | fetch_deps.bat 35 | 36 | - name: Fetch gn and ninja 37 | run: | 38 | python build\\fetch_binaries.py 39 | 40 | - name: Compile 41 | run: | 42 | build\\bin\\gn gen out/${{ matrix.config }}_${{ matrix.arch }} --args="target_cpu=\"${{ matrix.arch }}\" is_debug=${{ matrix.config == 'debug' }}" 43 | build\\bin\\ninja -C out/${{ matrix.config }}_${{ matrix.arch }} 44 | 45 | - name: Test 46 | run: | 47 | python sample\\test.py out\\${{ matrix.config }}_${{ matrix.arch }} 48 | 49 | - name: Make package 50 | run: | 51 | Compress-Archive ` 52 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrap.lib,` 53 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\src\zlibwrap_cc.pdb,` 54 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\thirdparty\minizip_c.pdb,` 55 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\thirdparty\zlib_c.pdb,` 56 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrapd.dll,` 57 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrapd.dll.lib,` 58 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\src\zlibwrapd_cc.pdb,` 59 | out\${{ matrix.config }}_${{ matrix.arch }}\zip.exe,` 60 | out\${{ matrix.config }}_${{ matrix.arch }}\zip.exe.pdb,` 61 | out\${{ matrix.config }}_${{ matrix.arch }}\unzip.exe,` 62 | out\${{ matrix.config }}_${{ matrix.arch }}\unzip.exe.pdb ` 63 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrap_win32_${{ matrix.arch }}.zip 64 | shell: pwsh 65 | 66 | - name: Upload Release on push tag 67 | if: startsWith(github.ref, 'refs/tags/v') && matrix.config == 'release' && matrix.arch == 'x64' 68 | uses: svenstaro/upload-release-action@v2 69 | with: 70 | repo_token: ${{ secrets.GITHUB_TOKEN }} 71 | file: out/${{ matrix.config }}_${{ matrix.arch }}/zlibwrap_win32_${{ matrix.arch }}.zip 72 | -------------------------------------------------------------------------------- /.github/workflows/winxp.yml: -------------------------------------------------------------------------------- 1 | name: winxp 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: published 8 | 9 | jobs: 10 | 11 | build: 12 | name: ${{ matrix.os }} ${{ matrix.arch }} ${{ matrix.config }} 13 | runs-on: ${{ matrix.os }} 14 | defaults: 15 | run: 16 | shell: cmd 17 | strategy: 18 | matrix: 19 | os: [ windows-2019 ] 20 | arch: [ x86 ] 21 | config: [ debug, release ] 22 | 23 | steps: 24 | 25 | - name: Setup python environment 26 | run: | 27 | pip install PyYAML 28 | 29 | - name: Check out code 30 | uses: actions/checkout@v3 31 | 32 | - name: Fetch dependencies 33 | run: | 34 | fetch_deps.bat 35 | 36 | - name: Fetch gn and ninja 37 | run: | 38 | python build\\fetch_binaries.py 39 | 40 | - name: Compile 41 | run: | 42 | build\\bin\\gn gen out/${{ matrix.config }}_${{ matrix.arch }} --args="target_cpu=\"${{ matrix.arch }}\" is_debug=${{ matrix.config == 'debug' }} is_winxp=true" 43 | build\\bin\\ninja -C out/${{ matrix.config }}_${{ matrix.arch }} 44 | 45 | - name: Test 46 | run: | 47 | python sample\\test.py out\\${{ matrix.config }}_${{ matrix.arch }} 48 | 49 | - name: Make package 50 | run: | 51 | Compress-Archive ` 52 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrap.lib,` 53 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\src\zlibwrap_cc.pdb,` 54 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\thirdparty\minizip_c.pdb,` 55 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\thirdparty\zlib_c.pdb,` 56 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrapd.dll,` 57 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrapd.dll.lib,` 58 | out\${{ matrix.config }}_${{ matrix.arch }}\obj\src\zlibwrapd_cc.pdb,` 59 | out\${{ matrix.config }}_${{ matrix.arch }}\zip.exe,` 60 | out\${{ matrix.config }}_${{ matrix.arch }}\zip.exe.pdb,` 61 | out\${{ matrix.config }}_${{ matrix.arch }}\unzip.exe,` 62 | out\${{ matrix.config }}_${{ matrix.arch }}\unzip.exe.pdb ` 63 | out\${{ matrix.config }}_${{ matrix.arch }}\zlibwrap_win32_${{ matrix.arch }}.zip 64 | shell: pwsh 65 | 66 | - name: Upload Release on push tag 67 | if: startsWith(github.ref, 'refs/tags/v') && matrix.config == 'release' && matrix.arch == 'x86' 68 | uses: svenstaro/upload-release-action@v2 69 | with: 70 | repo_token: ${{ secrets.GITHUB_TOKEN }} 71 | file: out/${{ matrix.config }}_${{ matrix.arch }}/zlibwrap_win32_${{ matrix.arch }}.zip 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | /build/ 3 | /out/ 4 | /thirdparty/**/ 5 | -------------------------------------------------------------------------------- /.gn: -------------------------------------------------------------------------------- 1 | buildconfig = "//build/BUILDCONFIG.gn" 2 | -------------------------------------------------------------------------------- /BUILD.gn: -------------------------------------------------------------------------------- 1 | group("all") { 2 | deps = [ 3 | "src:zlibwrap", 4 | "src:zlibwrapd", 5 | ] 6 | } 7 | 8 | group("test") { 9 | testonly = true 10 | deps = [ 11 | "sample:unzip", 12 | "sample:zip", 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Streamlet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZLibWrap 2 | 3 | ![](https://github.com/Streamlet/ZLibWrap/actions/workflows/windows.yml/badge.svg) ![](https://github.com/Streamlet/ZLibWrap/actions/workflows/winxp.yml/badge.svg) ![](https://github.com/Streamlet/ZLibWrap/actions/workflows/linux.yml/badge.svg) ![](https://github.com/Streamlet/ZLibWrap/actions/workflows/macos.yml/badge.svg) 4 | --- 5 | A wrap of Zlib, making the use of ZLib easier in ZIP compressing/extracting. 6 | -------------------------------------------------------------------------------- /SRCDEP.yaml: -------------------------------------------------------------------------------- 1 | # SRCDEP.yaml 2 | # processed by https://github.com/Streamlet/srcdep, A simple, source code based dependency management tool. 3 | 4 | DEPS: 5 | build: 6 | GIT_REPO: https://github.com/Streamlet/gn_toolchain.git 7 | GIT_TAG: v1.2 8 | 9 | thirdparty/loki: 10 | URL: https://sourceforge.net/projects/loki-lib/files/Loki/Loki%200.1.7/loki-0.1.7.tar.bz2/download 11 | URL_FORMAT: tar.bz2 12 | ROOT_DIR: loki-0.1.7 13 | URL_HASH: 14 | SHA1: 006c630217b1e1fd33015dc0597d2d743d8ee4e3 15 | 16 | thirdparty/zlib: 17 | GIT_REPO: https://github.com/madler/zlib.git 18 | GIT_TAG: v1.2.13 19 | -------------------------------------------------------------------------------- /fetch_deps: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file can be redistributed to your source code root, 4 | # to help those who don't have srcdep environment, to fetch dependencies conveniently. 5 | 6 | BASEDIR=$(dirname "$0") 7 | if [ ! -d "$BASEDIR/.srcdep" ]; then 8 | mkdir -p "$BASEDIR/.srcdep" 9 | fi 10 | if [ ! -d "$BASEDIR/.srcdep/repo" ]; then 11 | git clone https://github.com/Streamlet/srcdep.git "$BASEDIR/.srcdep/repo" 12 | fi 13 | 14 | exec "$BASEDIR/.srcdep/repo/srcdep" 15 | -------------------------------------------------------------------------------- /fetch_deps.bat: -------------------------------------------------------------------------------- 1 | :: This file can be redistributed to your source code root, 2 | :: to help those who don't have srcdep environment, to fetch dependencies conveniently. 3 | 4 | @Echo Off 5 | 6 | If Not Exist "%~dp0.srcdep" ( 7 | MkDir "%~dp0.srcdep" 8 | ) 9 | If Not Exist "%~dp0.srcdep\repo" ( 10 | git clone https://github.com/Streamlet/srcdep.git "%~dp0.srcdep\repo" 11 | ) 12 | 13 | Call "%~dp0.srcdep\repo\srcdep.bat" 14 | -------------------------------------------------------------------------------- /include/zlibwrap/zlibwrap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WIN32 4 | #include 5 | #endif 6 | 7 | namespace zlibwrap { 8 | 9 | /** 10 | * @brief Compress files to a ZIP file. 11 | * 12 | * @param zip_file Target ZIP file path. 13 | * @param pattern Source files, supporting wildcards. 14 | * @return true/false 15 | */ 16 | #ifdef _WIN32 17 | bool ZipCompress(const TCHAR *zip_file, const TCHAR *pattern); 18 | #else 19 | bool ZipCompress(const char *zip_file, const char *pattern); 20 | #endif 21 | 22 | /** 23 | * @brief Extract files from a ZIP file. 24 | * 25 | * @param zip_file Source ZIP file. 26 | * @param target_dir Directory to output files. 27 | * @return true/false 28 | */ 29 | #ifdef _WIN32 30 | bool ZipExtract(const TCHAR *zip_file, const TCHAR *target_dir); 31 | #else 32 | bool ZipExtract(const char *zip_file, const char *target_dir); 33 | #endif 34 | 35 | } // namespace zlibwrap 36 | -------------------------------------------------------------------------------- /include/zlibwrap/zlibwrapd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #ifdef _WIN32 5 | # ifdef ZLIBWRAP_EXPORTS 6 | # define ZLIBWRAP_API __declspec(dllexport) 7 | # else 8 | # define ZLIBWRAP_API __declspec(dllimport) 9 | # endif 10 | #else 11 | # ifdef ZLIBWRAP_EXPORTS 12 | # define ZLIBWRAP_API __attribute__((visibility("default"))) 13 | # else 14 | # define ZLIBWRAP_API 15 | # endif 16 | #endif 17 | // clang-format on 18 | 19 | #ifdef _WIN32 20 | #include 21 | #endif 22 | 23 | /** 24 | * @brief Compress files to a ZIP file. 25 | * 26 | * @param zip_file Target ZIP file path. 27 | * @param pattern Source files, supporting wildcards. 28 | * @return true/false 29 | */ 30 | #ifdef _WIN32 31 | ZLIBWRAP_API bool ZipCompress(const TCHAR *zip_file, const TCHAR *pattern); 32 | #else 33 | ZLIBWRAP_API bool ZipCompress(const char *zip_file, const char *pattern); 34 | #endif 35 | 36 | /** 37 | * @brief Extract files from a ZIP file. 38 | * 39 | * @param zip_file Source ZIP file. 40 | * @param target_dir Directory to output files. 41 | * @return true/false 42 | */ 43 | #ifdef _WIN32 44 | ZLIBWRAP_API bool ZipExtract(const TCHAR *zip_file, const TCHAR *target_dir); 45 | #else 46 | ZLIBWRAP_API bool ZipExtract(const char *zip_file, const char *target_dir); 47 | #endif 48 | -------------------------------------------------------------------------------- /sample/BUILD.gn: -------------------------------------------------------------------------------- 1 | executable("zip") { 2 | testonly = true 3 | if (is_win) { 4 | configs += [ "../build/config/win:console_subsystem" ] 5 | } 6 | sources = [ "zip.cc" ] 7 | include_dirs = [ "../include" ] 8 | deps = [ "../src:zlibwrap" ] 9 | } 10 | 11 | executable("unzip") { 12 | testonly = true 13 | if (is_win) { 14 | configs += [ "../build/config/win:console_subsystem" ] 15 | } 16 | sources = [ "unzip.cc" ] 17 | include_dirs = [ "../include" ] 18 | deps = [ "../src:zlibwrap" ] 19 | } 20 | -------------------------------------------------------------------------------- /sample/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import shutil 7 | import locale 8 | import codecs 9 | 10 | 11 | def write_file(path, content): 12 | with codecs.open(path, 'w', 'utf-8') as f: 13 | f.write(content) 14 | 15 | 16 | def check_file(path, content): 17 | file_content = '' 18 | try: 19 | with codecs.open(path, 'r', 'utf-8') as f: 20 | file_content = f.read() 21 | except: 22 | file_content = None 23 | assert file_content == content, 'Check file content failed, expected: "%s", actual: "%s"' % ( 24 | content if content is not None else '', file_content if file_content is not None else '') 25 | 26 | 27 | def test_single_file(zip_cmd, unzip_cmd): 28 | write_file('test_root/f1', 'content') 29 | os.system('%s test_root/test.zip test_root/f1' % zip_cmd) 30 | os.system('%s test_root/test.zip test_root/unzip' % unzip_cmd) 31 | check_file('test_root/unzip/f1', 'content') 32 | 33 | 34 | def test_single_directory(zip_cmd, unzip_cmd): 35 | os.makedirs('test_root/d1') 36 | write_file('test_root/d1/f1', 'content') 37 | os.system('%s test_root/test.zip test_root/d1' % zip_cmd) 38 | os.system('%s test_root/test.zip test_root/unzip' % unzip_cmd) 39 | check_file('test_root/unzip/d1/f1', 'content') 40 | 41 | 42 | def test_directory_tree(zip_cmd, unzip_cmd): 43 | os.makedirs('test_root/d1/d2/d3') 44 | write_file('test_root/d1/f1', 'content1') 45 | write_file('test_root/d1/d2/f2', 'content2') 46 | write_file('test_root/d1/d2/d3/f3', 'content3') 47 | os.system('%s test_root/test.zip test_root/d1' % zip_cmd) 48 | os.system('%s test_root/test.zip test_root/unzip' % unzip_cmd) 49 | check_file('test_root/unzip/d1/f1', 'content1') 50 | check_file('test_root/unzip/d1/d2/f2', 'content2') 51 | check_file('test_root/unzip/d1/d2/d3/f3', 'content3') 52 | 53 | 54 | def test_wildcard1(zip_cmd, unzip_cmd): 55 | os.makedirs('test_root/d1') 56 | write_file('test_root/d1/f1', 'content1') 57 | os.system('%s test_root/test.zip "test_root/d1/*"' % zip_cmd) 58 | os.system('%s test_root/test.zip test_root/unzip' % unzip_cmd) 59 | check_file('test_root/unzip/f1', 'content1') 60 | 61 | 62 | def test_wildcard2(zip_cmd, unzip_cmd): 63 | os.makedirs('test_root/d1/d2/d22') 64 | os.makedirs('test_root/d1/d2/d33') 65 | write_file('test_root/d1/f1', 'content1') 66 | write_file('test_root/d1/d2/f2', 'content2') 67 | write_file('test_root/d1/d2/d22/f22', 'content22') 68 | write_file('test_root/d1/d2/d33/f33', 'content33') 69 | os.system('%s test_root/test.zip "test_root/d1/*2"' % zip_cmd) 70 | os.system('%s test_root/test.zip test_root/unzip' % unzip_cmd) 71 | check_file('test_root/unzip/f1', None) 72 | check_file('test_root/unzip/d2/f2', 'content2') 73 | check_file('test_root/unzip/d2/d22/f22', 'content22') 74 | check_file('test_root/unzip/d2/d33/f33', 'content33') 75 | 76 | 77 | def test_non_ascii_file_name(zip_cmd, unzip_cmd): 78 | os.makedirs('test_root/目录1/目录2/目录3') 79 | write_file('test_root/目录1/文件1', u'内容1') 80 | write_file('test_root/目录1/目录2/文件2', u'内容2') 81 | write_file('test_root/目录1/目录2/目录3/文件3', u'内容3') 82 | os.system('%s test_root/测试.zip test_root/目录1' % zip_cmd) 83 | os.system('%s test_root/测试.zip test_root/解压' % unzip_cmd) 84 | check_file('test_root/解压/目录1/文件1', u'内容1') 85 | check_file('test_root/解压/目录1/目录2/文件2', u'内容2') 86 | check_file('test_root/解压/目录1/目录2/目录3/文件3', u'内容3') 87 | 88 | 89 | def run_tests(): 90 | if sys.platform == 'win32': 91 | zip_cmd = 'zip.exe' 92 | unzip_cmd = 'unzip.exe' 93 | else: 94 | zip_cmd = './zip' 95 | unzip_cmd = './unzip' 96 | 97 | for test_function in ( 98 | test_single_file, 99 | test_single_directory, 100 | test_directory_tree, 101 | test_wildcard1, 102 | test_wildcard2, 103 | test_non_ascii_file_name, 104 | ): 105 | if os.path.exists('test_root'): 106 | shutil.rmtree('test_root') 107 | os.makedirs('test_root') 108 | test_function(zip_cmd, unzip_cmd) 109 | 110 | shutil.rmtree('test_root') 111 | 112 | 113 | def main(): 114 | (out_dir,) = sys.argv[1:] 115 | os.chdir(out_dir) 116 | run_tests() 117 | 118 | 119 | if __name__ == '__main__': 120 | main() 121 | -------------------------------------------------------------------------------- /sample/unzip.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef _WIN32 7 | #define _T(s) s 8 | #define TCHAR char 9 | #define _tmain main 10 | #define _tsetlocale setlocale 11 | #define _tprintf printf 12 | #endif 13 | 14 | void ShowHelp() { 15 | _tprintf(_T("Usage: unzip \n")); 16 | } 17 | 18 | int _tmain(int argc, TCHAR *argv[]) { 19 | _tsetlocale(LC_ALL, _T("")); 20 | 21 | if (argc != 3) { 22 | ShowHelp(); 23 | return 0; 24 | } 25 | const TCHAR *zip_file = argv[1]; 26 | const TCHAR *target_dir = argv[2]; 27 | 28 | if (!zlibwrap::ZipExtract(zip_file, target_dir)) { 29 | _tprintf(_T("Failed to Extract %s to %s.\n"), zip_file, target_dir); 30 | return -1; 31 | } 32 | 33 | _tprintf(_T("Extracted %s to %s successfully.\n"), zip_file, target_dir); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /sample/zip.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef _WIN32 7 | #define _T(s) s 8 | #define TCHAR char 9 | #define _tmain main 10 | #define _tsetlocale setlocale 11 | #define _tprintf printf 12 | #endif 13 | 14 | void ShowHelp() { 15 | _tprintf(_T("Usage: zip \n")); 16 | } 17 | 18 | int _tmain(int argc, const TCHAR *argv[]) { 19 | _tsetlocale(LC_ALL, _T("")); 20 | 21 | if (argc != 3) { 22 | ShowHelp(); 23 | return 0; 24 | } 25 | const TCHAR *zip_file = argv[1]; 26 | const TCHAR *source_file_pattern = argv[2]; 27 | 28 | if (!zlibwrap::ZipCompress(zip_file, source_file_pattern)) { 29 | _tprintf(_T("Failed to compress %s to %s.\n"), source_file_pattern, zip_file); 30 | return -1; 31 | } 32 | 33 | _tprintf(_T("Compressed %s to %s successfully.\n"), source_file_pattern, zip_file); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/BUILD.gn: -------------------------------------------------------------------------------- 1 | static_library("zlibwrap") { 2 | sources = [ 3 | "../include/zlibwrap/zlibwrap.h", 4 | "zip.h", 5 | ] 6 | if (is_win) { 7 | sources += [ 8 | "encoding.h", 9 | "encoding_win.cc", 10 | "unzip_win.cc", 11 | "zip_win.cc", 12 | ] 13 | } else { 14 | sources += [ 15 | "unzip_posix.cc", 16 | "zip_posix.cc", 17 | ] 18 | } 19 | include_dirs = [ "../include" ] 20 | deps = [ 21 | "../thirdparty:loki", 22 | "../thirdparty:minizip", 23 | ] 24 | } 25 | 26 | shared_library("zlibwrapd") { 27 | sources = [ 28 | "../include/zlibwrap/zlibwrapd.h", 29 | "zlibwrapd.cc", 30 | ] 31 | if (is_win) { 32 | sources += [ "zlibwrap.rc" ] 33 | } 34 | include_dirs = [ "../include" ] 35 | defines = [ "ZLIBWRAP_EXPORTS" ] 36 | deps = [ ":zlibwrap" ] 37 | } 38 | -------------------------------------------------------------------------------- /src/encoding.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #if __cplusplus >= 201703L 5 | #include 6 | #endif 7 | 8 | namespace encoding { 9 | 10 | std::wstring UTF8ToUCS2(const std::string &utf8); 11 | #if __cplusplus >= 201703L 12 | std::wstring UTF8ToUCS2(const std::string_view &utf8); 13 | #endif 14 | std::wstring UTF8ToUCS2(const char *utf8); 15 | std::wstring UTF8ToUCS2(const char *utf8, size_t length); 16 | 17 | std::string UCS2ToUTF8(const std::wstring &ucs2); 18 | #if __cplusplus >= 201703L 19 | std::string UCS2ToUTF8(const std::wstring_view &ucs2); 20 | #endif 21 | std::string UCS2ToUTF8(const wchar_t *ucs2); 22 | std::string UCS2ToUTF8(const wchar_t *ucs2, size_t length); 23 | 24 | std::wstring ANSIToUCS2(const std::string &ansi); 25 | #if __cplusplus >= 201703L 26 | std::wstring ANSIToUCS2(const std::string_view &ansi); 27 | #endif 28 | std::wstring ANSIToUCS2(const char *ansi); 29 | std::wstring ANSIToUCS2(const char *ansi, size_t length); 30 | 31 | std::string UCS2ToANSI(const std::wstring &ucs2); 32 | #if __cplusplus >= 201703L 33 | std::string UCS2ToANSI(const std::wstring_view &ucs2); 34 | #endif 35 | std::string UCS2ToANSI(const wchar_t *ucs2); 36 | std::string UCS2ToANSI(const wchar_t *ucs2, size_t length); 37 | 38 | } // namespace encoding 39 | -------------------------------------------------------------------------------- /src/encoding_win.cc: -------------------------------------------------------------------------------- 1 | #include "encoding.h" 2 | #include 3 | 4 | #if MSC_VER < 1600 5 | #define nullptr NULL 6 | #endif 7 | 8 | namespace encoding { 9 | 10 | namespace { 11 | 12 | std::wstring ANSIToUCS2(const char *ansi, size_t length, UINT code_page) { 13 | std::wstring ucs2; 14 | int size = ::MultiByteToWideChar(code_page, 0, ansi, (int)length, nullptr, 0); 15 | if (size == 0) 16 | return ucs2; 17 | ucs2.resize(length == -1 || ansi[length - 1] == '\0' ? size - 1 : size); 18 | ::MultiByteToWideChar(code_page, 0, ansi, (int)length, &ucs2[0], size); 19 | return ucs2; 20 | } 21 | 22 | std::string UCS2ToANSI(const wchar_t *ucs2, size_t length, UINT code_page) { 23 | std::string ansi; 24 | int size = ::WideCharToMultiByte(code_page, 0, ucs2, (int)length, nullptr, 0, nullptr, nullptr); 25 | if (size == 0) 26 | return ansi; 27 | ansi.resize(length == -1 || ucs2[length - 1] == L'\0' ? size - 1 : size); 28 | ::WideCharToMultiByte(code_page, 0, ucs2, (int)length, &ansi[0], size, nullptr, nullptr); 29 | return ansi; 30 | } 31 | 32 | } // namespace 33 | 34 | std::wstring UTF8ToUCS2(const std::string &utf8) { 35 | return UTF8ToUCS2(utf8.c_str(), utf8.length()); 36 | } 37 | 38 | #if __cplusplus >= 201703L 39 | std::wstring UTF8ToUCS2(const std::string_view &utf8) { 40 | return UTF8ToUCS2(utf8.data(), utf8.length()); 41 | } 42 | #endif 43 | 44 | std::wstring UTF8ToUCS2(const char *utf8) { 45 | return UTF8ToUCS2(utf8, -1); 46 | } 47 | 48 | std::wstring UTF8ToUCS2(const char *utf8, size_t length) { 49 | return ANSIToUCS2(utf8, length, CP_UTF8); 50 | } 51 | 52 | std::string UCS2ToUTF8(const std::wstring &ucs2) { 53 | return UCS2ToUTF8(ucs2.c_str(), ucs2.length()); 54 | } 55 | 56 | #if __cplusplus >= 201703L 57 | std::string UCS2ToUTF8(const std::wstring_view &ucs2) { 58 | return UCS2ToUTF8(ucs2.data(), ucs2.length()); 59 | } 60 | #endif 61 | 62 | std::string UCS2ToUTF8(const wchar_t *ucs2) { 63 | return UCS2ToUTF8(ucs2, -1); 64 | } 65 | 66 | std::string UCS2ToUTF8(const wchar_t *ucs2, size_t length) { 67 | return UCS2ToANSI(ucs2, length, CP_UTF8); 68 | } 69 | 70 | std::wstring ANSIToUCS2(const std::string &ansi) { 71 | return ANSIToUCS2(ansi.c_str(), ansi.length()); 72 | } 73 | 74 | #if __cplusplus >= 201703L 75 | std::wstring ANSIToUCS2(const std::string_view &ansi) { 76 | return ANSIToUCS2(ansi.data(), ansi.length()); 77 | } 78 | #endif 79 | 80 | std::wstring ANSIToUCS2(const char *ansi) { 81 | return ANSIToUCS2(ansi, -1); 82 | } 83 | 84 | std::wstring ANSIToUCS2(const char *ansi, size_t length) { 85 | return ANSIToUCS2(ansi, length, CP_ACP); 86 | } 87 | 88 | std::string UCS2ToANSI(const std::wstring &ucs2) { 89 | return UCS2ToANSI(ucs2.c_str(), ucs2.length()); 90 | } 91 | 92 | #if __cplusplus >= 201703L 93 | std::string UCS2ToANSI(const std::wstring_view &ucs2) { 94 | return UCS2ToANSI(ucs2.data(), ucs2.length()); 95 | } 96 | #endif 97 | 98 | std::string UCS2ToANSI(const wchar_t *ucs2) { 99 | return UCS2ToANSI(ucs2, -1); 100 | } 101 | 102 | std::string UCS2ToANSI(const wchar_t *ucs2, size_t length) { 103 | return UCS2ToANSI(ucs2, length, CP_ACP); 104 | } 105 | } // namespace encoding 106 | -------------------------------------------------------------------------------- /src/unzip_posix.cc: -------------------------------------------------------------------------------- 1 | #include "zip.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace { 12 | 13 | void mkdirs(char *path) { 14 | for (char *p = strchr(path, '/'); p != NULL; p = strchr(p + 1, '/')) { 15 | *p = '\0'; 16 | mkdir(path, 0755); 17 | *p = '/'; 18 | } 19 | } 20 | 21 | bool ZipExtractCurrentFile(unzFile uf, const std::string &target_dir) { 22 | std::string inner_path; 23 | inner_path.resize(1024); 24 | unz_file_info64 file_info; 25 | char *inner_path_buffer = &inner_path[0]; 26 | if (unzGetCurrentFileInfo64(uf, &file_info, inner_path_buffer, (uLong)inner_path.size(), NULL, 0, NULL, 0) != UNZ_OK) 27 | return false; 28 | inner_path.resize(strlen(inner_path.c_str())); 29 | 30 | if (unzOpenCurrentFile(uf) != UNZ_OK) 31 | return false; 32 | LOKI_ON_BLOCK_EXIT(unzCloseCurrentFile, uf); 33 | 34 | std::string target_path = target_dir + inner_path; 35 | mkdirs(&target_path[0]); 36 | bool is_dir = *inner_path.rbegin() == '/'; 37 | 38 | if (!is_dir) { 39 | FILE *f = fopen(target_path.c_str(), "wb"); 40 | if (f == NULL) 41 | return false; 42 | LOKI_ON_BLOCK_EXIT(fclose, f); 43 | 44 | const size_t BUFFER_SIZE = 4096; 45 | unsigned char buffer[BUFFER_SIZE] = {}; 46 | while (true) { 47 | int size = unzReadCurrentFile(uf, buffer, BUFFER_SIZE); 48 | if (size < 0) 49 | return false; 50 | if (size == 0) 51 | break; 52 | if (fwrite(buffer, 1, size, f) != size) 53 | return false; 54 | } 55 | } 56 | 57 | tm date = {}; 58 | date.tm_sec = file_info.tmu_date.tm_sec; 59 | date.tm_min = file_info.tmu_date.tm_min; 60 | date.tm_hour = file_info.tmu_date.tm_hour; 61 | date.tm_mday = file_info.tmu_date.tm_mday; 62 | date.tm_mon = file_info.tmu_date.tm_mon; 63 | if (file_info.tmu_date.tm_year > 1900) 64 | date.tm_year = file_info.tmu_date.tm_year - 1900; 65 | else 66 | date.tm_year = file_info.tmu_date.tm_year; 67 | date.tm_isdst = -1; 68 | 69 | utimbuf ut = {}; 70 | ut.actime = ut.modtime = mktime(&date); 71 | utime(target_path.c_str(), &ut); 72 | return true; 73 | } 74 | 75 | } // namespace 76 | 77 | namespace zlibwrap { 78 | 79 | bool ZipExtract(const char *zip_file, const char *target_dir) { 80 | unzFile uf = unzOpen64(zip_file); 81 | if (uf == NULL) 82 | return false; 83 | LOKI_ON_BLOCK_EXIT(unzClose, uf); 84 | 85 | unz_global_info64 gi = {}; 86 | if (unzGetGlobalInfo64(uf, &gi) != UNZ_OK) 87 | return false; 88 | 89 | std::string root_dir = target_dir; 90 | if (!root_dir.empty() && (*root_dir.rbegin() != '\\' && *root_dir.rbegin() != '/')) 91 | root_dir += "/"; 92 | char *root_dir_buffer = &root_dir[0]; 93 | mkdirs(root_dir_buffer); 94 | 95 | for (int i = 0; i < gi.number_entry; ++i) { 96 | if (!ZipExtractCurrentFile(uf, root_dir)) 97 | return false; 98 | if (i < gi.number_entry - 1) { 99 | if (unzGoToNextFile(uf) != UNZ_OK) 100 | return false; 101 | } 102 | } 103 | 104 | return true; 105 | } 106 | 107 | } // namespace zlibwrap 108 | -------------------------------------------------------------------------------- /src/unzip_win.cc: -------------------------------------------------------------------------------- 1 | #include "encoding.h" 2 | #include "zip.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | // clang-format off 13 | #include 14 | #include 15 | // clang-format on 16 | 17 | namespace { 18 | 19 | #ifdef _UNICODE 20 | typedef std::wstring tstring; 21 | #else 22 | typedef std::string tstring; 23 | #endif 24 | 25 | void mkdirs(TCHAR *path) { 26 | for (TCHAR *p = _tcschr(path, _T('/')); p != NULL; p = _tcschr(p + 1, _T('/'))) { 27 | *p = _T('\0'); 28 | _tmkdir(path); 29 | *p = _T('/'); 30 | } 31 | } 32 | 33 | char inner_path_buffer[1024] = {0}; 34 | 35 | bool ZipExtractCurrentFile(unzFile uf, const tstring &target_dir) { 36 | unz_file_info64 file_info; 37 | if (unzGetCurrentFileInfo64(uf, &file_info, inner_path_buffer, (uLong)sizeof(inner_path_buffer), NULL, 0, NULL, 0) != 38 | UNZ_OK) 39 | return false; 40 | 41 | if (unzOpenCurrentFile(uf) != UNZ_OK) 42 | return false; 43 | LOKI_ON_BLOCK_EXIT(unzCloseCurrentFile, uf); 44 | 45 | tstring inner_path; 46 | if ((file_info.flag & ZIP_GPBF_LANGUAGE_ENCODING_FLAG) != 0) { 47 | #ifdef _UNICODE 48 | inner_path = encoding::UTF8ToUCS2(inner_path_buffer); 49 | #else 50 | inner_path = encoding::UCS2ToANSI(encoding::UTF8ToUCS2(inner_path_buffer)); 51 | #endif 52 | } else { 53 | #ifdef _UNICODE 54 | inner_path = encoding::ANSIToUCS2(inner_path_buffer); 55 | #else 56 | inner_path = inner_path_buffer 57 | #endif 58 | } 59 | 60 | tstring target_path = target_dir + inner_path; 61 | mkdirs(&target_path[0]); 62 | bool is_dir = *inner_path.rbegin() == _T('/'); 63 | 64 | if (!is_dir) { 65 | FILE *f = _tfopen(target_path.c_str(), _T("wb")); 66 | if (f == NULL) 67 | return false; 68 | LOKI_ON_BLOCK_EXIT(fclose, f); 69 | 70 | const size_t BUFFER_SIZE = 4096; 71 | unsigned char buffer[BUFFER_SIZE] = {}; 72 | while (true) { 73 | int size = unzReadCurrentFile(uf, buffer, BUFFER_SIZE); 74 | if (size < 0) 75 | return false; 76 | if (size == 0) 77 | break; 78 | if (fwrite(buffer, 1, size, f) != size) 79 | return false; 80 | } 81 | } 82 | 83 | HANDLE hFile = CreateFile(target_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 84 | is_dir ? FILE_ATTRIBUTE_DIRECTORY : 0, NULL); 85 | if (hFile != INVALID_HANDLE_VALUE) { 86 | FILETIME ftLocal, ftUTC; 87 | DosDateTimeToFileTime((WORD)(file_info.dosDate >> 16), (WORD)file_info.dosDate, &ftLocal); 88 | LocalFileTimeToFileTime(&ftLocal, &ftUTC); 89 | SetFileTime(hFile, &ftUTC, &ftUTC, &ftUTC); 90 | CloseHandle(hFile); 91 | } 92 | return true; 93 | } 94 | 95 | } // namespace 96 | 97 | namespace zlibwrap { 98 | 99 | bool ZipExtract(const TCHAR *zip_file, const TCHAR *target_dir) { 100 | zlib_filefunc64_def zlib_filefunc_def; 101 | fill_win32_filefunc64(&zlib_filefunc_def); 102 | unzFile uf = unzOpen2_64(zip_file, &zlib_filefunc_def); 103 | if (uf == NULL) 104 | return false; 105 | LOKI_ON_BLOCK_EXIT(unzClose, uf); 106 | 107 | unz_global_info64 gi = {}; 108 | if (unzGetGlobalInfo64(uf, &gi) != UNZ_OK) 109 | return false; 110 | 111 | std::wstring root_dir = target_dir; 112 | if (!root_dir.empty() && (*root_dir.rbegin() != _T('\\') && *root_dir.rbegin() != _T('/'))) 113 | root_dir += _T("/"); 114 | TCHAR *root_dir_buffer = &root_dir[0]; 115 | mkdirs(root_dir_buffer); 116 | 117 | for (int i = 0; i < gi.number_entry; ++i) { 118 | if (!ZipExtractCurrentFile(uf, root_dir)) 119 | return false; 120 | if (i < gi.number_entry - 1) { 121 | if (unzGoToNextFile(uf) != UNZ_OK) 122 | return false; 123 | } 124 | } 125 | 126 | return true; 127 | } 128 | 129 | } // namespace zlibwrap 130 | -------------------------------------------------------------------------------- /src/zip.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ZIP_GPBF_LANGUAGE_ENCODING_FLAG 0x800 4 | -------------------------------------------------------------------------------- /src/zip_posix.cc: -------------------------------------------------------------------------------- 1 | #include "zip.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace { 11 | 12 | bool ZipAddFile(zipFile zf, const std::string &inner_path, const std::string &source_file, const struct stat &st) { 13 | 14 | zip_fileinfo file_info = {}; 15 | file_info.internal_fa = 0; 16 | file_info.external_fa = st.st_mode; 17 | tm *date = localtime(&st.st_mtime); 18 | file_info.tmz_date.tm_sec = date->tm_sec; 19 | file_info.tmz_date.tm_min = date->tm_min; 20 | file_info.tmz_date.tm_hour = date->tm_hour; 21 | file_info.tmz_date.tm_mday = date->tm_mday; 22 | file_info.tmz_date.tm_mon = date->tm_mon; 23 | file_info.tmz_date.tm_year = date->tm_year; 24 | 25 | if (zipOpenNewFileInZip4(zf, inner_path.c_str(), &file_info, NULL, 0, NULL, 0, NULL, Z_DEFLATED, 9, 0, -MAX_WBITS, 26 | DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, 0, ZIP_GPBF_LANGUAGE_ENCODING_FLAG) != ZIP_OK) { 27 | return false; 28 | } 29 | LOKI_ON_BLOCK_EXIT(zipCloseFileInZip, zf); 30 | 31 | if (S_ISDIR(st.st_mode)) 32 | return true; 33 | 34 | FILE *f = fopen(source_file.c_str(), "rb"); 35 | if (f == NULL) 36 | return false; 37 | LOKI_ON_BLOCK_EXIT(fclose, f); 38 | 39 | const unsigned int BUFFER_SIZE = 4096; 40 | unsigned char buffer[BUFFER_SIZE]; 41 | while (!feof(f)) { 42 | size_t size = fread(buffer, 1, BUFFER_SIZE, f); 43 | if (size < BUFFER_SIZE && ferror(f)) 44 | return false; 45 | if (zipWriteInFileInZip(zf, buffer, (unsigned int)size) < 0) 46 | return false; 47 | } 48 | return true; 49 | } 50 | 51 | bool ZipAddFiles(zipFile zf, const std::string &inner_dir, const std::string &pattern) { 52 | glob_t globbuf = {}; 53 | if (glob(pattern.c_str(), 0, NULL, &globbuf) != 0) 54 | return false; 55 | LOKI_ON_BLOCK_EXIT(globfree, &globbuf); 56 | 57 | for (size_t i = 0; i < globbuf.gl_pathc; ++i) { 58 | std::string inner_path = inner_dir; 59 | std::string source_path = globbuf.gl_pathv[i]; 60 | size_t slash_pos = source_path.rfind('/'); 61 | if (slash_pos != std::string::npos) 62 | inner_path += source_path.substr(slash_pos + 1); 63 | else 64 | inner_path += source_path; 65 | 66 | struct stat st = {}; 67 | if (stat(source_path.c_str(), &st) != 0) 68 | return false; 69 | if (S_ISDIR(st.st_mode)) { 70 | inner_path += "/"; 71 | if (!ZipAddFile(zf, inner_path, source_path, st)) 72 | return false; 73 | if (!ZipAddFiles(zf, inner_path, source_path + "/*")) 74 | return false; 75 | } else { 76 | if (!ZipAddFile(zf, inner_path, source_path, st)) 77 | return false; 78 | } 79 | } 80 | return true; 81 | } 82 | 83 | } // namespace 84 | 85 | namespace zlibwrap { 86 | 87 | bool ZipCompress(const char *zip_file, const char *pattern) { 88 | zipFile zf = zipOpen64(zip_file, 0); 89 | if (zf == NULL) 90 | return false; 91 | LOKI_ON_BLOCK_EXIT(zipClose, zf, (const char *)NULL); 92 | 93 | return ZipAddFiles(zf, "", pattern); 94 | } 95 | 96 | } // namespace zlibwrap 97 | -------------------------------------------------------------------------------- /src/zip_win.cc: -------------------------------------------------------------------------------- 1 | #include "encoding.h" 2 | #include "zip.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | // clang-format off 11 | #include 12 | #include 13 | // clang-format on 14 | namespace { 15 | 16 | #ifdef _UNICODE 17 | typedef std::wstring tstring; 18 | #else 19 | typedef std::string tstring; 20 | #endif 21 | 22 | bool ZipAddFile(zipFile zf, const tstring &inner_path, const tstring &source_file, const _wfinddata64_t &find_data) { 23 | zip_fileinfo file_info = {}; 24 | file_info.internal_fa = 0; 25 | file_info.external_fa = find_data.attrib; 26 | HANDLE hFile = CreateFile(source_file.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 27 | (find_data.attrib & _A_SUBDIR) != 0 ? FILE_ATTRIBUTE_DIRECTORY : 0, NULL); 28 | if (hFile != INVALID_HANDLE_VALUE) { 29 | FILETIME ftLocal = {}, ftUTC = {}; 30 | GetFileTime(hFile, NULL, NULL, &ftUTC); 31 | FileTimeToLocalFileTime(&ftUTC, &ftLocal); 32 | WORD wDate = 0, wTime = 0; 33 | FileTimeToDosDateTime(&ftLocal, &wDate, &wTime); 34 | CloseHandle(hFile); 35 | file_info.dosDate = ((((DWORD)wDate) << 16) | (DWORD)wTime); 36 | } 37 | 38 | #ifdef _UNICODE 39 | const std::string inner_path_utf8 = encoding::UCS2ToUTF8(inner_path); 40 | #else 41 | const std::string inner_path_utf8 = encoding::UCS2ToUTF8(encoding::ANSIToUCS2(inner_path)); 42 | #endif 43 | if (zipOpenNewFileInZip4(zf, inner_path_utf8.c_str(), &file_info, NULL, 0, NULL, 0, NULL, Z_DEFLATED, 9, 0, 44 | -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, 0, 45 | ZIP_GPBF_LANGUAGE_ENCODING_FLAG) != ZIP_OK) { 46 | return false; 47 | } 48 | LOKI_ON_BLOCK_EXIT(zipCloseFileInZip, zf); 49 | 50 | if ((find_data.attrib & _A_SUBDIR) != 0) 51 | return true; 52 | 53 | FILE *f = _tfopen(source_file.c_str(), _T("rb")); 54 | if (f == NULL) 55 | return false; 56 | LOKI_ON_BLOCK_EXIT(fclose, f); 57 | 58 | const unsigned int BUFFER_SIZE = 4096; 59 | unsigned char buffer[BUFFER_SIZE]; 60 | while (!feof(f)) { 61 | size_t size = fread(buffer, 1, BUFFER_SIZE, f); 62 | if (size < BUFFER_SIZE && ferror(f)) 63 | return false; 64 | if (zipWriteInFileInZip(zf, buffer, (unsigned int)size) < 0) 65 | return false; 66 | } 67 | return true; 68 | } 69 | 70 | bool ZipAddFiles(zipFile zf, const tstring &inner_dir, const tstring &pattern) { 71 | size_t slash = pattern.rfind(_T('/')); 72 | size_t back_slash = pattern.rfind(_T('\\')); 73 | size_t slash_pos = slash != tstring::npos && back_slash != tstring::npos 74 | ? max(slash, back_slash) 75 | : (slash != tstring::npos ? slash : back_slash); 76 | tstring source_dir; 77 | if (slash_pos != tstring::npos) 78 | source_dir = pattern.substr(0, slash_pos) + _T("/"); 79 | 80 | _wfinddata64_t find_data = {}; 81 | intptr_t find = _wfindfirst64(pattern.c_str(), &find_data); 82 | if (find == -1) 83 | return false; 84 | LOKI_ON_BLOCK_EXIT(_findclose, find); 85 | 86 | do { 87 | if (wcscmp(find_data.name, _T(".")) == 0 || wcscmp(find_data.name, _T("..")) == 0) 88 | continue; 89 | 90 | tstring inner_path = inner_dir + find_data.name; 91 | tstring source_path = source_dir + find_data.name; 92 | if ((find_data.attrib & _A_SUBDIR) != 0) { 93 | inner_path += _T("/"); 94 | if (!ZipAddFile(zf, inner_path, source_path, find_data)) 95 | return false; 96 | if (!ZipAddFiles(zf, inner_path, source_path + _T("/*"))) 97 | return false; 98 | } else { 99 | if (!ZipAddFile(zf, inner_path, source_path, find_data)) 100 | return false; 101 | } 102 | } while (_wfindnext64(find, &find_data) == 0); 103 | 104 | return true; 105 | } 106 | 107 | } // namespace 108 | 109 | namespace zlibwrap { 110 | 111 | bool ZipCompress(const TCHAR *zip_file, const TCHAR *pattern) { 112 | zlib_filefunc64_def zlib_filefunc_def; 113 | fill_win32_filefunc64(&zlib_filefunc_def); 114 | zipFile zf = zipOpen2_64(zip_file, 0, NULL, &zlib_filefunc_def); 115 | if (zf == NULL) 116 | return false; 117 | LOKI_ON_BLOCK_EXIT(zipClose, zf, (const char *)NULL); 118 | 119 | return ZipAddFiles(zf, _T(""), pattern); 120 | } 121 | 122 | } // namespace zlibwrap 123 | -------------------------------------------------------------------------------- /src/zlibwrap.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Streamlet/ZLibWrap/dad4dec5e1658d5f619570e30a86995c92cbee10/src/zlibwrap.rc -------------------------------------------------------------------------------- /src/zlibwrapd.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef _WIN32 5 | 6 | ZLIBWRAP_API bool ZipCompress(const TCHAR *zip_file, const TCHAR *pattern) { 7 | return zlibwrap::ZipCompress(zip_file, pattern); 8 | } 9 | ZLIBWRAP_API bool ZipExtract(const TCHAR *zip_file, const TCHAR *target_dir) { 10 | return zlibwrap::ZipExtract(zip_file, target_dir); 11 | } 12 | 13 | #else 14 | 15 | ZLIBWRAP_API bool ZipCompress(const char *zip_file, const char *pattern) { 16 | return zlibwrap::ZipCompress(zip_file, pattern); 17 | } 18 | ZLIBWRAP_API bool ZipExtract(const char *zip_file, const char *target_dir) { 19 | return zlibwrap::ZipExtract(zip_file, target_dir); 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /thirdparty/BUILD.gn: -------------------------------------------------------------------------------- 1 | # loki 2 | 3 | config("loki_public_config") { 4 | include_dirs = [ "loki/include" ] 5 | if (is_win) { 6 | cflags = [ "/wd4828" ] 7 | } 8 | } 9 | 10 | group("loki") { 11 | public_configs = [ ":loki_public_config" ] 12 | } 13 | 14 | # zlib 15 | 16 | config("zlib_public_config") { 17 | include_dirs = [ 18 | "zlib", 19 | "zlib/contrib", 20 | ] 21 | } 22 | 23 | source_set("zlib") { 24 | sources = [ 25 | "zlib/adler32.c", 26 | "zlib/compress.c", 27 | "zlib/crc32.c", 28 | "zlib/crc32.h", 29 | "zlib/deflate.c", 30 | "zlib/deflate.h", 31 | "zlib/gzclose.c", 32 | "zlib/gzguts.h", 33 | "zlib/gzlib.c", 34 | "zlib/gzread.c", 35 | "zlib/gzwrite.c", 36 | "zlib/infback.c", 37 | "zlib/inffast.c", 38 | "zlib/inffast.h", 39 | "zlib/inffixed.h", 40 | "zlib/inflate.c", 41 | "zlib/inflate.h", 42 | "zlib/inftrees.c", 43 | "zlib/inftrees.h", 44 | "zlib/trees.c", 45 | "zlib/trees.h", 46 | "zlib/uncompr.c", 47 | "zlib/zconf.h", 48 | "zlib/zlib.h", 49 | "zlib/zutil.c", 50 | "zlib/zutil.h", 51 | ] 52 | if (!is_win) { 53 | cflags = [ 54 | "-Wno-implicit-function-declaration", 55 | "-Wno-deprecated-non-prototype", 56 | ] 57 | } 58 | public_configs = [ ":zlib_public_config" ] 59 | } 60 | 61 | source_set("minizip") { 62 | sources = [ 63 | "zlib/contrib/minizip/crypt.h", 64 | "zlib/contrib/minizip/ioapi.c", 65 | "zlib/contrib/minizip/ioapi.h", 66 | "zlib/contrib/minizip/mztools.c", 67 | "zlib/contrib/minizip/mztools.h", 68 | "zlib/contrib/minizip/unzip.c", 69 | "zlib/contrib/minizip/unzip.h", 70 | "zlib/contrib/minizip/zip.c", 71 | "zlib/contrib/minizip/zip.h", 72 | ] 73 | if (is_win) { 74 | sources += [ 75 | "zlib/contrib/minizip/iowin32.c", 76 | "zlib/contrib/minizip/iowin32.h", 77 | ] 78 | cflags = [ "/wd5105" ] 79 | } else { 80 | cflags = [ "-Wno-deprecated-non-prototype" ] 81 | } 82 | deps = [ ":zlib" ] 83 | public_configs = [ ":zlib_public_config" ] 84 | } 85 | --------------------------------------------------------------------------------