diff --git a/.dockerignore b/.dockerignore index f46d814c7..8568fe1d3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,11 +2,19 @@ /.github/ /.idea/ /.vs/ -/android/ /build/ -/cmake-build* +/build-*/ +/cmake-build*/ /vc17/ -Dockerfile +/vcpkg_installed/ +/android/app/build/ +/android/app/.cxx/ +/android/.gradle/ +Dockerfile* *.dll *.exe *.pdb +*.obj +*.lib +*.ilk +*.exp \ No newline at end of file diff --git a/.gitignore b/.gitignore index e27feb24f..5fff6df26 100644 --- a/.gitignore +++ b/.gitignore @@ -317,3 +317,13 @@ google-services.json # Android libraries android/app/libs android/app/src/main/assets + +# Android build artifacts (generated by build_luajit_android.sh and setup_android_deps.sh) +android/app/libs/lib/ +android/app/libs/include/ +android/app/src/main/assets/data.zip +android/app/build/ +android/app/.cxx/ +android/.gradle/ +android/build/ +luajit-src/ diff --git a/Dockerfile.android b/Dockerfile.android new file mode 100644 index 000000000..34138e155 --- /dev/null +++ b/Dockerfile.android @@ -0,0 +1,50 @@ +# Use a standard Android SDK image +FROM thyrlian/android-sdk:10.0 AS builder + +USER root +RUN apt-get update && apt-get install -y --no-install-recommends \ + git cmake curl zip unzip tar automake ca-certificates build-essential \ + libncurses5-dev libssl-dev python3 python3-pip ninja-build nasm gperf \ + pkg-config libtool bison flex \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install vcpkg with FULL history +WORKDIR /opt +RUN git clone https://github.com/microsoft/vcpkg.git \ + && cd vcpkg && ./bootstrap-vcpkg.sh + +ENV VCPKG_ROOT=/opt/vcpkg +ENV ANDROID_HOME=/opt/android-sdk +ENV ANDROID_NDK_HOME=/opt/android-sdk/ndk/29.0.13599879 +ENV VCPKG_FORCE_SYSTEM_BINARIES=1 + +# Install NDK and stable CMake via sdkmanager +RUN yes | sdkmanager --sdk_root=$ANDROID_HOME "ndk;29.0.13599879" "cmake;3.22.1" + +# Set workdir for the project +WORKDIR /otclient +COPY . /otclient + +# Install gcc-multilib for 32-bit LuaJIT cross-compilation +RUN apt-get update && apt-get install -y --no-install-recommends gcc-multilib \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Clone LuaJIT source and cross-compile for all Android ABIs +RUN git clone --depth 1 https://github.com/LuaJIT/LuaJIT.git /otclient/luajit-src \ + && cd /otclient/luajit-src && git fetch --depth 1 origin d0e88930ddde28ff662503f9f20facf34f7265aa \ + && git checkout d0e88930ddde28ff662503f9f20facf34f7265aa +RUN cd /otclient && chmod +x build_luajit_android.sh && ./build_luajit_android.sh + +# Generate data.zip asset bundle +RUN mkdir -p /otclient/android/app/src/main/assets \ + && cd /otclient && zip -r android/app/src/main/assets/data.zip data mods modules init.lua otclientrc.lua + +# Build APK inside the image +RUN cd /otclient/android \ + && tr -d '\r' < gradlew > gradlew_unix \ + && chmod +x gradlew_unix \ + && ./gradlew_unix assembleRelease + +# Minimal stage to extract APK +FROM scratch AS apk-artifacts +COPY --from=builder /otclient/android/app/build/outputs/apk/release/ / diff --git a/android/BUILDING.md b/android/BUILDING.md new file mode 100644 index 000000000..081a3b998 --- /dev/null +++ b/android/BUILDING.md @@ -0,0 +1,127 @@ +# Building OTClient for Android + +## Prerequisites + +- **Linux host** (Ubuntu 22.04+ or WSL2 recommended) +- **Java 17** (`sudo apt install openjdk-17-jdk`) +- **Android SDK** with NDK 29.0.13599879 and CMake 3.22.1 +- **vcpkg** (cloned and bootstrapped) +- **gcc-multilib** for 32-bit LuaJIT cross-compilation (`sudo apt install gcc-multilib g++-multilib`) + +### Environment variables + +```bash +export ANDROID_HOME=/path/to/android-sdk +export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/29.0.13599879 +export VCPKG_ROOT=/path/to/vcpkg +export JAVA_HOME=/usr # or path to JDK 17 +``` + +## Quick build (automated) + +```bash +# 1. Run the setup script (builds LuaJIT, installs vcpkg deps, generates data.zip) +./setup_android_deps.sh + +# 2. Build the APK +cd android +chmod +x gradlew +./gradlew assembleRelease +``` + +The APK will be at `android/app/build/outputs/apk/release/app-release.apk`. + +## Step-by-step build + +### Step 1: Build LuaJIT for Android + +LuaJIT must be cross-compiled separately because the vcpkg LuaJIT port cannot cross-compile for Android from an x64 host ([LuaJIT Issue #664](https://github.com/LuaJIT/LuaJIT/issues/664)). + +```bash +# Clone LuaJIT source (pinned to vcpkg-compatible commit) +git clone https://github.com/LuaJIT/LuaJIT.git luajit-src +cd luajit-src && git checkout d0e88930ddde28ff662503f9f20facf34f7265aa && cd .. + +# Cross-compile for all 4 ABIs +./build_luajit_android.sh +``` + +This produces static libraries in `android/app/libs/lib/{ABI}/libluajit-5.1.a` and headers in `android/app/libs/include/luajit/`. + +#### How it works + +LuaJIT's build is a two-stage process: host tools (`minilua`, `buildvm`) run on your machine to generate target-specific code, then the NDK cross-compiler compiles the library. Key flags: + +| Flag | Why | +|------|-----| +| `HOST_CC="gcc -m32"` | Required for 32-bit targets (armeabi-v7a, x86) — ensures matching pointer sizes between host tools and target ([#664](https://github.com/LuaJIT/LuaJIT/issues/664)) | +| `-DLUAJIT_UNWIND_EXTERNAL` | Required for Android NDK — prevents conflict between LuaJIT's internal unwinder and C++ exception handling | +| `-fno-stack-protector` | Avoids linker issues with NDK's stack protector implementation | +| `-fPIC` | Required for x86 ABI — static lib linked into a shared `.so` | +| `amalg` | Single-file build for better optimization | +| `TARGET_SYS=Linux` | Never use `TARGET_SYS=Android` — causes crashes ([#440](https://github.com/LuaJIT/LuaJIT/issues/440)) | + +### Step 2: Generate data.zip + +```bash +# Bundle game assets into data.zip for the APK +mkdir -p android/app/src/main/assets +cd /path/to/otclient +python3 -c " +import zipfile, os +with zipfile.ZipFile('android/app/src/main/assets/data.zip', 'w', zipfile.ZIP_DEFLATED) as zf: + for item in ['data', 'mods', 'modules']: + for root, dirs, files in os.walk(item): + for f in files: + zf.write(os.path.join(root, f)) + for f in ['init.lua', 'otclientrc.lua']: + if os.path.exists(f): + zf.write(f) +" +``` + +### Step 3: Build with Gradle + +```bash +cd android +chmod +x gradlew +./gradlew assembleRelease # or assembleDebug +``` + +### Step 4: Install on device + +```bash +adb install android/app/build/outputs/apk/release/app-release.apk +``` + +## Docker build + +```bash +docker build -f Dockerfile.android -t otclient-android . +docker create --name otc-build otclient-android +docker cp otc-build:/app-release.apk ./app-release.apk +docker rm otc-build +``` + +## Supported ABIs + +| ABI | Device type | LuaJIT host flag | +|-----|------------|------------------| +| arm64-v8a | Modern phones/tablets (95%+ of market) | `gcc` | +| armeabi-v7a | Older 32-bit ARM devices | `gcc -m32` | +| x86_64 | Chromebooks, emulators | `gcc` | +| x86 | Older emulators | `gcc -m32` | + +## Troubleshooting + +### Black screen on launch +- Ensure `data.zip` is in `android/app/src/main/assets/`. Run `setup_android_deps.sh` or generate it manually. + +### `assertion "hasIndex(-n)" failed` crash +- LuaJIT was compiled without `-DLUAJIT_UNWIND_EXTERNAL` or with wrong pointer size. Re-run `build_luajit_android.sh`. + +### `Unsupported target architecture` in vcpkg +- This is expected — vcpkg cannot build LuaJIT for Android. That's why we build it separately. The `vcpkg.json` excludes LuaJIT from Android. + +### 32-bit build fails with `gcc -m32` +- Install `gcc-multilib`: `sudo apt install gcc-multilib g++-multilib` diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index af6d98afa..200161e7e 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -16,16 +16,18 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" ndk { - abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64") + abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86") } externalNativeBuild { cmake { - cppFlags += listOf("-std=c++20", "-flto") + cppFlags += listOf("-std=c++20") arguments += listOf( "-DVCPKG_TARGET_ANDROID=ON", - "-DANDROID_STL=c++_shared" + "-DANDROID_STL=c++_shared", + "-DVCPKG_MANIFEST_INSTALL=ON", + "-DVCPKG_INSTALL_OPTIONS=--allow-unsupported" ) } } @@ -34,7 +36,7 @@ android { externalNativeBuild { cmake { path = file("../../CMakeLists.txt") - version = "4.0.2" + version = "3.22.1" } } @@ -63,7 +65,7 @@ android { prefab = true } - ndkVersion = "29.0.13599879 rc2" + ndkVersion = "29.0.13599879" } dependencies { @@ -71,4 +73,4 @@ dependencies { implementation("androidx.appcompat:appcompat:1.7.1") implementation("androidx.games:games-activity:1.2.1") implementation("com.google.android.material:material:1.13.0") -} \ No newline at end of file +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 29700ffbc..fd42c3a43 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,8 +2,8 @@ - - + + diff --git a/build_luajit_android.sh b/build_luajit_android.sh new file mode 100644 index 000000000..6416f4f9f --- /dev/null +++ b/build_luajit_android.sh @@ -0,0 +1,94 @@ +#!/bin/bash +set -euo pipefail +# Build LuaJIT 2.1 for all Android ABIs using NDK cross-compilation +# Based on official LuaJIT docs (luajit.org/install.html) and vcpkg build flags +# +# Requirements: +# - ANDROID_NDK_HOME set (NDK 25+) +# - gcc-multilib installed (for 32-bit targets: sudo apt install gcc-multilib) +# - LuaJIT source at ./luajit-src/ (commit d0e88930 recommended for vcpkg compat) +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +NDK="${ANDROID_NDK_HOME:-/home/dev/android-sdk/ndk/29.0.13599879}" +NDKBIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin" +LUAJIT_SRC="$SCRIPT_DIR/luajit-src" +INSTALL_BASE="$SCRIPT_DIR/android/app/libs" + +if [ ! -d "$LUAJIT_SRC/src" ]; then + echo "ERROR: LuaJIT source not found at $LUAJIT_SRC" + echo "Run: git clone https://github.com/LuaJIT/LuaJIT.git $LUAJIT_SRC" + exit 1 +fi + +# Verify gcc -m32 works (needed for 32-bit targets) +if ! gcc -m32 -x c -c /dev/null -o /dev/null 2>/dev/null; then + echo "Installing gcc-multilib for 32-bit cross-compilation..." + sudo apt-get install -y gcc-multilib g++-multilib +fi + +build_luajit() { + local ABI=$1 CROSS_PREFIX=$2 CC_PREFIX=$3 HOST_CC=$4 + + echo "=== Building LuaJIT for $ABI ===" + cd "$LUAJIT_SRC" + make clean 2>/dev/null || true + + # Official LuaJIT cross-compilation approach: + # - HOST_CC="gcc -m32" for 32-bit targets (correct struct offsets in buildvm) + # - CROSS= for NDK toolchain prefix + # - STATIC_CC/DYNAMIC_CC for clang compiler + # - TARGET_CFLAGS with -DLUAJIT_UNWIND_EXTERNAL (required for Android NDK C++ interop) + # - Never use TARGET_SYS=Android (causes crashes, LuaJIT Issue #440) + # - Use 'amalg' target for optimized single-file compilation + make -j$(nproc) amalg \ + HOST_CC="$HOST_CC" \ + CROSS="${NDKBIN}/${CROSS_PREFIX}" \ + STATIC_CC="${NDKBIN}/${CC_PREFIX}clang" \ + DYNAMIC_CC="${NDKBIN}/${CC_PREFIX}clang -fPIC" \ + TARGET_LD="${NDKBIN}/${CC_PREFIX}clang" \ + TARGET_AR="$NDKBIN/llvm-ar rcus" \ + TARGET_STRIP="$NDKBIN/llvm-strip" \ + TARGET_CFLAGS="-fPIC -DLUAJIT_UNWIND_EXTERNAL -fno-stack-protector" \ + BUILDMODE=static + + # Install lib + local LIB_DIR="$INSTALL_BASE/lib/$ABI" + mkdir -p "$LIB_DIR" + cp src/libluajit.a "$LIB_DIR/libluajit-5.1.a" + echo " -> $LIB_DIR/libluajit-5.1.a ($(du -h "$LIB_DIR/libluajit-5.1.a" | cut -f1))" +} + +# arm64-v8a: 64-bit ARM (most modern Android devices) +build_luajit "arm64-v8a" "aarch64-linux-android-" "aarch64-linux-android21-" "gcc" + +# armeabi-v7a: 32-bit ARM (older devices) — requires HOST_CC="gcc -m32" +build_luajit "armeabi-v7a" "arm-linux-androideabi-" "armv7a-linux-androideabi21-" "gcc -m32" + +# x86_64: 64-bit x86 (emulators, Chromebooks) +build_luajit "x86_64" "x86_64-linux-android-" "x86_64-linux-android21-" "gcc" + +# x86: 32-bit x86 (older emulators) — requires HOST_CC="gcc -m32" +build_luajit "x86" "i686-linux-android-" "i686-linux-android21-" "gcc -m32" + +# Install headers (shared across all ABIs) +echo "" +echo "=== Installing headers ===" +mkdir -p "$INSTALL_BASE/include/luajit" +cp "$LUAJIT_SRC/src/lua.h" "$LUAJIT_SRC/src/lualib.h" "$LUAJIT_SRC/src/lauxlib.h" \ + "$LUAJIT_SRC/src/luaconf.h" "$LUAJIT_SRC/src/luajit.h" \ + "$INSTALL_BASE/include/luajit/" + +# Create lua.hpp C++ wrapper +cat > "$INSTALL_BASE/include/luajit/lua.hpp" << 'LUAHPP' +// C++ wrapper for LuaJIT header files. + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include "luajit.h" +} +LUAHPP + +echo "" +echo "=== All ABIs built successfully ===" +ls -lh "$INSTALL_BASE/lib/"*/libluajit-5.1.a diff --git a/setup_android_deps.sh b/setup_android_deps.sh new file mode 100644 index 000000000..81e3c114e --- /dev/null +++ b/setup_android_deps.sh @@ -0,0 +1,164 @@ +#!/bin/bash +set -euo pipefail +# Pre-build script: installs all vcpkg dependencies for all Android ABIs +# Run this ONCE before building with Gradle +# Usage: ./setup_android_deps.sh + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +NDK="${ANDROID_NDK_HOME:-/home/dev/android-sdk/ndk/29.0.13599879}" +NDKBIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin" +VCPKG="${VCPKG_ROOT:-/home/dev/vcpkg}" +LUAJIT_SRC="$SCRIPT_DIR/luajit-src" + +# ABI -> vcpkg triplet mapping +declare -A ABI_MAP=( + ["arm64-v8a"]="arm64-android" + ["armeabi-v7a"]="arm-neon-android" + ["x86_64"]="x64-android" + ["x86"]="x86-android" +) + +# ABI -> NDK cross-compile prefix (CROSS= prefix, without API level) +declare -A CROSS_MAP=( + ["arm64-v8a"]="aarch64-linux-android-" + ["armeabi-v7a"]="arm-linux-androideabi-" + ["x86_64"]="x86_64-linux-android-" + ["x86"]="i686-linux-android-" +) + +# ABI -> NDK clang prefix (with API level, for STATIC_CC/DYNAMIC_CC/TARGET_LD) +declare -A CC_MAP=( + ["arm64-v8a"]="aarch64-linux-android21-" + ["armeabi-v7a"]="armv7a-linux-androideabi21-" + ["x86_64"]="x86_64-linux-android21-" + ["x86"]="i686-linux-android21-" +) + +# ABI -> HOST_CC (32-bit ABIs need -m32) +declare -A HOSTCC_MAP=( + ["arm64-v8a"]="gcc" + ["armeabi-v7a"]="gcc -m32" + ["x86_64"]="gcc" + ["x86"]="gcc -m32" +) + +build_luajit_for_abi() { + local ABI=$1 + local TRIPLET=${ABI_MAP[$ABI]} + local CROSS=${CROSS_MAP[$ABI]} + local CC=${CC_MAP[$ABI]} + local HOST_CC="${HOSTCC_MAP[$ABI]}" + local INSTALL_DIR="$SCRIPT_DIR/android/app/libs" + + echo "=== Building LuaJIT for $ABI ($TRIPLET) ===" + + if [ ! -d "$LUAJIT_SRC/src" ]; then + echo "ERROR: LuaJIT source not found at $LUAJIT_SRC" + echo "Clone it: git clone https://github.com/LuaJIT/LuaJIT.git $LUAJIT_SRC" + exit 1 + fi + + cd "$LUAJIT_SRC" + make clean 2>/dev/null || true + + make -j"$(nproc)" amalg \ + HOST_CC="$HOST_CC" \ + CROSS="${NDKBIN}/${CROSS}" \ + STATIC_CC="${NDKBIN}/${CC}clang" \ + DYNAMIC_CC="${NDKBIN}/${CC}clang -fPIC" \ + TARGET_LD="${NDKBIN}/${CC}clang" \ + TARGET_AR="$NDKBIN/llvm-ar rcus" \ + TARGET_STRIP="$NDKBIN/llvm-strip" \ + TARGET_CFLAGS="-fPIC -DLUAJIT_UNWIND_EXTERNAL -fno-stack-protector" \ + BUILDMODE=static + + # Install lib per-ABI + mkdir -p "$INSTALL_DIR/lib/$ABI" + cp src/libluajit.a "$INSTALL_DIR/lib/$ABI/libluajit-5.1.a" + + # Install headers (shared across ABIs) + mkdir -p "$INSTALL_DIR/include/luajit" + cp src/lua.h src/lualib.h src/lauxlib.h src/luaconf.h \ + src/luajit.h src/luajit_rolling.h "$INSTALL_DIR/include/luajit/" + + # Create lua.hpp C++ wrapper if missing + if [ ! -f "$INSTALL_DIR/include/luajit/lua.hpp" ]; then + cat > "$INSTALL_DIR/include/luajit/lua.hpp" << 'LUAHPP' +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} +LUAHPP + fi + + echo " -> $ABI OK ($(du -h "$INSTALL_DIR/lib/$ABI/libluajit-5.1.a" | cut -f1))" +} + +install_vcpkg_deps() { + local TRIPLET=$1 + echo "=== Installing vcpkg deps for $TRIPLET ===" + cd "$SCRIPT_DIR" + "$VCPKG/vcpkg" install --triplet "$TRIPLET" --x-manifest-root=. --allow-unsupported 2>&1 | tail -3 +} + +# Step 1: Build LuaJIT for all ABIs +echo "=============================" +echo "Step 1: Building LuaJIT" +echo "=============================" + +# Check gcc-multilib for 32-bit builds +if ! gcc -m32 -x c -c /dev/null -o /dev/null 2>/dev/null; then + echo "Installing gcc-multilib for 32-bit cross-compilation..." + sudo apt-get install -y gcc-multilib g++-multilib +fi + +for ABI in "${!ABI_MAP[@]}"; do + build_luajit_for_abi "$ABI" +done + +# Step 2: Install vcpkg deps (LuaJIT excluded from vcpkg, everything else) +echo "" +echo "=============================" +echo "Step 2: Installing vcpkg deps" +echo "=============================" +for ABI in "${!ABI_MAP[@]}"; do + TRIPLET=${ABI_MAP[$ABI]} + install_vcpkg_deps "$TRIPLET" +done + +# Step 3: Generate data.zip asset bundle +echo "" +echo "=============================" +echo "Step 3: Generating data.zip" +echo "=============================" +mkdir -p "$SCRIPT_DIR/android/app/src/main/assets" +cd "$SCRIPT_DIR" +if command -v zip &>/dev/null; then + zip -r android/app/src/main/assets/data.zip data mods modules init.lua otclientrc.lua +elif command -v python3 &>/dev/null; then + python3 -c " +import zipfile, os +with zipfile.ZipFile('android/app/src/main/assets/data.zip', 'w', zipfile.ZIP_DEFLATED) as zf: + for item in ['data', 'mods', 'modules']: + for root, dirs, files in os.walk(item): + for f in files: + zf.write(os.path.join(root, f)) + for f in ['init.lua', 'otclientrc.lua']: + if os.path.exists(f): + zf.write(f) +" +else + echo "ERROR: neither zip nor python3 available to create data.zip" + exit 1 +fi +echo "data.zip created: $(du -h android/app/src/main/assets/data.zip | cut -f1)" + +echo "" +echo "=============================" +echo "All dependencies installed!" +echo "=============================" +echo "LuaJIT libs:" +ls -la "$SCRIPT_DIR/android/app/libs/lib/"*/libluajit-5.1.a +echo "" +echo "Now run: cd android && ./gradlew assembleRelease" diff --git a/setup_android_wsl.sh b/setup_android_wsl.sh new file mode 100644 index 000000000..114750f31 --- /dev/null +++ b/setup_android_wsl.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -euo pipefail + +ANDROID_HOME=/home/dev/android-sdk +VCPKG_ROOT=/home/dev/vcpkg +PROJECT_DIR=/home/dev/kizu-otc + +mkdir -p "$ANDROID_HOME" + +# Install commandline-tools if missing +if [ ! -f "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then + echo "Downloading Android commandline-tools..." + cd /tmp + curl -sL "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip" -o cmdtools.zip + unzip -qo cmdtools.zip -d "$ANDROID_HOME/cmdline-tools-tmp" + mkdir -p "$ANDROID_HOME/cmdline-tools/latest" + mv "$ANDROID_HOME/cmdline-tools-tmp/cmdline-tools/"* "$ANDROID_HOME/cmdline-tools/latest/" + rm -rf "$ANDROID_HOME/cmdline-tools-tmp" cmdtools.zip + echo "commandline-tools installed." +else + echo "cmdline-tools already present." +fi + +# Install NDK, platform, build-tools +echo "Installing SDK components..." +yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --sdk_root="$ANDROID_HOME" \ + "ndk;29.0.13599879" \ + "platforms;android-36" \ + "build-tools;35.0.0" \ + 2>&1 | tail -5 + +echo "NDK:" && ls "$ANDROID_HOME/ndk/" + +# Set environment +export ANDROID_HOME="$ANDROID_HOME" +export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/29.0.13599879" +export VCPKG_ROOT="$VCPKG_ROOT" + +# Build +echo "=== Starting Gradle build ===" +cd "$PROJECT_DIR/android" + +# Ensure data.zip exists +mkdir -p "$PROJECT_DIR/android/app/src/main/assets" +if [ ! -f "$PROJECT_DIR/android/app/src/main/assets/data.zip" ]; then + echo "Generating data.zip..." + cd "$PROJECT_DIR" + zip -r android/app/src/main/assets/data.zip data mods modules init.lua otclientrc.lua +fi + +cd "$PROJECT_DIR/android" +chmod +x gradlew +./gradlew assembleRelease 2>&1 + +echo "=== BUILD COMPLETE ===" +ls -lh "$PROJECT_DIR/android/app/build/outputs/apk/release/" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 229b2ec15..ddc933f20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -76,7 +76,9 @@ endif() if (TOGGLE_FRAMEWORK_EDITOR) set(FRAMEWORK_DEFINITIONS ${FRAMEWORK_DEFINITIONS} -DFRAMEWORK_EDITOR) endif() -if (ANDROID OR WASM) +if (ANDROID) + set(FRAMEWORK_DEFINITIONS ${FRAMEWORK_DEFINITIONS} -DOPENGL_ES=3) +elseif(WASM) set(FRAMEWORK_DEFINITIONS ${FRAMEWORK_DEFINITIONS} -DOPENGL_ES=2) endif() @@ -216,12 +218,27 @@ if(TOGGLE_FRAMEWORK_SOUND) endif() endif() if(ANDROID) - set(LUA_LIBRARY ${LUA_LIBRARY} ${Android_LIBRARIES}/liblua.a) - set(LUAJIT_INCLUDE_DIR ${LUAJIT_LIBRARY} ${Android_INCLUDES}/luajit) - set(LUAJIT_LIBRARY ${LUAJIT_LIBRARY} ${Android_LIBRARIES}/libluajit.a) - set(OpenAL_LIBRARY ${OpenAL_LIBRARY} ${Android_LIBRARIES}/libopenal.a) - set(MINIZIP_INCLUDE_DIR ${MINIZIP_INCLUDE_DIR} ${Android_INCLUDES}/minizip) - set(MINIZIP_LIBRARY ${MINIZIP_LIBRARY} ${Android_LIBRARIES}/libminizip.a) + # LuaJIT: pre-compiled via build_luajit_android.sh or setup_android_deps.sh + # vcpkg LuaJIT port can't cross-compile for Android from x64 host + set(LUAJIT_ANDROID_LIBS "${CMAKE_SOURCE_DIR}/android/app/libs") + set(LUAJIT_ANDROID_LIB "${LUAJIT_ANDROID_LIBS}/lib/${CMAKE_ANDROID_ARCH_ABI}/libluajit-5.1.a") + set(LUAJIT_ANDROID_INC "${LUAJIT_ANDROID_LIBS}/include/luajit") + if(EXISTS "${LUAJIT_ANDROID_LIB}") + set(LUAJIT_INCLUDE_DIR "${LUAJIT_ANDROID_INC}" CACHE PATH "" FORCE) + set(LUAJIT_LIBRARY "${LUAJIT_ANDROID_LIB}" CACHE FILEPATH "" FORCE) + set(LUA_LIBRARY "${LUAJIT_ANDROID_LIB}") + include_directories("${LUAJIT_ANDROID_INC}") + message(STATUS "LuaJIT: using pre-compiled lib for ${CMAKE_ANDROID_ARCH_ABI}") + else() + message(FATAL_ERROR "LuaJIT not found for ${CMAKE_ANDROID_ARCH_ABI}. Run ./build_luajit_android.sh first.") + endif() + find_package(OpenAL CONFIG REQUIRED) + set(OpenAL_LIBRARY OpenAL::OpenAL) + + # Vendored minizip (nmoinvaz fork) for Android APK asset extraction + include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/framework/core/minizip) + set(MINIZIP_LIBRARY "") + find_package(game-activity REQUIRED CONFIG) find_package(EGL REQUIRED) else() @@ -449,6 +466,16 @@ if (WASM) ) endif() +if(ANDROID) + # Vendored minizip sources for APK asset extraction (must be after SOURCE_FILES definition) + set(SOURCE_FILES ${SOURCE_FILES} + framework/core/minizip/ioapi.c + framework/core/minizip/ioapi_mem.c + framework/core/minizip/unzip.c + framework/core/minizip/crypt.c + ) +endif() + add_library(otclient_core STATIC ${SOURCE_FILES}) target_link_libraries(otclient_core PRIVATE unofficial::inih::inireader) @@ -465,6 +492,19 @@ else() message(STATUS "Use precompiled header: OFF") endif(TOGGLE_PRE_COMPILED_HEADER) +# Exclude vendored minizip C files from PCH and unity build (Android only, C files cannot use C++ PCH) +if(ANDROID) + set_source_files_properties( + framework/core/minizip/ioapi.c + framework/core/minizip/ioapi_mem.c + framework/core/minizip/unzip.c + framework/core/minizip/crypt.c + PROPERTIES + SKIP_PRECOMPILE_HEADERS ON + SKIP_UNITY_BUILD_INCLUSION ON + ) +endif() + if(SPEED_UP_BUILD_UNITY) set_target_properties(otclient_core PROPERTIES UNITY_BUILD ON) log_option_enabled("Build unity for speed up compilation") @@ -641,7 +681,6 @@ elseif(ANDROID) ${EGL_INCLUDE_DIR} ${PARALLEL_HASHMAP_INCLUDE_DIRS} ${NLOHMANN_JSON_INCLUDE_DIR} - ${MINIZIP_INCLUDE_DIR} ${CPPCODEC_INCLUDE_DIRS} ) target_link_libraries(otclient_core @@ -671,6 +710,7 @@ elseif(ANDROID) GLESv3 android log + Freetype::Freetype pugixml::pugixml fmt::fmt-header-only utf8cpp::utf8cpp diff --git a/src/framework/core/minizip/crypt.c b/src/framework/core/minizip/crypt.c new file mode 100644 index 000000000..a074b8f08 --- /dev/null +++ b/src/framework/core/minizip/crypt.c @@ -0,0 +1,146 @@ +/* crypt.c -- base code for traditional PKWARE encryption + Version 1.2.0, September 16th, 2017 + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 1998-2005 Gilles Vollant + Modifications for Info-ZIP crypting + http://www.winimage.com/zLibDll/minizip.html + Copyright (C) 2003 Terry Thorsen + + This code is a modified version of crypting code in Info-ZIP distribution + + Copyright (C) 1990-2000 Info-ZIP. All rights reserved. + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. +*/ + +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +#endif + +#include "zlib.h" + +#include "crypt.h" + +/***************************************************************************/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((uint32_t)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/***************************************************************************/ + +uint8_t decrypt_byte(uint32_t *pkeys) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((uint32_t)(*(pkeys+2)) & 0xffff) | 2; + return (uint8_t)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +uint8_t update_keys(uint32_t *pkeys, const z_crc_t *pcrc_32_tab, int32_t c) +{ + (*(pkeys+0)) = (uint32_t)CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int32_t keyshift = (int32_t)((*(pkeys + 1)) >> 24); + (*(pkeys+2)) = (uint32_t)CRC32((*(pkeys+2)), keyshift); + } + return c; +} + +void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != 0) + { + update_keys(pkeys, pcrc_32_tab, *passwd); + passwd += 1; + } +} + +/***************************************************************************/ + +#ifndef NOCRYPT +int cryptrand(unsigned char *buf, unsigned int len) +{ +#ifdef _WIN32 + HCRYPTPROV provider; + unsigned __int64 pentium_tsc[1]; + int rlen = 0; + int result = 0; + + + if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + result = CryptGenRandom(provider, len, buf); + CryptReleaseContext(provider, 0); + if (result) + return len; + } + + for (rlen = 0; rlen < (int)len; ++rlen) + { + if (rlen % 8 == 0) + QueryPerformanceCounter((LARGE_INTEGER *)pentium_tsc); + buf[rlen] = ((unsigned char*)pentium_tsc)[rlen % 8]; + } + + return rlen; +#else + arc4random_buf(buf, len); + return len; +#endif +} + +int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys, + const z_crc_t *pcrc_32_tab, uint8_t verify1, uint8_t verify2) +{ + uint8_t n = 0; /* index in random header */ + uint8_t header[RAND_HEAD_LEN-2]; /* random header */ + uint16_t t = 0; /* temporary */ + + if (buf_size < RAND_HEAD_LEN) + return 0; + + init_keys(passwd, pkeys, pcrc_32_tab); + + /* First generate RAND_HEAD_LEN-2 random bytes. */ + cryptrand(header, RAND_HEAD_LEN-2); + + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + + for (n = 0; n < RAND_HEAD_LEN-2; n++) + buf[n] = (uint8_t)zencode(pkeys, pcrc_32_tab, header[n], t); + + buf[n++] = (uint8_t)zencode(pkeys, pcrc_32_tab, verify1, t); + buf[n++] = (uint8_t)zencode(pkeys, pcrc_32_tab, verify2, t); + return n; +} +#endif + +/***************************************************************************/ diff --git a/src/framework/core/minizip/crypt.h b/src/framework/core/minizip/crypt.h new file mode 100644 index 000000000..dbadb23ba --- /dev/null +++ b/src/framework/core/minizip/crypt.h @@ -0,0 +1,70 @@ +/* crypt.h -- base code for traditional PKWARE encryption + Version 1.2.0, September 16th, 2017 + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 1998-2005 Gilles Vollant + Modifications for Info-ZIP crypting + http://www.winimage.com/zLibDll/minizip.html + Copyright (C) 2003 Terry Thorsen + + This code is a modified version of crypting code in Info-ZIP distribution + + Copyright (C) 1990-2000 Info-ZIP. All rights reserved. + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _MINICRYPT_H +#define _MINICRYPT_H + +// Fix: legacy minizip v1.2 missing stdint.h for uint8_t/uint32_t/int32_t +// (upstream is archived, fix applied locally) +#include + +#if ZLIB_VERNUM < 0x1270 +typedef unsigned long z_crc_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define RAND_HEAD_LEN 12 + +/***************************************************************************/ + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab, c ^= decrypt_byte(pkeys))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t = decrypt_byte(pkeys), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +/***************************************************************************/ + +/* Return the next byte in the pseudo-random sequence */ +uint8_t decrypt_byte(uint32_t *pkeys); + +/* Update the encryption keys with the next byte of plain text */ +uint8_t update_keys(uint32_t *pkeys, const z_crc_t *pcrc_32_tab, int32_t c); + +/* Initialize the encryption keys and the random header according to the given password. */ +void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab); + +#ifndef NOCRYPT +/* Generate cryptographically secure random numbers */ +int cryptrand(unsigned char *buf, unsigned int len); + +/* Create encryption header */ +int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys, + const z_crc_t *pcrc_32_tab, uint8_t verify1, uint8_t verify2); +#endif + +/***************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/framework/core/minizip/ioapi.c b/src/framework/core/minizip/ioapi.c new file mode 100644 index 000000000..343197857 --- /dev/null +++ b/src/framework/core/minizip/ioapi.c @@ -0,0 +1,361 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + part of the MiniZip project + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson + http://result42.com + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#include +#include + +#if defined unix || defined __APPLE__ +#include +#include +#endif + +#include "ioapi.h" + +#if defined(_WIN32) +# define snprintf _snprintf +#endif + +voidpf call_zopen64(const zlib_filefunc64_32_def *pfilefunc, const void *filename, int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque, filename, mode); + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque, (const char*)filename, mode); +} + +voidpf call_zopendisk64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint32_t number_disk, int mode) +{ + if (pfilefunc->zfile_func64.zopendisk64_file != NULL) + return (*(pfilefunc->zfile_func64.zopendisk64_file)) (pfilefunc->zfile_func64.opaque, filestream, number_disk, mode); + return (*(pfilefunc->zopendisk32_file))(pfilefunc->zfile_func64.opaque, filestream, number_disk, mode); +} + +long call_zseek64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint64_t offset, int origin) +{ + uint32_t offset_truncated = 0; + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + offset_truncated = (uint32_t)offset; + if (offset_truncated != offset) + return -1; + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream, offset_truncated, origin); +} + +uint64_t call_ztell64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream) +{ + uint64_t position; + /* Fix: legacy minizip checked zseek64 instead of ztell64 (upstream archived) */ + if (pfilefunc->zfile_func64.ztell64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque, filestream); + position = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque, filestream); + if ((position) == UINT32_MAX) + return (uint64_t)-1; + return position; +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def *p_filefunc64_32, const zlib_filefunc_def *p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zfile_func64.zopendisk64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zopendisk32_file = p_filefunc32->zopendisk_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + +static voidpf ZCALLBACK fopen_file_func(ZIP_UNUSED voidpf opaque, const char *filename, int mode); +static uint32_t ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void* buf, uint32_t size); +static uint32_t ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void *buf, uint32_t size); +static uint64_t ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream); +static long ZCALLBACK fseek64_file_func(voidpf opaque, voidpf stream, uint64_t offset, int origin); +static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream); +static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream); + +typedef struct +{ + FILE *file; + int filenameLength; + void *filename; +} FILE_IOPOSIX; + +static voidpf file_build_ioposix(FILE *file, const char *filename) +{ + FILE_IOPOSIX *ioposix = NULL; + if (file == NULL) + return NULL; + /* Fix: legacy minizip missing null checks after malloc (upstream archived) */ + ioposix = (FILE_IOPOSIX*)malloc(sizeof(FILE_IOPOSIX)); + if (ioposix == NULL) + return NULL; + ioposix->file = file; + ioposix->filenameLength = (int)strlen(filename) + 1; + ioposix->filename = (char*)malloc(ioposix->filenameLength * sizeof(char)); + if (ioposix->filename == NULL) { + free(ioposix); + return NULL; + } + strncpy((char*)ioposix->filename, filename, ioposix->filenameLength); + return (voidpf)ioposix; +} + +static voidpf ZCALLBACK fopen_file_func(ZIP_UNUSED voidpf opaque, const char *filename, int mode) +{ + FILE* file = NULL; + const char *mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename != NULL) && (mode_fopen != NULL)) + { + file = fopen(filename, mode_fopen); + return file_build_ioposix(file, filename); + } + return file; +} + +static voidpf ZCALLBACK fopen64_file_func(ZIP_UNUSED voidpf opaque, const void *filename, int mode) +{ + FILE* file = NULL; + const char *mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename != NULL) && (mode_fopen != NULL)) + { + file = fopen64((const char*)filename, mode_fopen); + return file_build_ioposix(file, (const char*)filename); + } + return file; +} + +static voidpf ZCALLBACK fopendisk64_file_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) +{ + FILE_IOPOSIX *ioposix = NULL; + char *diskFilename = NULL; + voidpf ret = NULL; + int i = 0; + + if (stream == NULL) + return NULL; + ioposix = (FILE_IOPOSIX*)stream; + diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char)); + strncpy(diskFilename, (const char*)ioposix->filename, ioposix->filenameLength); + for (i = ioposix->filenameLength - 1; i >= 0; i -= 1) + { + if (diskFilename[i] != '.') + continue; + snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02u", number_disk + 1); + break; + } + if (i >= 0) + ret = fopen64_file_func(opaque, diskFilename, mode); + free(diskFilename); + return ret; +} + +static voidpf ZCALLBACK fopendisk_file_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) +{ + FILE_IOPOSIX *ioposix = NULL; + char *diskFilename = NULL; + voidpf ret = NULL; + int i = 0; + + if (stream == NULL) + return NULL; + ioposix = (FILE_IOPOSIX*)stream; + diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char)); + strncpy(diskFilename, (const char*)ioposix->filename, ioposix->filenameLength); + for (i = ioposix->filenameLength - 1; i >= 0; i -= 1) + { + if (diskFilename[i] != '.') + continue; + snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02u", number_disk + 1); + break; + } + if (i >= 0) + ret = fopen_file_func(opaque, diskFilename, mode); + free(diskFilename); + return ret; +} + +static uint32_t ZCALLBACK fread_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, void* buf, uint32_t size) +{ + FILE_IOPOSIX *ioposix = NULL; + uint32_t read = (uint32_t)-1; + if (stream == NULL) + return read; + ioposix = (FILE_IOPOSIX*)stream; + read = (uint32_t)fread(buf, 1, (size_t)size, ioposix->file); + return read; +} + +static uint32_t ZCALLBACK fwrite_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, const void *buf, uint32_t size) +{ + FILE_IOPOSIX *ioposix = NULL; + uint32_t written = (uint32_t)-1; + if (stream == NULL) + return written; + ioposix = (FILE_IOPOSIX*)stream; + written = (uint32_t)fwrite(buf, 1, (size_t)size, ioposix->file); + return written; +} + +static long ZCALLBACK ftell_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + long ret = -1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + ret = ftell(ioposix->file); + return ret; +} + +static uint64_t ZCALLBACK ftell64_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + uint64_t ret = (uint64_t)-1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + ret = ftello64(ioposix->file); + return ret; +} + +static long ZCALLBACK fseek_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint32_t offset, int origin) +{ + FILE_IOPOSIX *ioposix = NULL; + int fseek_origin = 0; + long ret = 0; + + if (stream == NULL) + return -1; + ioposix = (FILE_IOPOSIX*)stream; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END: + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET: + fseek_origin = SEEK_SET; + break; + default: + return -1; + } + if (fseek(ioposix->file, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint64_t offset, int origin) +{ + FILE_IOPOSIX *ioposix = NULL; + int fseek_origin = 0; + long ret = 0; + + if (stream == NULL) + return -1; + ioposix = (FILE_IOPOSIX*)stream; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END: + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET: + fseek_origin = SEEK_SET; + break; + default: + return -1; + } + + if (fseeko64(ioposix->file, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + +static int ZCALLBACK fclose_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + int ret = -1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + if (ioposix->filename != NULL) + free(ioposix->filename); + ret = fclose(ioposix->file); + free(ioposix); + return ret; +} + +static int ZCALLBACK ferror_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + int ret = -1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + ret = ferror(ioposix->file); + return ret; +} + +void fill_fopen_filefunc(zlib_filefunc_def *pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zopendisk_file = fopendisk_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zopendisk64_file = fopendisk64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/framework/core/minizip/ioapi.h b/src/framework/core/minizip/ioapi.h new file mode 100644 index 000000000..b4a0d461d --- /dev/null +++ b/src/framework/core/minizip/ioapi.h @@ -0,0 +1,153 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support + http://result42.com + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#include +#include +#include + +#include "zlib.h" + +#ifdef __GNUC__ +# define ZIP_UNUSED __attribute__((__unused__)) +#else +# define ZIP_UNUSED +#endif + +#if defined(USE_FILE32API) +# define fopen64 fopen +# define ftello64 ftell +# define fseeko64 fseek +#else +# if defined(_MSC_VER) +# define fopen64 fopen +# if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) +# define ftello64 _ftelli64 +# define fseeko64 _fseeki64 +# else /* old MSC */ +# define ftello64 ftell +# define fseeko64 fseek +# endif +# else +# define fopen64 fopen +# define ftello64 ftello +# define fseeko64 fseeko +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + +#ifndef ZCALLBACK +# if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || \ + defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +# define ZCALLBACK CALLBACK +# else +# define ZCALLBACK +# endif +#endif + +typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char *filename, int mode); +typedef voidpf (ZCALLBACK *opendisk_file_func) (voidpf opaque, voidpf stream, uint32_t number_disk, int mode); +typedef uint32_t (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uint32_t size); +typedef uint32_t (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void *buf, uint32_t size); +typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); +typedef int (ZCALLBACK *error_file_func) (voidpf opaque, voidpf stream); + +typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uint32_t offset, int origin); + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + opendisk_file_func zopendisk_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + error_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef uint64_t (ZCALLBACK *tell64_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, uint64_t offset, int origin); +typedef voidpf (ZCALLBACK *open64_file_func) (voidpf opaque, const void *filename, int mode); +typedef voidpf (ZCALLBACK *opendisk64_file_func)(voidpf opaque, voidpf stream, uint32_t number_disk, int mode); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + opendisk64_file_func zopendisk64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + error_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen_filefunc(zlib_filefunc_def *pzlib_filefunc_def); +void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + opendisk_file_func zopendisk32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +/*#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))*/ +/*#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode))*/ +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64(const zlib_filefunc64_32_def *pfilefunc,const void*filename, int mode); +voidpf call_zopendisk64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint32_t number_disk, int mode); +long call_zseek64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint64_t offset, int origin); +uint64_t call_ztell64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def *p_filefunc64_32, const zlib_filefunc_def *p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZOPENDISK64(filefunc,filestream,diskn,mode) (call_zopendisk64((&(filefunc)),(filestream),(diskn),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/framework/core/minizip/ioapi_mem.c b/src/framework/core/minizip/ioapi_mem.c new file mode 100644 index 000000000..4d4d36a95 --- /dev/null +++ b/src/framework/core/minizip/ioapi_mem.c @@ -0,0 +1,174 @@ +/* ioapi_mem.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + This version of ioapi is designed to access memory rather than files. + We do use a region of memory to put data in to and take it out of. We do + not have auto-extending buffers and do not inform anyone else that the + data has been written. It is really intended for accessing a zip archive + embedded in an application such that I can write an installer with no + external files. Creation of archives has not been attempted, although + parts of the framework are present. + + Based on Unzip ioapi.c version 0.22, May 19th, 2003 + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 2003 Justin Fletcher + Copyright (C) 1998-2003 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This file is under the same license as the Unzip tool it is distributed + with. +*/ + + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + +#include "ioapi_mem.h" + +#ifndef IOMEM_BUFFERSIZE +# define IOMEM_BUFFERSIZE (UINT16_MAX) +#endif + +voidpf ZCALLBACK fopen_mem_func(voidpf opaque, ZIP_UNUSED const char *filename, int mode) +{ + ourmemory_t *mem = (ourmemory_t *)opaque; + if (mem == NULL) + return NULL; /* Mem structure passed in was null */ + + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + if (mem->grow) + { + mem->size = IOMEM_BUFFERSIZE; + mem->base = (char *)malloc(mem->size); + // Fix: legacy minizip missing null check after malloc (upstream archived) + if (mem->base == NULL) + return NULL; + } + + mem->limit = 0; /* When writing we start with 0 bytes written */ + } + else + mem->limit = mem->size; + + mem->cur_offset = 0; + + return mem; +} + +voidpf ZCALLBACK fopendisk_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream, ZIP_UNUSED uint32_t number_disk, ZIP_UNUSED int mode) +{ + /* Not used */ + return NULL; +} + +uint32_t ZCALLBACK fread_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, void *buf, uint32_t size) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + + if (size > mem->size - mem->cur_offset) + size = mem->size - mem->cur_offset; + + memcpy(buf, mem->base + mem->cur_offset, size); + mem->cur_offset += size; + + return size; +} + +uint32_t ZCALLBACK fwrite_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, const void *buf, uint32_t size) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + uint32_t newmemsize = 0; + char *newbase = NULL; + + if (size > mem->size - mem->cur_offset) + { + if (mem->grow) + { + newmemsize = mem->size; + if (size < IOMEM_BUFFERSIZE) + newmemsize += IOMEM_BUFFERSIZE; + else + newmemsize += size; + newbase = (char *)malloc(newmemsize); + // Fix: legacy minizip v1.2 missing null check after malloc + // (upstream is archived, fix applied locally) + if (newbase == NULL) + return 0; + memcpy(newbase, mem->base, mem->size); + free(mem->base); + mem->base = newbase; + mem->size = newmemsize; + } + else + size = mem->size - mem->cur_offset; + } + memcpy(mem->base + mem->cur_offset, buf, size); + mem->cur_offset += size; + if (mem->cur_offset > mem->limit) + mem->limit = mem->cur_offset; + + return size; +} + +long ZCALLBACK ftell_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + return mem->cur_offset; +} + +long ZCALLBACK fseek_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint32_t offset, int origin) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + uint32_t new_pos = 0; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + new_pos = mem->cur_offset + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + new_pos = mem->limit + offset; + break; + case ZLIB_FILEFUNC_SEEK_SET: + new_pos = offset; + break; + default: + return -1; + } + + if (new_pos > mem->size) + return 1; /* Failed to seek that far */ + mem->cur_offset = new_pos; + return 0; +} + +int ZCALLBACK fclose_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream) +{ + /* Even with grow = 1, caller must always free() memory */ + return 0; +} + +int ZCALLBACK ferror_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream) +{ + /* We never return errors */ + return 0; +} + +void fill_memory_filefunc(zlib_filefunc_def *pzlib_filefunc_def, ourmemory_t *ourmem) +{ + pzlib_filefunc_def->zopen_file = fopen_mem_func; + pzlib_filefunc_def->zopendisk_file = fopendisk_mem_func; + pzlib_filefunc_def->zread_file = fread_mem_func; + pzlib_filefunc_def->zwrite_file = fwrite_mem_func; + pzlib_filefunc_def->ztell_file = ftell_mem_func; + pzlib_filefunc_def->zseek_file = fseek_mem_func; + pzlib_filefunc_def->zclose_file = fclose_mem_func; + pzlib_filefunc_def->zerror_file = ferror_mem_func; + pzlib_filefunc_def->opaque = ourmem; +} diff --git a/src/framework/core/minizip/ioapi_mem.h b/src/framework/core/minizip/ioapi_mem.h new file mode 100644 index 000000000..7061d6fae --- /dev/null +++ b/src/framework/core/minizip/ioapi_mem.h @@ -0,0 +1,52 @@ +/* ioapi_mem.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + This version of ioapi is designed to access memory rather than files. + We do use a region of memory to put data in to and take it out of. + + Copyright (C) 2012-2017 Nathan Moinvaziri (https://github.com/nmoinvaz/minizip) + (C) 2003 Justin Fletcher + (C) 1998-2003 Gilles Vollant + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _IOAPI_MEM_H +#define _IOAPI_MEM_H + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +voidpf ZCALLBACK fopen_mem_func(voidpf opaque, const char* filename, int mode); +voidpf ZCALLBACK fopendisk_mem_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode); +uint32_t ZCALLBACK fread_mem_func(voidpf opaque, voidpf stream, void* buf, uint32_t size); +uint32_t ZCALLBACK fwrite_mem_func(voidpf opaque, voidpf stream, const void* buf, uint32_t size); +long ZCALLBACK ftell_mem_func(voidpf opaque, voidpf stream); +long ZCALLBACK fseek_mem_func(voidpf opaque, voidpf stream, uint32_t offset, int origin); +int ZCALLBACK fclose_mem_func(voidpf opaque, voidpf stream); +int ZCALLBACK ferror_mem_func(voidpf opaque, voidpf stream); + +typedef struct ourmemory_s { + char *base; /* Base of the region of memory we're using */ + uint32_t size; /* Size of the region of memory we're using */ + uint32_t limit; /* Furthest we've written */ + uint32_t cur_offset; /* Current offset in the area */ + int grow; /* Growable memory buffer */ +} ourmemory_t; + +void fill_memory_filefunc(zlib_filefunc_def* pzlib_filefunc_def, ourmemory_t *ourmem); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/framework/core/minizip/unzip.c b/src/framework/core/minizip/unzip.c new file mode 100644 index 000000000..c439ed271 --- /dev/null +++ b/src/framework/core/minizip/unzip.c @@ -0,0 +1,1990 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.2.0, September 16th, 2017 + part of the MiniZip project + + Copyright (C) 2010-2017 Nathan Moinvaziri + Modifications for AES, PKWARE disk spanning + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support on both zip and unzip + http://result42.com + Copyright (C) 2007-2008 Even Rouault + Modifications of Unzip for Zip64 + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#include +#include +#include +#include +#include +#include + +#include "zlib.h" +#include "unzip.h" + +#ifdef HAVE_AES +# define AES_METHOD (99) +# define AES_PWVERIFYSIZE (2) +# define AES_MAXSALTLENGTH (16) +# define AES_AUTHCODESIZE (10) +# define AES_HEADERSIZE (11) +# define AES_KEYSIZE(mode) (64 + (mode * 64)) + +# include "aes/aes.h" +# include "aes/fileenc.h" +#endif +#ifdef HAVE_APPLE_COMPRESSION +# include +#endif + +#ifndef NOUNCRYPT +# include "crypt.h" +#endif + +#define DISKHEADERMAGIC (0x08074b50) +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x06064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x07064b50) + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZECENTRALHEADERLOCATOR (0x14) +#define SIZEZIPLOCALHEADER (0x1e) + +#ifndef BUFREADCOMMENT +# define BUFREADCOMMENT (0x400) +#endif + +#ifndef UNZ_BUFSIZE +# define UNZ_BUFSIZE (UINT16_MAX) +#endif +#ifndef UNZ_MAXFILENAMEINZIP +# define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +const char unz_copyright[] = " unzip 1.2.0 Copyright 1998-2017 - https://github.com/nmoinvaz/minizip"; + +/* unz_file_info_internal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + uint64_t offset_curfile; /* relative offset of local header 8 bytes */ + uint64_t byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx) */ +#ifdef HAVE_AES + uint8_t aes_encryption_mode; + uint16_t aes_compression_method; + uint16_t aes_version; +#endif +} unz_file_info64_internal; + +/* file_in_zip_read_info_s contain internal information about a file in zipfile */ +typedef struct +{ + uint8_t *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif +#ifdef HAVE_APPLE_COMPRESSION + compression_stream astream; /* libcompression stream structure */ +#endif +#ifdef HAVE_AES + fcrypt_ctx aes_ctx; +#endif + uint64_t pos_in_zipfile; /* position in byte on the zipfile, for fseek */ + uint8_t stream_initialised; /* flag set if stream structure is initialised */ + + uint64_t offset_local_extrafield; /* offset of the local extra field */ + uint16_t size_local_extrafield; /* size of the local extra field */ + uint64_t pos_local_extrafield; /* position in the local extra field in read */ + uint64_t total_out_64; + + uint32_t crc32; /* crc32 of all data uncompressed */ + uint32_t crc32_expected; /* crc32 we must obtain after decompress all */ + uint64_t rest_read_compressed; /* number of byte to be decompressed */ + uint64_t rest_read_uncompressed; /* number of byte to be obtained after decomp */ + + zlib_filefunc64_32_def z_filefunc; + + voidpf filestream; /* io structore of the zipfile */ + uint16_t compression_method; /* compression method (0==store) */ + uint64_t byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx) */ + int raw; +} file_in_zip64_read_info_s; + +/* unz64_s contain internal information about the zipfile */ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + + voidpf filestream; /* io structure of the current zipfile */ + voidpf filestream_with_CD; /* io structure of the disk with the central directory */ + + unz_global_info64 gi; /* public global information */ + + uint64_t byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx) */ + uint64_t num_file; /* number of the current file in the zipfile */ + uint64_t pos_in_central_dir; /* pos of the current file in the central dir */ + uint64_t current_file_ok; /* flag about the usability of the current file */ + uint64_t central_pos; /* position of the beginning of the central dir */ + uint32_t number_disk; /* number of the current disk, used for spanning ZIP */ + uint64_t size_central_dir; /* size of the central directory */ + uint64_t offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; + /* private info about it*/ + file_in_zip64_read_info_s *pfile_in_zip_read; + /* structure about the current file if we are decompressing it */ + int is_zip64; /* is the current file zip64 */ +#ifndef NOUNCRYPT + uint32_t keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t *pcrc_32_tab; +#endif +} unz64_internal; + +/* Read a byte from a gz_stream; Return EOF for end of file. */ +static int unzReadUInt8(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint8_t *value) +{ + uint8_t c = 0; + if (ZREAD64(*pzlib_filefunc_def, filestream, &c, 1) == 1) + { + *value = (uint8_t)c; + return UNZ_OK; + } + *value = 0; + if (ZERROR64(*pzlib_filefunc_def, filestream)) + return UNZ_ERRNO; + return UNZ_EOF; +} + +static int unzReadUInt16(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint16_t *value) +{ + uint16_t x; + uint8_t c = 0; + int err = UNZ_OK; + + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x = (uint16_t)c; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x |= ((uint16_t)c) << 8; + + if (err == UNZ_OK) + *value = x; + else + *value = 0; + return err; +} + +static int unzReadUInt32(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint32_t *value) +{ + uint32_t x = 0; + uint8_t c = 0; + int err = UNZ_OK; + + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x = (uint32_t)c; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x |= ((uint32_t)c) << 8; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x |= ((uint32_t)c) << 16; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint32_t)c) << 24; + + if (err == UNZ_OK) + *value = x; + else + *value = 0; + return err; +} + +static int unzReadUInt64(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint64_t *value) +{ + uint64_t x = 0; + uint8_t i = 0; + int err = UNZ_OK; + + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x = (uint64_t)i; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 8; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 16; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 24; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 32; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 40; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 48; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 56; + + if (err == UNZ_OK) + *value = x; + else + *value = 0; + return err; +} + +/* Locate the Central directory of a zip file (at the end, just before the global comment) */ +static int unzSearchCentralDir(const zlib_filefunc64_32_def *pzlib_filefunc_def, uint64_t *pos_found, voidpf filestream) +{ + uint8_t buf[BUFREADCOMMENT + 4]; + uint64_t file_size = 0; + uint64_t back_read = 4; + uint64_t max_back = UINT16_MAX; /* maximum size of global comment */ + uint32_t read_size = 0; + uint64_t read_pos = 0; + uint32_t i = 0; + *pos_found = 0; + + if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0) + return UNZ_ERRNO; + + file_size = ZTELL64(*pzlib_filefunc_def, filestream); + + if (max_back > file_size) + max_back = file_size; + + while (back_read < max_back) + { + if (back_read + BUFREADCOMMENT > max_back) + back_read = max_back; + else + back_read += BUFREADCOMMENT; + + read_pos = file_size - back_read; + read_size = ((BUFREADCOMMENT + 4) < (file_size - read_pos)) ? + (BUFREADCOMMENT + 4) : (uint32_t)(file_size - read_pos); + + if (ZSEEK64(*pzlib_filefunc_def, filestream, read_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + break; + if (ZREAD64(*pzlib_filefunc_def, filestream, buf, read_size) != read_size) + break; + + for (i = read_size - 3; (i--) > 0;) + if (((*(buf+i)) == (ENDHEADERMAGIC & 0xff)) && + ((*(buf+i+1)) == (ENDHEADERMAGIC >> 8 & 0xff)) && + ((*(buf+i+2)) == (ENDHEADERMAGIC >> 16 & 0xff)) && + ((*(buf+i+3)) == (ENDHEADERMAGIC >> 24 & 0xff))) + { + *pos_found = read_pos+i; + return UNZ_OK; + } + } + + return UNZ_ERRNO; +} + +/* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */ +static int unzSearchCentralDir64(const zlib_filefunc64_32_def *pzlib_filefunc_def, uint64_t *offset, voidpf filestream, + const uint64_t endcentraloffset) +{ + uint32_t value32 = 0; + *offset = 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def, filestream, endcentraloffset - SIZECENTRALHEADERLOCATOR, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + /* Read locator signature */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + if (value32 != ZIP64ENDLOCHEADERMAGIC) + return UNZ_ERRNO; + /* Number of the disk with the start of the zip64 end of central directory */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + /* Relative offset of the zip64 end of central directory record */ + if (unzReadUInt64(pzlib_filefunc_def, filestream, offset) != UNZ_OK) + return UNZ_ERRNO; + /* Total number of disks */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def, filestream, *offset, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + /* The signature */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + if (value32 != ZIP64ENDHEADERMAGIC) + return UNZ_ERRNO; + + return UNZ_OK; +} + +static unzFile unzOpenInternal(const void *path, zlib_filefunc64_32_def *pzlib_filefunc64_32_def) +{ + unz64_internal us; + unz64_internal *s = NULL; + uint64_t central_pos = 0; + uint64_t central_pos64 = 0; + uint64_t number_entry_CD = 0; + uint16_t value16 = 0; + uint32_t value32 = 0; + uint64_t value64 = 0; + voidpf filestream = NULL; + int err = UNZ_OK; + + us.filestream = NULL; + us.filestream_with_CD = NULL; + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + + if (pzlib_filefunc64_32_def == NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + + us.filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); + + if (us.filestream == NULL) + return NULL; + + us.filestream_with_CD = us.filestream; + us.is_zip64 = 0; + + /* Search for end of central directory header */ + err = unzSearchCentralDir(&us.z_filefunc, ¢ral_pos, us.filestream); + if (err == UNZ_OK) + { + if (ZSEEK64(us.z_filefunc, us.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* The signature, already checked */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + /* Number of this disk */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + us.number_disk = value16; + /* Number of the disk with the start of the central directory */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + us.gi.number_disk_with_CD = value16; + /* Total number of entries in the central directory on this disk */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + us.gi.number_entry = value16; + /* Total number of entries in the central directory */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + number_entry_CD = value16; + if (number_entry_CD != us.gi.number_entry) + err = UNZ_BADZIPFILE; + /* Size of the central directory */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + us.size_central_dir = value32; + /* Offset of start of central directory with respect to the starting disk number */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + us.offset_central_dir = value32; + /* Zipfile comment length */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &us.gi.size_comment) != UNZ_OK) + err = UNZ_ERRNO; + + if (err == UNZ_OK) + { + /* Search for Zip64 end of central directory header */ + int err64 = unzSearchCentralDir64(&us.z_filefunc, ¢ral_pos64, us.filestream, central_pos); + if (err64 == UNZ_OK) + { + central_pos = central_pos64; + us.is_zip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* the signature, already checked */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + /* size of zip64 end of central directory record */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &value64) != UNZ_OK) + err = UNZ_ERRNO; + /* version made by */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + /* version needed to extract */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + /* number of this disk */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &us.number_disk) != UNZ_OK) + err = UNZ_ERRNO; + /* number of the disk with the start of the central directory */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &us.gi.number_disk_with_CD) != UNZ_OK) + err = UNZ_ERRNO; + /* total number of entries in the central directory on this disk */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.gi.number_entry) != UNZ_OK) + err = UNZ_ERRNO; + /* total number of entries in the central directory */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &number_entry_CD) != UNZ_OK) + err = UNZ_ERRNO; + if (number_entry_CD != us.gi.number_entry) + err = UNZ_BADZIPFILE; + /* size of the central directory */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.size_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + /* offset of start of central directory with respect to the starting disk number */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.offset_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + } + else if ((us.gi.number_entry == UINT16_MAX) || (us.size_central_dir == UINT16_MAX) || (us.offset_central_dir == UINT32_MAX)) + err = UNZ_BADZIPFILE; + } + } + else + err = UNZ_ERRNO; + + if ((err == UNZ_OK) && (central_pos < us.offset_central_dir + us.size_central_dir)) + err = UNZ_BADZIPFILE; + + if (err != UNZ_OK) + { + ZCLOSE64(us.z_filefunc, us.filestream); + return NULL; + } + + if (us.gi.number_disk_with_CD == 0) + { + /* If there is only one disk open another stream so we don't have to seek between the CD + and the file headers constantly */ + filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); + if (filestream != NULL) + us.filestream = filestream; + } + + /* Hack for zip files that have no respect for zip64 + if ((central_pos > 0xffffffff) && (us.offset_central_dir < 0xffffffff)) + us.offset_central_dir = central_pos - us.size_central_dir;*/ + + us.byte_before_the_zipfile = central_pos - (us.offset_central_dir + us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + + s = (unz64_internal*)ALLOC(sizeof(unz64_internal)); + if (s != NULL) + { + *s = us; + unzGoToFirstFile((unzFile)s); + } + return (unzFile)s; +} + +extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def *pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill, pzlib_filefunc32_def); + return unzOpenInternal(path, &zlib_filefunc64_32_def_fill); + } + return unzOpenInternal(path, NULL); +} + +extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def *pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return unzOpenInternal(path, &zlib_filefunc64_32_def_fill); + } + return unzOpenInternal(path, NULL); +} + +extern unzFile ZEXPORT unzOpen(const char *path) +{ + return unzOpenInternal(path, NULL); +} + +extern unzFile ZEXPORT unzOpen64(const void *path) +{ + return unzOpenInternal(path, NULL); +} + +extern int ZEXPORT unzClose(unzFile file) +{ + unz64_internal *s; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if ((s->filestream != NULL) && (s->filestream != s->filestream_with_CD)) + ZCLOSE64(s->z_filefunc, s->filestream); + if (s->filestream_with_CD != NULL) + ZCLOSE64(s->z_filefunc, s->filestream_with_CD); + + s->filestream = NULL; + s->filestream_with_CD = NULL; + TRYFREE(s); + return UNZ_OK; +} + +/* Goto to the next available disk for spanned archives */ +static int unzGoToNextDisk(unzFile file) +{ + unz64_internal *s; + uint32_t number_disk_next = 0; + + s = (unz64_internal*)file; + if (s == NULL) + return UNZ_PARAMERROR; + number_disk_next = s->number_disk; + + if ((s->pfile_in_zip_read != NULL) && (s->pfile_in_zip_read->rest_read_uncompressed > 0)) + /* We are currently reading a file and we need the next sequential disk */ + number_disk_next += 1; + else + /* Goto the disk for the current file */ + number_disk_next = s->cur_file_info.disk_num_start; + + if (number_disk_next != s->number_disk) + { + /* Switch disks */ + if ((s->filestream != NULL) && (s->filestream != s->filestream_with_CD)) + ZCLOSE64(s->z_filefunc, s->filestream); + + if (number_disk_next == s->gi.number_disk_with_CD) + { + s->filestream = s->filestream_with_CD; + } + else + { + s->filestream = ZOPENDISK64(s->z_filefunc, s->filestream_with_CD, number_disk_next, + ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); + } + + if (s->filestream == NULL) + return UNZ_ERRNO; + + s->number_disk = number_disk_next; + } + + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info* pglobal_info32) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + pglobal_info32->number_entry = (uint32_t)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + pglobal_info32->number_disk_with_CD = s->gi.number_disk_with_CD; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + *pglobal_info = s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalComment(unzFile file, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + uint16_t bytes_to_read = comment_size; + if (file == NULL) + return (int)UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (bytes_to_read > s->gi.size_comment) + bytes_to_read = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, s->central_pos + 22, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (bytes_to_read > 0) + { + *comment = 0; + if (ZREAD64(s->z_filefunc, s->filestream_with_CD, comment, bytes_to_read) != bytes_to_read) + return UNZ_ERRNO; + } + + if ((comment != NULL) && (comment_size > s->gi.size_comment)) + *(comment + s->gi.size_comment) = 0; + + return (int)bytes_to_read; +} + +static int unzGetCurrentFileInfoField(unzFile file, uint32_t *seek, void *field, uint16_t field_size, uint16_t size_file_field, int null_terminated_field) +{ + unz64_internal *s = NULL; + uint32_t bytes_to_read = 0; + int err = UNZ_OK; + + if (file == NULL) + return (int)UNZ_PARAMERROR; + s = (unz64_internal*)file; + + /* Read field */ + if (field != NULL) + { + if (size_file_field < field_size) + { + if (null_terminated_field) + *((char *)field+size_file_field) = 0; + + bytes_to_read = size_file_field; + } + else + bytes_to_read = field_size; + + if (*seek != 0) + { + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, *seek, ZLIB_FILEFUNC_SEEK_CUR) == 0) + *seek = 0; + else + err = UNZ_ERRNO; + } + + if ((size_file_field > 0) && (field_size > 0)) + { + if (ZREAD64(s->z_filefunc, s->filestream_with_CD, field, bytes_to_read) != bytes_to_read) + err = UNZ_ERRNO; + } + *seek += size_file_field - bytes_to_read; + } + else + { + *seek += size_file_field; + } + + return err; +} + +/* Get info about the current file in the zipfile, with internal only info */ +static int unzGetCurrentFileInfoInternal(unzFile file, unz_file_info64 *pfile_info, + unz_file_info64_internal *pfile_info_internal, char *filename, uint16_t filename_size, void *extrafield, + uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + uint32_t magic = 0; + uint64_t current_pos = 0; + uint32_t seek = 0; + uint32_t extra_pos = 0; + uint16_t extra_header_id = 0; + uint16_t extra_data_size = 0; + uint16_t value16 = 0; + uint32_t value32 = 0; + uint64_t value64 = 0; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, + s->pos_in_central_dir + s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* Check the magic */ + if (err == UNZ_OK) + { + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &magic) != UNZ_OK) + err = UNZ_ERRNO; + else if (magic != CENTRALHEADERMAGIC) + err = UNZ_BADZIPFILE; + } + + /* Read central directory header */ + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.version) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.version_needed) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.flag) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.compression_method) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.dos_date) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.crc) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK) + err = UNZ_ERRNO; + file_info.compressed_size = value32; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK) + err = UNZ_ERRNO; + file_info.uncompressed_size = value32; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_filename) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_file_extra) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_file_comment) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK) + err = UNZ_ERRNO; + file_info.disk_num_start = value16; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.internal_fa) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.external_fa) != UNZ_OK) + err = UNZ_ERRNO; + /* Relative offset of local header */ + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK) + err = UNZ_ERRNO; + + file_info.size_file_extra_internal = 0; + file_info.disk_offset = value32; + file_info_internal.offset_curfile = value32; +#ifdef HAVE_AES + file_info_internal.aes_compression_method = 0; + file_info_internal.aes_encryption_mode = 0; + file_info_internal.aes_version = 0; +#endif + + if (err == UNZ_OK) + err = unzGetCurrentFileInfoField(file, &seek, filename, filename_size, file_info.size_filename, 1); + + /* Read extrafield */ + if (err == UNZ_OK) + err = unzGetCurrentFileInfoField(file, &seek, extrafield, extrafield_size, file_info.size_file_extra, 0); + + if ((err == UNZ_OK) && (file_info.size_file_extra != 0)) + { + if (seek != 0) + { + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, seek, ZLIB_FILEFUNC_SEEK_CUR) == 0) + seek = 0; + else + err = UNZ_ERRNO; + } + + /* We are going to parse the extra field so we need to move back */ + current_pos = ZTELL64(s->z_filefunc, s->filestream_with_CD); + if (current_pos < file_info.size_file_extra) + err = UNZ_ERRNO; + current_pos -= file_info.size_file_extra; + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, current_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + while ((err != UNZ_ERRNO) && (extra_pos < file_info.size_file_extra)) + { + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &extra_header_id) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &extra_data_size) != UNZ_OK) + err = UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (extra_header_id == 0x0001) + { + /* Subtract size of ZIP64 field, since ZIP64 is handled internally */ + file_info.size_file_extra_internal += 2 + 2 + extra_data_size; + + if (file_info.uncompressed_size == UINT32_MAX) + { + if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &file_info.uncompressed_size) != UNZ_OK) + err = UNZ_ERRNO; + } + if (file_info.compressed_size == UINT32_MAX) + { + if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &file_info.compressed_size) != UNZ_OK) + err = UNZ_ERRNO; + } + if (file_info_internal.offset_curfile == UINT32_MAX) + { + /* Relative Header offset */ + if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &value64) != UNZ_OK) + err = UNZ_ERRNO; + file_info_internal.offset_curfile = value64; + file_info.disk_offset = value64; + } + /* Fix: central directory reads disk_num_start as 16-bit, sentinel is 0xFFFF not UINT32_MAX */ + if (file_info.disk_num_start == UINT16_MAX) + { + /* Disk Start Number */ + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.disk_num_start) != UNZ_OK) + err = UNZ_ERRNO; + } + } +#ifdef HAVE_AES + /* AES header */ + else if (extra_header_id == 0x9901) + { + uint8_t value8 = 0; + + /* Subtract size of AES field, since AES is handled internally */ + file_info.size_file_extra_internal += 2 + 2 + extra_data_size; + + /* Verify version info */ + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK) + err = UNZ_ERRNO; + /* Support AE-1 and AE-2 */ + if (value16 != 1 && value16 != 2) + err = UNZ_ERRNO; + file_info_internal.aes_version = value16; + if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK) + err = UNZ_ERRNO; + if ((char)value8 != 'A') + err = UNZ_ERRNO; + if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK) + err = UNZ_ERRNO; + if ((char)value8 != 'E') + err = UNZ_ERRNO; + /* Get AES encryption strength and actual compression method */ + if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK) + err = UNZ_ERRNO; + file_info_internal.aes_encryption_mode = value8; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK) + err = UNZ_ERRNO; + file_info_internal.aes_compression_method = value16; + } +#endif + else + { + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD,extra_data_size, ZLIB_FILEFUNC_SEEK_CUR) != 0) + err = UNZ_ERRNO; + } + + extra_pos += 2 + 2 + extra_data_size; + } + } + + if (file_info.disk_num_start == s->gi.number_disk_with_CD) + file_info_internal.byte_before_the_zipfile = s->byte_before_the_zipfile; + else + file_info_internal.byte_before_the_zipfile = 0; + + if (err == UNZ_OK) + err = unzGetCurrentFileInfoField(file, &seek, comment, comment_size, file_info.size_file_comment, 1); + + if ((err == UNZ_OK) && (pfile_info != NULL)) + *pfile_info = file_info; + if ((err == UNZ_OK) && (pfile_info_internal != NULL)) + *pfile_info_internal = file_info_internal; + + return err; +} + +extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz_file_info64 file_info64; + int err = UNZ_OK; + + err = unzGetCurrentFileInfoInternal(file, &file_info64, NULL, filename, filename_size, + extrafield, extrafield_size, comment, comment_size); + + if ((err == UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dos_date = file_info64.dos_date; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra - file_info64.size_file_extra_internal; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = (uint16_t)file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->compressed_size = (uint32_t)file_info64.compressed_size; + pfile_info->uncompressed_size = (uint32_t)file_info64.uncompressed_size; + } + return err; +} + +extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 * pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + return unzGetCurrentFileInfoInternal(file, pfile_info, NULL, filename, filename_size, + extrafield, extrafield_size, comment,comment_size); +} + +/* Read the local header of the current zipfile. Check the coherency of the local header and info in the + end of central directory about this file store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) */ +static int unzCheckCurrentFileCoherencyHeader(unz64_internal *s, uint32_t *psize_variable, uint64_t *poffset_local_extrafield, + uint16_t *psize_local_extrafield) +{ + uint32_t magic = 0; + uint16_t value16 = 0; + uint32_t value32 = 0; + uint32_t flags = 0; + uint16_t size_filename = 0; + uint16_t size_extra_field = 0; + uint16_t compression_method = 0; + int err = UNZ_OK; + + if (psize_variable == NULL) + return UNZ_PARAMERROR; + *psize_variable = 0; + if (poffset_local_extrafield == NULL) + return UNZ_PARAMERROR; + *poffset_local_extrafield = 0; + if (psize_local_extrafield == NULL) + return UNZ_PARAMERROR; + *psize_local_extrafield = 0; + + err = unzGoToNextDisk((unzFile)s); + if (err != UNZ_OK) + return err; + + if (ZSEEK64(s->z_filefunc, s->filestream, s->cur_file_info_internal.offset_curfile + + s->cur_file_info_internal.byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (err == UNZ_OK) + { + if (unzReadUInt32(&s->z_filefunc, s->filestream, &magic) != UNZ_OK) + err = UNZ_ERRNO; + else if (magic != LOCALHEADERMAGIC) + err = UNZ_BADZIPFILE; + } + + if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + flags = value16; + if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (value16 != s->cur_file_info.compression_method)) + err = UNZ_BADZIPFILE; + + compression_method = s->cur_file_info.compression_method; +#ifdef HAVE_AES + if (compression_method == AES_METHOD) + compression_method = s->cur_file_info_internal.aes_compression_method; +#endif + + if ((err == UNZ_OK) && (compression_method != 0) && (compression_method != Z_DEFLATED)) + { +#ifdef HAVE_BZIP2 + if (compression_method != Z_BZIP2ED) +#endif + err = UNZ_BADZIPFILE; + } + + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* date/time */ + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* crc */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (value32 != s->cur_file_info.crc) && ((flags & 8) == 0)) + err = UNZ_BADZIPFILE; + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* size compr */ + err = UNZ_ERRNO; + else if ((value32 != UINT32_MAX) && (err == UNZ_OK) && (value32 != s->cur_file_info.compressed_size) && ((flags & 8) == 0)) + err = UNZ_BADZIPFILE; + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* size uncompr */ + err = UNZ_ERRNO; + else if ((value32 != UINT32_MAX) && (err == UNZ_OK) && (value32 != s->cur_file_info.uncompressed_size) && ((flags & 8) == 0)) + err = UNZ_BADZIPFILE; + if (unzReadUInt16(&s->z_filefunc, s->filestream, &size_filename) != UNZ_OK) + err = UNZ_ERRNO; + + *psize_variable += size_filename; + + if (unzReadUInt16(&s->z_filefunc, s->filestream, &size_extra_field) != UNZ_OK) + err = UNZ_ERRNO; + + *poffset_local_extrafield = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = size_extra_field; + *psize_variable += size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, int raw, const char *password) +{ + unz64_internal *s = NULL; + file_in_zip64_read_info_s *pfile_in_zip_read_info = NULL; + uint16_t compression_method = 0; + uint64_t offset_local_extrafield = 0; + uint16_t size_local_extrafield = 0; + uint32_t size_variable = 0; + int err = UNZ_OK; +#ifndef NOUNCRYPT + char source[12]; +#else + if (password != NULL) + return UNZ_PARAMERROR; +#endif + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzCheckCurrentFileCoherencyHeader(s, &size_variable, &offset_local_extrafield, &size_local_extrafield) != UNZ_OK) + return UNZ_BADZIPFILE; + + compression_method = s->cur_file_info.compression_method; +#ifdef HAVE_AES + if (compression_method == AES_METHOD) + { + compression_method = s->cur_file_info_internal.aes_compression_method; + if (password == NULL) + { + return UNZ_PARAMERROR; + } + } +#endif + + if (method != NULL) + *method = compression_method; + + if (level != NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((compression_method != 0) && (compression_method != Z_DEFLATED)) + { +#ifdef HAVE_BZIP2 + if (compression_method != Z_BZIP2ED) +#endif + { + return UNZ_BADZIPFILE; + } + } + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info == NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer = (uint8_t*)ALLOC(UNZ_BUFSIZE); + if (pfile_in_zip_read_info->read_buffer == NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised = 0; + + pfile_in_zip_read_info->filestream = s->filestream; + pfile_in_zip_read_info->z_filefunc = s->z_filefunc; + + pfile_in_zip_read_info->raw = raw; + pfile_in_zip_read_info->crc32 = 0; + pfile_in_zip_read_info->crc32_expected = s->cur_file_info.crc; + pfile_in_zip_read_info->total_out_64 = 0; + pfile_in_zip_read_info->compression_method = compression_method; + + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield = 0; + + pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size; + pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size; + pfile_in_zip_read_info->byte_before_the_zipfile = 0; + + if (s->number_disk == s->gi.number_disk_with_CD) + pfile_in_zip_read_info->byte_before_the_zipfile = s->byte_before_the_zipfile; + + pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_variable; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)s; + pfile_in_zip_read_info->stream.total_out = 0; + pfile_in_zip_read_info->stream.total_in = 0; + pfile_in_zip_read_info->stream.next_in = NULL; + pfile_in_zip_read_info->stream.avail_in = 0; + + if (!raw) + { + if (compression_method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + err = BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + { + pfile_in_zip_read_info->stream_initialised = Z_BZIP2ED; + } + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw = 1; +#endif + } + else if (compression_method == Z_DEFLATED) + { +#ifdef HAVE_APPLE_COMPRESSION + err = compression_stream_init(&pfile_in_zip_read_info->astream, COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB); + if (err == COMPRESSION_STATUS_ERROR) + err = UNZ_INTERNALERROR; + else + err = Z_OK; +#else + err = inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); +#endif + if (err == Z_OK) + { + pfile_in_zip_read_info->stream_initialised = Z_DEFLATED; + } + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + } + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +#ifndef NOUNCRYPT + s->pcrc_32_tab = NULL; + + if ((password != NULL) && ((s->cur_file_info.flag & 1) != 0)) + { + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_INTERNALERROR; +#ifdef HAVE_AES + if (s->cur_file_info.compression_method == AES_METHOD) + { + unsigned char passverify_archive[AES_PWVERIFYSIZE]; + unsigned char passverify_password[AES_PWVERIFYSIZE]; + unsigned char salt_value[AES_MAXSALTLENGTH]; + uint32_t salt_length = 0; + + if ((s->cur_file_info_internal.aes_encryption_mode < 1) || + (s->cur_file_info_internal.aes_encryption_mode > 3)) + return UNZ_INTERNALERROR; + + salt_length = SALT_LENGTH(s->cur_file_info_internal.aes_encryption_mode); + + if (ZREAD64(s->z_filefunc, s->filestream, salt_value, salt_length) != salt_length) + return UNZ_INTERNALERROR; + if (ZREAD64(s->z_filefunc, s->filestream, passverify_archive, AES_PWVERIFYSIZE) != AES_PWVERIFYSIZE) + return UNZ_INTERNALERROR; + + fcrypt_init(s->cur_file_info_internal.aes_encryption_mode, (uint8_t *)password, + (uint32_t)strlen(password), salt_value, passverify_password, &s->pfile_in_zip_read->aes_ctx); + + if (memcmp(passverify_archive, passverify_password, AES_PWVERIFYSIZE) != 0) + return UNZ_BADPASSWORD; + + s->pfile_in_zip_read->rest_read_compressed -= salt_length + AES_PWVERIFYSIZE; + s->pfile_in_zip_read->rest_read_compressed -= AES_AUTHCODESIZE; + + s->pfile_in_zip_read->pos_in_zipfile += salt_length + AES_PWVERIFYSIZE; + } + else +#endif + { + int i; + uint8_t expected; + uint8_t actual; + + s->pcrc_32_tab = (const z_crc_t*)get_crc_table(); + init_keys(password, s->keys, s->pcrc_32_tab); + + if (ZREAD64(s->z_filefunc, s->filestream, source, 12) < 12) + return UNZ_INTERNALERROR; + + for (i = 0; i < 12; i++) + zdecode(s->keys, s->pcrc_32_tab, source[i]); + expected = (s->cur_file_info.flag & (1 << 3)) ? + s->cur_file_info.dos_date >> 8 : + s->cur_file_info.crc >> 24; + actual = (uint8_t)source[11]; + if ((actual != 0) && (expected != actual)) { + return UNZ_BADPASSWORD; + } + + s->pfile_in_zip_read->rest_read_compressed -= 12; + s->pfile_in_zip_read->pos_in_zipfile += 12; + } + } +#endif + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile(unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char *password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int *method, int *level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if some bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ +extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, uint32_t len) +{ + unz64_internal *s = NULL; + uint32_t read = 0; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + if (s->pfile_in_zip_read->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len == 0) + return 0; + // avail_out is uInt, so uint32_t len might allow requesting a larger buffer than zlib can support + if (len > UINT_MAX) + return UNZ_PARAMERROR; + + s->pfile_in_zip_read->stream.next_out = (uint8_t*)buf; + s->pfile_in_zip_read->stream.avail_out = (uInt)len; + + if ((s->pfile_in_zip_read->compression_method == 0) || (s->pfile_in_zip_read->raw)) + { + if (len > s->pfile_in_zip_read->rest_read_compressed + s->pfile_in_zip_read->stream.avail_in) + s->pfile_in_zip_read->stream.avail_out = (uInt)s->pfile_in_zip_read->rest_read_compressed + + s->pfile_in_zip_read->stream.avail_in; + } + + do + { + if (s->pfile_in_zip_read->stream.avail_in == 0) + { + uint32_t bytes_to_read = UNZ_BUFSIZE; + uint32_t bytes_not_read = 0; + uint32_t bytes_read = 0; + uint32_t total_bytes_read = 0; + + if (s->pfile_in_zip_read->stream.next_in != NULL) + bytes_not_read = (uint32_t)(s->pfile_in_zip_read->read_buffer + UNZ_BUFSIZE - + s->pfile_in_zip_read->stream.next_in); + bytes_to_read -= bytes_not_read; + if (bytes_not_read > 0) + memmove(s->pfile_in_zip_read->read_buffer, s->pfile_in_zip_read->stream.next_in, bytes_not_read); + if (s->pfile_in_zip_read->rest_read_compressed < bytes_to_read) + bytes_to_read = (uint32_t)s->pfile_in_zip_read->rest_read_compressed; + + while (total_bytes_read != bytes_to_read) + { + if (ZSEEK64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, + s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + bytes_read = ZREAD64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, + s->pfile_in_zip_read->read_buffer + bytes_not_read + total_bytes_read, + bytes_to_read - total_bytes_read); + + total_bytes_read += bytes_read; + s->pfile_in_zip_read->pos_in_zipfile += bytes_read; + + if (bytes_read == 0) + { + if (ZERROR64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream)) + return UNZ_ERRNO; + + err = unzGoToNextDisk(file); + if (err != UNZ_OK) + return err; + + s->pfile_in_zip_read->pos_in_zipfile = 0; + s->pfile_in_zip_read->filestream = s->filestream; + } + } + +#ifndef NOUNCRYPT + if ((s->cur_file_info.flag & 1) != 0) + { +#ifdef HAVE_AES + if (s->cur_file_info.compression_method == AES_METHOD) + { + fcrypt_decrypt(s->pfile_in_zip_read->read_buffer, bytes_to_read, &s->pfile_in_zip_read->aes_ctx); + } + else +#endif + if (s->pcrc_32_tab != NULL) + { + uint32_t i = 0; + + for (i = 0; i < total_bytes_read; i++) + s->pfile_in_zip_read->read_buffer[i] = + zdecode(s->keys, s->pcrc_32_tab, s->pfile_in_zip_read->read_buffer[i]); + } + } +#endif + + s->pfile_in_zip_read->rest_read_compressed -= total_bytes_read; + s->pfile_in_zip_read->stream.next_in = (uint8_t*)s->pfile_in_zip_read->read_buffer; + s->pfile_in_zip_read->stream.avail_in = (uInt)(bytes_not_read + total_bytes_read); + } + + if ((s->pfile_in_zip_read->compression_method == 0) || (s->pfile_in_zip_read->raw)) + { + uint32_t i = 0; + uint32_t copy = 0; + + if ((s->pfile_in_zip_read->stream.avail_in == 0) && + (s->pfile_in_zip_read->rest_read_compressed == 0)) + return (read == 0) ? UNZ_EOF : read; + + if (s->pfile_in_zip_read->stream.avail_out < s->pfile_in_zip_read->stream.avail_in) + copy = s->pfile_in_zip_read->stream.avail_out; + else + copy = s->pfile_in_zip_read->stream.avail_in; + + for (i = 0; i < copy; i++) + *(s->pfile_in_zip_read->stream.next_out + i) = + *(s->pfile_in_zip_read->stream.next_in + i); + + s->pfile_in_zip_read->total_out_64 = s->pfile_in_zip_read->total_out_64 + copy; + s->pfile_in_zip_read->rest_read_uncompressed -= copy; + s->pfile_in_zip_read->crc32 = (uint32_t)crc32(s->pfile_in_zip_read->crc32, + s->pfile_in_zip_read->stream.next_out, copy); + + s->pfile_in_zip_read->stream.avail_in -= copy; + s->pfile_in_zip_read->stream.avail_out -= copy; + s->pfile_in_zip_read->stream.next_out += copy; + s->pfile_in_zip_read->stream.next_in += copy; + s->pfile_in_zip_read->stream.total_out += copy; + + read += copy; + } + else if (s->pfile_in_zip_read->compression_method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uint64_t total_out_before = 0; + uint64_t total_out_after = 0; + uint64_t out_bytes = 0; + const uint8_t *buf_before = NULL; + + s->pfile_in_zip_read->bstream.next_in = (char*)s->pfile_in_zip_read->stream.next_in; + s->pfile_in_zip_read->bstream.avail_in = s->pfile_in_zip_read->stream.avail_in; + s->pfile_in_zip_read->bstream.total_in_lo32 = (uint32_t)s->pfile_in_zip_read->stream.total_in; + s->pfile_in_zip_read->bstream.total_in_hi32 = s->pfile_in_zip_read->stream.total_in >> 32; + + s->pfile_in_zip_read->bstream.next_out = (char*)s->pfile_in_zip_read->stream.next_out; + s->pfile_in_zip_read->bstream.avail_out = s->pfile_in_zip_read->stream.avail_out; + s->pfile_in_zip_read->bstream.total_out_lo32 = (uint32_t)s->pfile_in_zip_read->stream.total_out; + s->pfile_in_zip_read->bstream.total_out_hi32 = s->pfile_in_zip_read->stream.total_out >> 32; + + /* Fix: legacy minizip v1.2 UB — uint32_t << 32, changed to uint64_t (upstream archived) */ + total_out_before = s->pfile_in_zip_read->bstream.total_out_lo32 + + (((uint64_t)s->pfile_in_zip_read->bstream.total_out_hi32) << 32); + buf_before = (const uint8_t*)s->pfile_in_zip_read->bstream.next_out; + + err = BZ2_bzDecompress(&s->pfile_in_zip_read->bstream); + + total_out_after = s->pfile_in_zip_read->bstream.total_out_lo32 + + (((uint64_t)s->pfile_in_zip_read->bstream.total_out_hi32) << 32); + + out_bytes = total_out_after - total_out_before; + + s->pfile_in_zip_read->total_out_64 = s->pfile_in_zip_read->total_out_64 + out_bytes; + s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes; + s->pfile_in_zip_read->crc32 = crc32(s->pfile_in_zip_read->crc32, buf_before, (uint32_t)out_bytes); + + read += (uint32_t)out_bytes; + + s->pfile_in_zip_read->stream.next_in = (uint8_t*)s->pfile_in_zip_read->bstream.next_in; + s->pfile_in_zip_read->stream.avail_in = s->pfile_in_zip_read->bstream.avail_in; + s->pfile_in_zip_read->stream.total_in = s->pfile_in_zip_read->bstream.total_in_lo32; + s->pfile_in_zip_read->stream.next_out = (uint8_t*)s->pfile_in_zip_read->bstream.next_out; + s->pfile_in_zip_read->stream.avail_out = s->pfile_in_zip_read->bstream.avail_out; + s->pfile_in_zip_read->stream.total_out = s->pfile_in_zip_read->bstream.total_out_lo32; + + if (err == BZ_STREAM_END) + return (read == 0) ? UNZ_EOF : read; + if (err != BZ_OK) + break; +#endif + } +#ifdef HAVE_APPLE_COMPRESSION + else + { + uint64_t total_out_before = 0; + uint64_t total_out_after = 0; + uint64_t out_bytes = 0; + const uint8_t *buf_before = NULL; + + s->pfile_in_zip_read->astream.src_ptr = s->pfile_in_zip_read->stream.next_in; + s->pfile_in_zip_read->astream.src_size = s->pfile_in_zip_read->stream.avail_in; + s->pfile_in_zip_read->astream.dst_ptr = s->pfile_in_zip_read->stream.next_out; + s->pfile_in_zip_read->astream.dst_size = len; + + total_out_before = s->pfile_in_zip_read->stream.total_out; + buf_before = s->pfile_in_zip_read->stream.next_out; + + compression_status status; + compression_stream_flags flags; + + if (s->pfile_in_zip_read->stream.avail_in == 0) + { + flags = COMPRESSION_STREAM_FINALIZE; + } + + status = compression_stream_process(&s->pfile_in_zip_read->astream, flags); + + total_out_after = len - s->pfile_in_zip_read->astream.dst_size; + out_bytes = total_out_after - total_out_before; + + s->pfile_in_zip_read->total_out_64 += out_bytes; + s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes; + s->pfile_in_zip_read->crc32 = + crc32(s->pfile_in_zip_read->crc32, buf_before, (uint32_t)out_bytes); + + read += (uint32_t)out_bytes; + + s->pfile_in_zip_read->stream.next_in = s->pfile_in_zip_read->astream.src_ptr; + s->pfile_in_zip_read->stream.avail_in = s->pfile_in_zip_read->astream.src_size; + s->pfile_in_zip_read->stream.next_out = s->pfile_in_zip_read->astream.dst_ptr; + s->pfile_in_zip_read->stream.avail_out = s->pfile_in_zip_read->astream.dst_size; + + if (status == COMPRESSION_STATUS_END) + return (read == 0) ? UNZ_EOF : read; + if (status == COMPRESSION_STATUS_ERROR) + return Z_DATA_ERROR; + return read; + } +#else + else + { + uint64_t total_out_before = 0; + uint64_t total_out_after = 0; + uint64_t out_bytes = 0; + const uint8_t *buf_before = NULL; + int flush = Z_SYNC_FLUSH; + + total_out_before = s->pfile_in_zip_read->stream.total_out; + buf_before = s->pfile_in_zip_read->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err = inflate(&s->pfile_in_zip_read->stream, flush); + + if ((err >= 0) && (s->pfile_in_zip_read->stream.msg != NULL)) + err = Z_DATA_ERROR; + + total_out_after = s->pfile_in_zip_read->stream.total_out; + out_bytes = total_out_after - total_out_before; + + s->pfile_in_zip_read->total_out_64 += out_bytes; + s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes; + s->pfile_in_zip_read->crc32 = + (uint32_t)crc32(s->pfile_in_zip_read->crc32,buf_before, (uint32_t)out_bytes); + + read += (uint32_t)out_bytes; + + if (err == Z_STREAM_END) + return (read == 0) ? UNZ_EOF : read; + if (err != Z_OK) + break; + } +#endif + } + while (s->pfile_in_zip_read->stream.avail_out > 0); + + if (err == Z_OK) + return read; + return err; +} + +extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, uint32_t len) +{ + unz64_internal *s = NULL; + uint64_t size_to_read = 0; + uint32_t read_now = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + + size_to_read = s->pfile_in_zip_read->size_local_extrafield - s->pfile_in_zip_read->pos_local_extrafield; + + if (buf == NULL) + return (int)size_to_read; + + if (len > size_to_read) + read_now = (uint32_t)size_to_read; + else + read_now = len; + + if (read_now == 0) + return 0; + + if (ZSEEK64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, + s->pfile_in_zip_read->offset_local_extrafield + s->pfile_in_zip_read->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (ZREAD64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, buf, read_now) != read_now) + return UNZ_ERRNO; + + /* Fix: legacy minizip never advanced the cursor (upstream archived) */ + s->pfile_in_zip_read->pos_local_extrafield += read_now; + + return (int)read_now; +} + +extern int ZEXPORT unzCloseCurrentFile(unzFile file) +{ + unz64_internal *s = NULL; + file_in_zip64_read_info_s *pfile_in_zip_read_info = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + +#ifdef HAVE_AES + if (s->cur_file_info.compression_method == AES_METHOD) + { + unsigned char authcode[AES_AUTHCODESIZE]; + unsigned char rauthcode[AES_AUTHCODESIZE]; + + if (ZREAD64(s->z_filefunc, s->filestream, authcode, AES_AUTHCODESIZE) != AES_AUTHCODESIZE) + return UNZ_ERRNO; + + if (fcrypt_end(rauthcode, &s->pfile_in_zip_read->aes_ctx) != AES_AUTHCODESIZE) + err = UNZ_CRCERROR; + if (memcmp(authcode, rauthcode, AES_AUTHCODESIZE) != 0) + err = UNZ_CRCERROR; + } + /* AES zip version AE-1 will expect a valid crc as well */ + if ((s->cur_file_info.compression_method != AES_METHOD) || + (s->cur_file_info_internal.aes_version == 0x0001)) +#endif + { + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_expected) + err = UNZ_CRCERROR; + } + } + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + { +#ifdef HAVE_APPLE_COMPRESSION + if (compression_stream_destroy) + compression_stream_destroy(&pfile_in_zip_read_info->astream); +#else + inflateEnd(&pfile_in_zip_read_info->stream); +#endif + + } +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read = NULL; + + return err; +} + +extern int ZEXPORT unzGoToFirstFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->gi.number_entry == 0) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir = s->offset_central_dir; + s->num_file = 0; + + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, + filename, filename_size, extrafield, extrafield_size, comment,comment_size); + + s->current_file_ok = (err == UNZ_OK); + if ((err == UNZ_OK) && (pfile_info != NULL)) + memcpy(pfile_info, &s->cur_file_info, sizeof(unz_file_info64)); + + return err; +} + +extern int ZEXPORT unzGoToFirstFile(unzFile file) +{ + return unzGoToFirstFile2(file, NULL, NULL, 0, NULL, 0, NULL, 0); +} + +extern int ZEXPORT unzGoToNextFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != UINT16_MAX) /* 2^16 files overflow hack */ + { + if (s->num_file+1 == s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + } + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment; + s->num_file += 1; + + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, + filename, filename_size, extrafield,extrafield_size, comment, comment_size); + + s->current_file_ok = (err == UNZ_OK); + if ((err == UNZ_OK) && (pfile_info != NULL)) + memcpy(pfile_info, &s->cur_file_info, sizeof(unz_file_info64)); + + return err; +} + +extern int ZEXPORT unzGoToNextFile(unzFile file) +{ + return unzGoToNextFile2(file, NULL, NULL, 0, NULL, 0, NULL, 0); +} + +extern int ZEXPORT unzLocateFile(unzFile file, const char *filename, unzFileNameComparer filename_compare_func) +{ + unz64_internal *s = NULL; + unz_file_info64 cur_file_info_saved; + unz_file_info64_internal cur_file_info_internal_saved; + uint64_t num_file_saved = 0; + uint64_t pos_in_central_dir_saved = 0; + char current_filename[UNZ_MAXFILENAMEINZIP+1]; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + if (strlen(filename) >= UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_file_saved = s->num_file; + pos_in_central_dir_saved = s->pos_in_central_dir; + cur_file_info_saved = s->cur_file_info; + cur_file_info_internal_saved = s->cur_file_info_internal; + + err = unzGoToFirstFile2(file, NULL, current_filename, sizeof(current_filename)-1, NULL, 0, NULL, 0); + + while (err == UNZ_OK) + { + if (filename_compare_func != NULL) + err = filename_compare_func(file, current_filename, filename); + else + err = strcmp(current_filename, filename); + if (err == 0) + return UNZ_OK; + err = unzGoToNextFile2(file, NULL, current_filename, sizeof(current_filename)-1, NULL, 0, NULL, 0); + } + + /* We failed, so restore the state of the 'current file' to where we were. */ + s->num_file = num_file_saved; + s->pos_in_central_dir = pos_in_central_dir_saved; + s->cur_file_info = cur_file_info_saved; + s->cur_file_info_internal = cur_file_info_internal_saved; + return err; +} + +extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos *file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file, &file_pos64); + if (err == UNZ_OK) + { + file_pos->pos_in_zip_directory = (uint32_t)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uint32_t)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos *file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file, &file_pos64); +} + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos *file_pos) +{ + unz64_internal *s = NULL; + + if (file == NULL || file_pos == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos *file_pos) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL || file_pos == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + /* Jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* Set the current file */ + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, NULL, 0, NULL, 0, NULL, 0); + /* Return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int32_t ZEXPORT unzGetOffset(unzFile file) +{ + uint64_t offset64 = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (int32_t)offset64; +} + +extern int64_t ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_internal *s = NULL; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != UINT16_MAX) + { + if (s->num_file == s->gi.number_entry) + return 0; + } + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset(unzFile file, uint32_t pos) +{ + return unzSetOffset64(file, pos); +} + +extern int ZEXPORT unzSetOffset64(unzFile file, uint64_t pos) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, NULL, 0, NULL, 0, NULL, 0); + + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int32_t ZEXPORT unzTell(unzFile file) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + return (int32_t)s->pfile_in_zip_read->stream.total_out; +} + +extern int64_t ZEXPORT unzTell64(unzFile file) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + return s->pfile_in_zip_read->total_out_64; +} + +extern int ZEXPORT unzSeek(unzFile file, uint32_t offset, int origin) +{ + return unzSeek64(file, offset, origin); +} + +extern int ZEXPORT unzSeek64(unzFile file, uint64_t offset, int origin) +{ + unz64_internal *s = NULL; + uint64_t stream_pos_begin = 0; + uint64_t stream_pos_end = 0; + uint64_t position = 0; + int is_within_buffer = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->pfile_in_zip_read == NULL) + return UNZ_ERRNO; + if (s->pfile_in_zip_read->compression_method != 0) + return UNZ_ERRNO; + + if (origin == SEEK_SET) + position = offset; + else if (origin == SEEK_CUR) + position = s->pfile_in_zip_read->total_out_64 + offset; + else if (origin == SEEK_END) + position = s->cur_file_info.compressed_size + offset; + else + return UNZ_PARAMERROR; + + if (position > s->cur_file_info.compressed_size) + return UNZ_PARAMERROR; + + stream_pos_end = s->pfile_in_zip_read->pos_in_zipfile; + stream_pos_begin = stream_pos_end; + + if (stream_pos_begin > UNZ_BUFSIZE) + stream_pos_begin -= UNZ_BUFSIZE; + else + stream_pos_begin = 0; + + is_within_buffer = + (s->pfile_in_zip_read->stream.avail_in != 0) && + (s->pfile_in_zip_read->rest_read_compressed != 0 || s->cur_file_info.compressed_size < UNZ_BUFSIZE) && + (position >= stream_pos_begin && position < stream_pos_end); + + if (is_within_buffer) + { + s->pfile_in_zip_read->stream.next_in += position - s->pfile_in_zip_read->total_out_64; + s->pfile_in_zip_read->stream.avail_in = (uInt)(stream_pos_end - position); + } + else + { + s->pfile_in_zip_read->stream.avail_in = 0; + s->pfile_in_zip_read->stream.next_in = 0; + + s->pfile_in_zip_read->pos_in_zipfile = s->pfile_in_zip_read->offset_local_extrafield + position; + s->pfile_in_zip_read->rest_read_compressed = s->cur_file_info.compressed_size - position; + } + + s->pfile_in_zip_read->rest_read_uncompressed -= (position - s->pfile_in_zip_read->total_out_64); + s->pfile_in_zip_read->stream.total_out = (uint32_t)position; + s->pfile_in_zip_read->total_out_64 = position; + + return UNZ_OK; +} + +extern int ZEXPORT unzEndOfFile(unzFile file) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + if (s->pfile_in_zip_read->rest_read_uncompressed == 0) + return 1; + return 0; +} diff --git a/src/framework/core/minizip/unzip.h b/src/framework/core/minizip/unzip.h new file mode 100644 index 000000000..8889333c8 --- /dev/null +++ b/src/framework/core/minizip/unzip.h @@ -0,0 +1,308 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.2.0, September 16th, 2017 + part of the MiniZip project + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support on both zip and unzip + http://result42.com + Copyright (C) 2007-2008 Even Rouault + Modifications of Unzip for Zip64 + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _UNZ_H +#define _UNZ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unz_file__; +typedef unz_file__ *unzFile; +#else +typedef voidp unzFile; +#endif + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) +#define UNZ_BADPASSWORD (-106) + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + uint64_t number_entry; /* total number of entries in the central dir on this disk */ + uint32_t number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP*/ + uint16_t size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uint32_t number_entry; /* total number of entries in the central dir on this disk */ + uint32_t number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP*/ + uint16_t size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uint16_t version; /* version made by 2 bytes */ + uint16_t version_needed; /* version needed to extract 2 bytes */ + uint16_t flag; /* general purpose bit flag 2 bytes */ + uint16_t compression_method; /* compression method 2 bytes */ + uint32_t dos_date; /* last mod file date in Dos fmt 4 bytes */ + uint32_t crc; /* crc-32 4 bytes */ + uint64_t compressed_size; /* compressed size 8 bytes */ + uint64_t uncompressed_size; /* uncompressed size 8 bytes */ + uint16_t size_filename; /* filename length 2 bytes */ + uint16_t size_file_extra; /* extra field length 2 bytes */ + uint16_t size_file_comment; /* file comment length 2 bytes */ + + uint32_t disk_num_start; /* disk number start 4 bytes */ + uint16_t internal_fa; /* internal file attributes 2 bytes */ + uint32_t external_fa; /* external file attributes 4 bytes */ + + uint64_t disk_offset; + + uint16_t size_file_extra_internal; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uint16_t version; /* version made by 2 bytes */ + uint16_t version_needed; /* version needed to extract 2 bytes */ + uint16_t flag; /* general purpose bit flag 2 bytes */ + uint16_t compression_method; /* compression method 2 bytes */ + uint32_t dos_date; /* last mod file date in Dos fmt 4 bytes */ + uint32_t crc; /* crc-32 4 bytes */ + uint32_t compressed_size; /* compressed size 4 bytes */ + uint32_t uncompressed_size; /* uncompressed size 4 bytes */ + uint16_t size_filename; /* filename length 2 bytes */ + uint16_t size_file_extra; /* extra field length 2 bytes */ + uint16_t size_file_comment; /* file comment length 2 bytes */ + + uint16_t disk_num_start; /* disk number start 2 bytes */ + uint16_t internal_fa; /* internal file attributes 2 bytes */ + uint32_t external_fa; /* external file attributes 4 bytes */ + + uint64_t disk_offset; +} unz_file_info; + +/***************************************************************************/ +/* Opening and close a zip file */ + +extern unzFile ZEXPORT unzOpen(const char *path); +extern unzFile ZEXPORT unzOpen64(const void *path); +/* Open a Zip file. + + path should contain the full path (by example, on a Windows XP computer + "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". + return NULL if zipfile cannot be opened or doesn't exist + return unzFile handle if no error + + NOTE: The "64" function take a const void *pointer, because the path is just the value passed to the + open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char *does not describe the reality */ + +extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def *pzlib_filefunc_def); +/* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write operations */ +extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def *pzlib_filefunc_def); +/* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write 64-bit operations */ + +extern int ZEXPORT unzClose(unzFile file); +/* Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile, + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + + return UNZ_OK if there is no error */ + +extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info); +extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info); +/* Write info about the ZipFile in the *pglobal_info structure. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzGetGlobalComment(unzFile file, char *comment, uint16_t comment_size); +/* Get the global comment string of the ZipFile, in the comment buffer. + + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 */ + +/***************************************************************************/ +/* Reading the content of the current zipfile, you can open it, read data from it, and close it + (you can close it before reading all the file) */ + +extern int ZEXPORT unzOpenCurrentFile(unzFile file); +/* Open for reading data the current file in the zipfile. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char *password); +/* Open for reading data the current file in the zipfile. + password is a crypting password + + return UNZ_OK if no error */ + +extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int *method, int *level, int raw); +/* Same as unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 *method will receive method of compression, *level will receive level of compression + + NOTE: you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL */ + +extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, int raw, const char *password); +/* Same as unzOpenCurrentFile, but takes extra parameter password for encrypted files */ + +extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, uint32_t len); +/* Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ + +extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +/* Get Info about the current file + + pfile_info if != NULL, the *pfile_info structure will contain somes info about the current file + filename if != NULL, the file name string will be copied in filename + filename_size is the size of the filename buffer + extrafield if != NULL, the extra field information from the central header will be copied in to + extrafield_size is the size of the extraField buffer + comment if != NULL, the comment string of the file will be copied in to + comment_size is the size of the comment buffer */ + +extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, uint32_t len); +/* Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf == NULL, it return the size of the local extra field + if buf != NULL, len is the size of the buffer, the extra header is copied in buf. + + return number of bytes copied in buf, or (if <0) the error code */ + +extern int ZEXPORT unzCloseCurrentFile(unzFile file); +/* Close the file in zip opened with unzOpenCurrentFile + + return UNZ_CRCERROR if all the file was read but the CRC is not good */ + +/***************************************************************************/ +/* Browse the directory of the zipfile */ + +typedef int (*unzFileNameComparer)(unzFile file, const char *filename1, const char *filename2); +typedef int (*unzIteratorFunction)(unzFile file); +typedef int (*unzIteratorFunction2)(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); + +extern int ZEXPORT unzGoToFirstFile(unzFile file); +/* Set the current file of the zipfile to the first file. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzGoToFirstFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +/* Set the current file of the zipfile to the first file and retrieves the current info on success. + Not as seek intensive as unzGoToFirstFile + unzGetCurrentFileInfo. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzGoToNextFile(unzFile file); +/* Set the current file of the zipfile to the next file. + + return UNZ_OK if no error + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */ + +extern int ZEXPORT unzGoToNextFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +/* Set the current file of the zipfile to the next file and retrieves the current + info on success. Does less seeking around than unzGotoNextFile + unzGetCurrentFileInfo. + + return UNZ_OK if no error + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */ + +extern int ZEXPORT unzLocateFile(unzFile file, const char *filename, unzFileNameComparer filename_compare_func); +/* Try locate the file szFileName in the zipfile. For custom filename comparison pass in comparison function. + + return UNZ_OK if the file is found (it becomes the current file) + return UNZ_END_OF_LIST_OF_FILE if the file is not found */ + +/***************************************************************************/ +/* Raw access to zip file */ + +typedef struct unz_file_pos_s +{ + uint32_t pos_in_zip_directory; /* offset in zip file directory */ + uint32_t num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos *file_pos); +extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos *file_pos); + +typedef struct unz64_file_pos_s +{ + uint64_t pos_in_zip_directory; /* offset in zip file directory */ + uint64_t num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos *file_pos); +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos *file_pos); + +extern int32_t ZEXPORT unzGetOffset(unzFile file); +extern int64_t ZEXPORT unzGetOffset64(unzFile file); +/* Get the current file offset */ + +extern int ZEXPORT unzSetOffset(unzFile file, uint32_t pos); +extern int ZEXPORT unzSetOffset64(unzFile file, uint64_t pos); +/* Set the current file offset */ + +extern int32_t ZEXPORT unzTell(unzFile file); +extern int64_t ZEXPORT unzTell64(unzFile file); +/* return current position in uncompressed data */ + +extern int ZEXPORT unzSeek(unzFile file, uint32_t offset, int origin); +extern int ZEXPORT unzSeek64(unzFile file, uint64_t offset, int origin); +/* Seek within the uncompressed data if compression method is storage */ + +extern int ZEXPORT unzEndOfFile(unzFile file); +/* return 1 if the end of file was reached, 0 elsewhere */ + +/***************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _UNZ_H */ diff --git a/src/framework/core/unzipper.cpp b/src/framework/core/unzipper.cpp index 755d93dac..a9eeb31a7 100644 --- a/src/framework/core/unzipper.cpp +++ b/src/framework/core/unzipper.cpp @@ -26,9 +26,9 @@ #include "logger.h" #include "resourcemanager.h" #include -#include -#include -#include +#include "minizip/ioapi.h" +#include "minizip/ioapi_mem.h" +#include "minizip/unzip.h" const int MAX_FILENAME = 512; const int READ_SIZE = 8192; @@ -37,7 +37,7 @@ const char dir_delimiter = '/'; void unzipper::extract(const char* fileBuffer, uint fileLength, std::string& destinationPath) { const std::filesystem::path destinationFolder { destinationPath }; if (!std::filesystem::exists(destinationFolder)) { - std::filesystem::create_directory(destinationFolder); + std::filesystem::create_directories(destinationFolder); } zlib_filefunc_def filefunc32 = { nullptr }; @@ -81,9 +81,20 @@ void unzipper::extract(const char* fileBuffer, uint fileLength, std::string& des // Check if this entry is a directory or file. const size_t filename_length = strlen( filename ); + + // Guard against zip-slip: reject entries that escape the destination folder + const auto entryPath = std::filesystem::path(filename).lexically_normal(); + if (entryPath.is_absolute() || entryPath.string().starts_with("..")) { + g_logger.warning("skipping zip entry with unsafe path: {}", filename); + if ( ( i+1 ) < global_info.number_entry ) + unzGoToNextFile( zipfile ); + continue; + } + const auto outputPath = destinationFolder / entryPath; + if (filename[ filename_length-1 ] == dir_delimiter ) { - std::filesystem::create_directory({ destinationPath + filename }); + std::filesystem::create_directories(outputPath); } else { @@ -95,7 +106,12 @@ void unzipper::extract(const char* fileBuffer, uint fileLength, std::string& des } // Open a file to write out the data. - std::string destFilePath = destinationPath + filename; + const std::string destFilePath = outputPath.string(); + // Ensure parent directory exists (zip may not have explicit directory entries) + const std::filesystem::path parentDir = outputPath.parent_path(); + if (!std::filesystem::exists(parentDir)) { + std::filesystem::create_directories(parentDir); + } FILE *out = fopen(destFilePath.c_str(), "wb"); if ( out == nullptr ) { diff --git a/src/framework/platform/androidmanager.cpp b/src/framework/platform/androidmanager.cpp index 6f659c143..2f8fb5de8 100644 --- a/src/framework/platform/androidmanager.cpp +++ b/src/framework/platform/androidmanager.cpp @@ -104,6 +104,11 @@ void AndroidManager::unZipAssetData() { "data.zip", AASSET_MODE_BUFFER); + if (!dataAsset) { + g_logger.fatal("Failed to open data.zip from APK assets. Run setup_android_deps.sh to generate it."); + return; + } + auto dataFileLength = AAsset_getLength(dataAsset); char* dataContent = (char *) malloc(dataFileLength + 1); AAsset_read(dataAsset, dataContent, dataFileLength); diff --git a/src/framework/platform/androidwindow.cpp b/src/framework/platform/androidwindow.cpp index 95ccc24b4..a9b1e39d5 100644 --- a/src/framework/platform/androidwindow.cpp +++ b/src/framework/platform/androidwindow.cpp @@ -27,6 +27,8 @@ #include #include "framework/core/clock.h" #include +#include +#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "OTClientMobile", __VA_ARGS__) AndroidWindow& g_androidWindow = (AndroidWindow&) g_window; @@ -90,7 +92,9 @@ void AndroidWindow::internalCheckGL() { void AndroidWindow::internalChooseGL() { static int attrList[] = { -#if OPENGL_ES==2 +#if OPENGL_ES>=3 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, +#elif OPENGL_ES==2 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, #else EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, @@ -121,7 +125,9 @@ void AndroidWindow::internalCreateGLContext() { } EGLint attrList[] = { -#if OPENGL_ES==2 +#if OPENGL_ES>=3 + EGL_CONTEXT_CLIENT_VERSION, 3, +#elif OPENGL_ES==2 EGL_CONTEXT_CLIENT_VERSION, 2, #else EGL_CONTEXT_CLIENT_VERSION, 1, @@ -175,6 +181,11 @@ void AndroidWindow::internalCreateGLSurface() { } void AndroidWindow::queryGlSize() { + // Surface may not be created yet during early CONFIG_CHANGED events + if (m_eglDisplay == EGL_NO_DISPLAY || m_eglSurface == EGL_NO_SURFACE) { + return; + } + int width, height; if (EGL_FALSE == eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &width) || EGL_FALSE == eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &height)) { @@ -250,8 +261,7 @@ void AndroidWindow::processTextInput() { void AndroidWindow::processFingerDownAndUp() { bool isTouchdown = m_currentEvent.type == TOUCH_DOWN; - Fw::MouseButton mouseButton = (m_currentEvent.type == TOUCH_UP && !m_isDragging && stdext::millis() > m_lastPress + 500) ? - Fw::MouseRightButton : Fw::MouseLeftButton; + Fw::MouseButton mouseButton = Fw::MouseLeftButton; m_inputEvent.reset(); m_inputEvent.type = (isTouchdown) ? Fw::MousePressInputEvent : Fw::MouseReleaseInputEvent; @@ -261,11 +271,9 @@ void AndroidWindow::processFingerDownAndUp() { m_lastPress = g_clock.millis(); m_mouseButtonStates |= 1 << mouseButton; } else if (m_currentEvent.type == TOUCH_UP) { - if (!m_isDragging) { - if (stdext::millis() > m_lastPress + 500) { - mouseButton = Fw::MouseRightButton; - m_inputEvent.mouseButton = mouseButton; - } + if (!m_isDragging && stdext::millis() > m_lastPress + 500) { + mouseButton = Fw::MouseRightButton; + m_inputEvent.mouseButton = mouseButton; } m_isDragging = false; g_dispatcher.addEvent([this, mouseButton] { m_mouseButtonStates &= ~(1 << mouseButton); }); @@ -276,7 +284,7 @@ void AndroidWindow::processFingerDownAndUp() { void AndroidWindow::processFingerMotion() { static Point lastMousePos(-1, -1); - static const int dragThreshold = 5; + static const int dragThreshold = 15; // Increased for touch (was 5, too sensitive) m_inputEvent.reset(); m_inputEvent.type = Fw::MouseMoveInputEvent; @@ -306,7 +314,21 @@ void AndroidWindow::handleInputEvent() { } void AndroidWindow::swapBuffers() { - eglSwapBuffers(m_eglDisplay, m_eglSurface); + if (m_eglDisplay == EGL_NO_DISPLAY || m_eglSurface == EGL_NO_SURFACE) + return; + + if (!eglSwapBuffers(m_eglDisplay, m_eglSurface)) { + EGLint error = eglGetError(); + if (error == EGL_BAD_SURFACE || error == EGL_BAD_NATIVE_WINDOW) { + // Surface was lost (e.g. during config change), try to recreate + internalDestroySurface(); + if (m_app->window != nullptr) { + internalCreateGLSurface(); + internalConnectSurface(); + queryGlSize(); + } + } + } } void AndroidWindow::setVerticalSync(bool enable) { @@ -314,12 +336,12 @@ void AndroidWindow::setVerticalSync(bool enable) { } std::string AndroidWindow::getClipboardText() { - // TODO + // TODO: implement via JNI ClipboardManager (see PR #1674) return ""; } void AndroidWindow::setClipboardText(const std::string_view text) { - // TODO + // TODO: implement via JNI ClipboardManager (see PR #1674) } Size AndroidWindow::getDisplaySize() { @@ -427,9 +449,11 @@ void AndroidWindow::handleNativeEvents() { } void AndroidWindow::handleCmd(int32_t cmd) { + ALOGD("handleCmd: cmd=%d", cmd); switch (cmd) { case APP_CMD_INIT_WINDOW: if (m_app->window != nullptr) { + ALOGD("INIT_WINDOW: window=%p, hasContext=%d", m_app->window, m_eglContext != EGL_NO_CONTEXT); if (m_eglContext) { internalCreateGLSurface(); internalConnectSurface(); @@ -437,13 +461,21 @@ void AndroidWindow::handleCmd(int32_t cmd) { internalInitGL(); } updateDisplayDensityFromSystem(g_androidManager.getScreenDensity()); + queryGlSize(); + ALOGD("INIT_WINDOW: size=%dx%d, density=%.2f, visible=true", m_size.width(), m_size.height(), m_displayDensity); m_visible = true; } else { + ALOGD("INIT_WINDOW: window is null, visible=false"); m_visible = false; } break; case APP_CMD_LOW_MEMORY: + ALOGD("LOW_MEMORY: ignoring surface destruction to keep rendering alive"); + // Do NOT destroy the surface on low memory - it prevents recovery + // since APP_CMD_INIT_WINDOW won't be sent again. + break; case APP_CMD_TERM_WINDOW: + ALOGD("TERM_WINDOW: setting visible=false"); m_visible = false; internalDestroySurface(); break; @@ -451,6 +483,7 @@ void AndroidWindow::handleCmd(int32_t cmd) { case APP_CMD_CONFIG_CHANGED: updateDisplayDensityFromSystem(g_androidManager.getScreenDensity()); queryGlSize(); + ALOGD("RESIZED/CONFIG_CHANGED: size=%dx%d, density=%.2f", m_size.width(), m_size.height(), m_displayDensity); break; default: break; diff --git a/src/framework/ui/uitextedit.cpp b/src/framework/ui/uitextedit.cpp index 227059d40..e45031109 100644 --- a/src/framework/ui/uitextedit.cpp +++ b/src/framework/ui/uitextedit.cpp @@ -1357,7 +1357,8 @@ void UITextEdit::onFocusChange(const bool focused, const Fw::FocusReason reason) blinkCursor(); update(true); #ifdef ANDROID - if (getProp(PropEditable)) { + // Only show keyboard on user interaction (mouse/touch), not programmatic focus + if (getProp(PropEditable) && reason == Fw::MouseFocusReason) { g_androidManager.showKeyboardSoft(); g_androidManager.showInputPreview(getText()); } diff --git a/src/main.cpp b/src/main.cpp index eeb8288a0..cbd28632f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,12 @@ #include "framework/core/resourcemanager.h" #include "framework/luaengine/luainterface.h" #include "framework/platform/platform.h" +#ifdef ANDROID +#include +#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "OTClientMobile", __VA_ARGS__) +#else +#define ALOGD(...) +#endif #ifdef FRAMEWORK_EDITOR #include "tools/datdump.h" #endif @@ -77,12 +83,16 @@ void printHelp(const std::string& executableName) // process args encoding g_platform.init(args); + ALOGD("main: platform init done"); // initialize resources #ifdef ANDROID // Unzip Android assets/data.zip + ALOGD("main: starting unZipAssetData..."); g_androidManager.unZipAssetData(); + ALOGD("main: unZipAssetData complete"); g_resources.init(nullptr); + ALOGD("main: resources init done"); #else g_resources.init(args[0].data()); #endif @@ -104,8 +114,10 @@ void printHelp(const std::string& executableName) } // find script init.lua and run it + ALOGD("main: discovering work dir..."); if (!g_resources.discoverWorkDir("init.lua")) g_logger.fatal("Unable to find work directory, the application cannot be initialized."); + ALOGD("main: work dir found: %s", g_resources.getWorkDir().c_str()); if (shouldShowHelp(args)) { printHelp(args[0]); @@ -119,8 +131,10 @@ void printHelp(const std::string& executableName) #endif // initialize application framework and otclient + ALOGD("main: initializing app framework..."); const auto drawEvents = ApplicationDrawEventsPtr(&g_client, [](ApplicationDrawEvents*) {}); g_app.init(args, new GraphicalApplicationContext(g_gameConfig.getSpriteSize(), drawEvents)); + ALOGD("main: app framework initialized"); #ifndef ANDROID #if ENABLE_DISCORD_RPC == 1 @@ -142,13 +156,16 @@ void printHelp(const std::string& executableName) #endif #endif + ALOGD("main: initializing client..."); g_client.init(args); #ifdef FRAMEWORK_NET g_http.init(); #endif + ALOGD("main: running init.lua..."); if (!g_lua.safeRunScript("init.lua")) g_logger.fatal("Unable to run script init.lua!"); + ALOGD("main: init.lua executed successfully"); // the run application main loop g_app.run();