1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

Merge pull request #5563 from kambala-decapitator/conan-v2

migrate to Conan v2
This commit is contained in:
Ivan Savenko
2025-08-30 21:09:18 +03:00
committed by GitHub
78 changed files with 639 additions and 14375 deletions

View File

@@ -17,7 +17,7 @@ jobs:
matrix:
include:
- platform: mac-intel
os: macos-13
os: macos-14
pack: 1
upload: 1
pack_type: Release
@@ -26,11 +26,11 @@ jobs:
preset: macos-conan-ninja-release
conan_profile: macos-intel
conan_prebuilts: dependencies-mac-intel
conan_options: --options with_apple_system_libs=True
conan_options: --profile=dependencies/conan_profiles/base/apple-system
artifact_platform: intel
- platform: mac-arm
os: macos-13
os: macos-14
pack: 1
upload: 1
pack_type: Release
@@ -39,11 +39,11 @@ jobs:
preset: macos-arm-conan-ninja-release
conan_profile: macos-arm
conan_prebuilts: dependencies-mac-arm
conan_options: --options with_apple_system_libs=True
conan_options: --profile=dependencies/conan_profiles/base/apple-system
artifact_platform: arm
- platform: ios
os: macos-13
os: macos-14
pack: 1
upload: 1
pack_type: Release
@@ -52,7 +52,7 @@ jobs:
preset: ios-release-conan-ccache
conan_profile: ios-arm64
conan_prebuilts: dependencies-ios
conan_options: --options with_apple_system_libs=True
conan_options: --profile=dependencies/conan_profiles/base/apple-system
- platform: msvc-x64
arch: x64
@@ -61,8 +61,10 @@ jobs:
upload: 0
pack_type: RelWithDebInfo
extension: zip
before_install: msvc.sh
preset: windows-msvc-ninja-release
conan_profile: msvc-x64
conan_prebuilts: dependencies-windows-x64
conan_options: -s "&:build_type=RelWithDebInfo" -c tools.env.virtualenv:powershell=pwsh -o "&:target_pre_windows10=True"
artifact_platform: x64
cl: Hostx64/x64/cl.exe
@@ -73,8 +75,10 @@ jobs:
upload: 0
pack_type: RelWithDebInfo
extension: zip
before_install: msvc.sh
preset: windows-msvc-ninja-release-x86
conan_profile: msvc-x86
conan_prebuilts: dependencies-windows-x86
conan_options: -s "&:build_type=RelWithDebInfo" -c tools.env.virtualenv:powershell=pwsh -o "&:target_pre_windows10=True"
artifact_platform: x86
cl: Hostx64/x86/cl.exe
@@ -85,55 +89,43 @@ jobs:
upload: 0
pack_type: RelWithDebInfo
extension: zip
before_install: msvc.sh
preset: windows-msvc-ninja-release-arm64
conan_profile: msvc-arm64
conan_prebuilts: dependencies-windows-arm64
conan_options: -s "&:build_type=RelWithDebInfo" -c tools.env.virtualenv:powershell=pwsh -o "&:lua_lib=lua"
artifact_platform: arm64
cl: HostARM64/ARM64/cl.exe
- platform: mingw_x86_64
arch: x86_64
os: ubuntu-24.04
pack: 1
pack_type: Release
extension: zip
cmake_args: -G Ninja
before_install: mingw.sh
preset: windows-mingw-conan-linux
conan_profile: mingw64-linux.jinja
conan_prebuilts: dependencies-mingw-x86-64
- platform: mingw_x86
arch: x86
os: ubuntu-24.04
pack: 1
pack_type: Release
extension: zip
cmake_args: -G Ninja
before_install: mingw.sh
preset: windows-mingw-conan-linux
conan_profile: mingw32-linux.jinja
conan_prebuilts: dependencies-mingw-x86
- platform: android-32
os: ubuntu-24.04
os: ubuntu-latest
upload: 1
extension: apk
preset: android-conan-ninja-release
before_install: android.sh
conan_profile: android-32-ndk
conan_prebuilts: dependencies-android-armeabi-v7a
conan_options: --profile=dependencies/conan_profiles/base/android-system
artifact_platform: armeabi-v7a
- platform: android-64
os: ubuntu-24.04
os: ubuntu-latest
upload: 1
extension: apk
preset: android-conan-ninja-release
before_install: android.sh
conan_profile: android-64-ndk
conan_prebuilts: dependencies-android-arm64-v8a
conan_options: --profile=dependencies/conan_profiles/base/android-system
artifact_platform: arm64-v8a
- platform: android-64-intel
os: ubuntu-latest
upload: 1
extension: apk
preset: android-conan-ninja-release
conan_profile: android-x64-ndk
conan_prebuilts: dependencies-android-x64
conan_options: --profile=dependencies/conan_profiles/base/android-system
artifact_platform: x64
runs-on: ${{ matrix.os }}
# Allow non-MSVC builds to fail without failing whole job
# This keeps pipeline moving so Windows Installer job can still run
@@ -148,11 +140,11 @@ jobs:
uses: actions/checkout@v5
with:
submodules: recursive
- name: Prepare APT staging dir
if: contains(matrix.os, 'ubuntu')
run: mkdir -p "$RUNNER_TEMP/apt-cache"
- name: APT cache restore
if: contains(matrix.os, 'ubuntu')
id: aptcache
@@ -166,7 +158,7 @@ jobs:
- name: Prepare CI
if: "${{ matrix.before_install != '' }}"
run: source '${{github.workspace}}/CI/before_install/${{matrix.before_install}}' '${{matrix.arch}}'
# Save only on cache miss, GitHub caches are immutable per key
- name: APT cache save
if: contains(matrix.os, 'ubuntu') && steps.aptcache.outputs.cache-hit != 'true'
@@ -177,11 +169,9 @@ jobs:
- name: Install Conan Dependencies
if: "${{ matrix.conan_prebuilts != '' }}"
run: source '${{github.workspace}}/CI/install_conan_dependencies.sh' '${{matrix.conan_prebuilts}}'
- name: Install vcpkg Dependencies
if: ${{ startsWith(matrix.platform, 'msvc') }}
run: source '${{github.workspace}}/CI/install_vcpkg_dependencies.sh' '${{matrix.platform}}' 'vcpkg'
run: |
pipx install conan
source '${{github.workspace}}/CI/install_conan_dependencies.sh' '${{matrix.conan_prebuilts}}'
- name: Setup MSVC Developer Command Prompt
if: ${{ startsWith(matrix.platform, 'msvc') }}
@@ -206,7 +196,7 @@ jobs:
max-size: '5G'
verbose: 2
job-summary: "" # <-- disable built-in summary to avoid duplicate block
- name: Setup compiler cache for branch builds
uses: hendrikmuhs/ccache-action@v1.2
if: ${{ github.event.number == '' }}
@@ -220,7 +210,7 @@ jobs:
max-size: '5G'
verbose: 2
job-summary: "" # <-- disable built-in summary to avoid duplicate block
- name: CCache tuning (Android)
if: ${{ startsWith(matrix.platform, 'android') }}
run: |
@@ -230,33 +220,36 @@ jobs:
ccache --set-config=hash_dir=true
ccache --set-config=sloppiness=time_macros
- name: Install Conan
if: "${{ matrix.conan_profile != '' }}"
run: pipx install 'conan<2.0'
- name: Install Conan profile
if: "${{ matrix.conan_profile != '' }}"
run: |
conan profile new default --detect
conan profile detect
conan install . \
--install-folder=conan-generated \
--no-imports \
--output-folder=conan-generated \
--build=never \
--profile:build=default \
--profile:host=CI/conan/${{ matrix.conan_profile }} \
--profile=dependencies/conan_profiles/${{ matrix.conan_profile }} \
${{ matrix.conan_options }}
env:
GENERATE_ONLY_BUILT_CONFIG: 1
# Can't be set in Gradle project
- name: Configure enableUncompressedNativeLibs
if: ${{ startsWith(matrix.platform, 'android') }}
run: mkdir -p ~/.gradle && echo "android.bundle.enableUncompressedNativeLibs=true" > ~/.gradle/gradle.properties
# Workaround for gradle not discovering SDK that was installed via conan
- name: Find Android NDK
# Workaround for gradle not discovering SDK that was installed via Сonan
- name: Link Android NDK from Conan cache to Android SDK path
if: ${{ startsWith(matrix.platform, 'android') }}
run: sudo ln -s -T /home/runner/.conan/data/android-ndk/r25c/_/_/package/4db1be536558d833e52e862fd84d64d75c2b3656/bin /usr/local/lib/android/sdk/ndk/25.2.9519653
run: |
ndkPackage='android-ndk'
hexRegex='[[:xdigit:]]+'
ndkPackageRevision=$(conan list "$ndkPackage/*:*" --format=compact \
| egrep --only-matching "$ndkPackage/\\w+#$hexRegex:$hexRegex")
ndkPackagePath=$(conan cache path "$ndkPackageRevision")
# format: Pkg.Revision = 25.2.9519653
ndkPath="$ndkPackagePath/bin"
ndkVersion=$(fgrep 'Pkg.Revision' "$ndkPath/source.properties" | cut -d ' ' -f 3)
ln -s -T "$ndkPath" "$ANDROID_HOME/ndk/$ndkVersion"
- name: Install Java
uses: actions/setup-java@v5
@@ -266,6 +259,7 @@ jobs:
java-version: '17'
# a hack to build ID for x64 build in order for Google Play to allow upload of both 32 and 64 bit builds
# TODO: x86_64
- name: Bump Android x64 build ID
if: ${{ matrix.platform == 'android-64' }}
run: perl -i -pe 's/versionCode (\d+)/$x=$1+1; "versionCode $x"/e' android/vcmi-app/build.gradle
@@ -283,27 +277,33 @@ jobs:
env:
PULL_REQUEST: ${{ github.event.pull_request.number }}
- name: Configure
- name: Configure (non-MSVC)
if: ${{ !startsWith(matrix.platform, 'msvc') }}
run: |
if [[ ("${{ matrix.preset }}" == "android-conan-ninja-release") && ("${{ github.ref }}" != 'refs/heads/master') ]]; then
cmake -DENABLE_CCACHE:BOOL=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DANDROID_GRADLE_PROPERTIES="applicationIdSuffix=.daily;signingConfig=dailySigning;applicationLabel=VCMI daily;applicationVariant=daily" \
--preset ${{ matrix.preset }}
elif ${{ startsWith(matrix.platform, 'msvc') }}; then
CL="$VCToolsInstallDir/bin/${{ matrix.cl }}"
cmake \
-D CMAKE_C_COMPILER:FILEPATH="$CL" \
-D CMAKE_CXX_COMPILER:FILEPATH="$CL" \
--preset ${{ matrix.preset }}
else
cmake -DENABLE_CCACHE:BOOL=ON --preset ${{ matrix.preset }}
# key1=value1;key2=value2;...
gradleProperties=$(python3 CI/android/gradle_daily_props.py)
androidOptions=("-DANDROID_GRADLE_PROPERTIES=$gradleProperties")
androidOptions+=("-DCMAKE_C_COMPILER_LAUNCHER=ccache" "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache")
fi
cmake -DENABLE_CCACHE:BOOL=ON "${androidOptions[@]}" --preset ${{ matrix.preset }}
- name: Configure (MSVC)
if: ${{ startsWith(matrix.platform, 'msvc') }}
run: |
& conan-generated\conanrun.ps1
$CL = "$($env:VCToolsInstallDir)/bin/${{ matrix.cl }}"
cmake `
-D "CMAKE_C_COMPILER:FILEPATH=$CL" `
-D "CMAKE_CXX_COMPILER:FILEPATH=$CL" `
--preset ${{ matrix.preset }}
shell: pwsh
- name: Build
run: |
${{ startsWith(matrix.platform, 'msvc') && '& conan-generated\conanrun.ps1' }}
cmake --build --preset ${{matrix.preset}}
shell: pwsh
env:
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
@@ -320,15 +320,10 @@ jobs:
id: cpack
if: ${{ matrix.pack == 1 }}
run: |
cd '${{github.workspace}}/out/build/${{matrix.preset}}'
# Workaround for CPack bug on macOS 13
counter=0
until cpack -C ${{matrix.pack_type}} || ((counter > 20)); do
sleep 3
((counter++))
done
rm -rf _CPack_Packages
${{ startsWith(matrix.platform, 'msvc') && '& conan-generated\conanrun.ps1' }}
cd "${{github.workspace}}/out/build/${{matrix.preset}}"
cpack -C ${{matrix.pack_type}}
shell: pwsh
- name: Find Android package
if: ${{ startsWith(matrix.platform, 'android') }}
@@ -410,14 +405,14 @@ jobs:
run: |
source '${{github.workspace}}/CI/get_package_name.sh'
echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
- name: Create source code archive (including submodules)
run: |
git archive HEAD -o "release.tar" --worktree-attributes -v
git submodule update --init --recursive
git submodule --quiet foreach 'cd "$toplevel"; tar -rvf "release.tar" "$sm_path"'
gzip release.tar
- name: Upload source code archive
id: upload_source
uses: actions/upload-artifact@v4
@@ -434,7 +429,7 @@ jobs:
cat > .summary/source.json <<JSON
{"source_url": "${{ steps.upload_source.outputs.artifact-url }}"}
JSON
- name: Upload partial JSON with source informations
uses: actions/upload-artifact@v4
with:
@@ -477,7 +472,7 @@ jobs:
compiler_cxx: clang++-13
compiler_cc: clang-13
preset: linux-clang-test
runs-on: ${{ matrix.os }}
defaults:
run:
@@ -504,7 +499,7 @@ jobs:
${{ matrix.platform }}-apt-${{ matrix.os }}
- name: Prepare CI
run: source '${{github.workspace}}/CI/before_install/${{matrix.before_install}}'
run: source '${{github.workspace}}/CI/before_install/${{matrix.before_install}}' '${{matrix.arch}}'
- name: APT cache save
if: contains(matrix.os, 'ubuntu') && steps.aptcache.outputs.cache-hit != 'true'
@@ -549,7 +544,7 @@ jobs:
7za x h3_assets.zip -p$HEROES_3_DATA_PASSWORD
mkdir -p ~/.local/share/vcmi/
mv h3_assets/* ~/.local/share/vcmi/
- name: Configure
run: |
cmake -DENABLE_CCACHE:BOOL=ON -DCMAKE_C_COMPILER=${{ matrix.compiler_cc }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler_cxx }} --preset ${{ matrix.preset }}
@@ -595,7 +590,6 @@ jobs:
- name: Extract version info
id: extract-version
shell: bash
run: |
filePath="${GITHUB_WORKSPACE}/cmake_modules/VersionDefinition.cmake"
@@ -609,8 +603,8 @@ jobs:
echo "short_version=${short_version}" >> "$GITHUB_OUTPUT"
echo "version_timestamp=${version_timestamp}" >> "$GITHUB_OUTPUT"
- name: Install ucrt Dependencies
run: source '${{github.workspace}}/CI/install_vcpkg_dependencies.sh' '${{matrix.platform}}' 'ucrt'
- name: Download UCRT
run: source '${{github.workspace}}/CI/wininstaller/download_ucrt.sh' '${{matrix.platform}}'
- name: Build Number
run: |
@@ -630,13 +624,11 @@ jobs:
path: ${{github.workspace}}/artifact
- name: Extract Artifact
shell: bash
run: |
mkdir artifact/extracted
unzip "artifact/${{ env.VCMI_PACKAGE_FILE_NAME }}.zip" -d artifact/extracted
- name: Ensure Inno Setup is installed
shell: bash
run: |
if [ ! -f "/c/Program Files (x86)/Inno Setup 6/ISCC.exe" ] && [ ! -f "/c/ProgramData/Chocolatey/bin/ISCC.exe" ]; then
choco install innosetup --no-progress -y
@@ -728,7 +720,7 @@ jobs:
steps:
- name: Checkout (for script path)
uses: actions/checkout@v5
- name: Download all partial JSON artifacts
continue-on-error: true
uses: actions/download-artifact@v5
@@ -736,7 +728,7 @@ jobs:
pattern: partial-json-*
merge-multiple: true
path: partials
- name: Run final summary
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@@ -56,8 +56,6 @@ fuzzylite.pc
/client/RD
/launcher/RD
/lib/RD
/lib/minizip/RD
/lib/minizip/*/RD
/scripting/erm/RD
/scripting/erm/*/RD
/server/RD

3
.gitmodules vendored
View File

@@ -10,3 +10,6 @@
path = launcher/lib/innoextract
url = https://github.com/vcmi/innoextract.git
branch = vcmi
[submodule "dependencies"]
path = dependencies
url = https://github.com/vcmi/vcmi-dependencies.git

View File

@@ -19,10 +19,6 @@ else()
set(fuzzylite_FOUND FALSE)
endif()
if(TARGET fuzzylite::fuzzylite AND MSVC)
install_vcpkg_imported_tgt(fuzzylite::fuzzylite)
endif()
if(NOT fuzzylite_FOUND)
set(FL_BUILD_BINARY OFF CACHE BOOL "")
set(FL_BUILD_SHARED OFF CACHE BOOL "")

View File

@@ -0,0 +1,7 @@
dic = {
"applicationIdSuffix": ".daily",
"applicationLabel": "VCMI daily",
"applicationVariant": "daily",
"signingConfig": "dailySigning",
}
print(";".join([f"{key}={value}" for key, value in dic.items()]))

View File

@@ -1,4 +0,0 @@
#!/bin/sh
sudo apt-get update
sudo apt-get install ninja-build

View File

@@ -24,4 +24,4 @@ sudo eatmydata apt -yq --no-install-recommends \
sudo rm -f "$APT_CACHE/lock" || true
sudo rm -rf "$APT_CACHE/partial" || true
sudo chown -R "$USER:$USER" "$APT_CACHE"
sudo chown -R "$USER:$USER" "$APT_CACHE"

View File

@@ -24,4 +24,4 @@ sudo eatmydata apt -yq --no-install-recommends \
sudo rm -f "$APT_CACHE/lock" || true
sudo rm -rf "$APT_CACHE/partial" || true
sudo chown -R "$USER:$USER" "$APT_CACHE"
sudo chown -R "$USER:$USER" "$APT_CACHE"

View File

@@ -1,3 +1,3 @@
#!/usr/bin/env bash
echo DEVELOPER_DIR=/Applications/Xcode_14.2.app >> $GITHUB_ENV
echo DEVELOPER_DIR=/Applications/Xcode_16.2.app >> $GITHUB_ENV

View File

@@ -1,35 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
ARCH="${1:-x86_64}"
case "$ARCH" in
x86) triplet=i686-w64-mingw32 ;;
x86_64) triplet=x86_64-w64-mingw32 ;;
*) echo "Unsupported ARCH '$ARCH' (use: x86 | x86_64)"; exit 2 ;;
esac
APT_CACHE="${APT_CACHE:-${RUNNER_TEMP:-/tmp}/apt-cache}"
sudo mkdir -p "$APT_CACHE"
sudo apt -yq -o Acquire::Retries=3 update
sudo apt -yq install eatmydata
sudo eatmydata apt -yq --no-install-recommends \
-o Dir::Cache::archives="$APT_CACHE" \
-o APT::Keep-Downloaded-Packages=true \
-o Acquire::Retries=3 -o Dpkg::Use-Pty=0 \
install \
ninja-build nsis mingw-w64 g++-mingw-w64
if [[ -x "/usr/bin/${triplet}-g++-posix" ]]; then
sudo update-alternatives --set "${triplet}-g++" "/usr/bin/${triplet}-g++-posix"
fi
if [[ -x "/usr/bin/${triplet}-gcc-posix" ]]; then
sudo update-alternatives --set "${triplet}-gcc" "/usr/bin/${triplet}-gcc-posix"
fi
sudo rm -f "$APT_CACHE/lock" || true
sudo rm -rf "$APT_CACHE/partial" || true
sudo chown -R "$USER:$USER" "$APT_CACHE"

View File

@@ -1,17 +0,0 @@
#!/usr/bin/env bash
MSVC_INSTALL_PATH=$(vswhere -latest -property installationPath)
echo "MSVC_INSTALL_PATH = $MSVC_INSTALL_PATH"
echo "Installed toolset versions:"
ls -vr "$MSVC_INSTALL_PATH/VC/Tools/MSVC"
TOOLS_DIR=$(ls -vr "$MSVC_INSTALL_PATH/VC/Tools/MSVC/" | head -1)
DUMPBIN_PATH="$MSVC_INSTALL_PATH/VC/Tools/MSVC/$TOOLS_DIR/bin/Hostx64/x64/dumpbin.exe"
# This command should work as well, but for some reason it is *extremely* slow on the Github CI (~7 minutes)
#DUMPBIN_PATH=$(vswhere -latest -find **/dumpbin.exe | head -n 1)
echo "TOOLS_DIR = $TOOLS_DIR"
echo "DUMPBIN_PATH = $DUMPBIN_PATH"
dirname "$DUMPBIN_PATH" > "$GITHUB_PATH"

View File

@@ -1,5 +0,0 @@
include(base/android)
[settings]
arch=armv7
os.api_level=19

View File

@@ -1,4 +0,0 @@
include(android-32)
[tool_requires]
android-ndk/r25c

View File

@@ -1,5 +0,0 @@
include(base/android)
[settings]
arch=armv8
os.api_level=21

View File

@@ -1,4 +0,0 @@
include(android-64)
[tool_requires]
android-ndk/r25c

View File

@@ -1,10 +0,0 @@
[settings]
build_type=Release
compiler=clang
compiler.libcxx=c++_shared
compiler.version=14
os=Android
[buildenv]
# fixes shared libiconv build
LD=ld

View File

@@ -1,12 +0,0 @@
[settings]
compiler=apple-clang
compiler.version=14
compiler.libcxx=libc++
build_type=Release
# required for Boost.Locale in versions >= 1.81
compiler.cppstd=11
[conf]
tools.apple:enable_bitcode = False
tools.cmake.cmaketoolchain:generator = Ninja

View File

@@ -1,21 +0,0 @@
{% macro generate_env(target_host) -%}
CONAN_CROSS_COMPILE={{ target_host }}-
CHOST={{ target_host }}
AR={{ target_host }}-ar
AS={{ target_host }}-as
CC={{ target_host }}-gcc
CXX={{ target_host }}-g++
RANLIB={{ target_host }}-ranlib
STRIP={{ target_host }}-strip
{%- endmacro -%}
{% macro generate_env_win32(target_host) -%}
CONAN_SYSTEM_LIBRARY_LOCATION=/usr/lib/gcc/{{ target_host }}/13-posix/
RC={{ target_host }}-windres
{%- endmacro -%}
{% macro generate_conf(target_host) -%}
tools.build:compiler_executables = {"c": "{{ target_host }}-gcc", "cpp": "{{ target_host }}-g++"}
tools.build:sysroot = /usr/{{ target_host }}
tools.build:defines = ["WINVER=0x0601", "_WIN32_WINNT=0x0601"]
{%- endmacro -%}

View File

@@ -1,10 +0,0 @@
[settings]
os=Windows
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=10
compiler.cppstd=11
build_type=Release
[conf]
tools.cmake.cmaketoolchain:generator = Ninja

View File

@@ -1,5 +0,0 @@
include(apple)
[settings]
os=iOS
os.sdk=iphoneos

View File

@@ -1,4 +0,0 @@
include(apple)
[settings]
os=Macos

View File

@@ -1,5 +0,0 @@
include(base/ios)
[settings]
os.version=12.0
arch=armv8

View File

@@ -1,8 +0,0 @@
include(base/ios)
[settings]
os.version=10.0
arch=armv7
# Xcode 13.x is the last version that can build for armv7
compiler.version=13

View File

@@ -1,5 +0,0 @@
include(base/macos)
[settings]
os.version=11.0
arch=armv8

View File

@@ -1,5 +0,0 @@
include(base/macos)
[settings]
os.version=10.13
arch=x86_64

View File

@@ -1,15 +0,0 @@
{% import 'base/cross-macro.j2' as cross -%}
include(base/cross-windows)
{% set target_host="i686-w64-mingw32" %}
[settings]
arch=x86
[conf]
{{ cross.generate_conf(target_host)}}
tools.build:cflags = ["-msse2"]
tools.build:cxxflags = ["-msse2"]
[env]
{{ cross.generate_env(target_host) }}
{{ cross.generate_env_win32(target_host) }}

View File

@@ -1,13 +0,0 @@
{% import 'base/cross-macro.j2' as cross -%}
include(base/cross-windows)
{% set target_host="x86_64-w64-mingw32" %}
[settings]
arch=x86_64
[conf]
{{ cross.generate_conf(target_host)}}
[env]
{{ cross.generate_env(target_host) }}
{{ cross.generate_env_win32(target_host) }}

View File

@@ -83,7 +83,7 @@ def arch_label(platform: str, arch_env: Optional[str]) -> str:
return arch_env
mapping = {
"mac-intel": "Intel",
"mac-arm": "ARM64",
"mac-arm": "Apple Silicon",
"ios": "ARM64",
"msvc-x64": "x64",
"msvc-x86": "x86",
@@ -92,6 +92,7 @@ def arch_label(platform: str, arch_env: Optional[str]) -> str:
"mingw_x86_64": "x64",
"android-32": "ARMv7",
"android-64": "ARM64",
"android-64-intel": "x86_64",
}
return mapping.get(platform, platform)

View File

@@ -184,7 +184,7 @@
// MD040/fenced-code-language : Fenced code blocks should have a language specified : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md040.md
"MD040": {
// List of languages
"allowed_languages": [ "cpp", "json", "sh", "text", "nix", "powershell", "lua" ],
"allowed_languages": [ "cpp", "json", "sh", "text", "nix", "powershell", "lua", "batchfile" ],
// Require language only
"language_only": true
},

View File

@@ -1,9 +1,9 @@
#!/usr/bin/env bash
RELEASE_TAG="1.3"
FILENAME="$1"
DOWNLOAD_URL="https://github.com/vcmi/vcmi-dependencies/releases/download/$RELEASE_TAG/$FILENAME.txz"
RELEASE_TAG="2025-08-24"
FILENAME="$1.tgz"
DOWNLOAD_URL="https://github.com/vcmi/vcmi-dependencies/releases/download/$RELEASE_TAG/$FILENAME"
mkdir ~/.conan
cd ~/.conan
curl -L "$DOWNLOAD_URL" | tar -xf - --xz
downloadedFile="$RUNNER_TEMP/$FILENAME"
curl -Lo "$downloadedFile" "$DOWNLOAD_URL"
conan cache restore "$downloadedFile"

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env bash
ACCOUNT="vcmi"
# Fetch latest release tag from GitHub API
# RELEASE_TAG=$(curl -s "https://api.github.com/repos/$ACCOUNT/vcmi-deps-windows/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
RELEASE_TAG="v1.9"
# 2. parameter: all | vcpkg | ucrt (default: all)
PART="${2:-all}"
# --- VCPKG ---
if [[ "$PART" == "all" || "$PART" == "vcpkg" ]]; then
DEP_FILENAME="dependencies-$1"
DEP_URL="https://github.com/$ACCOUNT/vcmi-deps-windows/releases/download/$RELEASE_TAG/$DEP_FILENAME.txz"
curl -L "$DEP_URL" | tar -xf - --xz
fi
# --- UCRT ---
if [[ "$PART" == "all" || "$PART" == "ucrt" ]]; then
UCRT_FILENAME="ucrtRedist-$1"
UCRT_URL="https://github.com/$ACCOUNT/vcmi-deps-windows/releases/download/$RELEASE_TAG/$UCRT_FILENAME.txz"
mkdir -p ucrt
curl -L "$UCRT_URL" | tar -xf - --xz -C ucrt
fi

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
RELEASE_TAG="v1.9"
UCRT_FILENAME="ucrtRedist-$1"
UCRT_URL="https://github.com/vcmi/vcmi-deps-windows/releases/download/$RELEASE_TAG/$UCRT_FILENAME.txz"
UCRT_DIR="ucrt"
mkdir -p "$UCRT_DIR"
curl -L "$UCRT_URL" | tar -xf - --xz -C "$UCRT_DIR"

View File

@@ -183,7 +183,7 @@ include(VersionDefinition)
vcmi_print_important_variables()
# Options to enable folders in CMake generated projects for Visual Studio, Xcode, etc
# Very useful to put 3rd-party libraries such as Minizip, GoogleTest and FuzzyLite in their own folders
# Very useful to put 3rd-party libraries such as GoogleTest and FuzzyLite in their own folders
set_property(GLOBAL PROPERTY USE_FOLDERS TRUE)
# Make FOLDER property inheritable
# So when we set FOLDER property on AI directory all and targets inside will inherit it
@@ -310,7 +310,7 @@ endif()
if(MINGW OR MSVC)
# Windows Vista or newer for FuzzyLite 6 to compile
# Except for conan which already has this definition in its preset
# Except for conan which already has this definition in its profile
if(NOT USING_CONAN)
add_definitions(-D_WIN32_WINNT=0x0600)
endif()
@@ -373,14 +373,9 @@ if(MINGW OR MSVC)
# Reported to Microsoft here:
# https://developercommunity.visualstudio.com/content/problem/224597/linker-failing-because-of-multiple-definitions-of.html
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
# Required at least for compatibility with Boost 1.68 on Vcpkg
# psapi required for ARM64 builds
set(SYSTEM_LIBS ${SYSTEM_LIBS} bcrypt psapi)
endif(MSVC)
if(MINGW)
# Temporary (?) workaround for failing builds on MinGW CI due to bug in TBB
set(CMAKE_CXX_EXTENSIONS ON)
@@ -505,14 +500,7 @@ if(TARGET zlib::zlib)
add_library(ZLIB::ZLIB ALIAS zlib::zlib)
endif()
option(FORCE_BUNDLED_MINIZIP "Force bundled Minizip library" OFF)
if(NOT FORCE_BUNDLED_MINIZIP)
find_package(minizip)
if(TARGET minizip::minizip)
add_definitions(-DUSE_SYSTEM_MINIZIP)
endif()
endif()
find_package(minizip REQUIRED)
if (ENABLE_CLIENT)
if (ENABLE_VIDEO)
@@ -626,6 +614,9 @@ elseif(ANDROID)
include(GNUInstallDirs)
set(LIB_DIR "libs/${ANDROID_ABI}")
file(READ "${CMAKE_ANDROID_NDK}/meta/abis.json" ndkAbiInfo)
string(JSON ANDROID_SYSROOT_LIB_SUBDIR GET "${ndkAbiInfo}" "${ANDROID_ABI}" "triple")
# required by Qt
set(androidPackageSourceDir "${CMAKE_SOURCE_DIR}/android")
set(androidQtBuildDir "${CMAKE_BINARY_DIR}/android-build")
@@ -707,11 +698,6 @@ if(ENABLE_LUA)
add_subdirectory(scripting/lua)
endif()
if(NOT TARGET minizip::minizip)
add_subdirectory_with_folder("3rdparty" lib/minizip)
add_library(minizip::minizip ALIAS minizip)
endif()
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
add_subdirectory(vcmiqt)
endif()
@@ -746,6 +732,8 @@ endif()
# Installation section #
#######################################
vcmi_install_conan_deps()
if(ANDROID)
string(REPLACE ";" "\n" ANDROID_GRADLE_PROPERTIES_MULTILINE "${ANDROID_GRADLE_PROPERTIES}")
file(WRITE "${androidPackageSourceDir}/vcmi-app/gradle.properties" "signingRoot=${CMAKE_SOURCE_DIR}/CI/android\n${ANDROID_GRADLE_PROPERTIES_MULTILINE}")
@@ -774,59 +762,6 @@ if(NOT WIN32 AND NOT APPLE_IOS AND NOT ANDROID)
WORLD_READ WORLD_EXECUTE)
endif()
if(WIN32)
if(TBB_FOUND AND MSVC)
install_vcpkg_imported_tgt(TBB::tbb)
endif()
if(USING_CONAN)
#Conan imports enabled
vcmi_install_conan_deps("\${CMAKE_INSTALL_PREFIX}")
file(GLOB dep_files
${dep_files}
"${CMAKE_SYSROOT}/bin/*.dll"
"${CMAKE_SYSROOT}/lib/*.dll"
"${CONAN_SYSTEM_LIBRARY_LOCATION}/libgcc_s_dw2-1.dll" # for 32-bit only?
"${CONAN_SYSTEM_LIBRARY_LOCATION}/libgcc_s_seh-1.dll" # for 64-bit only?
"${CONAN_SYSTEM_LIBRARY_LOCATION}/libstdc++-6.dll")
else()
file(GLOB dep_files
${dep_files}
"${CMAKE_FIND_ROOT_PATH}/bin/*.dll")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Copy debug versions of libraries if build type is debug
set(debug_postfix d)
endif()
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
get_target_property(QtCore_location Qt${QT_VERSION_MAJOR}::Core LOCATION)
get_filename_component(Qtbin_folder ${QtCore_location} PATH)
file(GLOB dep_files
${dep_files}
${Qtbin_folder}/Qt5Core${debug_postfix}.dll
${Qtbin_folder}/Qt5Gui${debug_postfix}.dll
${Qtbin_folder}/Qt5Widgets${debug_postfix}.dll
${Qtbin_folder}/Qt5Network${debug_postfix}.dll
${Qtbin_folder}/icu*.dll)
get_target_property(integration_type Qt${QT_VERSION_MAJOR}::QWindowsIntegrationPlugin TYPE)
if(NOT(integration_type STREQUAL "INTERFACE_LIBRARY"))
get_target_property(integration_loc Qt${QT_VERSION_MAJOR}::QWindowsIntegrationPlugin LOCATION)
install(
FILES ${integration_loc}
DESTINATION ${BIN_DIR}/platforms
)
install(
FILES "$<TARGET_FILE:Qt${QT_VERSION_MAJOR}::QWindowsVistaStylePlugin>"
DESTINATION ${BIN_DIR}/styles)
endif()
endif()
install(FILES ${dep_files} DESTINATION ${BIN_DIR})
endif(WIN32)
#######################################
# Packaging section #
#######################################

View File

@@ -10,8 +10,7 @@
"name": "build-with-conan",
"hidden": true,
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/conan-generated/conan_toolchain.cmake",
"FORCE_BUNDLED_MINIZIP": "OFF"
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/conan-generated/conan_toolchain.cmake"
}
},
{
@@ -155,28 +154,30 @@
"name": "windows-msvc-release",
"displayName": "Windows x64 RelWithDebInfo",
"description": "VCMI RelWithDebInfo build",
"inherits": "default-release",
"inherits": [
"build-with-conan",
"default-release"
],
"generator": "Visual Studio 17 2022",
"architecture": {
"value": "x64",
"strategy": "set"
},
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW",
"FORCE_BUNDLED_MINIZIP": "ON"
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW"
}
},
{
"name": "windows-msvc-release-x86",
"displayName": "Windows x86 RelWithDebInfo",
"description": "VCMI RelWithDebInfo build",
"inherits": "default-release",
"inherits": [
"build-with-conan",
"default-release"
],
"generator": "Visual Studio 17 2022",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW",
"FORCE_BUNDLED_MINIZIP": "ON",
"CMAKE_GENERATOR_PLATFORM": "WIN32"
}
},
@@ -184,16 +185,17 @@
"name": "windows-msvc-release-arm64",
"displayName": "Windows ARM64 RelWithDebInfo",
"description": "VCMI Windows ARM64 build",
"inherits": "default-release",
"inherits": [
"build-with-conan",
"default-release"
],
"generator": "Visual Studio 17 2022",
"architecture": {
"value": "ARM64",
"strategy": "set"
},
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW",
"FORCE_BUNDLED_MINIZIP": "ON"
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW"
}
},
{
@@ -206,57 +208,38 @@
}
},
{
"name": "windows-msvc-ninja-release",
"displayName": "Windows x64 RelWithDebInfo (Ninja)",
"description": "VCMI RelWithDebInfo build using Ninja + sccache",
"inherits": "default-release",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW",
"FORCE_BUNDLED_MINIZIP": "ON",
"ENABLE_CCACHE": "ON",
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
"CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "Embedded",
"ENABLE_MULTI_PROCESS_BUILDS": "OFF",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"CMAKE_C_FLAGS_RELWITHDEBINFO": "/O2 /Ob1 /DNDEBUG /Z7",
"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/O2 /Ob1 /DNDEBUG /Z7"
}
},
{
"name": "windows-msvc-ninja-release-x86",
"displayName": "Windows x86 RelWithDebInfo (Ninja)",
"description": "VCMI RelWithDebInfo build using Ninja + sccache (x86)",
"inherits": "windows-msvc-ninja-release",
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x86-windows"
}
},
{
"name": "windows-msvc-ninja-release-arm64",
"displayName": "Windows ARM64 RelWithDebInfo (Ninja)",
"description": "VCMI RelWithDebInfo build using Ninja + sccache (ARM64)",
"inherits": "windows-msvc-ninja-release",
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "arm64-windows"
}
},
{
"name": "windows-mingw-conan-linux",
"displayName": "Ninja+Conan release",
"description": "VCMI Windows Ninja using Conan on Linux",
"name": "windows-msvc-ninja-release",
"displayName": "Windows x64 RelWithDebInfo (Ninja)",
"description": "VCMI RelWithDebInfo build using Ninja + sccache",
"inherits": [
"build-with-conan",
"default-release"
],
"generator": "Ninja",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"FORCE_BUNDLED_FL": "ON",
"ENABLE_TEMPLATE_EDITOR": "OFF"
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW",
"ENABLE_CCACHE": "ON",
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
"CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "Embedded",
"ENABLE_MULTI_PROCESS_BUILDS": "OFF",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"CMAKE_C_FLAGS_RELWITHDEBINFO": "/O2 /Ob1 /DNDEBUG /Z7",
"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/O2 /Ob1 /DNDEBUG /Z7"
}
},
{
"name": "windows-msvc-ninja-release-x86",
"displayName": "Windows x86 RelWithDebInfo (Ninja)",
"description": "VCMI RelWithDebInfo build using Ninja + sccache (x86)",
"inherits": "windows-msvc-ninja-release"
},
{
"name": "windows-msvc-ninja-release-arm64",
"displayName": "Windows ARM64 RelWithDebInfo (Ninja)",
"description": "VCMI RelWithDebInfo build using Ninja + sccache (ARM64)",
"inherits": "windows-msvc-ninja-release"
},
{
"name": "macos-ninja-release",
"displayName": "Ninja release",
@@ -290,36 +273,20 @@
"generator": "Xcode"
},
{
"name": "ios-device",
"displayName": "Base iOS device",
"description": "Base VCMI preset for iOS device",
"name": "ios-device-conan",
"displayName": "Base iOS device using Conan",
"description": "Base VCMI preset for iOS device using Conan",
"inherits": [
"build-with-conan"
],
"generator": "Xcode",
"binaryDir": "../build-${presetName}",
"cacheVariables": {
"CMAKE_SYSTEM_NAME": "iOS",
"FORCE_BUNDLED_FL": "ON",
"FORCE_BUNDLED_MINIZIP": "ON",
"ENABLE_EDITOR" : "OFF"
}
},
{
"name": "ios-simulator",
"displayName": "Base iOS simulator",
"description": "Base VCMI preset for iOS simulator",
"inherits": "ios-device",
"cacheVariables": {
"CMAKE_OSX_SYSROOT": "iphonesimulator"
}
},
{
"name": "ios-device-conan",
"displayName": "Base iOS device using Conan",
"description": "Base VCMI preset for iOS device using Conan",
"inherits": [
"build-with-conan",
"ios-device"
]
},
{
"name": "base-ios-release",
"displayName": "Base iOS release",
@@ -352,18 +319,6 @@
"ENABLE_CCACHE": "ON"
}
},
{
"name": "ios-release-legacy",
"displayName": "iOS release using legacy dependencies",
"description": "VCMI iOS release using legacy dependencies",
"inherits": [
"base-ios-release",
"ios-device"
],
"cacheVariables": {
"CMAKE_PREFIX_PATH": "${sourceDir}/build/iphoneos"
}
},
{
"name": "android-conan-ninja-release",
"displayName": "Android release",
@@ -522,12 +477,6 @@
"configurePreset": "windows-msvc-ninja-release-arm64",
"inherits": "default-release"
},
{
"name": "windows-mingw-conan-linux",
"configurePreset": "windows-mingw-conan-linux",
"inherits": "default-release",
"configuration": "Release"
},
{
"name": "ios-release-conan",
"configurePreset": "ios-release-conan",
@@ -543,11 +492,6 @@
"configurePreset": "ios-release-conan-ccache",
"inherits": "ios-release-conan"
},
{
"name": "ios-release-legacy",
"configurePreset": "ios-release-legacy",
"inherits": "ios-release-conan"
},
{
"name": "android-conan-ninja-release",
"configurePreset": "android-conan-ninja-release",
@@ -613,11 +557,6 @@
"name": "windows-msvc-release",
"configurePreset": "windows-msvc-release",
"inherits": "default-release"
},
{
"name": "windows-mingw-conan-linux",
"configurePreset": "windows-mingw-conan-linux",
"inherits": "default-release"
}
]
}

View File

@@ -46,7 +46,7 @@ android {
manifest.srcFile '../AndroidManifest.xml'
jniLibs.srcDirs = ['../libs']
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
java.srcDirs = [qt5AndroidDir + '/src', 'src', SDL_JAVA_SRC_DIR]
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qt5AndroidDir + '/res', 'src/main/res', '../res']
}
@@ -102,24 +102,6 @@ android {
}
}
def CommandOutput(final cmd, final arguments, final cwd) {
try {
new ByteArrayOutputStream().withStream { final os ->
exec {
executable cmd
args arguments
workingDir cwd
standardOutput os
}
return os.toString().trim()
}
}
catch (final Exception ex) {
print("Broken: " + cmd + " " + arguments + " in " + cwd + " :: " + ex.toString())
return ""
}
}
def SigningPropertiesPath(final basePath, final signingConfigKey) {
return file("${basePath}/${signingConfigKey}.properties")
}
@@ -142,7 +124,6 @@ def LoadSigningConfig(final signingConfigKey) {
&& props.containsKey('KEY_ALIAS')) {
signingConfig.storeFile = SigningKeystorePath(signingRoot, props['STORE_FILE'])
signingConfig.storePassword = props['STORE_PASSWORD']
signingConfig.keyAlias = props['KEY_ALIAS']
if(props.containsKey('STORE_PASSWORD'))

View File

@@ -77,7 +77,7 @@ public class VcmiSDLActivity extends SDLActivity
@Override
protected String[] getLibraries() {
// SDL is linked statically, no need to load anything
// app main library and SDL are loaded when launcher starts, no extra work to do
return new String[] {
};
}

View File

@@ -1,22 +0,0 @@
package org.libsdl.app;
import android.hardware.usb.UsbDevice;
interface HIDDevice
{
public int getId();
public int getVendorId();
public int getProductId();
public String getSerialNumber();
public int getVersion();
public String getManufacturerName();
public String getProductName();
public UsbDevice getDevice();
public boolean open();
public int sendFeatureReport(byte[] report);
public int sendOutputReport(byte[] report);
public boolean getFeatureReport(byte[] report);
public void setFrozen(boolean frozen);
public void close();
public void shutdown();
}

View File

@@ -1,650 +0,0 @@
package org.libsdl.app;
import android.content.Context;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothGattService;
import android.hardware.usb.UsbDevice;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.os.*;
//import com.android.internal.util.HexDump;
import java.lang.Runnable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.UUID;
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi";
private HIDDeviceManager mManager;
private BluetoothDevice mDevice;
private int mDeviceId;
private BluetoothGatt mGatt;
private boolean mIsRegistered = false;
private boolean mIsConnected = false;
private boolean mIsChromebook = false;
private boolean mIsReconnecting = false;
private boolean mFrozen = false;
private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null;
private Handler mHandler;
private static final int TRANSPORT_AUTO = 0;
private static final int TRANSPORT_BREDR = 1;
private static final int TRANSPORT_LE = 2;
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
static class GattOperation {
private enum Operation {
CHR_READ,
CHR_WRITE,
ENABLE_NOTIFICATION
}
Operation mOp;
UUID mUuid;
byte[] mValue;
BluetoothGatt mGatt;
boolean mResult = true;
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mValue = value;
}
public void run() {
// This is executed in main thread
BluetoothGattCharacteristic chr;
switch (mOp) {
case CHR_READ:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
if (!mGatt.readCharacteristic(chr)) {
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case CHR_WRITE:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
chr.setValue(mValue);
if (!mGatt.writeCharacteristic(chr)) {
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case ENABLE_NOTIFICATION:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
if (chr != null) {
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
int properties = chr.getProperties();
byte[] value;
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
Log.e(TAG, "Unable to start notifications on input characteristic");
mResult = false;
return;
}
mGatt.setCharacteristicNotification(chr, true);
cccd.setValue(value);
if (!mGatt.writeDescriptor(cccd)) {
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
mResult = false;
return;
}
mResult = true;
}
}
}
}
public boolean finish() {
return mResult;
}
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
return null;
return valveService.getCharacteristic(uuid);
}
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.CHR_READ, uuid);
}
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
}
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
}
}
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
mManager = manager;
mDevice = device;
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
mIsRegistered = false;
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
mOperations = new LinkedList<GattOperation>();
mHandler = new Handler(Looper.getMainLooper());
mGatt = connectGatt();
// final HIDDeviceBLESteamController finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.checkConnectionForChromebookIssue();
// }
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
public String getIdentifier() {
return String.format("SteamController.%s", mDevice.getAddress());
}
public BluetoothGatt getGatt() {
return mGatt;
}
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
private BluetoothGatt connectGatt(boolean managed) {
if (Build.VERSION.SDK_INT >= 23) {
try {
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
} catch (Exception e) {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
} else {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
}
private BluetoothGatt connectGatt() {
return connectGatt(false);
}
protected int getConnectionState() {
Context context = mManager.getContext();
if (context == null) {
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
return BluetoothProfile.STATE_DISCONNECTED;
}
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
if (btManager == null) {
// This device doesn't support Bluetooth. We should never be here, because how did
// we instantiate a device to start with?
return BluetoothProfile.STATE_DISCONNECTED;
}
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
}
public void reconnect() {
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
mGatt.disconnect();
mGatt = connectGatt();
}
}
protected void checkConnectionForChromebookIssue() {
if (!mIsChromebook) {
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
// over and over.
return;
}
int connectionState = getConnectionState();
switch (connectionState) {
case BluetoothProfile.STATE_CONNECTED:
if (!mIsConnected) {
// We are in the Bad Chromebook Place. We can force a disconnect
// to try to recover.
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
else if (!isRegistered()) {
if (mGatt.getServices().size() > 0) {
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
probeService(this);
}
else {
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
}
else {
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
return;
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
case BluetoothProfile.STATE_CONNECTING:
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
break;
}
final HIDDeviceBLESteamController finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.checkConnectionForChromebookIssue();
}
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
private boolean isRegistered() {
return mIsRegistered;
}
private void setRegistered() {
mIsRegistered = true;
}
private boolean probeService(HIDDeviceBLESteamController controller) {
if (isRegistered()) {
return true;
}
if (!mIsConnected) {
return false;
}
Log.v(TAG, "probeService controller=" + controller);
for (BluetoothGattService service : mGatt.getServices()) {
if (service.getUuid().equals(steamControllerService)) {
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(inputCharacteristic)) {
Log.v(TAG, "Found input characteristic");
// Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
enableNotification(chr.getUuid());
}
}
}
return true;
}
}
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
mIsConnected = false;
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
}
return false;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private void finishCurrentGattOperation() {
GattOperation op = null;
synchronized (mOperations) {
if (mCurrentOperation != null) {
op = mCurrentOperation;
mCurrentOperation = null;
}
}
if (op != null) {
boolean result = op.finish(); // TODO: Maybe in main thread as well?
// Our operation failed, let's add it back to the beginning of our queue.
if (!result) {
mOperations.addFirst(op);
}
}
executeNextGattOperation();
}
private void executeNextGattOperation() {
synchronized (mOperations) {
if (mCurrentOperation != null)
return;
if (mOperations.isEmpty())
return;
mCurrentOperation = mOperations.removeFirst();
}
// Run in main thread
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mOperations) {
if (mCurrentOperation == null) {
Log.e(TAG, "Current operation null in executor?");
return;
}
mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation
}
}
});
}
private void queueGattOperation(GattOperation op) {
synchronized (mOperations) {
mOperations.add(op);
}
executeNextGattOperation();
}
private void enableNotification(UUID chrUuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
queueGattOperation(op);
}
public void writeCharacteristic(UUID uuid, byte[] value) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
queueGattOperation(op);
}
public void readCharacteristic(UUID uuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
queueGattOperation(op);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// BluetoothGattCallback overridden methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
mIsReconnecting = false;
if (newState == 2) {
mIsConnected = true;
// Run directly, without GattOperation
if (!isRegistered()) {
mHandler.post(new Runnable() {
@Override
public void run() {
mGatt.discoverServices();
}
});
}
}
else if (newState == 0) {
mIsConnected = false;
}
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onServicesDiscovered status=" + status);
if (status == 0) {
if (gatt.getServices().size() == 0) {
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
mIsReconnecting = true;
mIsConnected = false;
gatt.disconnect();
mGatt = connectGatt(false);
}
else {
probeService(this);
}
}
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
}
finishCurrentGattOperation();
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic)) {
// Only register controller with the native side once it has been fully configured
if (!isRegistered()) {
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
setRegistered();
}
}
finishCurrentGattOperation();
}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
}
}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//Log.v(TAG, "onDescriptorRead status=" + status);
}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
if (chr.getUuid().equals(inputCharacteristic)) {
boolean hasWrittenInputDescriptor = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
if (reportChr != null) {
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
gatt.writeCharacteristic(reportChr);
}
}
finishCurrentGattOperation();
}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Log.v(TAG, "onReadRemoteRssi status=" + status);
}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
//Log.v(TAG, "onMtuChanged status=" + status);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////// Public API
//////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
// Valve Corporation
final int VALVE_USB_VID = 0x28DE;
return VALVE_USB_VID;
}
@Override
public int getProductId() {
// We don't have an easy way to query from the Bluetooth device, but we know what it is
final int D0G_BLE2_PID = 0x1106;
return D0G_BLE2_PID;
}
@Override
public String getSerialNumber() {
// This will be read later via feature report by Steam
return "12345";
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
return "Valve Corporation";
}
@Override
public String getProductName() {
return "Steam Controller";
}
@Override
public UsbDevice getDevice() {
return null;
}
@Override
public boolean open() {
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
// We need to skip the first byte, as that doesn't go over the air
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
}
@Override
public int sendOutputReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
writeCharacteristic(reportCharacteristic, report);
return report.length;
}
@Override
public boolean getFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return false;
}
//Log.v(TAG, "getFeatureReport");
readCharacteristic(reportCharacteristic);
return true;
}
@Override
public void close() {
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
@Override
public void shutdown() {
close();
BluetoothGatt g = mGatt;
if (g != null) {
g.disconnect();
g.close();
mGatt = null;
}
mManager = null;
mIsRegistered = false;
mIsConnected = false;
mOperations.clear();
}
}

View File

@@ -1,679 +0,0 @@
package org.libsdl.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.os.Build;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.usb.*;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class HIDDeviceManager {
private static final String TAG = "hidapi";
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
private static HIDDeviceManager sManager;
private static int sManagerRefCount = 0;
public static HIDDeviceManager acquire(Context context) {
if (sManagerRefCount == 0) {
sManager = new HIDDeviceManager(context);
}
++sManagerRefCount;
return sManager;
}
public static void release(HIDDeviceManager manager) {
if (manager == sManager) {
--sManagerRefCount;
if (sManagerRefCount == 0) {
sManager.close();
sManager = null;
}
}
}
private Context mContext;
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
private int mNextDeviceId = 0;
private SharedPreferences mSharedPreferences = null;
private boolean mIsChromebook = false;
private UsbManager mUsbManager;
private Handler mHandler;
private BluetoothManager mBluetoothManager;
private List<BluetoothDevice> mLastBluetoothDevices;
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceAttached(usbDevice);
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceDetached(usbDevice);
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
}
}
};
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// Bluetooth device was connected. If it was a Steam Controller, handle it
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device connected: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// Bluetooth device was disconnected, remove from controller manager (if any)
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device disconnected: " + device);
disconnectBluetoothDevice(device);
}
}
};
private HIDDeviceManager(final Context context) {
mContext = context;
HIDDeviceRegisterCallback();
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
// if (shouldClear) {
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
// spedit.clear();
// spedit.commit();
// }
// else
{
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
}
}
public Context getContext() {
return mContext;
}
public int getDeviceIDForIdentifier(String identifier) {
SharedPreferences.Editor spedit = mSharedPreferences.edit();
int result = mSharedPreferences.getInt(identifier, 0);
if (result == 0) {
result = mNextDeviceId++;
spedit.putInt("next_device_id", mNextDeviceId);
}
spedit.putInt(identifier, result);
spedit.commit();
return result;
}
private void initializeUSB() {
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
if (mUsbManager == null) {
return;
}
/*
// Logging
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
Log.i(TAG,"Path: " + device.getDeviceName());
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
Log.i(TAG,"Product: " + device.getProductName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++) {
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
*/
// Register for USB broadcasts and permission completions
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
mContext.registerReceiver(mUsbBroadcast, filter);
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
handleUsbDeviceAttached(usbDevice);
}
}
UsbManager getUSBManager() {
return mUsbManager;
}
private void shutdownUSB() {
try {
mContext.unregisterReceiver(mUsbBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
return true;
}
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
return true;
}
return false;
}
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB360_IFACE_SUBCLASS = 93;
final int XB360_IFACE_PROTOCOL = 1; // Wired
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
final int[] SUPPORTED_VENDORS = {
0x0079, // GPD Win 2
0x044f, // Thrustmaster
0x045e, // Microsoft
0x046d, // Logitech
0x056e, // Elecom
0x06a3, // Saitek
0x0738, // Mad Catz
0x07ff, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1038, // SteelSeries
0x11c9, // Nacon
0x12ab, // Unknown
0x1430, // RedOctane
0x146b, // BigBen
0x1532, // Razer Sabertooth
0x15e4, // Numark
0x162e, // Joytech
0x1689, // Razer Onza
0x1949, // Lab126, Inc.
0x1bad, // Harmonix
0x20d6, // PowerA
0x24c6, // PowerA
0x2c22, // Qanba
};
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = {
0x045e, // Microsoft
0x0738, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1532, // Razer Wildcat
0x20d6, // PowerA
0x24c6, // PowerA
0x2dc8, /* 8BitDo */
0x2e24, // Hyperkin
};
if (usbInterface.getId() == 0 &&
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
connectHIDDeviceUSB(usbDevice);
}
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
List<Integer> devices = new ArrayList<Integer>();
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
devices.add(device.getId());
}
}
for (int id : devices) {
HIDDevice device = mDevicesById.get(id);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
boolean opened = false;
if (permission_granted) {
opened = device.open();
}
HIDDeviceOpenResult(device.getId(), opened);
}
}
}
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
synchronized (this) {
int interface_mask = 0;
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
// Check to see if we've already added this interface
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
int interface_id = usbInterface.getId();
if ((interface_mask & (1 << interface_id)) != 0) {
continue;
}
interface_mask |= (1 << interface_id);
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
int id = device.getId();
mDevicesById.put(id, device);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
}
}
}
}
private void initializeBluetooth() {
Log.d(TAG, "Initializing Bluetooth");
if (Build.VERSION.SDK_INT <= 30 &&
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
return;
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18)) {
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
return;
}
// Find bonded bluetooth controllers and create SteamControllers for them
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
// This device doesn't support Bluetooth.
return;
}
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
if (btAdapter == null) {
// This device has Bluetooth support in the codebase, but has no available adapters.
return;
}
// Get our bonded devices.
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
Log.d(TAG, "Bluetooth device available: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// NOTE: These don't work on Chromebooks, to my undying dismay.
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mContext.registerReceiver(mBluetoothBroadcast, filter);
if (mIsChromebook) {
mHandler = new Handler(Looper.getMainLooper());
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
// final HIDDeviceManager finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.chromebookConnectionHandler();
// }
// }, 5000);
}
}
private void shutdownBluetooth() {
try {
mContext.unregisterReceiver(mBluetoothBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
// This function provides a sort of dummy version of that, watching for changes in the
// connected devices and attempting to add controllers as things change.
public void chromebookConnectionHandler() {
if (!mIsChromebook) {
return;
}
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
for (BluetoothDevice bluetoothDevice : currentConnected) {
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
connected.add(bluetoothDevice);
}
}
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
if (!currentConnected.contains(bluetoothDevice)) {
disconnected.add(bluetoothDevice);
}
}
mLastBluetoothDevices = currentConnected;
for (BluetoothDevice bluetoothDevice : disconnected) {
disconnectBluetoothDevice(bluetoothDevice);
}
for (BluetoothDevice bluetoothDevice : connected) {
connectBluetoothDevice(bluetoothDevice);
}
final HIDDeviceManager finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.chromebookConnectionHandler();
}
}, 10000);
}
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
synchronized (this) {
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
device.reconnect();
return false;
}
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
int id = device.getId();
mBluetoothDevices.put(bluetoothDevice, device);
mDevicesById.put(id, device);
// The Steam Controller will mark itself connected once initialization is complete
}
return true;
}
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
synchronized (this) {
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
if (device == null)
return;
int id = device.getId();
mBluetoothDevices.remove(bluetoothDevice);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
if (bluetoothDevice == null) {
return false;
}
// If the device has no local name, we really don't want to try an equality check against it.
if (bluetoothDevice.getName() == null) {
return false;
}
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
}
private void close() {
shutdownUSB();
shutdownBluetooth();
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.shutdown();
}
mDevicesById.clear();
mBluetoothDevices.clear();
HIDDeviceReleaseCallback();
}
}
public void setFrozen(boolean frozen) {
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.setFrozen(frozen);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private HIDDevice getDevice(int id) {
synchronized (this) {
HIDDevice result = mDevicesById.get(id);
if (result == null) {
Log.v(TAG, "No device for id: " + id);
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
}
return result;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// JNI interface functions
//////////////////////////////////////////////////////////////////////////////////////////////////////
public boolean initialize(boolean usb, boolean bluetooth) {
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
if (usb) {
initializeUSB();
}
if (bluetooth) {
initializeBluetooth();
}
return true;
}
public boolean openDevice(int deviceID) {
Log.v(TAG, "openDevice deviceID=" + deviceID);
HIDDevice device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
// Look to see if this is a USB device and we have permission to access it
UsbDevice usbDevice = device.getDevice();
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
HIDDeviceOpenPending(deviceID);
try {
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
int flags;
if (Build.VERSION.SDK_INT >= 31) {
flags = FLAG_MUTABLE;
} else {
flags = 0;
}
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
} catch (Exception e) {
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
HIDDeviceOpenResult(deviceID, false);
}
return false;
}
try {
return device.open();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public int sendOutputReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendOutputReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public int sendFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public boolean getFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
return device.getFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public void closeDevice(int deviceID) {
try {
Log.v(TAG, "closeDevice deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return;
}
device.close();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////// Native methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
private native void HIDDeviceRegisterCallback();
private native void HIDDeviceReleaseCallback();
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
native void HIDDeviceOpenPending(int deviceID);
native void HIDDeviceOpenResult(int deviceID, boolean opened);
native void HIDDeviceDisconnected(int deviceID);
native void HIDDeviceInputReport(int deviceID, byte[] report);
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
}

View File

@@ -1,309 +0,0 @@
package org.libsdl.app;
import android.hardware.usb.*;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
class HIDDeviceUSB implements HIDDevice {
private static final String TAG = "hidapi";
protected HIDDeviceManager mManager;
protected UsbDevice mDevice;
protected int mInterfaceIndex;
protected int mInterface;
protected int mDeviceId;
protected UsbDeviceConnection mConnection;
protected UsbEndpoint mInputEndpoint;
protected UsbEndpoint mOutputEndpoint;
protected InputThread mInputThread;
protected boolean mRunning;
protected boolean mFrozen;
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
mManager = manager;
mDevice = usbDevice;
mInterfaceIndex = interface_index;
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
mRunning = false;
}
public String getIdentifier() {
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
}
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
return mDevice.getVendorId();
}
@Override
public int getProductId() {
return mDevice.getProductId();
}
@Override
public String getSerialNumber() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
try {
result = mDevice.getSerialNumber();
}
catch (SecurityException exception) {
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
}
}
if (result == null) {
result = "";
}
return result;
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
result = mDevice.getManufacturerName();
}
if (result == null) {
result = String.format("%x", getVendorId());
}
return result;
}
@Override
public String getProductName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
result = mDevice.getProductName();
}
if (result == null) {
result = String.format("%x", getProductId());
}
return result;
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
public String getDeviceName() {
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
}
@Override
public boolean open() {
mConnection = mManager.getUSBManager().openDevice(mDevice);
if (mConnection == null) {
Log.w(TAG, "Unable to open USB device " + getDeviceName());
return false;
}
// Force claim our interface
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
if (!mConnection.claimInterface(iface, true)) {
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
close();
return false;
}
// Find the endpoints
for (int j = 0; j < iface.getEndpointCount(); j++) {
UsbEndpoint endpt = iface.getEndpoint(j);
switch (endpt.getDirection()) {
case UsbConstants.USB_DIR_IN:
if (mInputEndpoint == null) {
mInputEndpoint = endpt;
}
break;
case UsbConstants.USB_DIR_OUT:
if (mOutputEndpoint == null) {
mOutputEndpoint = endpt;
}
break;
}
}
// Make sure the required endpoints were present
if (mInputEndpoint == null || mOutputEndpoint == null) {
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
close();
return false;
}
// Start listening for input
mRunning = true;
mInputThread = new InputThread();
mInputThread.start();
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
0x09/*HID set_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
return -1;
}
if (skipped_report_id) {
++length;
}
return length;
}
@Override
public int sendOutputReport(byte[] report) {
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (r != report.length) {
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
}
return r;
}
@Override
public boolean getFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID
will remain in byte 0. */
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
0x01/*HID get_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
return false;
}
if (skipped_report_id) {
++res;
++length;
}
byte[] data;
if (res == length) {
data = report;
} else {
data = Arrays.copyOfRange(report, 0, res);
}
mManager.HIDDeviceFeatureReport(mDeviceId, data);
return true;
}
@Override
public void close() {
mRunning = false;
if (mInputThread != null) {
while (mInputThread.isAlive()) {
mInputThread.interrupt();
try {
mInputThread.join();
} catch (InterruptedException e) {
// Keep trying until we're done
}
}
mInputThread = null;
}
if (mConnection != null) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
mConnection.close();
mConnection = null;
}
}
@Override
public void shutdown() {
close();
mManager = null;
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
protected class InputThread extends Thread {
@Override
public void run() {
int packetSize = mInputEndpoint.getMaxPacketSize();
byte[] packet = new byte[packetSize];
while (mRunning) {
int r;
try
{
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
}
catch (Exception e)
{
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
break;
}
if (r < 0) {
// Could be a timeout or an I/O error
}
if (r > 0) {
byte[] data;
if (r == packetSize) {
data = packet;
} else {
data = Arrays.copyOfRange(packet, 0, r);
}
if (!mFrozen) {
mManager.HIDDeviceInputReport(mDeviceId, data);
}
}
}
}
}
}

View File

@@ -1,85 +0,0 @@
package org.libsdl.app;
import android.content.Context;
import java.lang.Class;
import java.lang.reflect.Method;
/**
SDL library initialization
*/
public class SDL {
// This function should be called first and sets up the native code
// so it can call into the Java classes
public static void setupJNI() {
SDLActivity.nativeSetupJNI();
SDLAudioManager.nativeSetupJNI();
SDLControllerManager.nativeSetupJNI();
}
// This function should be called each time the activity is started
public static void initialize() {
setContext(null);
SDLActivity.initialize();
SDLAudioManager.initialize();
SDLControllerManager.initialize();
}
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
mContext = context;
}
public static Context getContext() {
return mContext;
}
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
if (libraryName == null) {
throw new NullPointerException("No library name provided.");
}
try {
// Let's see if we have ReLinker available in the project. This is necessary for
// some projects that have huge numbers of local libraries bundled, and thus may
// trip a bug in Android's native library loader which ReLinker works around. (If
// loadLibrary works properly, ReLinker will simply use the normal Android method
// internally.)
//
// To use ReLinker, just add it as a dependency. For more information, see
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
//
Class<?> relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
Class<?> relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
Class<?> contextClass = mContext.getClassLoader().loadClass("android.content.Context");
Class<?> stringClass = mContext.getClassLoader().loadClass("java.lang.String");
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
// they've changed during updates.
Method forceMethod = relinkClass.getDeclaredMethod("force");
Object relinkInstance = forceMethod.invoke(null);
Class<?> relinkInstanceClass = relinkInstance.getClass();
// Actually load the library!
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);
}
catch (final Throwable e) {
// Fall back
try {
System.loadLibrary(libraryName);
}
catch (final UnsatisfiedLinkError ule) {
throw ule;
}
catch (final SecurityException se) {
throw se;
}
}
}
protected static Context mContext;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,394 +0,0 @@
package org.libsdl.app;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
public class SDLAudioManager
{
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
}
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
}
}
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
int channelConfig;
int sampleSize;
int frameSize;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
/* On older devices let's use known good settings */
if (Build.VERSION.SDK_INT < 21) {
if (desiredChannels > 2) {
desiredChannels = 2;
}
}
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
if (Build.VERSION.SDK_INT < 22) {
if (sampleRate < 8000) {
sampleRate = 8000;
} else if (sampleRate > 48000) {
sampleRate = 48000;
}
}
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
int minSDKVersion = (isCapture ? 23 : 21);
if (Build.VERSION.SDK_INT < minSDKVersion) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
}
switch (audioFormat)
{
case AudioFormat.ENCODING_PCM_8BIT:
sampleSize = 1;
break;
case AudioFormat.ENCODING_PCM_16BIT:
sampleSize = 2;
break;
case AudioFormat.ENCODING_PCM_FLOAT:
sampleSize = 4;
break;
default:
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
sampleSize = 2;
break;
}
if (isCapture) {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_IN_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
}
} else {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
case 3:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 4:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
break;
case 5:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
case 7:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
break;
case 8:
if (Build.VERSION.SDK_INT >= 23) {
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
} else {
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
desiredChannels = 6;
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
}
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
}
/*
Log.v(TAG, "Speaker configuration (and order of channels):");
if ((channelConfig & 0x00000004) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
}
if ((channelConfig & 0x00000008) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
}
if ((channelConfig & 0x00000010) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
}
if ((channelConfig & 0x00000020) != 0) {
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
}
if ((channelConfig & 0x00000040) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
}
if ((channelConfig & 0x00000080) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
}
if ((channelConfig & 0x00000100) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
}
if ((channelConfig & 0x00000200) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
}
if ((channelConfig & 0x00000400) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
}
if ((channelConfig & 0x00000800) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
}
if ((channelConfig & 0x00001000) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
}
*/
}
frameSize = (sampleSize * desiredChannels);
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
int minBufferSize;
if (isCapture) {
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
} else {
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
}
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
int[] results = new int[4];
if (isCapture) {
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return null;
}
mAudioRecord.startRecording();
}
results[0] = mAudioRecord.getSampleRate();
results[1] = mAudioRecord.getAudioFormat();
results[2] = mAudioRecord.getChannelCount();
} else {
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
/* Try again, with safer values */
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack.release();
mAudioTrack = null;
return null;
}
mAudioTrack.play();
}
results[0] = mAudioTrack.getSampleRate();
results[1] = mAudioTrack.getAudioFormat();
results[2] = mAudioTrack.getChannelCount();
}
results[3] = desiredFrames;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
return results;
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteFloatBuffer(float[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(float)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
}
/** This method is called by SDL using JNI. */
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
/** This method is called by SDL using JNI. */
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
try {
/* Set thread name */
if (iscapture) {
Thread.currentThread().setName("SDLAudioC" + device_id);
} else {
Thread.currentThread().setName("SDLAudioP" + device_id);
}
/* Set thread priority */
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
} catch (Exception e) {
Log.v(TAG, "modify thread properties failed " + e.toString());
}
}
public static native int nativeSetupJNI();
}

View File

@@ -1,814 +0,0 @@
package org.libsdl.app;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
public class SDLControllerManager
{
public static native int nativeSetupJNI();
public static native int nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
boolean is_accelerometer, int button_mask,
int naxes, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;
private static final String TAG = "SDLControllerManager";
public static void initialize() {
if (mJoystickHandler == null) {
if (Build.VERSION.SDK_INT >= 19) {
mJoystickHandler = new SDLJoystickHandler_API19();
} else {
mJoystickHandler = new SDLJoystickHandler_API16();
}
}
if (mHapticHandler == null) {
if (Build.VERSION.SDK_INT >= 26) {
mHapticHandler = new SDLHapticHandler_API26();
} else {
mHapticHandler = new SDLHapticHandler();
}
}
}
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
/**
* This method is called by SDL using JNI.
*/
public static void pollInputDevices() {
mJoystickHandler.pollInputDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void pollHapticDevices() {
mHapticHandler.pollHapticDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticRun(int device_id, float intensity, int length) {
mHapticHandler.run(device_id, intensity, length);
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticStop(int device_id)
{
mHapticHandler.stop(device_id);
}
// Check if a given device is considered a possible SDL joystick
public static boolean isDeviceSDLJoystick(int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
// We cannot use InputDevice.isVirtual before API 16, so let's accept
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
if ((device == null) || (deviceId < 0)) {
return false;
}
int sources = device.getSources();
/* This is called for every button press, so let's not spam the logs */
/*
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
}
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
}
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
}
*/
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
);
}
}
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}
/* Actual joystick functionality available for API >= 12 devices */
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
static class SDLJoystick {
public int device_id;
public String name;
public String desc;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
int arg0Axis = arg0.getAxis();
int arg1Axis = arg1.getAxis();
if (arg0Axis == MotionEvent.AXIS_GAS) {
arg0Axis = MotionEvent.AXIS_BRAKE;
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
arg0Axis = MotionEvent.AXIS_GAS;
}
if (arg1Axis == MotionEvent.AXIS_GAS) {
arg1Axis = MotionEvent.AXIS_BRAKE;
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
arg1Axis = MotionEvent.AXIS_GAS;
}
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
// This is because the usual pairing are:
// - AXIS_X + AXIS_Y (left stick).
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
// This sorts the axes in the above order, which tends to be correct
// for Xbox-ish game pads that have the right stick on RX/RY and the
// triggers on Z/RZ.
//
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
//
// References:
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
if (arg0Axis == MotionEvent.AXIS_Z) {
arg0Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
--arg0Axis;
}
if (arg1Axis == MotionEvent.AXIS_Z) {
arg1Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
--arg1Axis;
}
return arg0Axis - arg1Axis;
}
}
private final ArrayList<SDLJoystick> mJoysticks;
public SDLJoystickHandler_API16() {
mJoysticks = new ArrayList<SDLJoystick>();
}
@Override
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
for (int device_id : deviceIds) {
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
SDLJoystick joystick = getJoystick(device_id);
if (joystick == null) {
InputDevice joystickDevice = InputDevice.getDevice(device_id);
joystick = new SDLJoystick();
joystick.device_id = device_id;
joystick.name = joystickDevice.getName();
joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
} else {
joystick.axes.add(range);
}
}
}
mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
getVendorId(joystickDevice), getProductId(joystickDevice), false,
getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = null;
for (SDLJoystick joystick : mJoysticks) {
int device_id = joystick.device_id;
int i;
for (i = 0; i < deviceIds.length; i++) {
if (device_id == deviceIds[i]) break;
}
if (i == deviceIds.length) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
}
removedDevices.add(device_id);
}
}
if (removedDevices != null) {
for (int device_id : removedDevices) {
SDLControllerManager.nativeRemoveJoystick(device_id);
for (int i = 0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
mJoysticks.remove(i);
break;
}
}
}
}
}
protected SDLJoystick getJoystick(int device_id) {
for (SDLJoystick joystick : mJoysticks) {
if (joystick.device_id == device_id) {
return joystick;
}
}
return null;
}
@Override
public boolean handleMotionEvent(MotionEvent event) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) {
SDLJoystick joystick = getJoystick(event.getDeviceId());
if (joystick != null) {
for (int i = 0; i < joystick.axes.size(); i++) {
InputDevice.MotionRange range = joystick.axes.get(i);
/* Normalize the value to -1...1 */
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
}
for (int i = 0; i < joystick.hats.size() / 2; i++) {
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
}
}
}
return true;
}
public String getJoystickDescriptor(InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && !desc.isEmpty()) {
return desc;
}
return joystickDevice.getName();
}
public int getProductId(InputDevice joystickDevice) {
return 0;
}
public int getVendorId(InputDevice joystickDevice) {
return 0;
}
public int getButtonMask(InputDevice joystickDevice) {
return -1;
}
}
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
@Override
public int getProductId(InputDevice joystickDevice) {
return joystickDevice.getProductId();
}
@Override
public int getVendorId(InputDevice joystickDevice) {
return joystickDevice.getVendorId();
}
@Override
public int getButtonMask(InputDevice joystickDevice) {
int button_mask = 0;
int[] keys = new int[] {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_MENU,
KeyEvent.KEYCODE_BUTTON_MODE,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_THUMBL,
KeyEvent.KEYCODE_BUTTON_THUMBR,
KeyEvent.KEYCODE_BUTTON_L1,
KeyEvent.KEYCODE_BUTTON_R1,
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_DPAD_CENTER,
// These don't map into any SDL controller buttons directly
KeyEvent.KEYCODE_BUTTON_L2,
KeyEvent.KEYCODE_BUTTON_R2,
KeyEvent.KEYCODE_BUTTON_C,
KeyEvent.KEYCODE_BUTTON_Z,
KeyEvent.KEYCODE_BUTTON_1,
KeyEvent.KEYCODE_BUTTON_2,
KeyEvent.KEYCODE_BUTTON_3,
KeyEvent.KEYCODE_BUTTON_4,
KeyEvent.KEYCODE_BUTTON_5,
KeyEvent.KEYCODE_BUTTON_6,
KeyEvent.KEYCODE_BUTTON_7,
KeyEvent.KEYCODE_BUTTON_8,
KeyEvent.KEYCODE_BUTTON_9,
KeyEvent.KEYCODE_BUTTON_10,
KeyEvent.KEYCODE_BUTTON_11,
KeyEvent.KEYCODE_BUTTON_12,
KeyEvent.KEYCODE_BUTTON_13,
KeyEvent.KEYCODE_BUTTON_14,
KeyEvent.KEYCODE_BUTTON_15,
KeyEvent.KEYCODE_BUTTON_16,
};
int[] masks = new int[] {
(1 << 0), // A -> A
(1 << 1), // B -> B
(1 << 2), // X -> X
(1 << 3), // Y -> Y
(1 << 4), // BACK -> BACK
(1 << 6), // MENU -> START
(1 << 5), // MODE -> GUIDE
(1 << 6), // START -> START
(1 << 7), // THUMBL -> LEFTSTICK
(1 << 8), // THUMBR -> RIGHTSTICK
(1 << 9), // L1 -> LEFTSHOULDER
(1 << 10), // R1 -> RIGHTSHOULDER
(1 << 11), // DPAD_UP -> DPAD_UP
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
(1 << 4), // SELECT -> BACK
(1 << 0), // DPAD_CENTER -> A
(1 << 15), // L2 -> ??
(1 << 16), // R2 -> ??
(1 << 17), // C -> ??
(1 << 18), // Z -> ??
(1 << 20), // 1 -> ??
(1 << 21), // 2 -> ??
(1 << 22), // 3 -> ??
(1 << 23), // 4 -> ??
(1 << 24), // 5 -> ??
(1 << 25), // 6 -> ??
(1 << 26), // 7 -> ??
(1 << 27), // 8 -> ??
(1 << 28), // 9 -> ??
(1 << 29), // 10 -> ??
(1 << 30), // 11 -> ??
(1 << 31), // 12 -> ??
// We're out of room...
0xFFFFFFFF, // 13 -> ??
0xFFFFFFFF, // 14 -> ??
0xFFFFFFFF, // 15 -> ??
0xFFFFFFFF, // 16 -> ??
};
boolean[] has_keys = joystickDevice.hasKeys(keys);
for (int i = 0; i < keys.length; ++i) {
if (has_keys[i]) {
button_mask |= masks[i];
}
}
return button_mask;
}
}
class SDLHapticHandler_API26 extends SDLHapticHandler {
@Override
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
if (intensity == 0.0f) {
stop(device_id);
return;
}
int vibeValue = Math.round(intensity * 255);
if (vibeValue > 255) {
vibeValue = 255;
}
if (vibeValue < 1) {
stop(device_id);
return;
}
try {
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
}
catch (Exception e) {
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
// something went horribly wrong with the Android 8.0 APIs.
haptic.vib.vibrate(length);
}
}
}
}
class SDLHapticHandler {
static class SDLHaptic {
public int device_id;
public String name;
public Vibrator vib;
}
private final ArrayList<SDLHaptic> mHaptics;
public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate(length);
}
}
public void stop(int device_id) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.cancel();
}
}
public void pollHapticDevices() {
final int deviceId_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for (int i = deviceIds.length - 1; i > -1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator();
if (vib.hasVibrator()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check VIBRATOR_SERVICE */
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null) {
hasVibratorService = vib.hasVibrator();
if (hasVibratorService) {
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
if (haptic == null) {
haptic = new SDLHaptic();
haptic.device_id = deviceId_VIBRATOR_SERVICE;
haptic.name = "VIBRATOR_SERVICE";
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = null;
for (SDLHaptic haptic : mHaptics) {
int device_id = haptic.device_id;
int i;
for (i = 0; i < deviceIds.length; i++) {
if (device_id == deviceIds[i]) break;
}
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
if (i == deviceIds.length) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
}
removedDevices.add(device_id);
}
} // else: don't remove the vibrator if it is still present
}
if (removedDevices != null) {
for (int device_id : removedDevices) {
SDLControllerManager.nativeRemoveHaptic(device_id);
for (int i = 0; i < mHaptics.size(); i++) {
if (mHaptics.get(i).device_id == device_id) {
mHaptics.remove(i);
break;
}
}
}
}
}
protected SDLHaptic getHaptic(int device_id) {
for (SDLHaptic haptic : mHaptics) {
if (haptic.device_id == device_id) {
return haptic;
}
}
return null;
}
}
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
public boolean supportsRelativeMouse() {
return false;
}
public boolean inRelativeMode() {
return false;
}
public boolean setRelativeMouseEnabled(boolean enabled) {
return false;
}
public void reclaimRelativeMouseModeIfNeeded()
{
}
public float getEventX(MotionEvent event) {
return event.getX(0);
}
public float getEventY(MotionEvent event) {
return event.getY(0);
}
}
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
// Handle relative mouse mode
if (mRelativeModeEnabled) {
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_MOVE) {
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
}
}
}
// Event was not managed, call SDLGenericMotionListener_API12 method
return super.onGenericMotion(v, event);
}
@Override
public boolean supportsRelativeMouse() {
return true;
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
mRelativeModeEnabled = enabled;
return true;
}
@Override
public float getEventX(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
} else {
return event.getX(0);
}
}
@Override
public float getEventY(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
} else {
return event.getY(0);
}
}
}
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
// DeX desktop mouse cursor is a separate non-standard input type.
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
case InputDevice.SOURCE_MOUSE_RELATIVE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
@Override
public boolean supportsRelativeMouse() {
return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27));
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) {
if (enabled) {
SDLActivity.getContentView().requestPointerCapture();
} else {
SDLActivity.getContentView().releasePointerCapture();
}
mRelativeModeEnabled = enabled;
return true;
} else {
return false;
}
}
@Override
public void reclaimRelativeMouseModeIfNeeded()
{
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
SDLActivity.getContentView().requestPointerCapture();
}
}
@Override
public float getEventX(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getX(0);
}
@Override
public float getEventY(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getY(0);
}
}

View File

@@ -1,405 +0,0 @@
package org.libsdl.app;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
/**
SDLSurface. This is what we draw on, so we need to know when it's created
in order to do anything useful.
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
protected Display mDisplay;
// Keep track of the surface size to normalize touch events
protected float mWidth, mHeight;
// Is SurfaceView ready for rendering
public boolean mIsSurfaceReady;
// Startup
public SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
setOnGenericMotionListener(SDLActivity.getMotionListener());
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
mHeight = 1.0f;
mIsSurfaceReady = false;
}
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
public Surface getNativeSurface() {
return getHolder().getSurface();
}
// Called when we have a valid drawing surface
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v("SDL", "surfaceCreated()");
SDLActivity.onNativeSurfaceCreated();
}
// Called when we lose the surface
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("SDL", "surfaceDestroyed()");
// Transition to pause, if needed
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
SDLActivity.handleNativeState();
mIsSurfaceReady = false;
SDLActivity.onNativeSurfaceDestroyed();
}
// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
Log.v("SDL", "surfaceChanged()");
if (SDLActivity.mSingleton == null) {
return;
}
mWidth = width;
mHeight = height;
int nDeviceWidth = width;
int nDeviceHeight = height;
try
{
if (Build.VERSION.SDK_INT >= 17) {
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
}
} catch(Exception ignored) {
}
synchronized(SDLActivity.getContext()) {
// In case we're waiting on a size change after going fullscreen, send a notification.
SDLActivity.getContext().notifyAll();
}
Log.v("SDL", "Window size: " + width + "x" + height);
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
SDLActivity.onNativeResize();
// Prevent a screen distortion glitch,
// for instance when the device is in Landscape and a Portrait App is resumed.
boolean skip = false;
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
if (mWidth > mHeight) {
skip = true;
}
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
if (mWidth < mHeight) {
skip = true;
}
}
// Special Patch for Square Resolution: Black Berry Passport
if (skip) {
double min = Math.min(mWidth, mHeight);
double max = Math.max(mWidth, mHeight);
if (max / min < 1.20) {
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
skip = false;
}
}
// Don't skip in MultiWindow.
if (skip) {
if (Build.VERSION.SDK_INT >= 24) {
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
Log.v("SDL", "Don't skip in Multi-Window");
skip = false;
}
}
}
if (skip) {
Log.v("SDL", "Skip .. Surface is not ready.");
mIsSurfaceReady = false;
return;
}
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
SDLActivity.onNativeSurfaceChanged();
/* Surface is ready */
mIsSurfaceReady = true;
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
SDLActivity.handleNativeState();
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
}
// Touch events
@Override
public boolean onTouch(View v, MotionEvent event) {
/* Ref: http://developer.android.com/training/gestures/multi.html */
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int i = -1;
float x,y,p;
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
// 12290 = Samsung DeX mode desktop mouse
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
// 0x2 = SOURCE_CLASS_POINTER
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
int mouseButton = 1;
try {
Object object = event.getClass().getMethod("getButtonState").invoke(event);
if (object != null) {
mouseButton = (Integer) object;
}
} catch(Exception ignored) {
}
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event);
y = motionListener.getEventY(event);
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
} else {
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
/* fallthrough */
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
// We thus should check here.
int newOrientation;
float x, y;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
break;
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
break;
}
if (newOrientation != SDLActivity.mCurrentOrientation) {
SDLActivity.mCurrentOrientation = newOrientation;
SDLActivity.onNativeOrientationChanged(newOrientation);
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
// Captured pointer events for API 26.
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(0);
y = event.getY(0);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
return false;
}
}

View File

@@ -269,7 +269,7 @@ void MusicEntry::load(const AudioPath & musicURI)
auto * musicFile = MakeSDLRWops(std::move(stream));
music = Mix_LoadMUS_RW(musicFile, SDL_TRUE);
}
catch(std::exception & e)
catch(const std::exception & e)
{
logGlobal->error("Failed to load music. setName=%s\tmusicURI=%s", setName, currentName.getOriginalName());
logGlobal->error("Exception: %s", e.what());

View File

@@ -30,6 +30,7 @@ if(ANDROID)
)
else()
add_executable(vcmiclient ${clientapp_SRCS} ${clientapp_HEADERS})
vcmi_create_exe_shim(vcmiclient)
endif()
target_link_libraries(vcmiclient PRIVATE vcmiclientcommon)
@@ -55,15 +56,6 @@ if(WIN32)
target_link_libraries(vcmiclient SDL2::SDL2main)
endif()
target_compile_definitions(vcmiclient PRIVATE WINDOWS_IGNORE_PACKING_MISMATCH)
# TODO: very hacky, find proper solution to copy AI dlls into bin dir
if(MSVC)
add_custom_command(TARGET vcmiclient POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:vcmiclient>"
COMMAND ${CMAKE_COMMAND} -E copy AI/fuzzylite.dll fuzzylite.dll
COMMAND ${CMAKE_COMMAND} -E copy AI/tbb12.dll tbb12.dll
)
endif()
elseif(APPLE_IOS)
set_target_properties(vcmiclient PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_LIST_DIR}/ios/Info.plist"
@@ -90,7 +82,6 @@ vcmi_set_output_dir(vcmiclient "")
enable_pch(vcmiclient)
if(APPLE_IOS)
vcmi_install_conan_deps("\${CMAKE_INSTALL_PREFIX}")
add_custom_command(TARGET vcmiclient POST_BUILD
COMMAND ios/set_build_version.sh "$<TARGET_BUNDLE_CONTENT_DIR:vcmiclient>"
COMMAND ${CMAKE_COMMAND} --install "${CMAKE_BINARY_DIR}" --component "${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}" --config "$<CONFIG>" --prefix "$<TARGET_BUNDLE_CONTENT_DIR:vcmiclient>"
@@ -103,11 +94,10 @@ elseif(ANDROID)
find_program(androidDeployQt androiddeployqt
PATHS "${qtBinDir}"
)
vcmi_install_conan_deps("\${CMAKE_INSTALL_PREFIX}/${LIB_DIR}")
add_custom_target(android_deploy ALL
COMMAND ${CMAKE_COMMAND} --install "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --prefix "${androidQtBuildDir}"
COMMAND "${androidDeployQt}" --input "${CMAKE_BINARY_DIR}/androiddeployqt.json" --output "${androidQtBuildDir}" --android-platform "android-${ANDROID_TARGET_SDK_VERSION}" --verbose $<$<NOT:$<CONFIG:Debug>>:--release> ${ANDROIDDEPLOYQT_OPTIONS}
COMMAND ${CMAKE_COMMAND} -E env "ORG_GRADLE_PROJECT_SDL_JAVA_SRC_DIR=${SDL_JAVA_SRC_DIR}" --
"${androidDeployQt}" --input "${CMAKE_BINARY_DIR}/androiddeployqt.json" --output "${androidQtBuildDir}" --android-platform "android-${ANDROID_TARGET_SDK_VERSION}" --verbose $<$<NOT:$<CONFIG:Debug>>:--release> ${ANDROIDDEPLOYQT_OPTIONS}
COMMAND_EXPAND_LISTS
VERBATIM
COMMENT "Create android package"

View File

@@ -118,25 +118,35 @@ function(vcmi_print_git_commit_hash)
endfunction()
#install imported target on windows
function(install_vcpkg_imported_tgt tgt)
get_target_property(TGT_LIB_LOCATION ${tgt} LOCATION)
get_filename_component(TGT_LIB_FOLDER ${TGT_LIB_LOCATION} PATH)
get_filename_component(tgt_name ${TGT_LIB_LOCATION} NAME_WE)
get_filename_component(TGT_DLL ${TGT_LIB_FOLDER}/../bin/${tgt_name}.dll ABSOLUTE)
message("${tgt_name}: ${TGT_DLL}")
install(FILES ${TGT_DLL} DESTINATION ${BIN_DIR})
endfunction(install_vcpkg_imported_tgt)
# install dependencies from Conan, install_dir should contain \${CMAKE_INSTALL_PREFIX}
function(vcmi_install_conan_deps install_dir)
# install dependencies from Conan, CONAN_RUNTIME_LIBS_FILE is set in conanfile.py
function(vcmi_install_conan_deps)
if(NOT USING_CONAN)
return()
endif()
install(CODE "
execute_process(COMMAND
conan imports \"${CMAKE_SOURCE_DIR}\" --install-folder \"${CONAN_INSTALL_FOLDER}\" --import-folder \"${install_dir}\"
)
file(REMOVE \"${install_dir}/conan_imports_manifest.txt\")
")
file(STRINGS "${CONAN_RUNTIME_LIBS_FILE}" runtimeLibs)
install(FILES ${runtimeLibs} DESTINATION ${LIB_DIR})
endfunction()
function(vcmi_deploy_qt deployQtToolName deployQtOptions)
# TODO: use qt_generate_deploy_app_script() with Qt 6
find_program(TOOL_DEPLOYQT NAMES ${deployQtToolName} PATHS "${qtBinDir}")
if(TOOL_DEPLOYQT)
install(CODE "
execute_process(COMMAND \"${TOOL_DEPLOYQT}\" ${deployQtOptions} -verbose=2)
")
else()
message(WARNING "${deployQtToolName} not found, running cpack would result in broken package")
endif()
endfunction()
# generate .bat for .exe with proper PATH
function(vcmi_create_exe_shim tgt)
if(NOT CONAN_RUNENV_SCRIPT)
return()
endif()
file(GENERATE OUTPUT "$<TARGET_FILE_DIR:${tgt}>/$<TARGET_FILE_BASE_NAME:${tgt}>.bat" CONTENT
"call ${CONAN_RUNENV_SCRIPT}
@start $<TARGET_FILE_NAME:${tgt}>"
)
endfunction()

View File

@@ -1,351 +1,54 @@
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.apple import is_apple_os
from conan.tools.build import cross_building
from conan.tools.cmake import CMakeDeps, CMakeToolchain
from conans import tools
from dependencies.conanfile import VCMI
required_conan_version = ">=1.51.3"
from conan.tools.cmake import CMakeToolchain
from conan.tools.files import save
class VCMI(ConanFile):
settings = "os", "compiler", "build_type", "arch"
from glob import glob
import os
_libRequires = [
"boost/[^1.69]",
"minizip/[~1.2.12]",
]
_clientRequires = [
# Versions between 2.5-2.8 have broken loading of palette sdl images which a lot of mods use
# there is workaround that require disabling cmake flag which is not available in conan recipes.
# Bug is fixed in version 2.8, however it is not available in conan at the moment
"sdl_image/2.0.5",
"sdl_ttf/[>=2.0.18]",
"onetbb/[^2021.7 <2021.10]", # 2021.10+ breaks mobile builds due to added hwloc dependency
"xz_utils/[>=5.2.5]", # Required for innoextract
]
class VCMIApp(VCMI):
generators = "CMakeDeps"
requires = _libRequires + _clientRequires
def _pathForCmake(self, path: str) -> str:
# CMake doesn't like \ in strings
return path.replace(os.path.sep, os.path.altsep) if os.path.altsep else path
options = {
"default_options_of_requirements": [True, False],
"with_apple_system_libs": [True, False],
"with_ffmpeg": [True, False],
"with_luajit": [True, False],
}
default_options = {
"default_options_of_requirements": False,
"with_apple_system_libs": False,
"with_ffmpeg": True,
"with_luajit": False,
def _generateRuntimeLibsFile(self) -> str:
# create file with list of libs to copy to the package for distribution
runtimeLibsFile = self._pathForCmake(os.path.join(self.build_folder, "_runtime_libs.txt"))
"boost/*:shared": True,
"minizip/*:shared": True,
}
runtimeLibExtension = {
"Android": "so",
"iOS": "dylib",
"Macos": "dylib",
"Windows": "dll",
}.get(str(self.settings.os))
def configure(self):
self.options["ffmpeg"].shared = self.settings.os == "Android" # using shared version results in less total project size on Android
self.options["freetype"].shared = self.settings.os == "Android"
runtimeLibs = []
for _, dep in self.dependencies.host.items():
# Qt libs are copied using *deployqt
if dep.ref.name == "qt":
continue
# SDL_image and Qt depend on it, in iOS both are static
self.options["libpng"].shared = self.settings.os != "iOS"
# static Qt for iOS is the only viable option at the moment
self.options["qt"].shared = self.settings.os != "iOS"
runtimeLibDir = ''
if self.settings.os == "Windows":
if len(dep.cpp_info.bindirs) > 0:
runtimeLibDir = dep.cpp_info.bindir
elif len(dep.cpp_info.libdirs) > 0:
runtimeLibDir = dep.cpp_info.libdir
if len(runtimeLibDir) > 0:
runtimeLibs += map(self._pathForCmake, glob(os.path.join(runtimeLibDir, f"*.{runtimeLibExtension}")))
save(self, runtimeLibsFile, "\n".join(runtimeLibs))
# TODO: enable for all platforms
if self.settings.os == "Android":
self.options["bzip2"].shared = True
self.options["libiconv"].shared = True
self.options["zlib"].shared = True
# TODO: enable for all platforms?
if self.settings.os == "Windows":
self.options["sdl"].shared = True
self.options["sdl_image"].shared = True
self.options["sdl_mixer"].shared = True
self.options["sdl_ttf"].shared = True
if self.settings.os == "iOS":
# TODO: ios - newer sdl fails to link
self.requires("sdl/2.26.1")
self.requires("sdl_mixer/2.0.4")
elif self.settings.os == "Android":
# On Android SDL version must be same as version of Java wrapper for SDL in VCMI source code
# Wrapper can be found in following directory: android/vcmi-app/src/main/java/org/libsdl/app
self.requires("sdl/2.26.5")
self.requires("sdl_mixer/2.0.4")
else:
# upcoming SDL version 3.0+ is not supported at the moment due to API breakage
# SDL versions between 2.22-2.26.1 have broken sound
self.requires("sdl/[^2.26 || >=2.0.20 <=2.22.0]")
self.requires("sdl_mixer/[>=2.0.4]")
if self.settings.os == "Android":
self.options["qt"].android_sdk = tools.get_env("ANDROID_HOME", default="")
if self.options.default_options_of_requirements:
return
# we need only the following Boost parts:
# date_time filesystem iostreams locale program_options system thread
# some other parts are also enabled because they're dependents
# see e.g. conan-center-index/recipes/boost/all/dependencies
self.options["boost"].without_context = True
self.options["boost"].without_contract = True
self.options["boost"].without_coroutine = True
self.options["boost"].without_fiber = True
self.options["boost"].without_graph = True
self.options["boost"].without_graph_parallel = True
self.options["boost"].without_json = True
self.options["boost"].without_log = True
self.options["boost"].without_math = True
self.options["boost"].without_mpi = True
self.options["boost"].without_nowide = True
self.options["boost"].without_python = True
self.options["boost"].without_serialization = True
self.options["boost"].without_stacktrace = True
self.options["boost"].without_test = True
self.options["boost"].without_timer = True
self.options["boost"].without_type_erasure = True
self.options["boost"].without_wave = True
self.options["boost"].without_url = True
self.options["ffmpeg"].disable_all_bitstream_filters = True
self.options["ffmpeg"].disable_all_decoders = True
self.options["ffmpeg"].disable_all_demuxers = True
self.options["ffmpeg"].disable_all_encoders = True
self.options["ffmpeg"].disable_all_filters = True
self.options["ffmpeg"].disable_all_hardware_accelerators = True
self.options["ffmpeg"].disable_all_muxers = True
self.options["ffmpeg"].disable_all_parsers = True
self.options["ffmpeg"].disable_all_protocols = True
self.options["ffmpeg"].with_asm = False
self.options["ffmpeg"].with_bzip2 = False
self.options["ffmpeg"].with_freetype = False
self.options["ffmpeg"].with_libaom = False
self.options["ffmpeg"].with_libdav1d = False
self.options["ffmpeg"].with_libiconv = False
self.options["ffmpeg"].with_libmp3lame = False
self.options["ffmpeg"].with_libsvtav1 = False
self.options["ffmpeg"].with_libvpx = False
self.options["ffmpeg"].with_libwebp = False
self.options["ffmpeg"].with_libx264 = False
self.options["ffmpeg"].with_libx265 = False
self.options["ffmpeg"].with_lzma = True
self.options["ffmpeg"].with_openh264 = False
self.options["ffmpeg"].with_openjpeg = False
self.options["ffmpeg"].with_opus = False
self.options["ffmpeg"].with_programs = False
self.options["ffmpeg"].with_sdl = False
self.options["ffmpeg"].with_ssl = False
self.options["ffmpeg"].with_vorbis = False
self.options["ffmpeg"].with_zlib = False
if self.settings.os != "Android":
self.options["ffmpeg"].with_libfdk_aac = False
self.options["ffmpeg"].avcodec = True
self.options["ffmpeg"].avdevice = False
self.options["ffmpeg"].avfilter = False
self.options["ffmpeg"].avformat = True
self.options["ffmpeg"].postproc = False
self.options["ffmpeg"].swresample = True # For resampling of audio in 'planar' formats
self.options["ffmpeg"].swscale = True # For video scaling
# We want following options supported:
# H3:SoD - .bik and .smk
# H3:HD - ogg container / theora video / vorbis sound (not supported by vcmi at the moment, but might be supported in future)
# and for mods - webm container / vp8 or vp9 video / opus sound
# TODO: add av1 support for mods (requires enabling libdav1d which currently fails to build via Conan)
self.options["ffmpeg"].enable_protocols = "file"
self.options["ffmpeg"].enable_demuxers = "bink,binka,ogg,smacker,webm_dash_manifest"
self.options["ffmpeg"].enable_parsers = "opus,vorbis,vp8,vp9,webp"
self.options["ffmpeg"].enable_decoders = "bink,binkaudio_dct,binkaudio_rdft,smackaud,smacker,theora,vorbis,vp8,vp9,opus"
#optionally, for testing - enable ffplay/ffprobe binaries in conan package:
#if self.settings.os == "Windows":
# self.options["ffmpeg"].with_programs = True
# self.options["ffmpeg"].avfilter = True
# self.options["ffmpeg"].with_sdl = True
# self.options["ffmpeg"].enable_filters = "aresample,scale"
self.options["sdl"].sdl2main = self.settings.os != "iOS"
self.options["sdl"].vulkan = False
# bmp, png are the only ones that needs to be supported
# dds support may be useful for HD edition, but not supported by sdl_image at the moment
self.options["sdl_image"].gif = False
self.options["sdl_image"].lbm = False
self.options["sdl_image"].pnm = False
self.options["sdl_image"].pcx = False
#self.options["sdl_image"].qoi = False # sdl_image >=2.6
self.options["sdl_image"].svg = False
self.options["sdl_image"].tga = False
self.options["sdl_image"].with_libjpeg = False
self.options["sdl_image"].with_libtiff = False
self.options["sdl_image"].with_libwebp = False
self.options["sdl_image"].xcf = False
self.options["sdl_image"].xpm = False
self.options["sdl_image"].xv = False
if is_apple_os(self):
self.options["sdl_image"].imageio = True
# mp3, ogg and wav are the only ones that needs to be supported
# opus is nice to have, but fails to build in CI
# flac can be considered, but generally unnecessary
self.options["sdl_mixer"].flac = False
self.options["sdl_mixer"].modplug = False
self.options["sdl_mixer"].opus = False
if self.settings.os == "iOS" or self.settings.os == "Android":
# only available in older sdl_mixer version, removed in newer version
self.options["sdl_mixer"].mad = False
self.options["sdl_mixer"].mikmod = False
self.options["sdl_mixer"].nativemidi = False
def _disableQtOptions(disableFlag, options):
return " ".join([f"-{disableFlag}-{tool}" for tool in options])
_qtOptions = [
_disableQtOptions("no", [
"gif",
"ico",
]),
_disableQtOptions("no-feature", [
# xpm format is required for Drag'n'Drop support
"imageformat_bmp",
"imageformat_jpeg",
"imageformat_ppm",
"imageformat_xbm",
# we need only macdeployqt
# TODO: disabling these doesn't disable generation of CMake targets
# TODO: in Qt 6.3 it's a part of qtbase
# "assistant",
# "designer",
# "distancefieldgenerator",
# "kmap2qmap",
# "linguist",
# "makeqpf",
# "pixeltool",
# "qdbus",
# "qev",
# "qtattributionsscanner",
# "qtdiag",
# "qtpaths",
# "qtplugininfo",
]),
]
self.options["qt"].config = " ".join(_qtOptions)
self.options["qt"].qttools = True
self.options["qt"].qtandroidextras = self.settings.os == "Android" # TODO: in Qt 6 it's part of Core
self.options["qt"].with_freetype = self.settings.os == "Android"
self.options["qt"].with_libjpeg = False
self.options["qt"].with_md4c = False
self.options["qt"].with_mysql = False
self.options["qt"].with_odbc = False
self.options["qt"].with_openal = False
self.options["qt"].with_pq = False
self.options["qt"].openssl = not is_apple_os(self)
if self.settings.os == "iOS" or self.settings.os == "Android":
self.options["qt"].opengl = "es2"
if not is_apple_os(self) and self.settings.os != "Android" and cross_building(self):
self.options["qt"].cross_compile = self.env["CONAN_CROSS_COMPILE"]
# TODO: add for all platforms after updating recipe
if self.settings.os == "Android":
self.options["qt"].essential_modules = False
# No Qt OpenGL for cross-compiling for Windows, Conan does not support it
if self.settings.os == "Windows" and cross_building(self):
self.options["qt"].opengl = "no"
# transitive deps
# doesn't link to overridden bzip2 & zlib, the tool isn't needed anyway
self.options["pcre2"].build_pcre2grep = False
# executable not needed
if self.settings.os == "Android":
self.options["sqlite3"].build_executable = False
def requirements(self):
self.requires("freetype/[~2.12.1]", override=True) # sdl_ttf / Qt
self.requires("libpng/[~1.6.39]", override=True) # freetype / qt / sdl_image
# client
if self.options.with_ffmpeg:
self.requires("ffmpeg/[>=4.4]")
# launcher
if self.settings.os == "Android":
self.requires("qt/[~5.15.14]")
else:
self.requires("qt/[~5.15.2]")
# TODO: version range doesn't work in Conan v1
if self.options["qt"].openssl:
self.requires("openssl/1.1.1s")
# use Apple system libraries instead of external ones
if self.options.with_apple_system_libs and is_apple_os(self):
systemLibsOverrides = [
"bzip2/1.0.8",
"libiconv/1.17",
"sqlite3/3.39.2",
"zlib/1.2.12",
]
for lib in systemLibsOverrides:
self.requires(f"{lib}@vcmi/apple", override=True)
elif self.settings.os == "Android":
self.requires("zlib/1.2.12@vcmi/android", override=True)
else:
self.requires("zlib/[~1.2.13]", override=True) # minizip / Qt
self.requires("libiconv/[~1.17]", override=True) # ffmpeg / sdl
# TODO: the latest official release of LuaJIT (which is quite old) can't be built for arm
if self.options.with_luajit and not str(self.settings.arch).startswith("arm"):
self.requires("luajit/[~2.0.5]")
def validate(self):
if self.options.with_apple_system_libs and not is_apple_os(self):
raise ConanInvalidConfiguration("with_apple_system_libs is only for Apple platforms")
if self.options.with_apple_system_libs and self.options.default_options_of_requirements:
raise ConanInvalidConfiguration("with_apple_system_libs and default_options_of_requirements can't be True at the same time")
return runtimeLibsFile
def generate(self):
tc = CMakeToolchain(self)
tc.variables["USING_CONAN"] = True
tc.variables["CONAN_INSTALL_FOLDER"] = self.install_folder
tc.variables["CONAN_RUNTIME_LIBS_FILE"] = self._generateRuntimeLibsFile()
if self.settings.os == "Android":
tc.variables["CMAKE_ANDROID_API"] = str(self.settings.os.api_level)
tc.variables["ANDROID_SYSROOT_LIB_SUBDIR"] = {
'armv7': 'arm-linux-androideabi',
'armv8': 'aarch64-linux-android',
'x86': 'i686-linux-android',
'x86_64': 'x86_64-linux-android',
}.get(str(self.settings.arch))
if cross_building(self) and self.settings.os == "Windows":
tc.variables["CONAN_SYSTEM_LIBRARY_LOCATION"] = self.env["CONAN_SYSTEM_LIBRARY_LOCATION"]
tc.generate()
deps = CMakeDeps(self)
if tools.get_env("GENERATE_ONLY_BUILT_CONFIG", default=False):
deps.generate()
return
# allow using prebuilt deps with all configs
# credits to https://github.com/conan-io/conan/issues/11607#issuecomment-1188500937 for the workaround
configs = [
"Debug",
"MinSizeRel",
"Release",
"RelWithDebInfo",
]
for config in configs:
print(f"generating CMakeDeps for {config}")
deps.configuration = config
deps.generate()
def imports(self):
if is_apple_os(self):
self.copy("*.dylib", "Frameworks", "lib")
tc.variables["SDL_JAVA_SRC_DIR"] = os.path.join(self.dependencies.host["sdl"].package_folder, "share", "java", "SDL2")
elif self.settings.os == "Windows":
self.copy("*.dll", src="bin/archdatadir/plugins/platforms", dst="platforms")
self.copy("*.dll", src="bin/archdatadir/plugins/styles", dst="styles")
self.copy("*.dll", src="@bindirs", dst="", excludes="archdatadir/*")
elif self.settings.os == "Android":
self.copy("*.so", ".", "lib")
tc.variables["CONAN_RUNENV_SCRIPT"] = self._pathForCmake(os.path.join(self.build_folder, "conanrun.bat"))
tc.generate()

1
dependencies Submodule

Submodule dependencies added at b6f03bd541

View File

@@ -6,35 +6,34 @@ RUN apt-get update && apt-get install -y openjdk-17-jdk python3 pipx cmake ccach
ENV PIPX_HOME="/opt/pipx"
ENV PIPX_BIN_DIR="/usr/local/bin"
ENV PIPX_MAN_DIR="/usr/local/share/man"
RUN pipx install 'conan<2.0'
RUN pipx install 'sdkmanager==0.6.10'
RUN pipx install 'conan'
RUN pipx install 'sdkmanager'
RUN conan profile new conan --detect
RUN conan profile detect
RUN wget https://github.com/vcmi/vcmi-dependencies/releases/download/1.3/dependencies-android-arm64-v8a.txz
RUN tar -xf dependencies-android-arm64-v8a.txz -C ~/.conan
RUN rm dependencies-android-arm64-v8a.txz
ENV DEPS_VERSION="2025-08-24"
ENV DEPS="dependencies-android-arm64-v8a.tgz"
RUN wget --https-only --max-redirect=20 https://github.com/vcmi/vcmi-dependencies/releases/download/$DEPS_VERSION/$DEPS
RUN conan cache restore $DEPS
RUN rm $DEPS
ENV JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
ENV ANDROID_HOME="/usr/lib/android-sdk"
ENV GRADLE_USER_HOME="/vcmi/.cache/grandle"
ENV GENERATE_ONLY_BUILT_CONFIG=1
ENV GRADLE_USER_HOME="/vcmi/.cache/gradle"
ENV NDK_VERSION="25.2.9519653"
RUN sdkmanager --install "platform-tools"
RUN sdkmanager --install "platforms;android-34"
RUN sdkmanager --install "platform-tools" "platforms;android-35" "ndk;$NDK_VERSION"
RUN yes | sdkmanager --licenses
RUN conan download android-ndk/r25c@:4db1be536558d833e52e862fd84d64d75c2b3656 -r conancenter
CMD ["sh", "-c", " \
# switch to mounted dir
cd /vcmi ; \
# install conan stuff
conan install . --install-folder=conan-generated --no-imports --build=never --profile:build=default --profile:host=CI/conan/android-64-ndk ; \
# link conan ndk that grandle can find it
mkdir -p /usr/lib/android-sdk/ndk ; \
ln -s -T ~/.conan/data/android-ndk/r25c/_/_/package/4db1be536558d833e52e862fd84d64d75c2b3656/bin /usr/lib/android-sdk/ndk/25.2.9519653 ; \
# prepare Gradle config
mkdir -p $GRADLE_USER_HOME ; \
echo android.bundle.enableUncompressedNativeLibs=true > $GRADLE_USER_HOME/gradle.properties ; \
# generate CMake toolchain
conan install . --output-folder=conan-generated --build=never --profile=dependencies/conan_profiles/android-64 --profile=dependencies/conan_profiles/base/android-system -c tools.android:ndk_path=$ANDROID_HOME/ndk/$NDK_VERSION && \
# build
cmake --preset android-daily-release ; \
cmake --preset android-daily-release && \
cmake --build --preset android-daily-release \
"]

View File

@@ -1,19 +1,19 @@
# Building Android
The following instructions apply to **v1.2 and later**. For earlier versions the best documentation is <https://github.com/vcmi/vcmi-android/blob/master/building.txt> (and reading scripts in that repo), however very limited to no support will be provided from our side if you wish to go down that rabbit hole.
The following instructions apply to **v1.7 and later**. For earlier versions see older Git revision, e.g. from [1.6.8 release](https://github.com/vcmi/vcmi/blob/1.6.8/docs/developers/Building_Android.md).
*Note*: building has been tested only on Linux and macOS. It may or may not work on Windows out of the box.
## Requirements
1. CMake 3.20+: download from your package manager or from <https://cmake.org/download/>
2. JDK 11, not necessarily from Oracle
1. CMake 3.26+: download from your package manager or from <https://cmake.org/download/>
2. JDK 17, not necessarily from Oracle
3. Android command line tools or Android Studio for your OS: <https://developer.android.com/studio/>
4. Android NDK version **r25c (25.2.9519653)**, there're multiple ways to obtain it:
4. Android NDK version **r25c (25.2.9519653)** (later version would also probably work), there're multiple ways to obtain it:
- recommended: download with Conan, especially if you're going to build VCMI dependencies from source, see [#NDK and Conan](#ndk-and-conan)
- install with Android Studio
- install with `sdkmanager` command line tool
- download from <https://developer.android.com/ndk/downloads>
- download with Conan, see [#NDK and Conan](#ndk-and-conan)
5. Optional:
- Ninja: download from your package manager or from <https://github.com/ninja-build/ninja/releases>
- Ccache: download from your package manager or from <https://github.com/ccache/ccache/releases>
@@ -32,15 +32,18 @@ We use Conan package manager to build/consume dependencies, find detailed usage
On the step where you need to replace **PROFILE**, choose:
- `android-32` to build for 32-bit architecture (armeabi-v7a)
- `android-64` to build for 64-bit architecture (aarch64-v8a)
- `android-32-ndk` to build for ARM 32-bit (armeabi-v7a)
- `android-64-ndk` to build for ARM 64-bit (aarch64-v8a)
- `android-x64-ndk` to build for Intel 64-bit (x86_64)
Advanced users may choose profile without `-ndk` suffix to use NDK that's already installed in their system.
### NDK and Conan
Conan must be aware of the NDK location when you execute `conan install`. There're multiple ways to achieve that as written in the [Conan docs](https://docs.conan.io/1/integrations/cross_platform/android.html):
Conan must be aware of the NDK location when you execute `conan install`. There're multiple ways to achieve that as written in the [Conan docs](https://docs.conan.io/2/examples/cross_build/android/ndk.html#examples-cross-build-android-ndk):
- the easiest is to download NDK from Conan (option 1 in the docs), then all the magic happens automatically. On the step where you need to replace **PROFILE**, choose *android-**X**-ndk* where ***X*** is either `32` or `64`.
- to use an already installed NDK, you can simply pass it on the command line to `conan install`: (note that this will work only when consuming the pre-built binaries)
- the easiest is to download NDK from Conan (option 1 in the docs), then all the magic happens automatically
- to use an already installed NDK, you can simply pass it on the command line to `conan install`: (note that this will likely work only when consuming the prebuilt binaries)
```sh
conan install -c tools.android:ndk_path=/path/to/ndk ...
@@ -48,37 +51,48 @@ conan install -c tools.android:ndk_path=/path/to/ndk ...
## Build process
Before starting the build, local Gradle configuration must be created (it's a requirement for Qt 5):
```sh
mkdir ~/.gradle
echo "android.bundle.enableUncompressedNativeLibs=true" > ~/.gradle/gradle.properties
```
Building for Android is a 2-step process. First, native C++ code is compiled to a shared library (unlike an executable on other platforms), then Java code is compiled to an actual executable which will be loading the native shared library at runtime.
This is a traditional CMake project, you can build it from command line or some IDE. You're not required to pass any custom options (except Conan toolchain file), defaults are already good. If you wish to use your own CMake presets, inherit them from our `build-with-conan` preset.
This is a traditional CMake project, you can build it from command line or some IDE. You're not required to pass any custom options (except Conan toolchain file), defaults are already good.
The Java code (located in the `android` directory of the repo) will be built automatically after the native code using the `androiddeployqt` tool. But you must set `JAVA_HOME` and `ANDROID_HOME` environment variables.
APK will appear in `<build dir>/android-build/vcmi-app/build/outputs/apk/debug` directory which you can then install to your device with `adb install -r /path/to/apk` (`adb` command is from Android command line tools).
APK will appear in `<build dir>/android-build/vcmi-app/build/outputs/apk/<build configuration>` directory which you can then install to your device with `adb install -r /path/to/apk` (`adb` command is from Android command line tools).
### Example
```sh
# the following environment variables must be set
export JAVA_HOME=/path/to/jdk11
export JAVA_HOME=/path/to/jdk17
export ANDROID_HOME=/path/to/android/sdk
cmake -S . -B ../build -G Ninja -D CMAKE_BUILD_TYPE=Debug -D ENABLE_CCACHE:BOOL=ON --toolchain ...
cmake --build ../build
```
You can also see a more detailed walkthrough on CMake configuration at [How to build VCMI (macOS)](./Building_macOS.md).
## Docker
For developing it's also possible to use Docker to build android APK. The only requirement is to have Docker installed. The container image contains all the other prerequisites.
To build using docker just open a terminal with `vcmi` as working directory.
To build using docker just open a terminal with `vcmi` repo root as working directory.
Build the image with (only needed once):
`docker build -f docker/BuildAndroid-aarch64.dockerfile -t vcmi-android-build .`
```sh
docker build -f docker/BuildAndroid-aarch64.dockerfile -t vcmi-android-build .
```
After building the image you can compile vcmi with:
`docker run -it --rm -v $PWD/:/vcmi vcmi-android-build`
The current dockerfile is aarch64 only but can adjusted manually for armv7.
```sh
docker run -it --rm -v $PWD/:/vcmi vcmi-android-build
```
The current dockerfile is aarch64 only but can be easily adjusted for other architectures.

View File

@@ -2,18 +2,17 @@
## Preparations
Windows builds can be made in more than one way and with more than one tool. This guide focuses on the simplest building process using Microsoft Visual Studio 2022
Windows builds can be made in more than one way and with more than one tool. This guide will show how to do it using Microsoft Visual Studio 2022 (MSVC compiler) and MSYS2 (MinGW compiler).
## Prerequisites
- Windows Vista or newer.
- [Microsoft Visual Studio](https://visualstudio.microsoft.com/downloads/)
- Git or git GUI, for example, SourceTree [download link](http://www.sourcetreeapp.com/download)
- CMake [download link](https://cmake.org/download/). During install after accepting license agreement make sure to check "Add CMake to the system PATH for all users".
- To unpack pre-build Vcpkg: [7-zip](http://www.7-zip.org/download.html)
- Optional:
- To create installer: [NSIS](http://nsis.sourceforge.net/Main_Page)
- To speed up recompilation: [CCache](https://github.com/ccache/ccache/releases)
1. Windows 7 or newer
2. [Microsoft Visual Studio](https://visualstudio.microsoft.com/downloads/)
3. Git or git GUI, for example, SourceTree [download link](http://www.sourcetreeapp.com/download)
4. CMake [download link](https://cmake.org/download/). During install after accepting license agreement make sure to check "Add CMake to the system PATH for all users". You can also install CMake from the Visual Studio Installer or from a package manager like [WinGet](https://github.com/microsoft/winget-cli).
5. Optional:
- To create installer: [Inno Setup](https://jrsoftware.org/isinfo.php)
- To speed up recompilation: [CCache](https://github.com/ccache/ccache/releases)
### Choose an installation directory
@@ -34,6 +33,111 @@ Bad locations:
## Install VCMI dependencies
This step is needed only for MSVC compiler. You can also find legacy Vcpkg instructions in the bottom of the document.
We use Conan package manager to build/consume dependencies, find detailed usage instructions [here](./Conan.md). Note that the link points to the state of the current branch, for the latest release check the same document in the [master branch](https://github.com/vcmi/vcmi/blob/master/docs/developers/Conan.md).
On the step where you need to replace **PROFILE**, choose:
- `msvc-x64` to build for Intel 64-bit (x64 / x86_64)
- `msvc-arm64` to build for ARM 64-bit (arm64)
- `msvc-x86` to build for Intel 32-bit (x86)
*Note*: we recommend using CMD (`cmd.exe`) for the next steps. If you absolutely want to use Powershell, then run `conan install` twice appending `-c tools.env.virtualenv:powershell=powershell.exe` on the second run.
## Install CCache
Extract `ccache` to a folder of your choosing, add the folder to the `PATH` environment variable and log out and back in.
## Build VCMI
### Clone VCMI repository
#### From Git GUI
1. Open SourceTree
2. File -> Clone
3. Select `https://github.com/vcmi/vcmi/` as source
4. Select `%VCMI_DIR%/source` as destination
5. Expand Advanced Options and change Checkout Branch to `develop`
6. Tick `Recursive submodules`
7. Click Clone
#### From command line
```sh
git clone --recursive https://github.com/vcmi/vcmi.git %VCMI_DIR%/source
```
### Compile VCMI with Visual Studio
#### Generate solution
1. Open command line prompt (`cmd.exe`)
2. Execute `cd %VCMI_DIR%`
3. Now you need to run a script* from the Conan directory that you passed to `conan install` (the one that you passed in `--output-folder` parameter). For example, if you passed `conan-msvc`, then the script will be in `source\conan-msvc`.
- for CMD: `source\conan-msvc\conanrun.bat`
- for Powershell: `source\conan-msvc\conanrun.ps1`. If it gives an error, also run `Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted`
4. Create VS solution: `cmake -S source -B build --toolchain source\conan-msvc\conan_toolchain.cmake`
\* This script sets up `PATH` required for Qt tools (`moc`, `uic` etc.) that run during CMake configure and build steps. Those tools depend on `zlib.dll` that was built with Conan, therefore its directory must be in `PATH`.
#### Build solution
You must launch Visual Studio in a modified `PATH` environment, see `*` in the previous subsection. You can launch it right from the current shell by pasting path to `devenv.exe` (Visual Studio executable, e.g. `"C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe"`). To launch it later with correct environment, you can create batch script (a file with `bat` extension) which you can double-click, here's an example (use your own path on the first line):
```batchfile
call "c:\Users\kamba\source\repos\vcmi\conan-msvc\conanrun.bat"
call "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe"
```
1. Open `%VCMI_DIR%/build/VCMI.sln` in Visual Studio
2. Select `RelWithDebInfo` build type in the combobox
3. If you want to use ccache:
- Select `Manage Configurations...` in the combobox
- Specify the following CMake variable: `ENABLE_CCACHE=ON`
- See the [Visual Studio documentation](https://learn.microsoft.com/en-us/cpp/build/customize-cmake-settings?view=msvc-170#cmake-variables-and-cache) for details
4. Right click on `BUILD_ALL` project. This `BUILD_ALL` project should be in `CMakePredefinedTargets` tree in Solution Explorer. You can also build individual targets if you want.
5. VCMI will be built in `%VCMI_DIR%/build/bin` folder
### Compile VCMI with MinGW via MSYS2
- Install MSYS2 from <https://www.msys2.org/>
- Start the `MSYS MinGW x64`-shell
- Install dependencies: `pacman -S mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-boost mingw-w64-x86_64-gcc mingw-w64-x86_64-ninja mingw-w64-x86_64-qt5-static mingw-w64-x86_64-qt5-tools mingw-w64-x86_64-tbb`
- Generate and build solution from VCMI-root dir: `cmake --preset windows-mingw-release && cmake --build --preset windows-mingw-release`
**NOTE:** This will link Qt5 statically to `VCMI_launcher.exe` and `VCMI_Mapeditor.exe`. See [PR #3421](https://github.com/vcmi/vcmi/pull/3421) for some background.
## Troubleshooting and workarounds
Vcpkg might be very unstable due to limited popularity and fact of using bleeding edge packages (such as most recent Boost). Using latest version of dependencies could also expose both problems in VCMI code or library interface changes that developers not checked yet. So if you're built Vcpkg yourself and can't get it working please try to use binary package.
Pre-built version we provide is always manually tested with all supported versions of MSVC for both Release and Debug builds and all known quirks are listed below.
### Build is successful but can not start new game
Make sure you have:
* Installed Heroes III from disk or using GOG installer
* Copied `Data`, `Maps` and `Mp3` folders from Heroes III to: `%USERPROFILE%\Documents\My Games\vcmi\`
### VCMI won't run since some library is missing
**If you open solution using vcmi.sln** Try to build INSTALL target and see if its output works as expected. Copy missing libraries or even all libraries from there to your build directory. Or run cpack and use produced installer and see if you can get libs from there. cpack -V will give more info. If cpack complains that it can not find dumpbin try Visual Studio Command Prompt (special version of cmd provided with Visual Studio with additional PATH) or modify PATH to have this tool available. Another alternative if you use prebuilt vcpkg package is to download latest msvc build, install it and copy missing/all libraries from there.
### Debug build is very slow
Debug builds with MSVC are generally extremely slow since it's not just VCMI binaries are built as debug, but every single dependency too and this usually means no optimizations at all. Debug information that available for release builds is often sufficient so just avoid full debug builds unless absolutely necessary. Instead use RelWithDebInfo configuration, optionally with Optimization Disabled (/Od) to avoid variables being optimized away. Also Debug configuration might have some compilation issues because it is not checked via CI for now.
### I got crash within library XYZ.dll
VCPKG generated projects quite often have both debug and regular libs available to linker so it can select wrong lib. For stable RelWithDebInfo build you may try to remove debug folder from VCPKG/installed/x64-windows. Same is done on CI. Also it reduces package size at least twice.
## Legacy instructions for Vcpkg package manager
We have switched to the Conan package manager in v1.7. Legacy Vcpkg integration is no longer supported and may or may not work out of the box.
You have two options: to use pre-built libraries or build your own. We strongly recommend start with using pre-built ones.
### Option A. Use pre-built Vcpkg
@@ -43,7 +147,7 @@ You have two options: to use pre-built libraries or build your own. We strongly
Vcpkg Archives are available at our GitHub: <https://github.com/vcmi/vcmi-deps-windows/releases>
- Download latest version available.
EG: v1.6 assets - [vcpkg-export-x64-windows-v143.7z](https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.6/vcpkg-export-x64-windows-v143.7z)
EG: v1.6 assets - [vcpkg-export-x64-windows-v143.7z](https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.6/vcpkg-export-x64-windows-v143.7z)
- Extract archive by right clicking on it and choosing "7-zip -> Extract Here".
#### Move dependencies to target directory
@@ -83,82 +187,6 @@ git clone https://github.com/microsoft/vcpkg.git %VCMI_DIR%/vcpkg
For the list of the packages used you can also consult [vcmi-deps-windows readme](https://github.com/vcmi/vcmi-deps-windows) in case this article gets outdated a bit.
## Install CCache
### CMake integration
Extract `ccache` to a folder of your choosing, add the folder to the `PATH` environment variable and log out and back in.
## Build VCMI
#### From GIT GUI
- Open SourceTree
- File -> Clone
- select `https://github.com/vcmi/vcmi/` as source
- select `%VCMI_DIR%/source` as destination
- expand Advanced Options and change Checkout Branch to `develop`
- tick `Recursive submodules`
- click Clone
#### From command line
- `git clone --recursive https://github.com/vcmi/vcmi.git %VCMI_DIR%/source`
### Generate solution for VCMI
- Create `%VCMI_DIR%/build` folder
- Open a command line prompt at `%VCMI_DIR%/build`
- Execute `cd %VCMI_DIR%/build`
- Create solution (Visual Studio 2022 64-bit) `cmake %VCMI_DIR%/source -DCMAKE_TOOLCHAIN_FILE=%VCMI_DIR%/vcpkg/scripts/buildsystems/vcpkg.cmake -G "Visual Studio 17 2022" -A x64`
### Compile VCMI with Visual Studio
- Open `%VCMI_DIR%/build/VCMI.sln` in Visual Studio
- Select `Release` build type in the combobox
- If you want to use ccache:
- Select `Manage Configurations...` in the combobox
- Specify the following CMake variable: `ENABLE_CCACHE=ON`
- See the [Visual Studio documentation](https://learn.microsoft.com/en-us/cpp/build/customize-cmake-settings?view=msvc-170#cmake-variables-and-cache) for details
- Right click on `BUILD_ALL` project. This `BUILD_ALL` project should be in `CMakePredefinedTargets` tree in Solution Explorer.
- VCMI will be built in `%VCMI_DIR%/build/bin` folder!
### Compile VCMI with MinGW via MSYS2
- Install MSYS2 from <https://www.msys2.org/>
- Start the `MSYS MinGW x64`-shell
- Install dependencies: `pacman -S mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-boost mingw-w64-x86_64-gcc mingw-w64-x86_64-ninja mingw-w64-x86_64-qt5-static mingw-w64-x86_64-qt5-tools mingw-w64-x86_64-tbb`
- Generate and build solution from VCMI-root dir: `cmake --preset windows-mingw-release && cmake --build --preset windows-mingw-release`
**NOTE:** This will link Qt5 statically to `VCMI_launcher.exe` and `VCMI_Mapeditor.exe`. See [PR #3421](https://github.com/vcmi/vcmi/pull/3421) for some background.
## Create VCMI installer (This step is not required for just building & development)
Make sure NSIS is installed to default directory or have registry entry so CMake can find it.
After you build VCMI execute following commands from `%VCMI_DIR%/build`.
- for release build: `cpack`
- for debug build: `cpack -C Debug`
## Troubleshooting and workarounds
Vcpkg might be very unstable due to limited popularity and fact of using bleeding edge packages (such as most recent Boost). Using latest version of dependencies could also expose both problems in VCMI code or library interface changes that developers not checked yet. So if you're built Vcpkg yourself and can't get it working please try to use binary package.
Pre-built version we provide is always manually tested with all supported versions of MSVC for both Release and Debug builds and all known quirks are listed below.
### Build is successful but can not start new game
Make sure you have:
* Installed Heroes III from disk or using GOG installer
* Copied `Data`, `Maps` and `Mp3` folders from Heroes III to: `%USERPROFILE%\Documents\My Games\vcmi\`
### VCMI won't run since some library is missing
**If you open solution using vcmi.sln** Try to build INSTALL target and see if its output works as expected. Copy missing libraries or even all libraries from there to your build directory. Or run cpack and use produced installer and see if you can get libs from there. cpack -V will give more info. If cpack complains that it can not find dumpbin try Visual Studio Command Prompt (special version of cmd provided with Visual Studio with additional PATH) or modify PATH to have this tool available. Another alternative if you use prebuilt vcpkg package is to download latest msvc build, install it and copy missing/all libraries from there.
### Debug build is very slow
Debug builds with MSVC are generally extremely slow since it's not just VCMI binaries are built as debug, but every single dependency too and this usually means no optimizations at all. Debug information that available for release builds is often sufficient so just avoid full debug builds unless absolutely necessary. Instead use RelWithDebInfo configuration, optionally with Optimization Disabled (/Od) to avoid variables being optimized away Also Debug configuration might have some compilation issues because it is not checked via CI for now.
### I got crash within library XYZ.dll
VCPKG generated projects quite often have both debug and regular libs available to linker so it can select wrong lib. For stable RelWithDebInfo build you may try to remove debug folder from VCPKG/installed/x64-windows. Same is done on CI. Also it reduces package size at least twice.
When executing `cmake` configure step, pass `--toolchain %VCMI_DIR%/vcpkg/scripts/buildsystems/vcpkg.cmake`

View File

@@ -18,10 +18,9 @@ git clone --recurse-submodules https://github.com/vcmi/vcmi.git
## Obtaining dependencies
There are 2 ways to get prebuilt dependencies:
The primary and officially supported way is [Conan package manager](./Conan.md). Note that the link points to the state of the current branch, for the latest release check the same document in the [master branch](https://github.com/vcmi/vcmi/blob/master/docs/developers/Conan.md).
- [Conan package manager](./Conan.md) - recommended. Note that the link points to the state of the current branch, for the latest release check the same document in the [master branch](https://github.com/vcmi/vcmi/blob/master/docs/developers/Conan.md).
- [legacy manually built libraries](https://github.com/vcmi/vcmi-ios-deps) - can be used if you have Xcode 11/12 or to build for simulator / armv7 device
There are also [legacy manually built libraries](https://github.com/vcmi/vcmi-ios-deps) which can be used if you have Xcode 11/12 or to build for simulator / armv7 device, but this way is no longer supported. Using Conan will also let you build with any Xcode version and for any architecture / SDK.
## Configuring project
@@ -30,9 +29,8 @@ Only Xcode generator (`-G Xcode`) is supported!
As a minimum, you must pass the following variables to CMake:
- `BUNDLE_IDENTIFIER_PREFIX`: unique bundle identifier prefix, something like `com.MY-NAME`
- (if using legacy dependencies) `CMAKE_PREFIX_PATH`: path to the downloaded dependencies, e.g. `~/Downloads/vcmi-ios-depends/build/iphoneos`
There're a few [CMake presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html): for device (Conan and legacy dependencies) and for simulator, named `ios-device-conan`, `ios-device` and `ios-simulator` respectively. You can also create your local "user preset" to avoid typing variables each time, see example [here](https://gist.github.com/kambala-decapitator/59438030c34b53aed7d3895aaa48b718).
There's a [CMake preset](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) for device named `ios-device-conan`. You can also create your local "user preset" to avoid typing variables each time, see example [here](https://gist.github.com/kambala-decapitator/59438030c34b53aed7d3895aaa48b718).
Open terminal and `cd` to the directory with source code. Configuration example for device with Conan:
@@ -76,4 +74,4 @@ Invoke `cpack` after building:
`cpack -C Release`
This will generate file with extension **ipa** if you use CMake 3.25+and **zip** otherwise (simply change extension to ipa).
This will generate file with extension **ipa** if you use CMake 3.25+ and **zip** otherwise (simply change extension to ipa).

View File

@@ -23,7 +23,9 @@ git clone --recurse-submodules https://github.com/vcmi/vcmi.git
There're 2 ways to get dependencies automatically.
### Conan package manager
### Conan package manager (recommended)
We use this to produce builds on CI.
Please find detailed instructions [here](./Conan.md). Note that the link points to the state of the current branch, for the latest release check the same document in the [master branch](https://github.com/vcmi/vcmi/blob/master/docs/developers/Conan.md).
@@ -32,47 +34,42 @@ On the step where you need to replace **PROFILE**, choose:
- if you're on an Intel Mac: `macos-intel`
- if you're on an Apple Silicon Mac: `macos-arm`
Note: if you wish to build 1.0 release in non-`Release` configuration, you should define `USE_CONAN_WITH_ALL_CONFIGS=1` environment variable when executing `conan install`.
### Homebrew
1. [Install Homebrew](https://brew.sh/)
2. Install dependencies: `brew install boost minizip sdl2 sdl2_image sdl2_mixer sdl2_ttf tbb`
3. If you want to watch in-game videos, also install FFmpeg: `brew install ffmpeg@4`
4. Install Qt dependency in either of the ways (note that you can skip this if you're not going to build Launcher):
3. If you want to watch in-game videos, also install FFmpeg: `brew install ffmpeg` (you can also use an earlier FFmpeg version)
4. Install Qt dependency in either of the ways (note that you can skip this if you're not going to build Launcher and Map editor):
- `brew install qt@5` for Qt 5 or `brew install qt` for Qt 6
- using [Qt Online Installer](https://www.qt.io/download) - choose **Go open source**
- using [Qt Online Installer](https://www.qt.io/download-qt-installer-oss)
## Preparing build environment
This applies only to Xcode-based toolchain. If `xcrun -f clang` prints errors, then use either of the following ways:
- select an Xcode instance from Xcode application - Preferences - Locations - Command Line Tools
- use `xcode-select` utility to set Xcode or Xcode Command Line Tools path: for example, `sudo xcode-select -s /Library/Developer/CommandLineTools`
- set `DEVELOPER_DIR` environment variable pointing to Xcode or Xcode Command Line Tools path: for example, `export DEVELOPER_DIR=/Applications/Xcode.app`
- use `xcode-select` utility to set Xcode or Xcode Command Line Tools path, example: `sudo xcode-select -s /Library/Developer/CommandLineTools`
- set `DEVELOPER_DIR` environment variable pointing to Xcode or Xcode Command Line Tools path, example: `export DEVELOPER_DIR=/Applications/Xcode.app`
## Configuring project for building
Note that if you wish to use Qt Creator IDE, you should skip this step and configure respective variables inside the IDE.
Note that if you wish to use Qt Creator or CLion IDE, you should skip this step and configure respective variables inside the IDE. Or you could create a [CMake preset](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) to avoid manual configuration.
The following walkthrough lists only the bare minimum of required CMake options.
1. In Terminal `cd` to the source code directory
2. Start assembling CMake invocation: type `cmake -S . -B BUILD_DIR` where *BUILD_DIR* can be any path, **don't press Return**
3. Decide which CMake generator you want to use:
- Makefiles: no extra option needed or pass `-G 'Unix Makefiles'`
- Ninja (if you have installed it): pass `-G Ninja`
- Xcode IDE (if you have installed it): pass `-G Xcode`
4. If you picked Makefiles or Ninja, pick desired *build type* - either of Debug / RelWithDebInfo / Release / MinSizeRel - and pass it in `CMAKE_BUILD_TYPE` option, for example: `-D CMAKE_BUILD_TYPE=Release`. If you don't pass this option, `RelWithDebInfo` will be used.
5. If you don't want to build Launcher, pass `-D ENABLE_LAUNCHER=OFF`
6. You can also pass `-Wno-dev` if you're not interested in CMake developer warnings
7. Next step depends on the dependency manager you have picked:
- Conan: pass `-D CMAKE_TOOLCHAIN_FILE=conan-generated/conan_toolchain.cmake` where **conan-generated** must be replaced with your directory choice
- Homebrew: if you installed FFmpeg or Qt 5, you need to pass `-D "CMAKE_PREFIX_PATH="` variable. See below what you can insert after `=` (but **before the closing quote**), multiple values must be separated with `;` (semicolon):
- if you installed FFmpeg, insert `$(brew --prefix ffmpeg@4)`
- if you installed Qt 5 from Homebrew, insert:`$(brew --prefix qt@5)`
- if you installed Qt from Online Installer, insert your path to Qt directory, for example: `/Users/kambala/dev/Qt-libs/5.15.2/Clang64`
- example for FFmpeg + Qt 5: `-D "CMAKE_PREFIX_PATH=$(brew --prefix ffmpeg@4);$(brew --prefix qt@5)"`
8. If you want to speed up the recompilation, add `-D ENABLE_CCACHE=ON`
9. Now press Return
- Ninja (if you have installed it): pass `-G Ninja`
- Makefiles: no extra option needed or pass `-G 'Unix Makefiles'`
4. If you picked Makefiles or Ninja, pick desired *build type* - either of `Debug` / `RelWithDebInfo` / `Release` / `MinSizeRel` - and pass it in `CMAKE_BUILD_TYPE` option, example: `-D CMAKE_BUILD_TYPE=Debug`. If you use don't pass this option, `RelWithDebInfo` will be used.
5. Next step depends on the dependency manager you have picked:
- Conan: pass `--toolchain conan-generated/conan_toolchain.cmake` (or via `CMAKE_TOOLCHAIN_FILE` variable) where **conan-generated** must be replaced with your directory choice
- Homebrew: if you installed Qt 5 or from the Online Installer, you need to pass `-D "CMAKE_PREFIX_PATH="` variable. See below what you can insert after `=` (but **before the closing quote**), multiple values must be separated with `;` (semicolon):
- if you installed Qt 5 from Homebrew, insert `$(brew --prefix qt@5)`
- if you installed Qt from Online Installer, insert your path to Qt directory, example: `/Users/kambala/dev/Qt-libs/5.15.2/Clang64`
6. Now press Return
## Building project
@@ -87,35 +84,13 @@ Open `VCMI.xcodeproj` from the build directory, select `vcmiclient` scheme and h
`cmake --build <path to build directory>`
- If using Makefiles generator, you'd want to utilize all your CPU cores by appending `-- -j$(sysctl -n hw.ncpu)` to the above
- If using Xcode generator, you can also choose which configuration to build by appending `--config <configuration name>` to the above, for example: `--config Debug`
## Packaging project into DMG file
After building, run `cpack` from the build directory. If using Xcode generator, also pass `-C <configuration name>` with the same configuration that you used to build the project.
If you use Conan, it's expected that you use **conan-generated** directory at step 4 of [Conan package manager](Conan.md).
- If using Xcode generator, you can also choose which configuration to build by appending `--config <configuration name>` to the above, example: `--config Debug`
## Running VCMI
You can run VCMI from DMG, but it's will also work from your IDE be it Xcode or Qt Creator.
Alternatively you can run binaries directly from the **bin** directory:
You can run binaries from your IDE or directly from the **bin** directory:
- BUILD_DIR/bin/vcmilauncher
- BUILD_DIR/bin/vcmiclient
- BUILD_DIR/bin/vcmiserver
CMake include commands to copy all needed assets from source directory into the **bin** directory on each build. They'll work when you build from Xcode too.
## Some useful debugging tips
Anyone who might want to debug builds, but new to macOS could find following commands useful:
- To attach DMG file from command line use `hdiutil attach vcmi-1.0.dmg`
- Detach volume: `hdiutil detach /Volumes/vcmi-1.0`
- To view dependency paths: `otool -L /Volumes/vcmi-1.0/VCMI.app/Contents/MacOS/vcmiclient`
- To display load commands such as `LC_RPATH`: `otool -l /Volumes/vcmi-1.0/VCMI.app/Contents/MacOS/vcmiclient`
## Troubleshooting
In case of troubles you can always consult our CI build scripts or contact the dev team via discord.
- BUILD_DIR/bin/vcmimapeditor

View File

@@ -6,137 +6,255 @@
The following platforms are supported and known to work, others might require changes to our [conanfile.py](../../conanfile.py) or upstream recipes.
- **macOS**: x86_64 (Intel) - target 10.13 (High Sierra), arm64 (Apple Silicon) - target 11.0 (Big Sur)
- **macOS**:
- x86_64 (Intel) - target 10.13 (High Sierra)
- arm64 (Apple Silicon) - target 11.0 (Big Sur)
- **iOS**: arm64 - target 12.0
- **Windows**: x86_64 and x86 fully supported with building from Linux
- **Android**: armeabi-v7a (32-bit ARM) - target 4.4 (API level 19), aarch64-v8a (64-bit ARM) - target 5.0 (API level 21)
- **Windows** using MSVC compiler:
- x86_64 (x64) - target Windows 7
- x86 - target Windows 7
- arm64 - target Windows 11
- **Android**:
- arvm7 / armeabi-v7a (32-bit ARM) - target 4.4 (API level 19)
- arm64 / aarch64-v8a (64-bit ARM) - target 5.0 (API level 21)
- x86_64 (64-bit Intel) - target 5.0 (API level 21)
## Getting started
1. [Install Conan](https://docs.conan.io/1/installation.html). Currently we support only Conan v1, you can install it with `pip` like this: `pip3 install 'conan<2.0'`
2. Execute in terminal: `conan profile new default --detect`
1. [Install Conan](https://docs.conan.io/2/installation.html). For example: `pip3 install conan`
2. Execute in terminal: `conan profile detect`. It will create *build profile* for your machine.
## Download dependencies
0. If your platform is not on the list of supported ones or you don't want to use our prebuilt binaries, you can still build dependencies from source or try consuming prebuilt binaries from the central Conan repository - [ConanCenter](https://conan.io/center/). In this case skip to the [next section](#generate-cmake-integration) directly.
1. Check if your build environment can use the prebuilt binaries: basically, that your compiler version (or Xcode major version) matches the information below. If you're unsure, simply advance to the next step.
- *macOS*: libraries are built with Apple clang 16 (Xcode 16.2), should be consumable by Xcode / Xcode CLT 14.x and later
- *iOS*: libraries are built with Apple clang 16 (Xcode 16.2), should be consumable by Xcode 14.x and later
- *Windows*: libraries are built with MSVC 19.4x (v143 toolset)
- *Android*: libraries are built with NDK r25c (25.2.9519653)
- **macOS**: libraries are built with Apple clang 14 (Xcode 14.2), should be consumable by Xcode / Xcode CLT 14.x and later (older library versions are also available for Xcode 13, see Releases in the respective repo)
- **iOS**: libraries are built with Apple clang 14 (Xcode 14.2), should be consumable by Xcode 14.x and later (older library versions are also available for Xcode 13, see Releases in the respective repo)
- **Windows**: libraries are built with x86_64-mingw-w64-gcc version 10 (which is available in repositories of Ubuntu 22.04)
- **Android**: libraries are built with NDK r25c (25.2.9519653)
2. Download the binaries archive from <https://github.com/vcmi/vcmi-dependencies/releases> (pre-release is for development version and the latest release is for respective VCMI release) and use `conan cache restore <path to archive>` command to unpack them.
- *macOS*: pick **dependencies-mac-intel.tgz** if you have Intel Mac, otherwise - **dependencies-mac-arm.tgz**
- *iOS*: pick **dependencies-ios.tgz**
- *Windows*: pick **dependencies-windows-x64.tgz** for Windows x64, **dependencies-windows-arm64.tgz** for Windows ARM64 or **dependencies-windows-x86.tgz** for Windows x86
- *Android*: pick **dependencies-android-arm64-v8a.tgz** for arm64 (ARM 64-bit), **dependencies-android-armeabi-v7a.tgz** for armv7 (ARM 32-bit) or **dependencies-android-x64.tgz** for x86_64 (Intel 64-bit)
2. Download the binaries archive and unpack it to `~/.conan` directory from <https://github.com/vcmi/vcmi-dependencies/releases/latest>
### Platform-specific preparation
- macOS: pick **dependencies-mac-intel.txz** if you have Intel Mac, otherwise - **dependencies-mac-arm.txz**
- iOS: pick ***dependencies-ios.txz***
- Windows: currently only mingw is supported. Pick **dependencies-mingw.tgz** if you want x86_64, otherwise pick **dependencies-mingw-32.tgz**
- Android: current limitation is that building works only on a macOS host due to Qt 5 for Android being compiled on macOS. Simply delete directory `~/.conan/data/qt/5.15.x/_/_/package` (`5.15.x` is a placeholder) after unpacking the archive and build Qt from source. Alternatively, if you have (or are [willing to build](https://github.com/vcmi/vcmi-ios-deps#note-for-arm-macs)) Qt host tools for your platform, then simply replace those in the archive with yours and most probably it would work.
Follow this subsection only if any of the following applies to you, otherwise skip to the [next section](#generate-cmake-integration) directly.
3. Only if you have Apple Silicon Mac and trying to build for macOS or iOS:
- you're trying to build for Android and your OS is Windows or macOS or Linux aarch64
- you're trying to build for iOS and you have Intel Mac
1. Open file `~/.conan/data/qt/5.15.x/_/_/export/conanfile.py` (`5.15.x` is a placeholder), search for string `Designer` (there should be only one match), comment this line and the one above it by inserting `#` in the beginning, and save the file.
2. (optional) If you don't want to use Rosetta, follow [instructions how to build Qt host tools for Apple Silicon](https://github.com/vcmi/vcmi-ios-deps#note-for-arm-macs), on step 3 copy them to `~/.conan/data/qt/5.15.x/_/_/package/SOME_HASH/bin` (`5.15.x` and `SOME_HASH` are placeholders). Make sure **not** to copy `qt.conf`!
Qt 5 has some utilities required for the build process (moc, uic etc.) that are built for your desktop OS. Our CI (GitHub Actions) makes Android builds on Ubuntu Linux amd64 and iOS builds on an Apple Silicon Mac, therefore those Qt utilities are built for Linux amd64 and macOS arm64 respectively and can't be used/run on other OS / CPU architectures. To solve that, you must add/replace those utilities built for your desktop OS.
Once you have the executable files of those utilities, copy them to the `bin` directory of Qt package. You can find the package at `~/.conan2/p/qt<some hash>/p`.
#### Option 1
The easiest way would be downloading prebuilt dependencies for your platform (Windows / macOS), unpacking the archive (using `conan cache restore` isn't required, unpack as an ordinary archive) and copying (or making a hard or soft link) executable files from `<unpacked dir>/b/qt<some hash>/p/bin` directory. We don't provide prebuilts for Linux - simply install Qt development package from your package manager and copy executables from its `bin` directory. But you can also build all the executables manually, see [below](#option-2).
However, Qt for Android requires one more executable that can't be found in your desktop Qt build - `androiddeployqt`. And you'll have to build it manually, see next subsection.
#### Option 2
Building all those executables is rather fast as it doesn't require building whole Qt. This is how you do it in Bash shell:
```sh
# set Qt version that you're going to download and build
qtVer=5.15.16
# for Android:
# ensure that ANDROID_HOME environment variable is set pointing to Android SDK directory
# also set Min SDK and NDK versions
minSdkVersion=21
ndkVersion=25.2.9519653
qtSrcDir="qt-everywhere-src-$qtVer"
qtInstallDir="$(pwd)/install"
# download Qt sources, unpack only the required subset of them
# use URL from a mirror if needed
curl -L "https://download.qt.io/official_releases/qt/5.15/$qtVer/single/qt-everywhere-opensource-src-$qtVer.tar.xz" \
| tar -xf - --xz "$qtSrcDir"/{'.*','LICENSE*','configure*',qt.pro,'qtbase/*','qttools/*'}
# create build directory
mkdir build
cd build
# configure Qt for building, also pass to the following command:
# for Android: -shared -xplatform android-clang -android-sdk "$ANDROID_HOME" -android-ndk "$ANDROID_HOME/ndk/$ndkVersion" -android-ndk-platform android-$minSdkVersion -android-abis arm64-v8a
# for iOS: -static -xplatform macx-ios-clang -no-framework
"../$qtSrcDir/configure" -prefix "$qtInstallDir" -opensource -confirm-license -release -strip -appstore-compliant -make libs -no-compile-examples -no-dbus -system-zlib -no-openssl -opengl es2 -no-gif -no-ico -nomake examples -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qttranslations -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtwinextras -skip qtx11extras -skip qtxmlpatterns
make --jobs=$(getconf _NPROCESSORS_ONLN) module-qtbase-qmake_all module-qttools-qmake_all
# if you need to build just androiddeployqt, on the second line pass only: sub-androiddeployqt
make --directory=qtbase/src --jobs=$(getconf _NPROCESSORS_ONLN) \
sub-bootstrap sub-moc sub-qlalr sub-qvkgen sub-rcc sub-tracegen sub-uic sub-androiddeployqt
make --directory=qttools/src/linguist --jobs=$(getconf _NPROCESSORS_ONLN) \
sub-lconvert sub-lprodump sub-lrelease sub-lrelease-pro sub-lupdate sub-lupdate-pro
```
After that you'll find all the executables in `build/qtbase/bin` and `build/qttools/bin` directories.
#### Option 3
Simply build whole Qt from source.
1. Remove Qt binary package: `conan remove "qt/5.15.*"`
2. In the next section use `--build=missing`
## Generate CMake integration
Conan needs to generate CMake toolchain file to make dependencies available to CMake. See `CMakeDeps` and `CMakeToolchain` [in the official documentation](https://docs.conan.io/1/reference/conanfile/tools/cmake.html) for details.
Conan needs to generate CMake toolchain file to make dependencies available to CMake. See `CMakeDeps` and `CMakeToolchain` [in the official documentation](https://docs.conan.io/2/reference/tools/cmake.html) for details.
In terminal `cd` to the VCMI source directory and run the following command. Also check subsections for additional requirements on consuming prebuilt binaries.
Make sure that you've cloned VCMI repository with submodules! (or initialized them afterwards)
In terminal `cd` to the VCMI source directory and run the following command (it's written in Bash syntax, for other shells like Cmd or Powershell use appropriate line continuation character instead of `\` or type everything on a single line). Also check subsections for additional requirements on consuming prebuilt binaries.
*Note*: if you're going to build for Windows MSVC, it's recommended to use Cmd shell. If you absolutely want to use Powershell, then run the below command twice appending `-c tools.env.virtualenv:powershell=powershell.exe` on the second run.
<pre>
conan install . \
--install-folder=<b><i>conan-generated</i></b> \
--no-imports \
--output-folder=<b><i>conan-generated</i></b> \
--build=<b><i>never</i></b> \
--profile:build=default \
--profile:host=<b><i>CI/conan/PROFILE</i></b>
--profile=<b><i>dependencies/conan_profiles/PROFILE</i></b> \
<b><i>EXTRA PARAMS</i></b>
</pre>
The highlighted parts can be adjusted:
- ***conan-generated***: directory (absolute or relative) where the generated files will appear. This value is used in CMake presets from VCMI, but you can actually use any directory and override it in your local CMake presets.
- ***never***: use this value to avoid building any dependency from source. You can also use `missing` to build recipes, that are not present in your local cache, from source.
- ***CI/conan/PROFILE***: if you want to consume our prebuilt binaries, ***PROFILE*** must be replaced with one of filenames from our [Conan profiles directory](../../CI/conan) (determining the right file should be straight-forward). Otherwise, either select one of our profiles or replace ***CI/conan/PROFILE*** with `default` (your default profile).
- ***note for Windows x86***: use profile mingw32-linux.jinja for building instead of mingw64-linux.jinja
- ***never***: use this value to avoid building any dependency from source. You can also use `missing` to build binary packages, that are not present in your local cache, from source. There're also other values, see `conan install -h` or the full documentation linked below.
- ***dependencies/conan_profiles/PROFILE***: if you want to consume our prebuilt binaries, ***PROFILE*** must be replaced with one of filenames from our [Conan profiles directory](../../dependencies/conan_profiles) (determining the right file should be straight-forward). Otherwise, either select one of our profiles or replace ***CI/conan/PROFILE*** with `default` (your default profile, will build for your desktop OS) or create your own profile for the desired platform.
- ***EXTRA PARAMS***: additional params to the `conan install` command, you can specify multiple:
- if you want to consume our prebuilt binaries for Apple platforms (macOS / iOS), pass `--profile=dependencies/conan_profiles/base/apple-system`
- if you want to consume our prebuilt binaries for Android, pass `--profile=dependencies/conan_profiles/base/android-system`
- if your intention is to make a Debug build, pass `-s "&:build_type=RelWithDebInfo"` for Windows MSVC and `-s "&:build_type=Debug"` for other platforms
- if you're building on Windows 11 ARM64, pass `-o "&:lua_lib=lua"`
- if you're building on (or for) Windows < 10, pass `-o "&:target_pre_windows10=True"`
If you use `--build=never` and this command fails, then it means that you can't use prebuilt binaries out of the box. For example, try using `--build=missing` instead.
VCMI "recipe" also has some options that you can specify. For example, if you don't care about game videos, you can disable FFmpeg dependency by passing `-o with_ffmpeg=False`. If you only want to make release build, you can use `GENERATE_ONLY_BUILT_CONFIG=1` environment variable to skip generating files for other configurations (our CI does this).
VCMI "recipe" also has some options that you can specify. For example, if you don't care about game videos, you can disable FFmpeg dependency by passing `-o "&:with_ffmpeg=False"`. Check [the recipe](../../dependencies/conanfile.py) for details.
*Note*: you can find full reference of this command [in the official documentation](https://docs.conan.io/1/reference/commands/consumer/install.html) or by executing `conan help install`.
### Using our prebuilt binaries for macOS/iOS
We use custom recipes for some libraries that are provided by the OS. You must additionally pass `-o with_apple_system_libs=True` for `conan install` to use them.
*Note*: you can find full reference of this command [in the official documentation](https://docs.conan.io/2/reference/commands/install.html) or by executing `conan install -h`.
### Using prebuilt binaries from ConanCenter
First, check if binaries for [your platform](https://github.com/conan-io/conan-center-index/blob/master/docs/supported_platforms_and_configurations.md) are produced.
You must adjust the above `conan install` command:
1. Replace ***CI/conan/PROFILE*** with `default`.
2. Additionally pass `-o default_options_of_requirements=True`: this disables all custom options of our `conanfile.py` to match ConanCenter builds.
You must adjust the above `conan install` command by replacing ***dependencies/conan_profiles/PROFILE*** with `default` (or simply omit the `--profile` parameter).
### Building dependencies from source
This subsection describes platform specifics to build libraries from source properly.
This subsection describes platform specifics to build libraries from source properly. Commands that our CI uses to build the dependencies can also be used as a reference, you can find them inside the [`dependencies` submodule](../../dependencies/.github/workflows/rebuildDependencies.yml) or in the [repository](https://github.com/vcmi/vcmi-dependencies/blob/main/.github/workflows/rebuildDependencies.yml).
You can use our Conan profiles or create your own (e.g. with different options), the choice is yours.
*Note:* our profiles expect you to have CMake and Ninja in `PATH`, otherwise build would fail.
#### Building for macOS/iOS
- To build Locale module of Boost in versions >= 1.81, you must use `compiler.cppstd=11` Conan setting (our profiles already contain it). To use it with another profile, either add this setting to your *host* profile or pass `-s compiler.cppstd=11` on the command line.
- If you wish to build dependencies against system libraries (like our prebuilt ones do), follow [below instructions](#using-recipes-for-system-libraries) executing `conan create` for all directories. Don't forget to pass `-o with_apple_system_libs=True` to `conan install` afterwards.
If you wish to build dependencies against system libraries (like our prebuilts do), follow [below instructions](#using-recipes-for-system-libraries) executing `conan create` for all directories.
If you're going to build for iOS without using our profile: to build Qt 5 with `md4c` library, make sure to enforce this library's version 0.5 or later. This is what we have in our profile:
> [replace_requires]\
> md4c/0.4.8: md4c/0.5.2
#### Building for Android
It's highly recommended to use NDK recipe provided by Conan, otherwise build may fail. If you're using your own Conan profile, you can include our [NDK profile](../../dependencies/conan_profiles/base/android-ndk) via an additional `--profile` parameter on the command line.
Android has issues loading self-built shared zlib library because binary name is identical to the system one, so we enforce using the OS-provided library. To achieve that, follow [below instructions](#using-recipes-for-system-libraries), you only need `zlib` directory.
Also, Android requires a few Qt patches. They are needed only for specific use cases, so you may evaluate whether you need them. The patches can be found [here](https://github.com/kambala-decapitator/Qt5-iOS-patches/tree/master/5.15.14/android). To apply selected patch(es), let Conan finish building dependencies first. Then `cd` to `qtbase` **source directory** (e.g. `~/.conan/data/qt/5.15.14/_/_/source/qt5/qtbase`) and run `patch -p1 < /path/to/patch` for each patch file (on Windows you'll need some sort of GNU environment like Git Bash to access `patch` utility).
Also, Android requires a few patches, but they are needed only for specific use cases, so you may evaluate whether you need them. The patches can be found inside the [`dependencies` submodule](../../dependencies/conan_patches/qt/patches) or in the [repository](https://github.com/vcmi/vcmi-dependencies/blob/main/conan_patches/qt/patches).
1. [Safety measure for Xiaomi devices](https://github.com/kambala-decapitator/Qt5-iOS-patches/blob/master/5.15.14/android/xiaomi.diff). It's unclear whether it's really needed, but without it [I faced a crash once](https://bugreports.qt.io/browse/QTBUG-111960).
2. [Fix running on Android 5.0-5.1](https://github.com/kambala-decapitator/Qt5-iOS-patches/blob/master/5.15.14/android/android-21-22.diff) (API level 21-22).
3. [Enable running on Android 4.4](https://github.com/kambala-decapitator/Qt5-iOS-patches/blob/master/5.15.14/android/android-19-jar.diff) (API level 19-20, 32-bit only). You must also apply [one more patch](https://github.com/kambala-decapitator/Qt5-iOS-patches/blob/master/5.15.14/android/android-19-java.diff), but you must `cd` to the **package directory** for that, e.g. `~/.conan/data/qt/5.15.14/_/_/package/SOME_HASH`.
##### Qt patches
After applying patch(es):
1. [Safety measure for Xiaomi devices](https://github.com/vcmi/vcmi-dependencies/blob/main/conan_patches/qt/patches/xiaomi.diff). It's unclear whether it's really needed, but without it [I faced a crash once](https://bugreports.qt.io/browse/QTBUG-111960).
2. [Fix running on Android 5.0-5.1](https://github.com/vcmi/vcmi-dependencies/blob/main/conan_patches/qt/patches/android-21-22.diff) (API level 21-22).
3. Enable running on Android 4.4 (API level 19-20, 32-bit only): [patch 1](https://github.com/vcmi/vcmi-dependencies/blob/main/conan_patches/qt/patches/android-19-jar.diff), [patch 1](https://github.com/vcmi/vcmi-dependencies/blob/main/conan_patches/qt/patches/android-19-java.diff).
1. `cd` to `qtbase/src/android/jar` in the **build directory**, e.g. `~/.conan/data/qt/5.15.14/_/_/build/SOME_HASH/build_folder/qtbase/src/android/jar`.
2. Run `make`
3. Copy file `qtbase/jar/QtAndroid.jar` from the build directory to the **package directory**, e.g. `~/.conan/data/qt/5.15.14/_/_/package/SOME_HASH/jar`.
##### Patches for other libraries
*Note*: if you plan to build Qt from source again, then you don't need to perform the above *After applying patch(es)* steps after building.
- Flac requires patch to be able to build it for 32-bit targeting API Level < 24
- Minizip requires patch to be able to build it for 32-bit targeting API Level < 21
##### Using recipes for system libraries
Also, to build Flac, Luajit and Opusfile for 32-bit targeting API Level < 24, a couple of C defines must be added to your Conan profile - see our [profile for ARM 32-bit](https://github.com/vcmi/vcmi-dependencies/blob/main/conan_profiles/android-32).
#### Using recipes for system libraries
1. Clone/download <https://github.com/kambala-decapitator/conan-system-libs>
2. Execute `conan create PACKAGE vcmi/CHANNEL`, where `PACKAGE` is a directory path in that repository and `CHANNEL` is **apple** for macOS/iOS and **android** for Android. Do it for each library you need.
3. Now you can execute `conan install` to build all dependencies.
2. Execute `conan create PACKAGE --user system`, where `PACKAGE` is a directory path in that repository. Do it for each library you need. (basically just read repo's readme)
#### Build it
First, you must build separate libraries with `conan create` if:
- you chose to use patches (ours listed above or your own) for a library. You must also pass `--core-conf core.sources.patch:extra_path=<patches path>` parameter where `<patches path>` is the path to the patches directory, ours is located at [dependencies/conan_patches](../../dependencies/conan_patches).
- you want to build LuaJIT for iOS or Android (they also require patches, see the above point). Upstream Conan recipe doesn't support this yet (but there's a [pull request](https://github.com/conan-io/conan-center-index/pull/26577)), so you'll have to use [fork](https://github.com/kambala-decapitator/conan-center-index/tree/package/luajit) where it works. *Note*: to build for 32-bit architecture (e.g. Android armv7) your OS must be able to run 32-bit executables, see [this issue](https://github.com/LuaJIT/LuaJIT/issues/664) for details (for example, macOS since 10.15 can't do that); on Linux amd64 you'll have to install `libc6-dev-i386` package.
After that you can execute `conan install` to build the rest of the dependencies.
## Configure project for building
You must pass the generated toolchain file to CMake invocation.
- if using custom CMake presets, just make sure to inherit our `build-with-conan` preset. If you store Conan generated files in a non-default directory, define the path to the generated toolchain in `toolchainFile` field (or `CMAKE_TOOLCHAIN_FILE` cache variable) or include CMake presets file generated by Conan.
- if using custom CMake presets, define the path to the generated toolchain in `toolchainFile` field (or `CMAKE_TOOLCHAIN_FILE` cache variable) or include CMake presets file generated by Conan.
- otherwise, if passing CMake options on the command line, use `--toolchain` option (available in CMake 3.21+) or `CMAKE_TOOLCHAIN_FILE` variable.
## Examples
In these examples only the minimum required amount of options is passed to `cmake` invocation, you can pass additional ones as needed.
### Use our prebuilt binaries to build for macOS x86_64 with Xcode
### Use our prebuilt binaries to build for Windows x64 with Visual Studio (CMD shell)
```batchfile
conan install . ^
--output-folder=conan-msvc ^
--build=never ^
--profile=dependencies\conan_profiles\msvc-x64 ^
-s "&:build_type=RelWithDebInfo"
REM this is important!
conan-msvc\conanrun.bat
cmake -S . -B build ^
--toolchain conan-msvc\conan_toolchain.cmake
```
### Use our prebuilt binaries to build for macOS arm64 with Xcode
```sh
conan install . \
--install-folder=conan-generated \
--no-imports \
--output-folder=conan-macos \
--build=never \
--profile:build=default \
--profile:host=CI/conan/macos-intel \
-o with_apple_system_libs=True
--profile=dependencies/conan_profiles/macos-arm \
--profile=dependencies/conan_profiles/base/apple-system \
-s "&:build_type=Debug"
cmake -S . -B build -G Xcode \
--toolchain conan-generated/conan_toolchain.cmake
--toolchain conan-macos/conan_toolchain.cmake
```
### Use our prebuilt binaries to build for Android arm64 with Ninja
```sh
conan install . \
--output-folder=conan-android64 \
--build=never \
--profile=dependencies/conan_profiles/android-64-ndk \
--profile=dependencies/conan_profiles/base/android-system \
-s "&:build_type=Debug"
cmake -S . -B build -G Ninja \
--toolchain conan-android64/conan_toolchain.cmake
```
### Try to use binaries from ConanCenter for your platform
@@ -146,11 +264,8 @@ If you also want to build the missing binaries from source, use `--build=missing
```sh
conan install . \
--install-folder=~/my-dir \
--no-imports \
--build=never \
--profile:build=default \
--profile:host=default \
-o default_options_of_requirements=True
-s "&:build_type=Debug"
cmake -S . -B build \
-D CMAKE_TOOLCHAIN_FILE=~/my-dir/conan_toolchain.cmake
@@ -160,12 +275,11 @@ cmake -S . -B build \
```sh
conan install . \
--install-folder=~/my-dir \
--no-imports \
--output-folder=conan-ios \
--build=never \
--profile:build=default \
--profile:host=CI/conan/ios-arm64 \
-o with_apple_system_libs=True
--profile=dependencies/conan_profiles/ios-arm64 \
--profile=dependencies/conan_profiles/base/apple-system \
-s "&:build_type=Debug"
cmake --preset ios-conan
```
@@ -184,8 +298,8 @@ cmake --preset ios-conan
{
"name": "ios-conan",
"displayName": "iOS",
"inherits": ["build-with-conan", "ios-device"],
"toolchainFile": "~/my-dir/conan_toolchain.cmake",
"inherits": ["ios-device"],
"toolchainFile": "conan-ios/conan_toolchain.cmake",
"cacheVariables": {
"BUNDLE_IDENTIFIER_PREFIX": "com.YOUR-NAME",
"CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM": "YOUR_TEAM_ID"
@@ -194,32 +308,3 @@ cmake --preset ios-conan
]
}
```
### Build VCMI with all deps for 32-bit windows in Ubuntu 22.04 WSL
```powershell
wsl --install
wsl --install -d Ubuntu
ubuntu
```
Next steps are identical both in WSL and in real Ubuntu 22.04
```sh
sudo pip3 install conan
sudo apt install cmake build-essential
sed -i 's/x86_64-w64-mingw32/i686-w64-mingw32/g' CI/mingw-ubuntu/before-install.sh
sed -i 's/x86-64/i686/g' CI/mingw-ubuntu/before-install.sh
sudo ./CI/mingw-ubuntu/before-install.sh
conan install . \
--install-folder=conan-generated \
--no-imports \
--build=missing \
--profile:build=default \
--profile:host=CI/conan/mingw32-linux \
-c tools.cmake.cmaketoolchain.presets:max_schema_version=2
cmake --preset windows-mingw-conan-linux
cmake --build --preset windows-mingw-conan-linux --target package
```
After that, you will have functional VCMI installer for 32-bit windows.

View File

@@ -1,4 +1,6 @@
if(ENABLE_INNOEXTRACT)
# libiconv not required for our use case
set(WITH_CONV "builtin" CACHE STRING "" FORCE)
add_subdirectory("lib/innoextract")
endif()
@@ -153,6 +155,7 @@ if(ENABLE_SINGLE_APP_BUILD OR ANDROID)
add_library(vcmilauncher OBJECT ${launcher_QM})
else()
add_executable(vcmilauncher WIN32 ${launcher_QM} ${launcher_ICON})
vcmi_create_exe_shim(vcmilauncher)
endif()
if(ENABLE_TRANSLATIONS)
@@ -248,11 +251,6 @@ if(APPLE_IOS)
"-framework UniformTypeIdentifiers"
)
# TODO: remove after switching prebuilt deps to a newer Conan's Qt recipe
if(XCODE_VERSION VERSION_GREATER_EQUAL 14.0)
target_link_libraries(vcmilauncher "-framework IOKit")
endif()
# workaround https://github.com/conan-io/conan-center-index/issues/13332
if(USING_CONAN)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/QIOSIntegrationPlugin.h

View File

@@ -9,15 +9,9 @@
*/
#pragma once
#ifdef USE_SYSTEM_MINIZIP
#include <minizip/unzip.h>
#include <minizip/zip.h>
#include <minizip/ioapi.h>
#else
#include "../minizip/unzip.h"
#include "../minizip/zip.h"
#include "../minizip/ioapi.h"
#endif
// system zlib on old Androids isn't capable of using _64 functions: https://github.com/madler/zlib/pull/436
#if defined(__ANDROID_API__) && (__ANDROID_API__ < 24)

View File

@@ -1,22 +0,0 @@
project(minizip)
include_directories(${ZLIB_INCLUDE_DIR})
#NOTE: full library consists from several more files
# but right now VCMI does not need any extra functionality
set(lib_SRCS
unzip.c
zip.c
ioapi.c
)
add_library(minizip SHARED ${lib_SRCS})
if(MSVC)
set_target_properties(minizip PROPERTIES COMPILE_DEFINITIONS "MINIZIP_DLL;ZLIB_DLL;ZLIB_INTERNAL")
endif()
vcmi_set_output_dir(minizip "")
target_link_libraries(minizip ${ZLIB_LIBRARIES})
install(TARGETS minizip RUNTIME DESTINATION ${LIB_DIR} LIBRARY DESTINATION ${LIB_DIR})

View File

@@ -1,6 +0,0 @@
MiniZip 1.1 was derived from MiniZip at version 1.01f
Change in 1.0 (Okt 2009)
- **TODO - Add history**

View File

@@ -1,74 +0,0 @@
MiniZip - Copyright (c) 1998-2010 - by Gilles Vollant - version 1.1 64 bits from Mathias Svensson
Introduction
---------------------
MiniZip 1.1 is built from MiniZip 1.0 by Gilles Vollant ( http://www.winimage.com/zLibDll/minizip.html )
When adding ZIP64 support into minizip it would result into risk of breaking compatibility with minizip 1.0.
All possible work was done for compatibility.
Background
---------------------
When adding ZIP64 support Mathias Svensson found that Even Rouault have added ZIP64
support for unzip.c into minizip for a open source project called gdal ( http://www.gdal.org/ )
That was used as a starting point. And after that ZIP64 support was added to zip.c
some refactoring and code cleanup was also done.
Changed from MiniZip 1.0 to MiniZip 1.1
---------------------------------------
* Added ZIP64 support for unzip ( by Even Rouault )
* Added ZIP64 support for zip ( by Mathias Svensson )
* Reverted some changed that Even Rouault did.
* Bunch of patches received from Gulles Vollant that he received for MiniZip from various users.
* Added unzip patch for BZIP Compression method (patch create by Daniel Borca)
* Added BZIP Compress method for zip
* Did some refactoring and code cleanup
Credits
Gilles Vollant - Original MiniZip author
Even Rouault - ZIP64 unzip Support
Daniel Borca - BZip Compression method support in unzip
Mathias Svensson - ZIP64 zip support
Mathias Svensson - BZip Compression method support in zip
Resources
ZipLayout http://result42.com/projects/ZipFileLayout
Command line tool for Windows that shows the layout and information of the headers in a zip archive.
Used when debugging and validating the creation of zip files using MiniZip64
ZIP App Note http://www.pkware.com/documents/casestudies/APPNOTE.TXT
Zip File specification
Notes.
* To be able to use BZip compression method in zip64.c or unzip64.c the BZIP2 lib is needed and HAVE_BZIP2 need to be defined.
License
----------------------------------------------------------
Condition of use and distribution are the same than zlib :
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
----------------------------------------------------------

View File

@@ -1,131 +0,0 @@
/* crypt.h -- base code for crypt/uncrypt ZIPfile
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2005 Gilles Vollant
This code is a modified version of crypting code in Infozip distribution
The encryption/decryption parts of this source code (as opposed to the
non-echoing password parts) were originally written in Europe. The
whole source package can be freely distributed, including from the USA.
(Prior to January 2000, re-export from the US was a violation of US law.)
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.
This code support the "Traditional PKWARE Encryption".
The new AES encryption added on Zip format by Winzip (see the page
http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
Encryption is not supported.
*/
#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
/***********************************************************************
* Return the next byte in the pseudo-random sequence
*/
static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab)
{
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 = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
}
/***********************************************************************
* Update the encryption keys with the next byte of plain text
*/
static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c)
{
(*(pkeys+0)) = CRC32((*(pkeys+0)), c);
(*(pkeys+1)) += (*(pkeys+0)) & 0xff;
(*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
{
register int keyshift = (int)((*(pkeys+1)) >> 24);
(*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
}
return c;
}
/***********************************************************************
* Initialize the encryption keys and the random header according to
* the given password.
*/
static void init_keys(const char* passwd,unsigned long* 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,(int)*passwd);
passwd++;
}
}
#define zdecode(pkeys,pcrc_32_tab,c) \
(update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
#define zencode(pkeys,pcrc_32_tab,c,t) \
(t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
#define RAND_HEAD_LEN 12
/* "last resort" source for second part of crypt seed pattern */
# ifndef ZCR_SEED2
# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
# endif
static int crypthead(const char* passwd, /* password string */
unsigned char* buf, /* where to write header */
int bufSize,
unsigned long* pkeys,
const z_crc_t* pcrc_32_tab,
unsigned long crcForCrypting)
{
int n; /* index in random header */
int t; /* temporary */
int c; /* random byte */
unsigned char header[RAND_HEAD_LEN-2]; /* random header */
static unsigned calls = 0; /* ensure different random header each time */
if (bufSize<RAND_HEAD_LEN)
return 0;
/* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
* output of rand() to get less predictability, since rand() is
* often poorly implemented.
*/
if (++calls == 1)
{
srand((unsigned)(time(NULL) ^ ZCR_SEED2));
}
init_keys(passwd, pkeys, pcrc_32_tab);
for (n = 0; n < RAND_HEAD_LEN-2; n++)
{
c = (rand() >> 7) & 0xff;
header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
}
/* 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] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
}
buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
return n;
}
#endif

View File

@@ -1,247 +0,0 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
*/
#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS)))
#define _CRT_SECURE_NO_WARNINGS
#endif
#if defined(__APPLE__) || defined(IOAPI_NO_64)
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
#define FTELLO_FUNC(stream) ftello(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define FTELLO_FUNC(stream) ftello64(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif
#include "ioapi.h"
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);
else
{
return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode);
}
}
long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)
{
if (pfilefunc->zfile_func64.zseek64_file != NULL)
return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin);
else
{
uLong offsetTruncated = (uLong)offset;
if (offsetTruncated != offset)
return -1;
else
return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin);
}
}
ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)
{
if (pfilefunc->zfile_func64.zseek64_file != NULL)
return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream);
else
{
uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream);
if ((tell_uLong) == MAXU32)
return (ZPOS64_T)-1;
else
return tell_uLong;
}
}
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->zopen32_file = p_filefunc32->zopen_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 OF((voidpf opaque, const char* filename, int mode));
static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream));
static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream));
static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream));
static voidpf ZCALLBACK fopen_file_func (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;
}
static voidpf ZCALLBACK fopen64_file_func (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 = FOPEN_FUNC((const char*)filename, mode_fopen);
return file;
}
static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
{
uLong ret;
ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
return ret;
}
static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size)
{
uLong ret;
ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
return ret;
}
static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream)
{
long ret;
ret = ftell((FILE *)stream);
return ret;
}
static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
{
ZPOS64_T ret;
ret = FTELLO_FUNC((FILE *)stream);
return ret;
}
static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin)
{
int fseek_origin=0;
long ret;
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;
}
ret = 0;
if (fseek((FILE *)stream, offset, fseek_origin) != 0)
ret = -1;
return ret;
}
static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
{
int fseek_origin=0;
long ret;
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;
}
ret = 0;
if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0)
ret = -1;
return ret;
}
static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
{
int ret;
ret = fclose((FILE *)stream);
return ret;
}
static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream)
{
int ret;
ret = ferror((FILE *)stream);
return ret;
}
void fill_fopen_filefunc(pzlib_filefunc_def)
zlib_filefunc_def* pzlib_filefunc_def;
{
pzlib_filefunc_def->zopen_file = fopen_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->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;
}

View File

@@ -1,237 +0,0 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
Changes
Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this)
Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux.
More if/def section may be needed to support other platforms
Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows.
(but you should use iowin32.c for windows instead)
*/
#ifndef _ZLIBIOAPI64_H
#define _ZLIBIOAPI64_H
#ifndef OF /* function prototypes */
# ifdef STDC
# define OF(args) args
# else
# define OF(args) ()
# endif
#endif
#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
// Linux needs this to support file operation on files larger then 4+GB
// But might need better if/def to select just the platforms that needs them.
#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif
#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include "zlib.h"
#if defined(USE_FILE32API)
#define fopen64 fopen
#define ftello64 ftell
#define fseeko64 fseek
#else
#ifdef __FreeBSD__
#define fopen64 fopen
#define ftello64 ftello
#define fseeko64 fseeko
#endif
#ifdef _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
#endif
#endif
#ifndef ZPOS64_T
#ifdef _WIN32
#define ZPOS64_T fpos_t
#else
#include <stdint.h>
#define ZPOS64_T uint64_t
#endif
#endif
#ifdef HAVE_MINIZIP64_CONF_H
#include "mz64conf.h"
#endif
/* a type chosen by DEFINE */
#ifdef HAVE_64BIT_INT_CUSTOM
typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T;
#else
#ifdef HAS_STDINT_H
#include "stdint.h"
typedef uint64_t ZPOS64_T;
#else
/* Maximum unsigned 32-bit value used as placeholder for zip64 */
#define MAXU32 0xffffffff
//#if defined(_MSC_VER) || defined(__BORLANDC__)
// typedef unsigned __int64 ZPOS64_T;
//#else
// typedef unsigned long long int ZPOS64_T;
//#endif
#endif
#endif
#if defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4005)
/* If building or using zlib as a DLL, define ZLIB_DLL.
* This is not mandatory, but it offers a little performance increase.
*/
# ifdef ZLIB_DLL
# ifdef ZLIB_INTERNAL
# define ZEXPORT __declspec(dllexport)
# else
# define ZEXPORT __declspec(dllimport)
# endif
# endif /* ZLIB_DLL */
#pragma warning (pop)
#ifdef MINIZIP_DLL
#define MINIZIP_EXPORT __declspec(dllexport)
#else
#define MINIZIP_EXPORT __declspec(dllimport)
#endif
#else //not MSVC
#define MINIZIP_EXPORT
#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) OF((voidpf opaque, const char* filename, int mode));
typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
/* here is the "old" 32 bits structure structure */
typedef struct zlib_filefunc_def_s
{
open_file_func zopen_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;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc_def;
typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode));
typedef struct zlib_filefunc64_def_s
{
open64_file_func zopen64_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;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc64_def;
void MINIZIP_EXPORT fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def));
void fill_fopen_filefunc OF((zlib_filefunc_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;
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 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode));
long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin));
ZPOS64_T call_ztell64 OF((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 ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream)))
#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode)))
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,461 +0,0 @@
/* iowin32.c -- IO base function header for compress/uncompress .zip
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
*/
#include <stdlib.h>
#include "zlib.h"
#include "ioapi.h"
#include "iowin32.h"
#ifndef INVALID_HANDLE_VALUE
#define INVALID_HANDLE_VALUE (0xFFFFFFFF)
#endif
#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
#if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API)))
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
#define IOWIN32_USING_WINRT_API 1
#endif
#endif
voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode));
uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
uLong ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
ZPOS64_T ZCALLBACK win32_tell64_file_func OF((voidpf opaque, voidpf stream));
long ZCALLBACK win32_seek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
int ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream));
int ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream));
typedef struct
{
HANDLE hf;
int error;
} WIN32FILE_IOWIN;
static void win32_translate_open_mode(int mode,
DWORD* lpdwDesiredAccess,
DWORD* lpdwCreationDisposition,
DWORD* lpdwShareMode,
DWORD* lpdwFlagsAndAttributes)
{
*lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
{
*lpdwDesiredAccess = GENERIC_READ;
*lpdwCreationDisposition = OPEN_EXISTING;
*lpdwShareMode = FILE_SHARE_READ;
}
else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
{
*lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
*lpdwCreationDisposition = OPEN_EXISTING;
}
else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
{
*lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
*lpdwCreationDisposition = CREATE_ALWAYS;
}
}
static voidpf win32_build_iowin(HANDLE hFile)
{
voidpf ret=NULL;
if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
WIN32FILE_IOWIN w32fiow;
w32fiow.hf = hFile;
w32fiow.error = 0;
ret = malloc(sizeof(WIN32FILE_IOWIN));
if (ret==NULL)
CloseHandle(hFile);
else
*((WIN32FILE_IOWIN*)ret) = w32fiow;
}
return ret;
}
voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode)
{
const char* mode_fopen = NULL;
DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
HANDLE hFile = NULL;
win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
#ifdef IOWIN32_USING_WINRT_API
#ifdef UNICODE
if ((filename!=NULL) && (dwDesiredAccess != 0))
hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL);
#else
if ((filename!=NULL) && (dwDesiredAccess != 0))
{
WCHAR filenameW[FILENAME_MAX + 0x200 + 1];
MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200);
hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL);
}
#endif
#else
if ((filename!=NULL) && (dwDesiredAccess != 0))
hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
#endif
return win32_build_iowin(hFile);
}
voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode)
{
const char* mode_fopen = NULL;
DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
HANDLE hFile = NULL;
win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
#ifdef IOWIN32_USING_WINRT_API
if ((filename!=NULL) && (dwDesiredAccess != 0))
{
WCHAR filenameW[FILENAME_MAX + 0x200 + 1];
MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200);
hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL);
}
#else
if ((filename!=NULL) && (dwDesiredAccess != 0))
hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
#endif
return win32_build_iowin(hFile);
}
voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode)
{
const char* mode_fopen = NULL;
DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
HANDLE hFile = NULL;
win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
#ifdef IOWIN32_USING_WINRT_API
if ((filename!=NULL) && (dwDesiredAccess != 0))
hFile = CreateFile2((LPCWSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition,NULL);
#else
if ((filename!=NULL) && (dwDesiredAccess != 0))
hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
#endif
return win32_build_iowin(hFile);
}
voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode)
{
const char* mode_fopen = NULL;
DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
HANDLE hFile = NULL;
win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
#ifdef IOWIN32_USING_WINRT_API
#ifdef UNICODE
if ((filename!=NULL) && (dwDesiredAccess != 0))
hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL);
#else
if ((filename!=NULL) && (dwDesiredAccess != 0))
{
WCHAR filenameW[FILENAME_MAX + 0x200 + 1];
MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200);
hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL);
}
#endif
#else
if ((filename!=NULL) && (dwDesiredAccess != 0))
hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
#endif
return win32_build_iowin(hFile);
}
uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size)
{
uLong ret=0;
HANDLE hFile = NULL;
if (stream!=NULL)
hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
if (hFile != NULL)
{
if (!ReadFile(hFile, buf, size, &ret, NULL))
{
DWORD dwErr = GetLastError();
if (dwErr == ERROR_HANDLE_EOF)
dwErr = 0;
((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
}
}
return ret;
}
uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size)
{
uLong ret=0;
HANDLE hFile = NULL;
if (stream!=NULL)
hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
if (hFile != NULL)
{
if (!WriteFile(hFile, buf, size, &ret, NULL))
{
DWORD dwErr = GetLastError();
if (dwErr == ERROR_HANDLE_EOF)
dwErr = 0;
((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
}
}
return ret;
}
static BOOL MySetFilePointerEx(HANDLE hFile, LARGE_INTEGER pos, LARGE_INTEGER *newPos, DWORD dwMoveMethod)
{
#ifdef IOWIN32_USING_WINRT_API
return SetFilePointerEx(hFile, pos, newPos, dwMoveMethod);
#else
LONG lHigh = pos.HighPart;
DWORD dwNewPos = SetFilePointer(hFile, pos.LowPart, &lHigh, FILE_CURRENT);
BOOL fOk = TRUE;
if (dwNewPos == 0xFFFFFFFF)
if (GetLastError() != NO_ERROR)
fOk = FALSE;
if ((newPos != NULL) && (fOk))
{
newPos->LowPart = dwNewPos;
newPos->HighPart = lHigh;
}
return fOk;
#endif
}
long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream)
{
long ret=-1;
HANDLE hFile = NULL;
if (stream!=NULL)
hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
if (hFile != NULL)
{
LARGE_INTEGER pos;
pos.QuadPart = 0;
if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT))
{
DWORD dwErr = GetLastError();
((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
ret = -1;
}
else
ret=(long)pos.LowPart;
}
return ret;
}
ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream)
{
ZPOS64_T ret= (ZPOS64_T)-1;
HANDLE hFile = NULL;
if (stream!=NULL)
hFile = ((WIN32FILE_IOWIN*)stream)->hf;
if (hFile)
{
LARGE_INTEGER pos;
pos.QuadPart = 0;
if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT))
{
DWORD dwErr = GetLastError();
((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
ret = (ZPOS64_T)-1;
}
else
ret=pos.QuadPart;
}
return ret;
}
long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin)
{
DWORD dwMoveMethod=0xFFFFFFFF;
HANDLE hFile = NULL;
long ret=-1;
if (stream!=NULL)
hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR :
dwMoveMethod = FILE_CURRENT;
break;
case ZLIB_FILEFUNC_SEEK_END :
dwMoveMethod = FILE_END;
break;
case ZLIB_FILEFUNC_SEEK_SET :
dwMoveMethod = FILE_BEGIN;
break;
default: return -1;
}
if (hFile != NULL)
{
LARGE_INTEGER pos;
pos.QuadPart = offset;
if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod))
{
DWORD dwErr = GetLastError();
((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
ret = -1;
}
else
ret=0;
}
return ret;
}
long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin)
{
DWORD dwMoveMethod=0xFFFFFFFF;
HANDLE hFile = NULL;
long ret=-1;
if (stream!=NULL)
hFile = ((WIN32FILE_IOWIN*)stream)->hf;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR :
dwMoveMethod = FILE_CURRENT;
break;
case ZLIB_FILEFUNC_SEEK_END :
dwMoveMethod = FILE_END;
break;
case ZLIB_FILEFUNC_SEEK_SET :
dwMoveMethod = FILE_BEGIN;
break;
default: return -1;
}
if (hFile)
{
LARGE_INTEGER pos;
pos.QuadPart = offset;
if (!MySetFilePointerEx(hFile, pos, NULL, FILE_CURRENT))
{
DWORD dwErr = GetLastError();
((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
ret = -1;
}
else
ret=0;
}
return ret;
}
int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream)
{
int ret=-1;
if (stream!=NULL)
{
HANDLE hFile;
hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
if (hFile != NULL)
{
CloseHandle(hFile);
ret=0;
}
free(stream);
}
return ret;
}
int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream)
{
int ret=-1;
if (stream!=NULL)
{
ret = ((WIN32FILE_IOWIN*)stream) -> error;
}
return ret;
}
void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def)
{
pzlib_filefunc_def->zopen_file = win32_open_file_func;
pzlib_filefunc_def->zread_file = win32_read_file_func;
pzlib_filefunc_def->zwrite_file = win32_write_file_func;
pzlib_filefunc_def->ztell_file = win32_tell_file_func;
pzlib_filefunc_def->zseek_file = win32_seek_file_func;
pzlib_filefunc_def->zclose_file = win32_close_file_func;
pzlib_filefunc_def->zerror_file = win32_error_file_func;
pzlib_filefunc_def->opaque = NULL;
}
void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def)
{
pzlib_filefunc_def->zopen64_file = win32_open64_file_func;
pzlib_filefunc_def->zread_file = win32_read_file_func;
pzlib_filefunc_def->zwrite_file = win32_write_file_func;
pzlib_filefunc_def->ztell64_file = win32_tell64_file_func;
pzlib_filefunc_def->zseek64_file = win32_seek64_file_func;
pzlib_filefunc_def->zclose_file = win32_close_file_func;
pzlib_filefunc_def->zerror_file = win32_error_file_func;
pzlib_filefunc_def->opaque = NULL;
}
void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def)
{
pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA;
pzlib_filefunc_def->zread_file = win32_read_file_func;
pzlib_filefunc_def->zwrite_file = win32_write_file_func;
pzlib_filefunc_def->ztell64_file = win32_tell64_file_func;
pzlib_filefunc_def->zseek64_file = win32_seek64_file_func;
pzlib_filefunc_def->zclose_file = win32_close_file_func;
pzlib_filefunc_def->zerror_file = win32_error_file_func;
pzlib_filefunc_def->opaque = NULL;
}
void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def)
{
pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW;
pzlib_filefunc_def->zread_file = win32_read_file_func;
pzlib_filefunc_def->zwrite_file = win32_write_file_func;
pzlib_filefunc_def->ztell64_file = win32_tell64_file_func;
pzlib_filefunc_def->zseek64_file = win32_seek64_file_func;
pzlib_filefunc_def->zclose_file = win32_close_file_func;
pzlib_filefunc_def->zerror_file = win32_error_file_func;
pzlib_filefunc_def->opaque = NULL;
}

View File

@@ -1,28 +0,0 @@
/* iowin32.h -- IO base function header for compress/uncompress .zip
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
*/
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def));
void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def));
void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def));
#ifdef __cplusplus
}
#endif

View File

@@ -1,660 +0,0 @@
/*
miniunz.c
Version 1.1, February 14h, 2010
sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications of Unzip for Zip64
Copyright (C) 2007-2008 Even Rouault
Modifications for Zip64 support on both zip and unzip
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
*/
#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif
#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
#endif
#ifdef __APPLE__
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
#define FTELLO_FUNC(stream) ftello(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define FTELLO_FUNC(stream) ftello64(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#ifdef _WIN32
# include <direct.h>
# include <io.h>
#else
# include <unistd.h>
# include <utime.h>
#endif
#include "unzip.h"
#define CASESENSITIVITY (0)
#define WRITEBUFFERSIZE (8192)
#define MAXFILENAME (256)
#ifdef _WIN32
#define USEWIN32IOAPI
#include "iowin32.h"
#endif
/*
mini unzip, demo of unzip package
usage :
Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir]
list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
if it exists
*/
/* change_file_date : change the date/time of a file
filename : the filename of the file where date/time must be modified
dosdate : the new date at the MSDos format (4 bytes)
tmu_date : the SAME new date at the tm_unz format */
void change_file_date(filename,dosdate,tmu_date)
const char *filename;
uLong dosdate;
tm_unz tmu_date;
{
#ifdef _WIN32
HANDLE hFile;
FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
0,NULL,OPEN_EXISTING,0,NULL);
GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
LocalFileTimeToFileTime(&ftLocal,&ftm);
SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
CloseHandle(hFile);
#else
#ifdef unix || __APPLE__
struct utimbuf ut;
struct tm newdate;
newdate.tm_sec = tmu_date.tm_sec;
newdate.tm_min=tmu_date.tm_min;
newdate.tm_hour=tmu_date.tm_hour;
newdate.tm_mday=tmu_date.tm_mday;
newdate.tm_mon=tmu_date.tm_mon;
if (tmu_date.tm_year > 1900)
newdate.tm_year=tmu_date.tm_year - 1900;
else
newdate.tm_year=tmu_date.tm_year ;
newdate.tm_isdst=-1;
ut.actime=ut.modtime=mktime(&newdate);
utime(filename,&ut);
#endif
#endif
}
/* mymkdir and change_file_date are not 100 % portable
As I don't know well Unix, I wait feedback for the unix portion */
int mymkdir(dirname)
const char* dirname;
{
int ret=0;
#ifdef _WIN32
ret = _mkdir(dirname);
#elif unix
ret = mkdir (dirname,0775);
#elif __APPLE__
ret = mkdir (dirname,0775);
#endif
return ret;
}
int makedir (newdir)
char *newdir;
{
char *buffer ;
char *p;
int len = (int)strlen(newdir);
if (len <= 0)
return 0;
buffer = (char*)malloc(len+1);
if (buffer==NULL)
{
printf("Error allocating memory\n");
return UNZ_INTERNALERROR;
}
strcpy(buffer,newdir);
if (buffer[len-1] == '/') {
buffer[len-1] = '\0';
}
if (mymkdir(buffer) == 0)
{
free(buffer);
return 1;
}
p = buffer+1;
while (1)
{
char hold;
while(*p && *p != '\\' && *p != '/')
p++;
hold = *p;
*p = 0;
if ((mymkdir(buffer) == -1) && (errno == ENOENT))
{
printf("couldn't create directory %s\n",buffer);
free(buffer);
return 0;
}
if (hold == 0)
break;
*p++ = hold;
}
free(buffer);
return 1;
}
void do_banner()
{
printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n");
printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
}
void do_help()
{
printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \
" -e Extract without pathname (junk paths)\n" \
" -x Extract with pathname\n" \
" -v list files\n" \
" -l list files\n" \
" -d directory to extract into\n" \
" -o overwrite files without prompting\n" \
" -p extract encrypted file using password\n\n");
}
void Display64BitsSize(ZPOS64_T n, int size_char)
{
/* to avoid compatibility problem , we do here the conversion */
char number[21];
int offset=19;
int pos_string = 19;
number[20]=0;
for (;;) {
number[offset]=(char)((n%10)+'0');
if (number[offset] != '0')
pos_string=offset;
n/=10;
if (offset==0)
break;
offset--;
}
{
int size_display_string = 19-pos_string;
while (size_char > size_display_string)
{
size_char--;
printf(" ");
}
}
printf("%s",&number[pos_string]);
}
int do_list(uf)
unzFile uf;
{
uLong i;
unz_global_info64 gi;
int err;
err = unzGetGlobalInfo64(uf,&gi);
if (err!=UNZ_OK)
printf("error %d with zipfile in unzGetGlobalInfo \n",err);
printf(" Length Method Size Ratio Date Time CRC-32 Name\n");
printf(" ------ ------ ---- ----- ---- ---- ------ ----\n");
for (i=0;i<gi.number_entry;i++)
{
char filename_inzip[256];
unz_file_info64 file_info;
uLong ratio=0;
const char *string_method;
char charCrypt=' ';
err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
break;
}
if (file_info.uncompressed_size>0)
ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size);
/* display a '*' if the file is encrypted */
if ((file_info.flag & 1) != 0)
charCrypt='*';
if (file_info.compression_method==0)
string_method="Stored";
else
if (file_info.compression_method==Z_DEFLATED)
{
uInt iLevel=(uInt)((file_info.flag & 0x6)/2);
if (iLevel==0)
string_method="Defl:N";
else if (iLevel==1)
string_method="Defl:X";
else if ((iLevel==2) || (iLevel==3))
string_method="Defl:F"; /* 2:fast , 3 : extra fast*/
}
else
if (file_info.compression_method==Z_BZIP2ED)
{
string_method="BZip2 ";
}
else
string_method="Unkn. ";
Display64BitsSize(file_info.uncompressed_size,7);
printf(" %6s%c",string_method,charCrypt);
Display64BitsSize(file_info.compressed_size,7);
printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n",
ratio,
(uLong)file_info.tmu_date.tm_mon + 1,
(uLong)file_info.tmu_date.tm_mday,
(uLong)file_info.tmu_date.tm_year % 100,
(uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
(uLong)file_info.crc,filename_inzip);
if ((i+1)<gi.number_entry)
{
err = unzGoToNextFile(uf);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzGoToNextFile\n",err);
break;
}
}
}
return 0;
}
int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)
unzFile uf;
const int* popt_extract_without_path;
int* popt_overwrite;
const char* password;
{
char filename_inzip[256];
char* filename_withoutpath;
char* p;
int err=UNZ_OK;
FILE *fout=NULL;
void* buf;
uInt size_buf;
unz_file_info64 file_info;
uLong ratio=0;
err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
return err;
}
size_buf = WRITEBUFFERSIZE;
buf = (void*)malloc(size_buf);
if (buf==NULL)
{
printf("Error allocating memory\n");
return UNZ_INTERNALERROR;
}
p = filename_withoutpath = filename_inzip;
while ((*p) != '\0')
{
if (((*p)=='/') || ((*p)=='\\'))
filename_withoutpath = p+1;
p++;
}
if ((*filename_withoutpath)=='\0')
{
if ((*popt_extract_without_path)==0)
{
printf("creating directory: %s\n",filename_inzip);
mymkdir(filename_inzip);
}
}
else
{
const char* write_filename;
int skip=0;
if ((*popt_extract_without_path)==0)
write_filename = filename_inzip;
else
write_filename = filename_withoutpath;
err = unzOpenCurrentFilePassword(uf,password);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
}
if (((*popt_overwrite)==0) && (err==UNZ_OK))
{
char rep=0;
FILE* ftestexist;
ftestexist = FOPEN_FUNC(write_filename,"rb");
if (ftestexist!=NULL)
{
fclose(ftestexist);
do
{
char answer[128];
int ret;
printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
ret = scanf("%1s",answer);
if (ret != 1)
{
exit(EXIT_FAILURE);
}
rep = answer[0] ;
if ((rep>='a') && (rep<='z'))
rep -= 0x20;
}
while ((rep!='Y') && (rep!='N') && (rep!='A'));
}
if (rep == 'N')
skip = 1;
if (rep == 'A')
*popt_overwrite=1;
}
if ((skip==0) && (err==UNZ_OK))
{
fout=FOPEN_FUNC(write_filename,"wb");
/* some zipfile don't contain directory alone before file */
if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
(filename_withoutpath!=(char*)filename_inzip))
{
char c=*(filename_withoutpath-1);
*(filename_withoutpath-1)='\0';
makedir(write_filename);
*(filename_withoutpath-1)=c;
fout=FOPEN_FUNC(write_filename,"wb");
}
if (fout==NULL)
{
printf("error opening %s\n",write_filename);
}
}
if (fout!=NULL)
{
printf(" extracting: %s\n",write_filename);
do
{
err = unzReadCurrentFile(uf,buf,size_buf);
if (err<0)
{
printf("error %d with zipfile in unzReadCurrentFile\n",err);
break;
}
if (err>0)
if (fwrite(buf,err,1,fout)!=1)
{
printf("error in writing extracted file\n");
err=UNZ_ERRNO;
break;
}
}
while (err>0);
if (fout)
fclose(fout);
if (err==0)
change_file_date(write_filename,file_info.dosDate,
file_info.tmu_date);
}
if (err==UNZ_OK)
{
err = unzCloseCurrentFile (uf);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzCloseCurrentFile\n",err);
}
}
else
unzCloseCurrentFile(uf); /* don't lose the error */
}
free(buf);
return err;
}
int do_extract(uf,opt_extract_without_path,opt_overwrite,password)
unzFile uf;
int opt_extract_without_path;
int opt_overwrite;
const char* password;
{
uLong i;
unz_global_info64 gi;
int err;
FILE* fout=NULL;
err = unzGetGlobalInfo64(uf,&gi);
if (err!=UNZ_OK)
printf("error %d with zipfile in unzGetGlobalInfo \n",err);
for (i=0;i<gi.number_entry;i++)
{
if (do_extract_currentfile(uf,&opt_extract_without_path,
&opt_overwrite,
password) != UNZ_OK)
break;
if ((i+1)<gi.number_entry)
{
err = unzGoToNextFile(uf);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzGoToNextFile\n",err);
break;
}
}
}
return 0;
}
int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)
unzFile uf;
const char* filename;
int opt_extract_without_path;
int opt_overwrite;
const char* password;
{
int err = UNZ_OK;
if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
{
printf("file %s not found in the zipfile\n",filename);
return 2;
}
if (do_extract_currentfile(uf,&opt_extract_without_path,
&opt_overwrite,
password) == UNZ_OK)
return 0;
else
return 1;
}
int main(argc,argv)
int argc;
char *argv[];
{
const char *zipfilename=NULL;
const char *filename_to_extract=NULL;
const char *password=NULL;
char filename_try[MAXFILENAME+16] = "";
int i;
int ret_value=0;
int opt_do_list=0;
int opt_do_extract=1;
int opt_do_extract_withoutpath=0;
int opt_overwrite=0;
int opt_extractdir=0;
const char *dirname=NULL;
unzFile uf=NULL;
do_banner();
if (argc==1)
{
do_help();
return 0;
}
else
{
for (i=1;i<argc;i++)
{
if ((*argv[i])=='-')
{
const char *p=argv[i]+1;
while ((*p)!='\0')
{
char c=*(p++);;
if ((c=='l') || (c=='L'))
opt_do_list = 1;
if ((c=='v') || (c=='V'))
opt_do_list = 1;
if ((c=='x') || (c=='X'))
opt_do_extract = 1;
if ((c=='e') || (c=='E'))
opt_do_extract = opt_do_extract_withoutpath = 1;
if ((c=='o') || (c=='O'))
opt_overwrite=1;
if ((c=='d') || (c=='D'))
{
opt_extractdir=1;
dirname=argv[i+1];
}
if (((c=='p') || (c=='P')) && (i+1<argc))
{
password=argv[i+1];
i++;
}
}
}
else
{
if (zipfilename == NULL)
zipfilename = argv[i];
else if ((filename_to_extract==NULL) && (!opt_extractdir))
filename_to_extract = argv[i] ;
}
}
}
if (zipfilename!=NULL)
{
# ifdef USEWIN32IOAPI
zlib_filefunc64_def ffunc;
# endif
strncpy(filename_try, zipfilename,MAXFILENAME-1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
filename_try[ MAXFILENAME ] = '\0';
# ifdef USEWIN32IOAPI
fill_win32_filefunc64A(&ffunc);
uf = unzOpen2_64(zipfilename,&ffunc);
# else
uf = unzOpen64(zipfilename);
# endif
if (uf==NULL)
{
strcat(filename_try,".zip");
# ifdef USEWIN32IOAPI
uf = unzOpen2_64(filename_try,&ffunc);
# else
uf = unzOpen64(filename_try);
# endif
}
}
if (uf==NULL)
{
printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename);
return 1;
}
printf("%s opened\n",filename_try);
if (opt_do_list==1)
ret_value = do_list(uf);
else if (opt_do_extract==1)
{
#ifdef _WIN32
if (opt_extractdir && _chdir(dirname))
#else
if (opt_extractdir && chdir(dirname))
#endif
{
printf("Error changing into %s, aborting\n", dirname);
exit(-1);
}
if (filename_to_extract == NULL)
ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
else
ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password);
}
unzClose(uf);
return ret_value;
}

View File

@@ -1,520 +0,0 @@
/*
minizip.c
Version 1.1, February 14h, 2010
sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications of Unzip for Zip64
Copyright (C) 2007-2008 Even Rouault
Modifications for Zip64 support on both zip and unzip
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
*/
#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif
#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
#endif
#ifdef __APPLE__
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
#define FTELLO_FUNC(stream) ftello(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define FTELLO_FUNC(stream) ftello64(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#ifdef _WIN32
# include <direct.h>
# include <io.h>
#else
# include <unistd.h>
# include <utime.h>
# include <sys/types.h>
# include <sys/stat.h>
#endif
#include "zip.h"
#ifdef _WIN32
#define USEWIN32IOAPI
#include "iowin32.h"
#endif
#define WRITEBUFFERSIZE (16384)
#define MAXFILENAME (256)
#ifdef _WIN32
uLong filetime(f, tmzip, dt)
char *f; /* name of file to get info on */
tm_zip *tmzip; /* return value: access, modific. and creation times */
uLong *dt; /* dostime */
{
int ret = 0;
{
FILETIME ftLocal;
HANDLE hFind;
WIN32_FIND_DATAA ff32;
hFind = FindFirstFileA(f,&ff32);
if (hFind != INVALID_HANDLE_VALUE)
{
FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
FindClose(hFind);
ret = 1;
}
}
return ret;
}
#else
#ifdef unix || __APPLE__
uLong filetime(f, tmzip, dt)
char *f; /* name of file to get info on */
tm_zip *tmzip; /* return value: access, modific. and creation times */
uLong *dt; /* dostime */
{
int ret=0;
struct stat s; /* results of stat() */
struct tm* filedate;
time_t tm_t=0;
if (strcmp(f,"-")!=0)
{
char name[MAXFILENAME+1];
int len = strlen(f);
if (len > MAXFILENAME)
len = MAXFILENAME;
strncpy(name, f,MAXFILENAME-1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
name[ MAXFILENAME ] = '\0';
if (name[len - 1] == '/')
name[len - 1] = '\0';
/* not all systems allow stat'ing a file with / appended */
if (stat(name,&s)==0)
{
tm_t = s.st_mtime;
ret = 1;
}
}
filedate = localtime(&tm_t);
tmzip->tm_sec = filedate->tm_sec;
tmzip->tm_min = filedate->tm_min;
tmzip->tm_hour = filedate->tm_hour;
tmzip->tm_mday = filedate->tm_mday;
tmzip->tm_mon = filedate->tm_mon ;
tmzip->tm_year = filedate->tm_year;
return ret;
}
#else
uLong filetime(f, tmzip, dt)
char *f; /* name of file to get info on */
tm_zip *tmzip; /* return value: access, modific. and creation times */
uLong *dt; /* dostime */
{
return 0;
}
#endif
#endif
int check_exist_file(filename)
const char* filename;
{
FILE* ftestexist;
int ret = 1;
ftestexist = FOPEN_FUNC(filename,"rb");
if (ftestexist==NULL)
ret = 0;
else
fclose(ftestexist);
return ret;
}
void do_banner()
{
printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n");
printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n");
}
void do_help()
{
printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \
" -o Overwrite existing file.zip\n" \
" -a Append to existing file.zip\n" \
" -0 Store only\n" \
" -1 Compress faster\n" \
" -9 Compress better\n\n" \
" -j exclude path. store only the file name.\n\n");
}
/* calculate the CRC32 of a file,
because to encrypt a file, we need known the CRC32 of the file before */
int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc)
{
unsigned long calculate_crc=0;
int err=ZIP_OK;
FILE * fin = FOPEN_FUNC(filenameinzip,"rb");
unsigned long size_read = 0;
unsigned long total_read = 0;
if (fin==NULL)
{
err = ZIP_ERRNO;
}
if (err == ZIP_OK)
do
{
err = ZIP_OK;
size_read = (int)fread(buf,1,size_buf,fin);
if (size_read < size_buf)
if (feof(fin)==0)
{
printf("error in reading %s\n",filenameinzip);
err = ZIP_ERRNO;
}
if (size_read>0)
calculate_crc = crc32(calculate_crc,buf,size_read);
total_read += size_read;
} while ((err == ZIP_OK) && (size_read>0));
if (fin)
fclose(fin);
*result_crc=calculate_crc;
printf("file %s crc %lx\n", filenameinzip, calculate_crc);
return err;
}
int isLargeFile(const char* filename)
{
int largeFile = 0;
ZPOS64_T pos = 0;
FILE* pFile = FOPEN_FUNC(filename, "rb");
if(pFile != NULL)
{
int n = FSEEKO_FUNC(pFile, 0, SEEK_END);
pos = FTELLO_FUNC(pFile);
printf("File : %s is %lld bytes\n", filename, pos);
if(pos >= 0xffffffff)
largeFile = 1;
fclose(pFile);
}
return largeFile;
}
int main(argc,argv)
int argc;
char *argv[];
{
int i;
int opt_overwrite=0;
int opt_compress_level=Z_DEFAULT_COMPRESSION;
int opt_exclude_path=0;
int zipfilenamearg = 0;
char filename_try[MAXFILENAME+16];
int zipok;
int err=0;
int size_buf=0;
void* buf=NULL;
const char* password=NULL;
do_banner();
if (argc==1)
{
do_help();
return 0;
}
else
{
for (i=1;i<argc;i++)
{
if ((*argv[i])=='-')
{
const char *p=argv[i]+1;
while ((*p)!='\0')
{
char c=*(p++);;
if ((c=='o') || (c=='O'))
opt_overwrite = 1;
if ((c=='a') || (c=='A'))
opt_overwrite = 2;
if ((c>='0') && (c<='9'))
opt_compress_level = c-'0';
if ((c=='j') || (c=='J'))
opt_exclude_path = 1;
if (((c=='p') || (c=='P')) && (i+1<argc))
{
password=argv[i+1];
i++;
}
}
}
else
{
if (zipfilenamearg == 0)
{
zipfilenamearg = i ;
}
}
}
}
size_buf = WRITEBUFFERSIZE;
buf = (void*)malloc(size_buf);
if (buf==NULL)
{
printf("Error allocating memory\n");
return ZIP_INTERNALERROR;
}
if (zipfilenamearg==0)
{
zipok=0;
}
else
{
int i,len;
int dot_found=0;
zipok = 1 ;
strncpy(filename_try, argv[zipfilenamearg],MAXFILENAME-1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
filename_try[ MAXFILENAME ] = '\0';
len=(int)strlen(filename_try);
for (i=0;i<len;i++)
if (filename_try[i]=='.')
dot_found=1;
if (dot_found==0)
strcat(filename_try,".zip");
if (opt_overwrite==2)
{
/* if the file don't exist, we not append file */
if (check_exist_file(filename_try)==0)
opt_overwrite=1;
}
else
if (opt_overwrite==0)
if (check_exist_file(filename_try)!=0)
{
char rep=0;
do
{
char answer[128];
int ret;
printf("The file %s exists. Overwrite ? [y]es, [n]o, [a]ppend : ",filename_try);
ret = scanf("%1s",answer);
if (ret != 1)
{
exit(EXIT_FAILURE);
}
rep = answer[0] ;
if ((rep>='a') && (rep<='z'))
rep -= 0x20;
}
while ((rep!='Y') && (rep!='N') && (rep!='A'));
if (rep=='N')
zipok = 0;
if (rep=='A')
opt_overwrite = 2;
}
}
if (zipok==1)
{
zipFile zf;
int errclose;
# ifdef USEWIN32IOAPI
zlib_filefunc64_def ffunc;
fill_win32_filefunc64A(&ffunc);
zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc);
# else
zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0);
# endif
if (zf == NULL)
{
printf("error opening %s\n",filename_try);
err= ZIP_ERRNO;
}
else
printf("creating %s\n",filename_try);
for (i=zipfilenamearg+1;(i<argc) && (err==ZIP_OK);i++)
{
if (!((((*(argv[i]))=='-') || ((*(argv[i]))=='/')) &&
((argv[i][1]=='o') || (argv[i][1]=='O') ||
(argv[i][1]=='a') || (argv[i][1]=='A') ||
(argv[i][1]=='p') || (argv[i][1]=='P') ||
((argv[i][1]>='0') || (argv[i][1]<='9'))) &&
(strlen(argv[i]) == 2)))
{
FILE * fin = NULL;
int size_read;
const char* filenameinzip = argv[i];
const char *savefilenameinzip;
zip_fileinfo zi;
unsigned long crcFile=0;
int zip64 = 0;
zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
zi.dosDate = 0;
zi.internal_fa = 0;
zi.external_fa = 0;
filetime(filenameinzip,&zi.tmz_date,&zi.dosDate);
/*
err = zipOpenNewFileInZip(zf,filenameinzip,&zi,
NULL,0,NULL,0,NULL / * comment * /,
(opt_compress_level != 0) ? Z_DEFLATED : 0,
opt_compress_level);
*/
if ((password != NULL) && (err==ZIP_OK))
err = getFileCrc(filenameinzip,buf,size_buf,&crcFile);
zip64 = isLargeFile(filenameinzip);
/* The path name saved, should not include a leading slash. */
/*if it did, windows/xp and dynazip couldn't read the zip file. */
savefilenameinzip = filenameinzip;
while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' )
{
savefilenameinzip++;
}
/*should the zip file contain any path at all?*/
if( opt_exclude_path )
{
const char *tmpptr;
const char *lastslash = 0;
for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++)
{
if( *tmpptr == '\\' || *tmpptr == '/')
{
lastslash = tmpptr;
}
}
if( lastslash != NULL )
{
savefilenameinzip = lastslash+1; // base filename follows last slash.
}
}
/**/
err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi,
NULL,0,NULL,0,NULL /* comment*/,
(opt_compress_level != 0) ? Z_DEFLATED : 0,
opt_compress_level,0,
/* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
password,crcFile, zip64);
if (err != ZIP_OK)
printf("error in opening %s in zipfile\n",filenameinzip);
else
{
fin = FOPEN_FUNC(filenameinzip,"rb");
if (fin==NULL)
{
err=ZIP_ERRNO;
printf("error in opening %s for reading\n",filenameinzip);
}
}
if (err == ZIP_OK)
do
{
err = ZIP_OK;
size_read = (int)fread(buf,1,size_buf,fin);
if (size_read < size_buf)
if (feof(fin)==0)
{
printf("error in reading %s\n",filenameinzip);
err = ZIP_ERRNO;
}
if (size_read>0)
{
err = zipWriteInFileInZip (zf,buf,size_read);
if (err<0)
{
printf("error in writing %s in the zipfile\n",
filenameinzip);
}
}
} while ((err == ZIP_OK) && (size_read>0));
if (fin)
fclose(fin);
if (err<0)
err=ZIP_ERRNO;
else
{
err = zipCloseFileInZip(zf);
if (err!=ZIP_OK)
printf("error in closing %s in the zipfile\n",
filenameinzip);
}
}
}
errclose = zipClose(zf,NULL);
if (errclose != ZIP_OK)
printf("error in closing %s\n",filename_try);
}
else
{
do_help();
}
free(buf);
return 0;
}

View File

@@ -1,298 +0,0 @@
/*
Additional tools for Minizip
Code: Xavier Roche '2004
License: Same as ZLIB (www.gzip.org)
*/
/* Code */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zlib.h"
#include "unzip.h"
#define READ_8(adr) ((unsigned char)*(adr))
#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
#define WRITE_8(buff, n) do { \
*((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
} while(0)
#define WRITE_16(buff, n) do { \
WRITE_8((unsigned char*)(buff), n); \
WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
} while(0)
#define WRITE_32(buff, n) do { \
WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
} while(0)
extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered)
const char* file;
const char* fileOut;
const char* fileOutTmp;
uLong* nRecovered;
uLong* bytesRecovered;
{
int err = Z_OK;
FILE* fpZip = fopen(file, "rb");
FILE* fpOut = fopen(fileOut, "wb");
FILE* fpOutCD = fopen(fileOutTmp, "wb");
if (fpZip != NULL && fpOut != NULL) {
int entries = 0;
uLong totalBytes = 0;
char header[30];
char filename[1024];
char extra[1024];
int offset = 0;
int offsetCD = 0;
while ( fread(header, 1, 30, fpZip) == 30 ) {
int currentOffset = offset;
/* File entry */
if (READ_32(header) == 0x04034b50) {
unsigned int version = READ_16(header + 4);
unsigned int gpflag = READ_16(header + 6);
unsigned int method = READ_16(header + 8);
unsigned int filetime = READ_16(header + 10);
unsigned int filedate = READ_16(header + 12);
unsigned int crc = READ_32(header + 14); /* crc */
unsigned int cpsize = READ_32(header + 18); /* compressed size */
unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
unsigned int fnsize = READ_16(header + 26); /* file name length */
unsigned int extsize = READ_16(header + 28); /* extra field length */
filename[0] = extra[0] = '\0';
/* Header */
if (fwrite(header, 1, 30, fpOut) == 30) {
offset += 30;
} else {
err = Z_ERRNO;
break;
}
/* Filename */
if (fnsize > 0) {
if (fnsize < sizeof(filename)) {
if (fread(filename, 1, fnsize, fpZip) == fnsize) {
if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
offset += fnsize;
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_STREAM_ERROR;
break;
}
/* Extra field */
if (extsize > 0) {
if (extsize < sizeof(extra)) {
if (fread(extra, 1, extsize, fpZip) == extsize) {
if (fwrite(extra, 1, extsize, fpOut) == extsize) {
offset += extsize;
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_ERRNO;
break;
}
}
/* Data */
{
int dataSize = cpsize;
if (dataSize == 0) {
dataSize = uncpsize;
}
if (dataSize > 0) {
char* data = malloc(dataSize);
if (data != NULL) {
if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
offset += dataSize;
totalBytes += dataSize;
} else {
err = Z_ERRNO;
}
} else {
err = Z_ERRNO;
}
free(data);
if (err != Z_OK) {
break;
}
} else {
err = Z_MEM_ERROR;
break;
}
}
}
/* Central directory entry */
{
char header[46];
char* comment = "";
int comsize = (int) strlen(comment);
WRITE_32(header, 0x02014b50);
WRITE_16(header + 4, version);
WRITE_16(header + 6, version);
WRITE_16(header + 8, gpflag);
WRITE_16(header + 10, method);
WRITE_16(header + 12, filetime);
WRITE_16(header + 14, filedate);
WRITE_32(header + 16, crc);
WRITE_32(header + 20, cpsize);
WRITE_32(header + 24, uncpsize);
WRITE_16(header + 28, fnsize);
WRITE_16(header + 30, extsize);
WRITE_16(header + 32, comsize);
WRITE_16(header + 34, 0); /* disk # */
WRITE_16(header + 36, 0); /* int attrb */
WRITE_32(header + 38, 0); /* ext attrb */
WRITE_32(header + 42, currentOffset);
/* Header */
if (fwrite(header, 1, 46, fpOutCD) == 46) {
offsetCD += 46;
/* Filename */
if (fnsize > 0) {
if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
offsetCD += fnsize;
} else {
err = Z_ERRNO;
break;
}
} else {
err = Z_STREAM_ERROR;
break;
}
/* Extra field */
if (extsize > 0) {
if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
offsetCD += extsize;
} else {
err = Z_ERRNO;
break;
}
}
/* Comment field */
if (comsize > 0) {
if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
offsetCD += comsize;
} else {
err = Z_ERRNO;
break;
}
}
} else {
err = Z_ERRNO;
break;
}
}
/* Success */
entries++;
} else {
break;
}
}
/* Final central directory */
{
int entriesZip = entries;
char header[22];
char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
int comsize = (int) strlen(comment);
if (entriesZip > 0xffff) {
entriesZip = 0xffff;
}
WRITE_32(header, 0x06054b50);
WRITE_16(header + 4, 0); /* disk # */
WRITE_16(header + 6, 0); /* disk # */
WRITE_16(header + 8, entriesZip); /* hack */
WRITE_16(header + 10, entriesZip); /* hack */
WRITE_32(header + 12, offsetCD); /* size of CD */
WRITE_32(header + 16, offset); /* offset to CD */
WRITE_16(header + 20, comsize); /* comment */
/* Header */
if (fwrite(header, 1, 22, fpOutCD) == 22) {
/* Comment field */
if (comsize > 0) {
if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
err = Z_ERRNO;
}
}
} else {
err = Z_ERRNO;
}
}
/* Final merge (file + central directory) */
fclose(fpOutCD);
if (err == Z_OK) {
fpOutCD = fopen(fileOutTmp, "rb");
if (fpOutCD != NULL) {
int nRead;
char buffer[8192];
while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
err = Z_ERRNO;
break;
}
}
fclose(fpOutCD);
}
}
/* Close */
fclose(fpZip);
fclose(fpOut);
/* Wipe temporary file */
(void)remove(fileOutTmp);
/* Number of recovered entries */
if (err == Z_OK) {
if (nRecovered != NULL) {
*nRecovered = entries;
}
if (bytesRecovered != NULL) {
*bytesRecovered = totalBytes;
}
}
} else {
if (fpZip)
fclose(fpZip);
if (fpOut)
fclose(fpOut);
if (fpOutCD)
fclose(fpOutCD);
err = Z_STREAM_ERROR;
}
return err;
}

View File

@@ -1,37 +0,0 @@
/*
Additional tools for Minizip
Code: Xavier Roche '2004
License: Same as ZLIB (www.gzip.org)
*/
#ifndef _zip_tools_H
#define _zip_tools_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _ZLIB_H
#include "zlib.h"
#endif
#include "unzip.h"
/* Repair a ZIP file (missing central directory)
file: file to recover
fileOut: output file after recovery
fileOutTmp: temporary file name used for recovery
*/
extern int ZEXPORT unzRepair(const char* file,
const char* fileOut,
const char* fileOutTmp,
uLong* nRecovered,
uLong* bytesRecovered);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,437 +0,0 @@
/* unzip.h -- IO for uncompress .zip files using zlib
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications of Unzip for Zip64
Copyright (C) 2007-2008 Even Rouault
Modifications for Zip64 support on both zip and unzip
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
---------------------------------------------------------------------------------
Condition of use and distribution are the same than zlib :
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
---------------------------------------------------------------------------------
Changes
See header of unzip64.c
*/
#ifndef _unz64_H
#define _unz64_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; } unzFile__;
typedef unzFile__ *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)
/* tm_unz contain date/time info */
typedef struct tm_unz_s
{
uInt tm_sec; /* seconds after the minute - [0,59] */
uInt tm_min; /* minutes after the hour - [0,59] */
uInt tm_hour; /* hours since midnight - [0,23] */
uInt tm_mday; /* day of the month - [1,31] */
uInt tm_mon; /* months since January - [0,11] */
uInt tm_year; /* years - [1980..2044] */
} tm_unz;
/* 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
{
ZPOS64_T number_entry; /* total number of entries in
the central dir on this disk */
uLong size_comment; /* size of the global comment of the zipfile */
} unz_global_info64;
typedef struct unz_global_info_s
{
uLong number_entry; /* total number of entries in
the central dir on this disk */
uLong 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
{
uLong version; /* version made by 2 bytes */
uLong version_needed; /* version needed to extract 2 bytes */
uLong flag; /* general purpose bit flag 2 bytes */
uLong compression_method; /* compression method 2 bytes */
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
uLong crc; /* crc-32 4 bytes */
ZPOS64_T compressed_size; /* compressed size 8 bytes */
ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */
uLong size_filename; /* filename length 2 bytes */
uLong size_file_extra; /* extra field length 2 bytes */
uLong size_file_comment; /* file comment length 2 bytes */
uLong disk_num_start; /* disk number start 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
tm_unz tmu_date;
} unz_file_info64;
typedef struct unz_file_info_s
{
uLong version; /* version made by 2 bytes */
uLong version_needed; /* version needed to extract 2 bytes */
uLong flag; /* general purpose bit flag 2 bytes */
uLong compression_method; /* compression method 2 bytes */
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
uLong crc; /* crc-32 4 bytes */
uLong compressed_size; /* compressed size 4 bytes */
uLong uncompressed_size; /* uncompressed size 4 bytes */
uLong size_filename; /* filename length 2 bytes */
uLong size_file_extra; /* extra field length 2 bytes */
uLong size_file_comment; /* file comment length 2 bytes */
uLong disk_num_start; /* disk number start 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
tm_unz tmu_date;
} unz_file_info;
extern int MINIZIP_EXPORT unzStringFileNameCompare OF ((const char* fileName1,
const char* fileName2,
int iCaseSensitivity));
/*
Compare two filename (fileName1,fileName2).
If iCaseSenisivity = 1, comparison is case sensitivity (like strcmp)
If iCaseSenisivity = 2, comparison is not case sensitivity (like strcmpi
or strcasecmp)
If iCaseSenisivity = 0, case sensitivity is default of your operating system
(like 1 on Unix, 2 on Windows)
*/
extern unzFile MINIZIP_EXPORT unzOpen OF((const char *path));
extern unzFile MINIZIP_EXPORT unzOpen64 OF((const void *path));
/*
Open a Zip file. path contain the full pathname (by example,
on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
"zlib/zlib113.zip".
If the zipfile cannot be opened (file don't exist or in not valid), the
return value is NULL.
Else, the return value is a unzFile Handle, usable with other function
of this unzip package.
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 MINIZIP_EXPORT unzOpen2 OF((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 the zip file (see ioapi.h)
*/
extern unzFile MINIZIP_EXPORT unzOpen2_64 OF((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 the zip file (see ioapi.h)
*/
extern int MINIZIP_EXPORT unzClose OF((unzFile file));
/*
Close a ZipFile opened with unzOpen.
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
these files MUST be closed with unzCloseCurrentFile before call unzClose.
return UNZ_OK if there is no problem. */
extern int MINIZIP_EXPORT unzGetGlobalInfo OF((unzFile file,
unz_global_info *pglobal_info));
extern int MINIZIP_EXPORT unzGetGlobalInfo64 OF((unzFile file,
unz_global_info64 *pglobal_info));
/*
Write info about the ZipFile in the *pglobal_info structure.
No preparation of the structure is needed
return UNZ_OK if there is no problem. */
extern int MINIZIP_EXPORT unzGetGlobalComment OF((unzFile file,
char *szComment,
uLong uSizeBuf));
/*
Get the global comment string of the ZipFile, in the szComment buffer.
uSizeBuf is the size of the szComment buffer.
return the number of byte copied or an error code <0
*/
/***************************************************************************/
/* Unzip package allow you browse the directory of the zipfile */
extern int MINIZIP_EXPORT unzGoToFirstFile OF((unzFile file));
/*
Set the current file of the zipfile to the first file.
return UNZ_OK if there is no problem
*/
extern int MINIZIP_EXPORT unzGoToNextFile OF((unzFile file));
/*
Set the current file of the zipfile to the next file.
return UNZ_OK if there is no problem
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
*/
extern int MINIZIP_EXPORT unzLocateFile OF((unzFile file,
const char *szFileName,
int iCaseSensitivity));
/*
Try locate the file szFileName in the zipfile.
For the iCaseSensitivity signification, see unzStringFileNameCompare
return value :
UNZ_OK if the file is found. It becomes the current file.
UNZ_END_OF_LIST_OF_FILE if the file is not found
*/
/* ****************************************** */
/* Ryan supplied functions */
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_pos_s
{
uLong pos_in_zip_directory; /* offset in zip file directory */
uLong num_of_file; /* # of file */
} unz_file_pos;
extern int MINIZIP_EXPORT unzGetFilePos(
unzFile file,
unz_file_pos* file_pos);
extern int MINIZIP_EXPORT unzGoToFilePos(
unzFile file,
unz_file_pos* file_pos);
typedef struct unz64_file_pos_s
{
ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */
ZPOS64_T num_of_file; /* # of file */
} unz64_file_pos;
extern int MINIZIP_EXPORT unzGetFilePos64(
unzFile file,
unz64_file_pos* file_pos);
extern int MINIZIP_EXPORT unzGoToFilePos64(
unzFile file,
const unz64_file_pos* file_pos);
/* ****************************************** */
extern int MINIZIP_EXPORT unzGetCurrentFileInfo64 OF((unzFile file,
unz_file_info64 *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize));
extern int MINIZIP_EXPORT unzGetCurrentFileInfo OF((unzFile file,
unz_file_info *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize));
/*
Get Info about the current file
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
the current file
if szFileName!=NULL, the filemane string will be copied in szFileName
(fileNameBufferSize is the size of the buffer)
if extraField!=NULL, the extra field information will be copied in extraField
(extraFieldBufferSize is the size of the buffer).
This is the Central-header version of the extra field
if szComment!=NULL, the comment string of the file will be copied in szComment
(commentBufferSize is the size of the buffer)
*/
/** Addition for GDAL : START */
extern ZPOS64_T MINIZIP_EXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
/** Addition for GDAL : END */
/***************************************************************************/
/* for 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 MINIZIP_EXPORT unzOpenCurrentFile OF((unzFile file));
/*
Open for reading data the current file in the zipfile.
If there is no error, the return value is UNZ_OK.
*/
extern int MINIZIP_EXPORT unzOpenCurrentFilePassword OF((unzFile file,
const char* password));
/*
Open for reading data the current file in the zipfile.
password is a crypting password
If there is no error, the return value is UNZ_OK.
*/
extern int MINIZIP_EXPORT unzOpenCurrentFile2 OF((unzFile file,
int* method,
int* level,
int raw));
/*
Same than 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 MINIZIP_EXPORT unzOpenCurrentFile3 OF((unzFile file,
int* method,
int* level,
int raw,
const char* password));
/*
Same than 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 MINIZIP_EXPORT unzCloseCurrentFile OF((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
*/
extern int MINIZIP_EXPORT unzReadCurrentFile OF((unzFile file,
voidp buf,
unsigned 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 z_off_t MINIZIP_EXPORT unztell OF((unzFile file));
extern ZPOS64_T MINIZIP_EXPORT unztell64 OF((unzFile file));
/*
Give the current position in uncompressed data
*/
extern int MINIZIP_EXPORT unzeof OF((unzFile file));
/*
return 1 if the end of file was reached, 0 elsewhere
*/
extern int MINIZIP_EXPORT unzGetLocalExtrafield OF((unzFile file,
voidp buf,
unsigned 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.
the return value is the number of bytes copied in buf, or (if <0)
the error code
*/
/***************************************************************************/
/* Get the current file offset */
extern ZPOS64_T MINIZIP_EXPORT unzGetOffset64 (unzFile file);
extern uLong MINIZIP_EXPORT unzGetOffset (unzFile file);
/* Set the current file offset */
extern int MINIZIP_EXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos);
extern int MINIZIP_EXPORT unzSetOffset (unzFile file, uLong pos);
#ifdef __cplusplus
}
#endif
#endif /* _unz64_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,362 +0,0 @@
/* zip.h -- IO on .zip files using zlib
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
---------------------------------------------------------------------------
Condition of use and distribution are the same than zlib :
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
---------------------------------------------------------------------------
Changes
See header of zip.h
*/
#ifndef _zip12_H
#define _zip12_H
#ifdef __cplusplus
extern "C" {
#endif
//#define HAVE_BZIP2
#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(STRICTZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagzipFile__ { int unused; } zipFile__;
typedef zipFile__ *zipFile;
#else
typedef voidp zipFile;
#endif
#define ZIP_OK (0)
#define ZIP_EOF (0)
#define ZIP_ERRNO (Z_ERRNO)
#define ZIP_PARAMERROR (-102)
#define ZIP_BADZIPFILE (-103)
#define ZIP_INTERNALERROR (-104)
#ifndef DEF_MEM_LEVEL
# if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
# else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
# endif
#endif
/* default memLevel */
/* tm_zip contain date/time info */
typedef struct tm_zip_s
{
uInt tm_sec; /* seconds after the minute - [0,59] */
uInt tm_min; /* minutes after the hour - [0,59] */
uInt tm_hour; /* hours since midnight - [0,23] */
uInt tm_mday; /* day of the month - [1,31] */
uInt tm_mon; /* months since January - [0,11] */
uInt tm_year; /* years - [1980..2044] */
} tm_zip;
typedef struct
{
tm_zip tmz_date; /* date in understandable format */
uLong dosDate; /* if dos_date == 0, tmu_date is used */
/* uLong flag; */ /* general purpose bit flag 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
} zip_fileinfo;
typedef const char* zipcharpc;
#define APPEND_STATUS_CREATE (0)
#define APPEND_STATUS_CREATEAFTER (1)
#define APPEND_STATUS_ADDINZIP (2)
extern zipFile MINIZIP_EXPORT zipOpen OF((const char *pathname, int append));
extern zipFile MINIZIP_EXPORT zipOpen64 OF((const void *pathname, int append));
/*
Create a zipfile.
pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on
an Unix computer "zlib/zlib113.zip".
if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip
will be created at the end of the file.
(useful if the file contain a self extractor code)
if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will
add files in existing zip (be sure you don't add file that doesn't exist)
If the zipfile cannot be opened, the return value is NULL.
Else, the return value is a zipFile Handle, usable with other function
of this zip package.
*/
/* Note : there is no delete function into a zipfile.
If you want delete file into a zipfile, you must open a zipfile, and create another
Of couse, you can use RAW reading and writing to copy the file you did not want delete
*/
extern zipFile MINIZIP_EXPORT zipOpen2 OF((const char *pathname,
int append,
zipcharpc* globalcomment,
zlib_filefunc_def* pzlib_filefunc_def));
extern zipFile MINIZIP_EXPORT zipOpen2_64 OF((const void *pathname,
int append,
zipcharpc* globalcomment,
zlib_filefunc64_def* pzlib_filefunc_def));
extern int MINIZIP_EXPORT zipOpenNewFileInZip OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level));
extern int MINIZIP_EXPORT zipOpenNewFileInZip64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int zip64));
/*
Open a file in the ZIP for writing.
filename : the filename in zip (if NULL, '-' without quote will be used
*zipfi contain supplemental information
if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
contains the extrafield data the the local header
if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
contains the extrafield data the the local header
if comment != NULL, comment contain the comment string
method contain the compression method (0 for store, Z_DEFLATED for deflate)
level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
zip64 is set to 1 if a zip64 extended information block should be added to the local file header.
this MUST be '1' if the uncompressed size is >= 0xffffffff.
*/
extern int MINIZIP_EXPORT zipOpenNewFileInZip2 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw));
extern int MINIZIP_EXPORT zipOpenNewFileInZip2_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int zip64));
/*
Same than zipOpenNewFileInZip, except if raw=1, we write raw file
*/
extern int MINIZIP_EXPORT zipOpenNewFileInZip3 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting));
extern int MINIZIP_EXPORT zipOpenNewFileInZip3_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
int zip64
));
/*
Same than zipOpenNewFileInZip2, except
windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
password : crypting password (NULL for no crypting)
crcForCrypting : crc of file to compress (needed for crypting)
*/
extern int MINIZIP_EXPORT zipOpenNewFileInZip4 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
uLong versionMadeBy,
uLong flagBase
));
extern int MINIZIP_EXPORT zipOpenNewFileInZip4_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
uLong versionMadeBy,
uLong flagBase,
int zip64
));
/*
Same than zipOpenNewFileInZip4, except
versionMadeBy : value for Version made by field
flag : value for flag field (compression level info will be added)
*/
extern int MINIZIP_EXPORT zipWriteInFileInZip OF((zipFile file,
const void* buf,
unsigned len));
/*
Write data in the zipfile
*/
extern int MINIZIP_EXPORT zipCloseFileInZip OF((zipFile file));
/*
Close the current file in the zipfile
*/
extern int MINIZIP_EXPORT zipCloseFileInZipRaw OF((zipFile file,
uLong uncompressed_size,
uLong crc32));
extern int MINIZIP_EXPORT zipCloseFileInZipRaw64 OF((zipFile file,
ZPOS64_T uncompressed_size,
uLong crc32));
/*
Close the current file in the zipfile, for file opened with
parameter raw=1 in zipOpenNewFileInZip2
uncompressed_size and crc32 are value for the uncompressed size
*/
extern int MINIZIP_EXPORT zipClose OF((zipFile file,
const char* global_comment));
/*
Close the zipfile
*/
extern int MINIZIP_EXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader));
/*
zipRemoveExtraInfoBlock - Added by Mathias Svensson
Remove extra information block from a extra information data for the local file header or central directory header
It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode.
0x0001 is the signature header for the ZIP64 extra information blocks
usage.
Remove ZIP64 Extra information from a central director extra field data
zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001);
Remove ZIP64 Extra information from a Local File Header extra field data
zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001);
*/
#ifdef __cplusplus
}
#endif
#endif /* _zip64_H */

View File

@@ -258,6 +258,7 @@ if(ENABLE_SINGLE_APP_BUILD OR ANDROID)
add_library(vcmieditor OBJECT ${editor_QM})
else()
add_executable(vcmieditor WIN32 ${editor_QM} ${editor_SRCS} ${editor_HEADERS} ${editor_UI_HEADERS} ${editor_ICON})
vcmi_create_exe_shim(vcmieditor)
endif()
if(ENABLE_TRANSLATIONS)

View File

@@ -5,41 +5,10 @@ if(APPLE_MACOS)
set(bundleContentsDir "${bundleDir}/Contents")
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
if(USING_CONAN)
# simulate macdeployqt behavior, main Qt libs are copied by conan
execute_process(COMMAND
"${qmakePath}" -query QT_INSTALL_PLUGINS
OUTPUT_VARIABLE qtPluginsDir
OUTPUT_STRIP_TRAILING_WHITESPACE
)
install(DIRECTORY
${qtPluginsDir}/
DESTINATION ${APP_BUNDLE_DIR}/Contents/PlugIns
)
install(CODE "
file(WRITE ${bundleContentsDir}/Resources/qt.conf
\"[Paths]\nPlugins = PlugIns\"
)
")
else()
# note: cross-compiled Qt 5 builds macdeployqt for target platform instead of host
# deploy Qt dylibs with macdeployqt
find_program(TOOL_MACDEPLOYQT NAMES macdeployqt PATHS "${qtBinDir}")
if(TOOL_MACDEPLOYQT)
install(CODE "
execute_process(COMMAND
\"${TOOL_MACDEPLOYQT}\" \"${bundleDir}\" -verbose=2
)
")
else()
message(WARNING "macdeployqt not found, running cpack would result in broken package")
endif()
endif()
# note: cross-compiled Qt 5 builds macdeployqt for target platform instead of host
vcmi_deploy_qt(macdeployqt "\"${bundleDir}\"")
endif()
# deploy other dylibs with conan
vcmi_install_conan_deps("${bundleContentsDir}")
# perform ad-hoc codesigning
# Intel Macs don't need it
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")

View File

@@ -9,6 +9,7 @@ set(serverapp_HEADERS
assign_source_group(${serverapp_SRCS} ${serverapp_HEADERS})
add_executable(vcmiserver ${serverapp_SRCS} ${serverapp_HEADERS})
vcmi_create_exe_shim(vcmiserver)
set(serverapp_LIBS vcmi)
if(CMAKE_SYSTEM_NAME MATCHES FreeBSD OR HAIKU)

View File

@@ -1,43 +1,10 @@
# We need to keep this code into separate directory so CMake will execute it after all other subdirectories install code
# Otherwise we can't fix win bundle dependencies since binaries wouldn't be there when this code executed
# This will likely only work for Vcpkg
if(WIN32)
#there are some weird issues with variables used in path not evaluated properly when trying to remove code duplication from below lines
if(ENABLE_LAUNCHER)
install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/qt.conf\"
\"[Paths]\nPlugins = .\"
)
")
if(WIN32 AND (ENABLE_LAUNCHER OR ENABLE_EDITOR))
if(TARGET vcmilauncher)
set(deployTarget vcmilauncher)
elseif(TARGET vcmieditor)
set(deployTarget vcmieditor)
endif()
#TODO: check if some equivalent of block below can be used for above block (easy qt dependencies copy)
#LuaJIT will not be copied automatically by not meeting criteria for this block of code
if(ENABLE_LUA)
install_vcpkg_imported_tgt(luajit::luajit)
endif()
if(MSVC)
set(gp_tool "dumpbin")
endif()
install(CODE "
set(dirs \"${CMAKE_PREFIX_PATH}\")
if(\"\${CMAKE_INSTALL_CONFIG_NAME}\" STREQUAL \"Debug\")
list(TRANSFORM dirs APPEND \"/debug/bin\")
else()
list(TRANSFORM dirs APPEND \"/bin\")
list(FILTER dirs EXCLUDE REGEX \".*debug.*\")
endif()
set(BU_CHMOD_BUNDLE_ITEMS ON)
set(gp_tool \"${gp_tool}\")
include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/VCMI_Client.exe\" \"\" \"\${dirs}\")
" COMPONENT Runtime)
endif(WIN32)
set(targetBinary "\"\${CMAKE_INSTALL_PREFIX}/${BIN_DIR}/$<TARGET_FILE_NAME:${deployTarget}>\"")
vcmi_deploy_qt(windeployqt "--no-compiler-runtime ${targetBinary}")
endif()