mirror of https://github.com/OpenMW/openmw.git
Merge branch 'master' into 'LTO-timing'
# Conflicts: # CI/before_script.msvc.shLTO-timing
commit
a8d4ba535f
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
Checks: "-*,
|
||||||
|
boost-*,
|
||||||
|
portability-*,
|
||||||
|
"
|
||||||
|
WarningsAsErrors: ''
|
||||||
|
HeaderFilterRegex: ''
|
||||||
|
FormatStyle: none
|
@ -0,0 +1,87 @@
|
|||||||
|
name: CMake
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUILD_TYPE: RelWithDebInfo
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Add OpenMW PPA Dependancies
|
||||||
|
run: sudo add-apt-repository ppa:openmw/openmw; sudo apt-get update
|
||||||
|
|
||||||
|
- name: Install Building Dependancies
|
||||||
|
run: sudo CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
||||||
|
|
||||||
|
- name: Prime ccache
|
||||||
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
|
with:
|
||||||
|
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
|
||||||
|
max-size: 1000M
|
||||||
|
|
||||||
|
- name: Install gtest
|
||||||
|
run: |
|
||||||
|
export CONFIGURATION="Release"
|
||||||
|
export GOOGLETEST_DIR="."
|
||||||
|
export GENERATOR="Unix Makefiles"
|
||||||
|
export CC="gcc"
|
||||||
|
export CXX="g++"
|
||||||
|
sudo -E CI/build_googletest.sh
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
run: cmake -S . -B . -DGTEST_ROOT="$(pwd)/googletest/build" -DGMOCK_ROOT="$(pwd)/googletest/build" -DBUILD_UNITTESTS=ON -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_FLAGS='-Werror' -DCMAKE_CXX_FLAGS="-Werror -Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build . --config ${{env.BUILD_TYPE}} --parallel 3
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ./openmw_test_suite
|
||||||
|
|
||||||
|
# - name: Install
|
||||||
|
# shell: bash
|
||||||
|
# run: cmake --install .
|
||||||
|
|
||||||
|
# - name: Create Artifact
|
||||||
|
# shell: bash
|
||||||
|
# working-directory: install
|
||||||
|
# run: |
|
||||||
|
# ls -laR
|
||||||
|
# 7z a ../build_artifact.7z .
|
||||||
|
|
||||||
|
# - name: Upload Artifact
|
||||||
|
# uses: actions/upload-artifact@v1
|
||||||
|
# with:
|
||||||
|
# path: ./build_artifact.7z
|
||||||
|
# name: build_artifact.7z
|
||||||
|
|
||||||
|
MacOS:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Building Dependancies
|
||||||
|
run: CI/before_install.osx.sh
|
||||||
|
|
||||||
|
- name: Prime ccache
|
||||||
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
|
with:
|
||||||
|
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
|
||||||
|
max-size: 1000M
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
rm -fr build # remove the build directory
|
||||||
|
CI/before_script.osx.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
make -j $(sysctl -n hw.logicalcpu) package
|
@ -0,0 +1,10 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
sphinx:
|
||||||
|
configuration: docs/source/conf.py
|
||||||
|
|
||||||
|
python:
|
||||||
|
version: 3.8
|
||||||
|
install:
|
||||||
|
- requirements: docs/requirements.txt
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
1471
|
||||||
|
1450
|
||||||
|
1420
|
||||||
|
1314
|
||||||
|
1216
|
||||||
|
1172
|
||||||
|
1160
|
||||||
|
1051
|
@ -1,105 +0,0 @@
|
|||||||
language: cpp
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- coverity_scan
|
|
||||||
- /openmw-.*$/
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
|
||||||
# via the "travis encrypt" command using the project repo's public key
|
|
||||||
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
|
|
||||||
cache: ccache
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- sourceline: 'ppa:openmw/openmw'
|
|
||||||
# - ubuntu-toolchain-r-test # for GCC-10
|
|
||||||
packages: [
|
|
||||||
# Dev
|
|
||||||
build-essential, cmake, clang-tools, ccache,
|
|
||||||
# Boost
|
|
||||||
libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,
|
|
||||||
# FFmpeg
|
|
||||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev,
|
|
||||||
# Audio, Video and Misc. deps
|
|
||||||
libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev
|
|
||||||
# The other ones from OpenMW ppa
|
|
||||||
libbullet-dev, libopenscenegraph-dev, libmygui-dev
|
|
||||||
]
|
|
||||||
coverity_scan: # TODO: currently takes too long, disabled openmw/openmw-cs for now.
|
|
||||||
project:
|
|
||||||
name: "OpenMW/openmw"
|
|
||||||
description: "<Your project description here>"
|
|
||||||
branch_pattern: coverity_scan
|
|
||||||
notification_email: 1122069+psi29a@users.noreply.github.com
|
|
||||||
build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENMW=FALSE -DBUILD_OPENCS=FALSE"
|
|
||||||
build_command: "make VERBOSE=1 -j3"
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: OpenMW (all) on MacOS 10.15 with Xcode 11.6
|
|
||||||
os: osx
|
|
||||||
osx_image: xcode11.6
|
|
||||||
if: branch != coverity_scan
|
|
||||||
- name: OpenMW (all) on Ubuntu Focal with GCC
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
if: branch != coverity_scan
|
|
||||||
- name: OpenMW (tests only) on Ubuntu Focal with GCC
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
if: branch != coverity_scan
|
|
||||||
env:
|
|
||||||
- BUILD_TESTS_ONLY: 1
|
|
||||||
- name: OpenMW (openmw) on Ubuntu Focal with Clang's Static Analysis
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
env:
|
|
||||||
- MATRIX_EVAL="CC=clang && CXX=clang++"
|
|
||||||
- ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++"
|
|
||||||
if: branch != coverity_scan
|
|
||||||
compiler: clang
|
|
||||||
- name: OpenMW Components Coverity Scan
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
if: branch = coverity_scan
|
|
||||||
# allow_failures:
|
|
||||||
# - name: OpenMW (openmw) on Ubuntu Focal with GCC-10
|
|
||||||
# env:
|
|
||||||
# - MATRIX_EVAL="CC=gcc-10 && CXX=g++-10"
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi
|
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_install.${TRAVIS_OS_NAME}.sh; fi
|
|
||||||
before_script:
|
|
||||||
- ccache -z
|
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_script.${TRAVIS_OS_NAME}.sh; fi
|
|
||||||
script:
|
|
||||||
- cd ./build
|
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi
|
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
|
||||||
- cd "${TRAVIS_BUILD_DIR}"
|
|
||||||
- ccache -s
|
|
||||||
deploy:
|
|
||||||
provider: script
|
|
||||||
script: ./CI/deploy.osx.sh
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
branch: master
|
|
||||||
condition: "$TRAVIS_EVENT_TYPE = cron && $TRAVIS_OS_NAME = osx"
|
|
||||||
repo: OpenMW/openmw
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
recipients:
|
|
||||||
- corrmage+travis-ci@gmail.com
|
|
||||||
on_success: change
|
|
||||||
on_failure: always
|
|
||||||
irc:
|
|
||||||
channels:
|
|
||||||
- "chat.freenode.net#openmw"
|
|
||||||
on_success: change
|
|
||||||
on_failure: always
|
|
||||||
use_notice: true
|
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh -ex
|
#!/bin/sh -ex
|
||||||
|
|
||||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20201018.zip -o ~/openmw-android-deps.zip
|
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20211114.zip -o ~/openmw-android-deps.zip
|
||||||
unzip -o ~/openmw-android-deps -d /usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
|
unzip -o ~/openmw-android-deps -d /android-ndk-r22/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
|
||||||
|
@ -1,9 +1,27 @@
|
|||||||
#!/bin/sh -e
|
#!/bin/sh -ex
|
||||||
|
|
||||||
|
export HOMEBREW_NO_EMOJI=1
|
||||||
|
brew update --quiet
|
||||||
|
|
||||||
|
# workaround python issue on travis
|
||||||
|
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies python@3.8 || true
|
||||||
|
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies python@3.9 || true
|
||||||
|
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies qt@6 || true
|
||||||
|
|
||||||
# Some of these tools can come from places other than brew, so check before installing
|
# Some of these tools can come from places other than brew, so check before installing
|
||||||
|
[ -z "${TRAVIS}" ] && brew reinstall xquartz
|
||||||
|
[ -z "${TRAVIS}" ] && brew reinstall fontconfig
|
||||||
command -v ccache >/dev/null 2>&1 || brew install ccache
|
command -v ccache >/dev/null 2>&1 || brew install ccache
|
||||||
command -v cmake >/dev/null 2>&1 || brew install cmake
|
command -v cmake >/dev/null 2>&1 || brew install cmake
|
||||||
command -v qmake >/dev/null 2>&1 || brew install qt
|
command -v qmake >/dev/null 2>&1 || brew install qt@5
|
||||||
|
brew install icu4c
|
||||||
|
brew install yaml-cpp
|
||||||
|
export PATH="/usr/local/opt/qt@5/bin:$PATH" # needed to use qmake in none default path as qt now points to qt6
|
||||||
|
|
||||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip
|
ccache --version
|
||||||
|
cmake --version
|
||||||
|
qmake --version
|
||||||
|
|
||||||
|
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20220225.zip -o ~/openmw-deps.zip
|
||||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||||
|
|
||||||
|
@ -1,44 +1,109 @@
|
|||||||
#!/bin/bash -ex
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -xeo pipefail
|
||||||
|
|
||||||
free -m
|
free -m
|
||||||
|
|
||||||
|
# Silence a git warning
|
||||||
|
git config --global advice.detachedHead false
|
||||||
|
|
||||||
|
BUILD_UNITTESTS=OFF
|
||||||
|
BUILD_BENCHMARKS=OFF
|
||||||
|
|
||||||
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||||
export GOOGLETEST_DIR="$(pwd)/googletest/build/install"
|
export GOOGLETEST_DIR="${PWD}/googletest/build/install"
|
||||||
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
|
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
|
||||||
|
BUILD_UNITTESTS=ON
|
||||||
|
BUILD_BENCHMARKS=ON
|
||||||
|
fi
|
||||||
|
|
||||||
|
# setup our basic cmake build options
|
||||||
|
declare -a CMAKE_CONF_OPTS=(
|
||||||
|
-DCMAKE_C_COMPILER="${CC:-/usr/bin/cc}"
|
||||||
|
-DCMAKE_CXX_COMPILER="${CXX:-/usr/bin/c++}"
|
||||||
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||||
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||||
|
-DCMAKE_INSTALL_PREFIX=install
|
||||||
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
|
-DBUILD_SHARED_LIBS=OFF
|
||||||
|
-DUSE_SYSTEM_TINYXML=ON
|
||||||
|
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON
|
||||||
|
-DOPENMW_CXX_FLAGS="-Werror -Werror=implicit-fallthrough" # flags specific to OpenMW project
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ $CI_OPENMW_USE_STATIC_DEPS ]]; then
|
||||||
|
CMAKE_CONF_OPTS+=(
|
||||||
|
-DOPENMW_USE_SYSTEM_MYGUI=OFF
|
||||||
|
-DOPENMW_USE_SYSTEM_OSG=OFF
|
||||||
|
-DOPENMW_USE_SYSTEM_BULLET=OFF
|
||||||
|
-DOPENMW_USE_SYSTEM_SQLITE3=OFF
|
||||||
|
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=OFF
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $CI_CLANG_TIDY ]]; then
|
||||||
|
CMAKE_CONF_OPTS+=(
|
||||||
|
-DCMAKE_CXX_CLANG_TIDY="clang-tidy;--warnings-as-errors=*"
|
||||||
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir build
|
|
||||||
|
if [[ "${CMAKE_BUILD_TYPE}" ]]; then
|
||||||
|
CMAKE_CONF_OPTS+=(
|
||||||
|
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${CMAKE_CXX_FLAGS_DEBUG}" ]]; then
|
||||||
|
CMAKE_CONF_OPTS+=(
|
||||||
|
-DCMAKE_CXX_FLAGS_DEBUG="${CMAKE_CXX_FLAGS_DEBUG}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${CMAKE_EXE_LINKER_FLAGS}" ]]; then
|
||||||
|
CMAKE_CONF_OPTS+=(
|
||||||
|
-DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then
|
||||||
|
CMAKE_CONF_OPTS+=(
|
||||||
|
-DBUILD_WITH_CODE_COVERAGE="${BUILD_WITH_CODE_COVERAGE}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||||
|
|
||||||
|
# flags specific to our test suite
|
||||||
|
CXX_FLAGS="-Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy"
|
||||||
|
if [[ "${CXX}" == 'clang++' ]]; then
|
||||||
|
CXX_FLAGS="${CXX_FLAGS} -Wno-error=unused-lambda-capture -Wno-error=gnu-zero-variadic-macro-arguments"
|
||||||
|
fi
|
||||||
|
CMAKE_CONF_OPTS+=(
|
||||||
|
-DCMAKE_CXX_FLAGS="${CXX_FLAGS}"
|
||||||
|
)
|
||||||
|
|
||||||
${ANALYZE} cmake \
|
${ANALYZE} cmake \
|
||||||
-D CMAKE_C_COMPILER="${CC}" \
|
"${CMAKE_CONF_OPTS[@]}" \
|
||||||
-D CMAKE_CXX_COMPILER="${CXX}" \
|
-DBUILD_OPENMW=OFF \
|
||||||
-D CMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DBUILD_BSATOOL=OFF \
|
||||||
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
-DBUILD_ESMTOOL=OFF \
|
||||||
-D CMAKE_INSTALL_PREFIX=install \
|
-DBUILD_LAUNCHER=OFF \
|
||||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DBUILD_MWINIIMPORTER=OFF \
|
||||||
-D USE_SYSTEM_TINYXML=TRUE \
|
-DBUILD_ESSIMPORTER=OFF \
|
||||||
-D BUILD_OPENMW=OFF \
|
-DBUILD_OPENCS=OFF \
|
||||||
-D BUILD_BSATOOL=OFF \
|
-DBUILD_WIZARD=OFF \
|
||||||
-D BUILD_ESMTOOL=OFF \
|
-DBUILD_NAVMESHTOOL=OFF \
|
||||||
-D BUILD_LAUNCHER=OFF \
|
-DBUILD_UNITTESTS=${BUILD_UNITTESTS} \
|
||||||
-D BUILD_MWINIIMPORTER=OFF \
|
-DBUILD_BENCHMARKS=${BUILD_BENCHMARKS} \
|
||||||
-D BUILD_ESSIMPORTER=OFF \
|
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||||
-D BUILD_OPENCS=OFF \
|
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||||
-D BUILD_WIZARD=OFF \
|
|
||||||
-D BUILD_UNITTESTS=ON \
|
|
||||||
-D GTEST_ROOT="${GOOGLETEST_DIR}" \
|
|
||||||
-D GMOCK_ROOT="${GOOGLETEST_DIR}" \
|
|
||||||
..
|
..
|
||||||
else
|
else
|
||||||
${ANALYZE} cmake \
|
${ANALYZE} cmake \
|
||||||
-D CMAKE_C_COMPILER="${CC}" \
|
"${CMAKE_CONF_OPTS[@]}" \
|
||||||
-D CMAKE_CXX_COMPILER="${CXX}" \
|
|
||||||
-D CMAKE_C_COMPILER_LAUNCHER=ccache \
|
|
||||||
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
|
||||||
-D USE_SYSTEM_TINYXML=TRUE \
|
|
||||||
-D CMAKE_INSTALL_PREFIX=install \
|
|
||||||
-D CMAKE_BUILD_TYPE=Debug \
|
|
||||||
..
|
..
|
||||||
fi
|
fi
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
print_help() {
|
||||||
|
echo "usage: $0 [group]..."
|
||||||
|
echo
|
||||||
|
echo " available groups: "${!GROUPED_DEPS[@]}""
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -rA GROUPED_DEPS=(
|
||||||
|
[gcc]="binutils gcc build-essential cmake ccache curl unzip git pkg-config"
|
||||||
|
[clang]="binutils clang make cmake ccache curl unzip git pkg-config"
|
||||||
|
|
||||||
|
# Common dependencies for building OpenMW.
|
||||||
|
[openmw-deps]="
|
||||||
|
libboost-filesystem-dev libboost-program-options-dev
|
||||||
|
libboost-system-dev libboost-iostreams-dev
|
||||||
|
|
||||||
|
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||||
|
libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev
|
||||||
|
libbullet-dev liblz4-dev libpng-dev libjpeg-dev libluajit-5.1-dev
|
||||||
|
librecast-dev libsqlite3-dev ca-certificates libicu-dev libyaml-cpp-dev
|
||||||
|
"
|
||||||
|
|
||||||
|
# These dependencies can alternatively be built and linked statically.
|
||||||
|
[openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev libsqlite3-dev"
|
||||||
|
[clang-tidy]="clang-tidy"
|
||||||
|
|
||||||
|
# Pre-requisites for building MyGUI and OSG for static linking.
|
||||||
|
#
|
||||||
|
# * MyGUI and OSG: libsdl2-dev liblz4-dev libfreetype6-dev
|
||||||
|
# * OSG: libgl-dev
|
||||||
|
#
|
||||||
|
# Plugins:
|
||||||
|
# * DAE: libcollada-dom-dev libboost-system-dev libboost-filesystem-dev
|
||||||
|
# * JPEG: libjpeg-dev
|
||||||
|
# * PNG: libpng-dev
|
||||||
|
[openmw-deps-static]="
|
||||||
|
libcollada-dom-dev libfreetype6-dev libjpeg-dev libpng-dev
|
||||||
|
libsdl2-dev libboost-system-dev libboost-filesystem-dev libgl-dev
|
||||||
|
"
|
||||||
|
|
||||||
|
[openmw-coverage]="gcovr"
|
||||||
|
|
||||||
|
[openmw-integration-tests]="
|
||||||
|
ca-certificates
|
||||||
|
git
|
||||||
|
git-lfs
|
||||||
|
libavcodec58
|
||||||
|
libavformat58
|
||||||
|
libavutil56
|
||||||
|
libboost-filesystem1.71.0
|
||||||
|
libboost-iostreams1.71.0
|
||||||
|
libboost-program-options1.71.0
|
||||||
|
libboost-system1.71.0
|
||||||
|
libbullet2.88
|
||||||
|
libcollada-dom2.4-dp0
|
||||||
|
libicu66
|
||||||
|
libjpeg8
|
||||||
|
libluajit-5.1-2
|
||||||
|
liblz4-1
|
||||||
|
libmyguiengine3debian1v5
|
||||||
|
libopenal1
|
||||||
|
libopenscenegraph161
|
||||||
|
libpng16-16
|
||||||
|
libqt5opengl5
|
||||||
|
librecast1
|
||||||
|
libsdl2-2.0-0
|
||||||
|
libsqlite3-0
|
||||||
|
libswresample3
|
||||||
|
libswscale5
|
||||||
|
libtinyxml2.6.2v5
|
||||||
|
libyaml-cpp0.6
|
||||||
|
python3-pip
|
||||||
|
xvfb
|
||||||
|
"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
>&2 print_help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
deps=()
|
||||||
|
for group in "$@"; do
|
||||||
|
if [[ ! -v GROUPED_DEPS[$group] ]]; then
|
||||||
|
>&2 echo "error: unknown group ${group}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
deps+=(${GROUPED_DEPS[$group]})
|
||||||
|
done
|
||||||
|
|
||||||
|
export APT_CACHE_DIR="${PWD}/apt-cache"
|
||||||
|
set -x
|
||||||
|
mkdir -pv "$APT_CACHE_DIR"
|
||||||
|
apt-get update -yqq
|
||||||
|
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common >/dev/null
|
||||||
|
add-apt-repository -y ppa:openmw/openmw
|
||||||
|
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
|
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
git clone --depth=1 https://gitlab.com/OpenMW/example-suite.git
|
||||||
|
|
||||||
|
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \
|
||||||
|
scripts/integration_tests.py --omw build/install/bin/openmw --workdir integration_tests_output example-suite/
|
||||||
|
|
||||||
|
ls integration_tests_output/*.osg_stats.log | while read v; do
|
||||||
|
scripts/osg_stats.py --stats '.*' --regexp_match < "${v}"
|
||||||
|
done
|
@ -0,0 +1,15 @@
|
|||||||
|
if(OPENMW_USE_SYSTEM_BENCHMARK)
|
||||||
|
find_package(benchmark REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
openmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark detournavigator/navmeshtilescache.cpp)
|
||||||
|
target_compile_features(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE cxx_std_17)
|
||||||
|
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components)
|
||||||
|
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||||
|
target_precompile_headers(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE <algorithm>)
|
||||||
|
endif()
|
@ -0,0 +1,290 @@
|
|||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
#include <components/detournavigator/navmeshtilescache.hpp>
|
||||||
|
#include <components/esm3/loadland.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace DetourNavigator;
|
||||||
|
|
||||||
|
struct Key
|
||||||
|
{
|
||||||
|
AgentBounds mAgentBounds;
|
||||||
|
TilePosition mTilePosition;
|
||||||
|
RecastMesh mRecastMesh;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Item
|
||||||
|
{
|
||||||
|
Key mKey;
|
||||||
|
PreparedNavMeshData mValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Random>
|
||||||
|
osg::Vec2i generateVec2i(int max, Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_int_distribution<int> distribution(0, max);
|
||||||
|
return osg::Vec2i(distribution(random), distribution(random));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Random>
|
||||||
|
osg::Vec3f generateAgentHalfExtents(float min, float max, Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_int_distribution<int> distribution(min, max);
|
||||||
|
return osg::Vec3f(distribution(random), distribution(random), distribution(random));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator, typename Random>
|
||||||
|
void generateVertices(OutputIterator out, std::size_t number, Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||||
|
std::generate_n(out, 3 * (number - number % 3), [&] { return distribution(random); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator, typename Random>
|
||||||
|
void generateIndices(OutputIterator out, int max, std::size_t number, Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_int_distribution<int> distribution(0, max);
|
||||||
|
std::generate_n(out, number - number % 3, [&] { return distribution(random); });
|
||||||
|
}
|
||||||
|
|
||||||
|
AreaType toAreaType(int index)
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0: return AreaType_null;
|
||||||
|
case 1: return AreaType_water;
|
||||||
|
case 2: return AreaType_door;
|
||||||
|
case 3: return AreaType_pathgrid;
|
||||||
|
case 4: return AreaType_ground;
|
||||||
|
}
|
||||||
|
return AreaType_null;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Random>
|
||||||
|
AreaType generateAreaType(Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_int_distribution<int> distribution(0, 4);
|
||||||
|
return toAreaType(distribution(random));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator, typename Random>
|
||||||
|
void generateAreaTypes(OutputIterator out, std::size_t triangles, Random& random)
|
||||||
|
{
|
||||||
|
std::generate_n(out, triangles, [&] { return generateAreaType(random); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator, typename Random>
|
||||||
|
void generateWater(OutputIterator out, std::size_t count, Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||||
|
std::generate_n(out, count, [&] {
|
||||||
|
return CellWater {generateVec2i(1000, random), Water {ESM::Land::REAL_SIZE, distribution(random)}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Random>
|
||||||
|
Mesh generateMesh(std::size_t triangles, Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||||
|
std::vector<float> vertices;
|
||||||
|
std::vector<int> indices;
|
||||||
|
std::vector<AreaType> areaTypes;
|
||||||
|
if (distribution(random) < 0.939)
|
||||||
|
{
|
||||||
|
generateVertices(std::back_inserter(vertices), triangles * 2.467, random);
|
||||||
|
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.279, random);
|
||||||
|
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
|
||||||
|
}
|
||||||
|
return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Random>
|
||||||
|
Heightfield generateHeightfield(Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||||
|
Heightfield result;
|
||||||
|
result.mCellPosition = generateVec2i(1000, random);
|
||||||
|
result.mCellSize = ESM::Land::REAL_SIZE;
|
||||||
|
result.mMinHeight = distribution(random);
|
||||||
|
result.mMaxHeight = result.mMinHeight + 1.0;
|
||||||
|
result.mLength = static_cast<std::uint8_t>(ESM::Land::LAND_SIZE);
|
||||||
|
std::generate_n(std::back_inserter(result.mHeights), ESM::Land::LAND_NUM_VERTS, [&]
|
||||||
|
{
|
||||||
|
return distribution(random);
|
||||||
|
});
|
||||||
|
result.mOriginalSize = ESM::Land::LAND_SIZE;
|
||||||
|
result.mMinX = 0;
|
||||||
|
result.mMinY = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Random>
|
||||||
|
FlatHeightfield generateFlatHeightfield(Random& random)
|
||||||
|
{
|
||||||
|
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||||
|
FlatHeightfield result;
|
||||||
|
result.mCellPosition = generateVec2i(1000, random);
|
||||||
|
result.mCellSize = ESM::Land::REAL_SIZE;
|
||||||
|
result.mHeight = distribution(random);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Random>
|
||||||
|
Key generateKey(std::size_t triangles, Random& random)
|
||||||
|
{
|
||||||
|
const CollisionShapeType agentShapeType = CollisionShapeType::Aabb;
|
||||||
|
const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random);
|
||||||
|
const TilePosition tilePosition = generateVec2i(10000, random);
|
||||||
|
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
|
||||||
|
const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
|
||||||
|
Mesh mesh = generateMesh(triangles, random);
|
||||||
|
std::vector<CellWater> water;
|
||||||
|
generateWater(std::back_inserter(water), 1, random);
|
||||||
|
RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water),
|
||||||
|
{generateHeightfield(random)}, {generateFlatHeightfield(random)}, {});
|
||||||
|
return Key {AgentBounds {agentShapeType, agentHalfExtents}, tilePosition, std::move(recastMesh)};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t trianglesPerTile = 239;
|
||||||
|
|
||||||
|
template <typename OutputIterator, typename Random>
|
||||||
|
void generateKeys(OutputIterator out, std::size_t count, Random& random)
|
||||||
|
{
|
||||||
|
std::generate_n(out, count, [&] { return generateKey(trianglesPerTile, random); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator, typename Random>
|
||||||
|
void fillCache(OutputIterator out, Random& random, NavMeshTilesCache& cache)
|
||||||
|
{
|
||||||
|
std::size_t size = cache.getStats().mNavMeshCacheSize;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Key key = generateKey(trianglesPerTile, random);
|
||||||
|
cache.set(key.mAgentBounds, key.mTilePosition, key.mRecastMesh,
|
||||||
|
std::make_unique<PreparedNavMeshData>());
|
||||||
|
*out++ = std::move(key);
|
||||||
|
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
|
||||||
|
if (size >= newSize)
|
||||||
|
break;
|
||||||
|
size = newSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t maxCacheSize, int hitPercentage>
|
||||||
|
void getFromFilledCache(benchmark::State& state)
|
||||||
|
{
|
||||||
|
NavMeshTilesCache cache(maxCacheSize);
|
||||||
|
std::minstd_rand random;
|
||||||
|
std::vector<Key> keys;
|
||||||
|
fillCache(std::back_inserter(keys), random, cache);
|
||||||
|
generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);
|
||||||
|
std::size_t n = 0;
|
||||||
|
|
||||||
|
while (state.KeepRunning())
|
||||||
|
{
|
||||||
|
const auto& key = keys[n++ % keys.size()];
|
||||||
|
const auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);
|
||||||
|
benchmark::DoNotOptimize(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_1m_100hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<1 * 1024 * 1024, 100>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_4m_100hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<4 * 1024 * 1024, 100>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_16m_100hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<16 * 1024 * 1024, 100>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_64m_100hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<64 * 1024 * 1024, 100>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_1m_70hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<1 * 1024 * 1024, 70>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_4m_70hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<4 * 1024 * 1024, 70>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_16m_70hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<16 * 1024 * 1024, 70>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFromFilledCache_64m_70hit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
getFromFilledCache<64 * 1024 * 1024, 70>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t maxCacheSize>
|
||||||
|
void setToBoundedNonEmptyCache(benchmark::State& state)
|
||||||
|
{
|
||||||
|
NavMeshTilesCache cache(maxCacheSize);
|
||||||
|
std::minstd_rand random;
|
||||||
|
std::vector<Key> keys;
|
||||||
|
fillCache(std::back_inserter(keys), random, cache);
|
||||||
|
generateKeys(std::back_inserter(keys), keys.size() * 2, random);
|
||||||
|
std::reverse(keys.begin(), keys.end());
|
||||||
|
std::size_t n = 0;
|
||||||
|
|
||||||
|
while (state.KeepRunning())
|
||||||
|
{
|
||||||
|
const auto& key = keys[n++ % keys.size()];
|
||||||
|
const auto result = cache.set(key.mAgentBounds, key.mTilePosition, key.mRecastMesh,
|
||||||
|
std::make_unique<PreparedNavMeshData>());
|
||||||
|
benchmark::DoNotOptimize(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToBoundedNonEmptyCache_1m(benchmark::State& state)
|
||||||
|
{
|
||||||
|
setToBoundedNonEmptyCache<1 * 1024 * 1024>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToBoundedNonEmptyCache_4m(benchmark::State& state)
|
||||||
|
{
|
||||||
|
setToBoundedNonEmptyCache<4 * 1024 * 1024>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToBoundedNonEmptyCache_16m(benchmark::State& state)
|
||||||
|
{
|
||||||
|
setToBoundedNonEmptyCache<16 * 1024 * 1024>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToBoundedNonEmptyCache_64m(benchmark::State& state)
|
||||||
|
{
|
||||||
|
setToBoundedNonEmptyCache<64 * 1024 * 1024>(state);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BENCHMARK(getFromFilledCache_1m_100hit);
|
||||||
|
BENCHMARK(getFromFilledCache_4m_100hit);
|
||||||
|
BENCHMARK(getFromFilledCache_16m_100hit);
|
||||||
|
BENCHMARK(getFromFilledCache_64m_100hit);
|
||||||
|
BENCHMARK(getFromFilledCache_1m_70hit);
|
||||||
|
BENCHMARK(getFromFilledCache_4m_70hit);
|
||||||
|
BENCHMARK(getFromFilledCache_16m_70hit);
|
||||||
|
BENCHMARK(getFromFilledCache_64m_70hit);
|
||||||
|
BENCHMARK(setToBoundedNonEmptyCache_1m);
|
||||||
|
BENCHMARK(setToBoundedNonEmptyCache_4m);
|
||||||
|
BENCHMARK(setToBoundedNonEmptyCache_16m);
|
||||||
|
BENCHMARK(setToBoundedNonEmptyCache_64m);
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
@ -0,0 +1,27 @@
|
|||||||
|
set(BULLETMESHTOOL
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
source_group(apps\\bulletobjecttool FILES ${BULLETMESHTOOL})
|
||||||
|
|
||||||
|
openmw_add_executable(openmw-bulletobjecttool ${BULLETMESHTOOL})
|
||||||
|
|
||||||
|
target_link_libraries(openmw-bulletobjecttool
|
||||||
|
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||||
|
components
|
||||||
|
)
|
||||||
|
|
||||||
|
if (BUILD_WITH_CODE_COVERAGE)
|
||||||
|
add_definitions(--coverage)
|
||||||
|
target_link_libraries(openmw-bulletobjecttool gcov)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
install(TARGETS openmw-bulletobjecttool RUNTIME DESTINATION ".")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||||
|
target_precompile_headers(openmw-bulletobjecttool PRIVATE
|
||||||
|
<string>
|
||||||
|
<vector>
|
||||||
|
)
|
||||||
|
endif()
|
@ -0,0 +1,203 @@
|
|||||||
|
#include <components/debug/debugging.hpp>
|
||||||
|
#include <components/esm3/esmreader.hpp>
|
||||||
|
#include <components/esm3/loadcell.hpp>
|
||||||
|
#include <components/esmloader/esmdata.hpp>
|
||||||
|
#include <components/esmloader/load.hpp>
|
||||||
|
#include <components/fallback/fallback.hpp>
|
||||||
|
#include <components/fallback/validate.hpp>
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
#include <components/resource/bulletshapemanager.hpp>
|
||||||
|
#include <components/resource/foreachbulletobject.hpp>
|
||||||
|
#include <components/resource/imagemanager.hpp>
|
||||||
|
#include <components/resource/niffilemanager.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/version/version.hpp>
|
||||||
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/registerarchives.hpp>
|
||||||
|
#include <components/esm3/readerscache.hpp>
|
||||||
|
#include <components/platform/platform.hpp>
|
||||||
|
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
|
using StringsVector = std::vector<std::string>;
|
||||||
|
|
||||||
|
bpo::options_description makeOptionsDescription()
|
||||||
|
{
|
||||||
|
using Fallback::FallbackMap;
|
||||||
|
|
||||||
|
bpo::options_description result;
|
||||||
|
|
||||||
|
result.add_options()
|
||||||
|
("help", "print help message")
|
||||||
|
|
||||||
|
("version", "print version information and quit")
|
||||||
|
|
||||||
|
("data", bpo::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "data")
|
||||||
|
->multitoken()->composing(), "set data directories (later directories have higher priority)")
|
||||||
|
|
||||||
|
("data-local", bpo::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(Files::MaybeQuotedPathContainer::value_type(), ""),
|
||||||
|
"set local data directory (highest priority)")
|
||||||
|
|
||||||
|
("fallback-archive", bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")
|
||||||
|
->multitoken()->composing(), "set fallback BSA archives (later archives have higher priority)")
|
||||||
|
|
||||||
|
("resources", bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
|
||||||
|
"set resources directory")
|
||||||
|
|
||||||
|
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
||||||
|
->multitoken()->composing(), "content file(s): esm/esp, or omwgame/omwaddon/omwscripts")
|
||||||
|
|
||||||
|
("fs-strict", bpo::value<bool>()->implicit_value(true)
|
||||||
|
->default_value(false), "strict file system handling (no case folding)")
|
||||||
|
|
||||||
|
("encoding", bpo::value<std::string>()->
|
||||||
|
default_value("win1252"),
|
||||||
|
"Character encoding used in OpenMW game messages:\n"
|
||||||
|
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||||
|
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||||
|
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||||
|
|
||||||
|
("fallback", bpo::value<FallbackMap>()->default_value(FallbackMap(), "")
|
||||||
|
->multitoken()->composing(), "fallback values")
|
||||||
|
;
|
||||||
|
|
||||||
|
Files::ConfigurationManager::addCommonOptions(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WriteArray
|
||||||
|
{
|
||||||
|
const float (&mValue)[3];
|
||||||
|
|
||||||
|
friend std::ostream& operator <<(std::ostream& stream, const WriteArray& value)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < 2; ++i)
|
||||||
|
stream << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue[i] << ", ";
|
||||||
|
return stream << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue[2];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string toHex(std::string_view value)
|
||||||
|
{
|
||||||
|
std::string buffer(value.size() * 2, '0');
|
||||||
|
char* out = buffer.data();
|
||||||
|
for (const char v : value)
|
||||||
|
{
|
||||||
|
const std::ptrdiff_t space = static_cast<std::ptrdiff_t>(static_cast<std::uint8_t>(v) <= 0xf);
|
||||||
|
const auto [ptr, ec] = std::to_chars(out + space, out + space + 2, static_cast<std::uint8_t>(v), 16);
|
||||||
|
if (ec != std::errc())
|
||||||
|
throw std::system_error(std::make_error_code(ec));
|
||||||
|
out += 2;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int runBulletObjectTool(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
Platform::init();
|
||||||
|
|
||||||
|
bpo::options_description desc = makeOptionsDescription();
|
||||||
|
|
||||||
|
bpo::parsed_options options = bpo::command_line_parser(argc, argv)
|
||||||
|
.options(desc).allow_unregistered().run();
|
||||||
|
bpo::variables_map variables;
|
||||||
|
|
||||||
|
bpo::store(options, variables);
|
||||||
|
bpo::notify(variables);
|
||||||
|
|
||||||
|
if (variables.find("help") != variables.end())
|
||||||
|
{
|
||||||
|
getRawStdout() << desc << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files::ConfigurationManager config;
|
||||||
|
|
||||||
|
bpo::variables_map composingVariables = Files::separateComposingVariables(variables, desc);
|
||||||
|
config.readConfiguration(variables, desc);
|
||||||
|
Files::mergeComposingVariables(variables, composingVariables, desc);
|
||||||
|
|
||||||
|
const std::string encoding(variables["encoding"].as<std::string>());
|
||||||
|
Log(Debug::Info) << ToUTF8::encodingUsingMessage(encoding);
|
||||||
|
ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(encoding));
|
||||||
|
|
||||||
|
Files::PathContainer dataDirs(asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>()));
|
||||||
|
|
||||||
|
auto local = variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>();
|
||||||
|
if (!local.empty())
|
||||||
|
dataDirs.push_back(std::move(local));
|
||||||
|
|
||||||
|
config.filterOutNonExistingPaths(dataDirs);
|
||||||
|
|
||||||
|
const auto fsStrict = variables["fs-strict"].as<bool>();
|
||||||
|
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
|
||||||
|
Version::Version v = Version::getOpenmwVersion(resDir.string());
|
||||||
|
Log(Debug::Info) << v.describe();
|
||||||
|
dataDirs.insert(dataDirs.begin(), resDir / "vfs");
|
||||||
|
const auto fileCollections = Files::Collections(dataDirs, !fsStrict);
|
||||||
|
const auto archives = variables["fallback-archive"].as<StringsVector>();
|
||||||
|
const auto contentFiles = variables["content"].as<StringsVector>();
|
||||||
|
|
||||||
|
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap);
|
||||||
|
|
||||||
|
VFS::Manager vfs(fsStrict);
|
||||||
|
|
||||||
|
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
||||||
|
|
||||||
|
Settings::Manager settings;
|
||||||
|
settings.load(config);
|
||||||
|
|
||||||
|
ESM::ReadersCache readers;
|
||||||
|
EsmLoader::Query query;
|
||||||
|
query.mLoadActivators = true;
|
||||||
|
query.mLoadCells = true;
|
||||||
|
query.mLoadContainers = true;
|
||||||
|
query.mLoadDoors = true;
|
||||||
|
query.mLoadGameSettings = true;
|
||||||
|
query.mLoadLands = true;
|
||||||
|
query.mLoadStatics = true;
|
||||||
|
const EsmLoader::EsmData esmData = EsmLoader::loadEsmData(query, contentFiles, fileCollections, readers, &encoder);
|
||||||
|
|
||||||
|
Resource::ImageManager imageManager(&vfs);
|
||||||
|
Resource::NifFileManager nifFileManager(&vfs);
|
||||||
|
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager);
|
||||||
|
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager);
|
||||||
|
|
||||||
|
Resource::forEachBulletObject(readers, vfs, bulletShapeManager, esmData,
|
||||||
|
[] (const ESM::Cell& cell, const Resource::BulletObject& object)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "Found bullet object in " << (cell.isExterior() ? "exterior" : "interior")
|
||||||
|
<< " cell \"" << cell.getDescription() << "\":"
|
||||||
|
<< " fileName=\"" << object.mShape->mFileName << '"'
|
||||||
|
<< " fileHash=" << toHex(object.mShape->mFileHash)
|
||||||
|
<< " collisionShape=" << std::boolalpha << (object.mShape->mCollisionShape == nullptr)
|
||||||
|
<< " avoidCollisionShape=" << std::boolalpha << (object.mShape->mAvoidCollisionShape == nullptr)
|
||||||
|
<< " position=(" << WriteArray {object.mPosition.pos} << ')'
|
||||||
|
<< " rotation=(" << WriteArray {object.mPosition.rot} << ')'
|
||||||
|
<< " scale=" << std::setprecision(std::numeric_limits<float>::max_exponent10) << object.mScale;
|
||||||
|
});
|
||||||
|
|
||||||
|
Log(Debug::Info) << "Done";
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return wrapApplication(runBulletObjectTool, argc, argv, "BulletObjectTool");
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef OPENMW_ESMTOOL_ARGUMENTS_H
|
||||||
|
#define OPENMW_ESMTOOL_ARGUMENTS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <components/esm/format.hpp>
|
||||||
|
|
||||||
|
namespace EsmTool
|
||||||
|
{
|
||||||
|
struct Arguments
|
||||||
|
{
|
||||||
|
std::optional<ESM::Format> mRawFormat;
|
||||||
|
bool quiet_given = false;
|
||||||
|
bool loadcells_given = false;
|
||||||
|
bool plain_given = false;
|
||||||
|
|
||||||
|
std::string mode;
|
||||||
|
std::string encoding;
|
||||||
|
std::string filename;
|
||||||
|
std::string outname;
|
||||||
|
|
||||||
|
std::vector<std::string> types;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,329 @@
|
|||||||
|
#include "tes4.hpp"
|
||||||
|
#include "arguments.hpp"
|
||||||
|
#include "labels.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <components/esm/esmcommon.hpp>
|
||||||
|
#include <components/esm4/reader.hpp>
|
||||||
|
#include <components/esm4/records.hpp>
|
||||||
|
|
||||||
|
namespace EsmTool
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Params
|
||||||
|
{
|
||||||
|
const bool mQuite;
|
||||||
|
|
||||||
|
explicit Params(const Arguments& info)
|
||||||
|
: mQuite(info.quiet_given || info.mode == "clone")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string toString(ESM4::GroupType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ESM4::Grp_RecordType: return "RecordType";
|
||||||
|
case ESM4::Grp_WorldChild: return "WorldChild";
|
||||||
|
case ESM4::Grp_InteriorCell: return "InteriorCell";
|
||||||
|
case ESM4::Grp_InteriorSubCell: return "InteriorSubCell";
|
||||||
|
case ESM4::Grp_ExteriorCell: return "ExteriorCell";
|
||||||
|
case ESM4::Grp_ExteriorSubCell: return "ExteriorSubCell";
|
||||||
|
case ESM4::Grp_CellChild: return "CellChild";
|
||||||
|
case ESM4::Grp_TopicChild: return "TopicChild";
|
||||||
|
case ESM4::Grp_CellPersistentChild: return "CellPersistentChild";
|
||||||
|
case ESM4::Grp_CellTemporaryChild: return "CellTemporaryChild";
|
||||||
|
case ESM4::Grp_CellVisibleDistChild: return "CellVisibleDistChild";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown (" + std::to_string(type) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class = std::void_t<>>
|
||||||
|
struct HasFormId : std::false_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct HasFormId<T, std::void_t<decltype(T::mFormId)>> : std::true_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
constexpr bool hasFormId = HasFormId<T>::value;
|
||||||
|
|
||||||
|
template <class T, class = std::void_t<>>
|
||||||
|
struct HasFlags : std::false_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct HasFlags<T, std::void_t<decltype(T::mFlags)>> : std::true_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
constexpr bool hasFlags = HasFlags<T>::value;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void readTypedRecord(const Params& params, ESM4::Reader& reader)
|
||||||
|
{
|
||||||
|
reader.getRecordData();
|
||||||
|
|
||||||
|
T value;
|
||||||
|
value.load(reader);
|
||||||
|
|
||||||
|
if (params.mQuite)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::cout << "\n Record: " << ESM::NAME(reader.hdr().record.typeId).toStringView();
|
||||||
|
if constexpr (hasFormId<T>)
|
||||||
|
std::cout << ' ' << value.mFormId;
|
||||||
|
if constexpr (hasFlags<T>)
|
||||||
|
std::cout << "\n Record flags: " << recordFlags(value.mFlags);
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void readRecord(const Params& params, ESM4::Reader& reader)
|
||||||
|
{
|
||||||
|
switch (static_cast<ESM4::RecordTypes>(reader.hdr().record.typeId))
|
||||||
|
{
|
||||||
|
case ESM4::REC_AACT: break;
|
||||||
|
case ESM4::REC_ACHR: return readTypedRecord<ESM4::ActorCharacter>(params, reader);
|
||||||
|
case ESM4::REC_ACRE: return readTypedRecord<ESM4::ActorCreature>(params, reader);
|
||||||
|
case ESM4::REC_ACTI: return readTypedRecord<ESM4::Activator>(params, reader);
|
||||||
|
case ESM4::REC_ADDN: break;
|
||||||
|
case ESM4::REC_ALCH: return readTypedRecord<ESM4::Potion>(params, reader);
|
||||||
|
case ESM4::REC_ALOC: return readTypedRecord<ESM4::MediaLocationController>(params, reader);
|
||||||
|
case ESM4::REC_AMMO: return readTypedRecord<ESM4::Ammunition>(params, reader);
|
||||||
|
case ESM4::REC_ANIO: return readTypedRecord<ESM4::AnimObject>(params, reader);
|
||||||
|
case ESM4::REC_APPA: return readTypedRecord<ESM4::Apparatus>(params, reader);
|
||||||
|
case ESM4::REC_ARMA: return readTypedRecord<ESM4::ArmorAddon>(params, reader);
|
||||||
|
case ESM4::REC_ARMO: return readTypedRecord<ESM4::Armor>(params, reader);
|
||||||
|
case ESM4::REC_ARTO: break;
|
||||||
|
case ESM4::REC_ASPC: return readTypedRecord<ESM4::AcousticSpace>(params, reader);
|
||||||
|
case ESM4::REC_ASTP: break;
|
||||||
|
case ESM4::REC_AVIF: break;
|
||||||
|
case ESM4::REC_BOOK: return readTypedRecord<ESM4::Book>(params, reader);
|
||||||
|
case ESM4::REC_BPTD: return readTypedRecord<ESM4::BodyPartData>(params, reader);
|
||||||
|
case ESM4::REC_CAMS: break;
|
||||||
|
case ESM4::REC_CCRD: break;
|
||||||
|
case ESM4::REC_CELL: return readTypedRecord<ESM4::Cell>(params, reader);
|
||||||
|
case ESM4::REC_CLAS: return readTypedRecord<ESM4::Class>(params, reader);
|
||||||
|
case ESM4::REC_CLFM: return readTypedRecord<ESM4::Colour>(params, reader);
|
||||||
|
case ESM4::REC_CLMT: break;
|
||||||
|
case ESM4::REC_CLOT: return readTypedRecord<ESM4::Clothing>(params, reader);
|
||||||
|
case ESM4::REC_CMNY: break;
|
||||||
|
case ESM4::REC_COBJ: break;
|
||||||
|
case ESM4::REC_COLL: break;
|
||||||
|
case ESM4::REC_CONT: return readTypedRecord<ESM4::Container>(params, reader);
|
||||||
|
case ESM4::REC_CPTH: break;
|
||||||
|
case ESM4::REC_CREA: return readTypedRecord<ESM4::Creature>(params, reader);
|
||||||
|
case ESM4::REC_CSTY: break;
|
||||||
|
case ESM4::REC_DEBR: break;
|
||||||
|
case ESM4::REC_DIAL: return readTypedRecord<ESM4::Dialogue>(params, reader);
|
||||||
|
case ESM4::REC_DLBR: break;
|
||||||
|
case ESM4::REC_DLVW: break;
|
||||||
|
case ESM4::REC_DOBJ: return readTypedRecord<ESM4::DefaultObj>(params, reader);
|
||||||
|
case ESM4::REC_DOOR: return readTypedRecord<ESM4::Door>(params, reader);
|
||||||
|
case ESM4::REC_DUAL: break;
|
||||||
|
case ESM4::REC_ECZN: break;
|
||||||
|
case ESM4::REC_EFSH: break;
|
||||||
|
case ESM4::REC_ENCH: break;
|
||||||
|
case ESM4::REC_EQUP: break;
|
||||||
|
case ESM4::REC_EXPL: break;
|
||||||
|
case ESM4::REC_EYES: return readTypedRecord<ESM4::Eyes>(params, reader);
|
||||||
|
case ESM4::REC_FACT: break;
|
||||||
|
case ESM4::REC_FLOR: return readTypedRecord<ESM4::Flora>(params, reader);
|
||||||
|
case ESM4::REC_FLST: return readTypedRecord<ESM4::FormIdList>(params, reader);
|
||||||
|
case ESM4::REC_FSTP: break;
|
||||||
|
case ESM4::REC_FSTS: break;
|
||||||
|
case ESM4::REC_FURN: return readTypedRecord<ESM4::Furniture>(params, reader);
|
||||||
|
case ESM4::REC_GLOB: return readTypedRecord<ESM4::GlobalVariable>(params, reader);
|
||||||
|
case ESM4::REC_GMST: break;
|
||||||
|
case ESM4::REC_GRAS: return readTypedRecord<ESM4::Grass>(params, reader);
|
||||||
|
case ESM4::REC_GRUP: break;
|
||||||
|
case ESM4::REC_HAIR: return readTypedRecord<ESM4::Hair>(params, reader);
|
||||||
|
case ESM4::REC_HAZD: break;
|
||||||
|
case ESM4::REC_HDPT: return readTypedRecord<ESM4::HeadPart>(params, reader);
|
||||||
|
case ESM4::REC_IDLE:
|
||||||
|
// FIXME: ESM4::IdleAnimation::load does not work with Oblivion.esm
|
||||||
|
// return readTypedRecord<ESM4::IdleAnimation>(params, reader);
|
||||||
|
break;
|
||||||
|
case ESM4::REC_IDLM: return readTypedRecord<ESM4::IdleMarker>(params, reader);
|
||||||
|
case ESM4::REC_IMAD: break;
|
||||||
|
case ESM4::REC_IMGS: break;
|
||||||
|
case ESM4::REC_IMOD: return readTypedRecord<ESM4::ItemMod>(params, reader);
|
||||||
|
case ESM4::REC_INFO: return readTypedRecord<ESM4::DialogInfo>(params, reader);
|
||||||
|
case ESM4::REC_INGR: return readTypedRecord<ESM4::Ingredient>(params, reader);
|
||||||
|
case ESM4::REC_IPCT: break;
|
||||||
|
case ESM4::REC_IPDS: break;
|
||||||
|
case ESM4::REC_KEYM: return readTypedRecord<ESM4::Key>(params, reader);
|
||||||
|
case ESM4::REC_KYWD: break;
|
||||||
|
case ESM4::REC_LAND: return readTypedRecord<ESM4::Land>(params, reader);
|
||||||
|
case ESM4::REC_LCRT: break;
|
||||||
|
case ESM4::REC_LCTN: break;
|
||||||
|
case ESM4::REC_LGTM: return readTypedRecord<ESM4::LightingTemplate>(params, reader);
|
||||||
|
case ESM4::REC_LIGH: return readTypedRecord<ESM4::Light>(params, reader);
|
||||||
|
case ESM4::REC_LSCR: break;
|
||||||
|
case ESM4::REC_LTEX: return readTypedRecord<ESM4::LandTexture>(params, reader);
|
||||||
|
case ESM4::REC_LVLC: return readTypedRecord<ESM4::LevelledCreature>(params, reader);
|
||||||
|
case ESM4::REC_LVLI: return readTypedRecord<ESM4::LevelledItem>(params, reader);
|
||||||
|
case ESM4::REC_LVLN: return readTypedRecord<ESM4::LevelledNpc>(params, reader);
|
||||||
|
case ESM4::REC_LVSP: break;
|
||||||
|
case ESM4::REC_MATO: return readTypedRecord<ESM4::Material>(params, reader);
|
||||||
|
case ESM4::REC_MATT: break;
|
||||||
|
case ESM4::REC_MESG: break;
|
||||||
|
case ESM4::REC_MGEF: break;
|
||||||
|
case ESM4::REC_MISC: return readTypedRecord<ESM4::MiscItem>(params, reader);
|
||||||
|
case ESM4::REC_MOVT: break;
|
||||||
|
case ESM4::REC_MSET: return readTypedRecord<ESM4::MediaSet>(params, reader);
|
||||||
|
case ESM4::REC_MSTT: return readTypedRecord<ESM4::MovableStatic>(params, reader);
|
||||||
|
case ESM4::REC_MUSC: return readTypedRecord<ESM4::Music>(params, reader);
|
||||||
|
case ESM4::REC_MUST: break;
|
||||||
|
case ESM4::REC_NAVI: return readTypedRecord<ESM4::Navigation>(params, reader);
|
||||||
|
case ESM4::REC_NAVM: return readTypedRecord<ESM4::NavMesh>(params, reader);
|
||||||
|
case ESM4::REC_NOTE: return readTypedRecord<ESM4::Note>(params, reader);
|
||||||
|
case ESM4::REC_NPC_: return readTypedRecord<ESM4::Npc>(params, reader);
|
||||||
|
case ESM4::REC_OTFT: return readTypedRecord<ESM4::Outfit>(params, reader);
|
||||||
|
case ESM4::REC_PACK: return readTypedRecord<ESM4::AIPackage>(params, reader);
|
||||||
|
case ESM4::REC_PERK: break;
|
||||||
|
case ESM4::REC_PGRD: return readTypedRecord<ESM4::Pathgrid>(params, reader);
|
||||||
|
case ESM4::REC_PGRE: return readTypedRecord<ESM4::PlacedGrenade>(params, reader);
|
||||||
|
case ESM4::REC_PHZD: break;
|
||||||
|
case ESM4::REC_PROJ: break;
|
||||||
|
case ESM4::REC_PWAT: return readTypedRecord<ESM4::PlaceableWater>(params, reader);
|
||||||
|
case ESM4::REC_QUST: return readTypedRecord<ESM4::Quest>(params, reader);
|
||||||
|
case ESM4::REC_RACE: return readTypedRecord<ESM4::Race>(params, reader);
|
||||||
|
case ESM4::REC_REFR: return readTypedRecord<ESM4::Reference>(params, reader);
|
||||||
|
case ESM4::REC_REGN: return readTypedRecord<ESM4::Region>(params, reader);
|
||||||
|
case ESM4::REC_RELA: break;
|
||||||
|
case ESM4::REC_REVB: break;
|
||||||
|
case ESM4::REC_RFCT: break;
|
||||||
|
case ESM4::REC_ROAD: return readTypedRecord<ESM4::Road>(params, reader);
|
||||||
|
case ESM4::REC_SBSP: return readTypedRecord<ESM4::SubSpace>(params, reader);
|
||||||
|
case ESM4::REC_SCEN: break;
|
||||||
|
case ESM4::REC_SCOL: return readTypedRecord<ESM4::StaticCollection>(params, reader);
|
||||||
|
case ESM4::REC_SCPT: return readTypedRecord<ESM4::Script>(params, reader);
|
||||||
|
case ESM4::REC_SCRL: return readTypedRecord<ESM4::Scroll>(params, reader);
|
||||||
|
case ESM4::REC_SGST: return readTypedRecord<ESM4::SigilStone>(params, reader);
|
||||||
|
case ESM4::REC_SHOU: break;
|
||||||
|
case ESM4::REC_SLGM: return readTypedRecord<ESM4::SoulGem>(params, reader);
|
||||||
|
case ESM4::REC_SMBN: break;
|
||||||
|
case ESM4::REC_SMEN: break;
|
||||||
|
case ESM4::REC_SMQN: break;
|
||||||
|
case ESM4::REC_SNCT: break;
|
||||||
|
case ESM4::REC_SNDR: return readTypedRecord<ESM4::SoundReference>(params, reader);
|
||||||
|
case ESM4::REC_SOPM: break;
|
||||||
|
case ESM4::REC_SOUN: return readTypedRecord<ESM4::Sound>(params, reader);
|
||||||
|
case ESM4::REC_SPEL: break;
|
||||||
|
case ESM4::REC_SPGD: break;
|
||||||
|
case ESM4::REC_STAT: return readTypedRecord<ESM4::Static>(params, reader);
|
||||||
|
case ESM4::REC_TACT: return readTypedRecord<ESM4::TalkingActivator>(params, reader);
|
||||||
|
case ESM4::REC_TERM: return readTypedRecord<ESM4::Terminal>(params, reader);
|
||||||
|
case ESM4::REC_TES4: return readTypedRecord<ESM4::Header>(params, reader);
|
||||||
|
case ESM4::REC_TREE: return readTypedRecord<ESM4::Tree>(params, reader);
|
||||||
|
case ESM4::REC_TXST: return readTypedRecord<ESM4::TextureSet>(params, reader);
|
||||||
|
case ESM4::REC_VTYP: break;
|
||||||
|
case ESM4::REC_WATR: break;
|
||||||
|
case ESM4::REC_WEAP: return readTypedRecord<ESM4::Weapon>(params, reader);
|
||||||
|
case ESM4::REC_WOOP: break;
|
||||||
|
case ESM4::REC_WRLD: return readTypedRecord<ESM4::World>(params, reader);
|
||||||
|
case ESM4::REC_WTHR: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.mQuite)
|
||||||
|
std::cout << "\n Unsupported record: " << ESM::NAME(reader.hdr().record.typeId).toStringView() << '\n';
|
||||||
|
|
||||||
|
reader.skipRecordData();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readItem(const Params& params, ESM4::Reader& reader);
|
||||||
|
|
||||||
|
bool readGroup(const Params& params, ESM4::Reader& reader)
|
||||||
|
{
|
||||||
|
const ESM4::RecordHeader& header = reader.hdr();
|
||||||
|
|
||||||
|
if (!params.mQuite)
|
||||||
|
std::cout << "\nGroup: " << toString(static_cast<ESM4::GroupType>(header.group.type))
|
||||||
|
<< " " << ESM::NAME(header.group.typeId).toStringView() << '\n';
|
||||||
|
|
||||||
|
switch (static_cast<ESM4::GroupType>(header.group.type))
|
||||||
|
{
|
||||||
|
case ESM4::Grp_RecordType:
|
||||||
|
case ESM4::Grp_InteriorCell:
|
||||||
|
case ESM4::Grp_InteriorSubCell:
|
||||||
|
case ESM4::Grp_ExteriorCell:
|
||||||
|
case ESM4::Grp_ExteriorSubCell:
|
||||||
|
reader.enterGroup();
|
||||||
|
return readItem(params, reader);
|
||||||
|
case ESM4::Grp_WorldChild:
|
||||||
|
case ESM4::Grp_CellChild:
|
||||||
|
case ESM4::Grp_TopicChild:
|
||||||
|
case ESM4::Grp_CellPersistentChild:
|
||||||
|
case ESM4::Grp_CellTemporaryChild:
|
||||||
|
case ESM4::Grp_CellVisibleDistChild:
|
||||||
|
reader.adjustGRUPFormId();
|
||||||
|
reader.enterGroup();
|
||||||
|
if (!reader.hasMoreRecs())
|
||||||
|
return false;
|
||||||
|
return readItem(params, reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.skipGroup();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readItem(const Params& params, ESM4::Reader& reader)
|
||||||
|
{
|
||||||
|
if (!reader.getRecordHeader() || !reader.hasMoreRecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const ESM4::RecordHeader& header = reader.hdr();
|
||||||
|
|
||||||
|
if (header.record.typeId == ESM4::REC_GRUP)
|
||||||
|
return readGroup(params, reader);
|
||||||
|
|
||||||
|
readRecord(params, reader);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int loadTes4(const Arguments& info, std::unique_ptr<std::ifstream>&& stream)
|
||||||
|
{
|
||||||
|
std::cout << "Loading TES4 file: " << info.filename << '\n';
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const ToUTF8::StatelessUtf8Encoder encoder(ToUTF8::calculateEncoding(info.encoding));
|
||||||
|
ESM4::Reader reader(std::move(stream), info.filename);
|
||||||
|
reader.setEncoder(&encoder);
|
||||||
|
const Params params(info);
|
||||||
|
|
||||||
|
if (!params.mQuite)
|
||||||
|
{
|
||||||
|
std::cout << "Author: " << reader.getAuthor() << '\n'
|
||||||
|
<< "Description: " << reader.getDesc() << '\n'
|
||||||
|
<< "File format version: " << reader.esmVersion() << '\n';
|
||||||
|
|
||||||
|
if (const std::vector<ESM::MasterData>& masterData = reader.getGameFiles(); !masterData.empty())
|
||||||
|
{
|
||||||
|
std::cout << "Masters:" << '\n';
|
||||||
|
for (const auto& master : masterData)
|
||||||
|
std::cout << " " << master.name << ", " << master.size << " bytes\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (reader.hasMoreRecs())
|
||||||
|
{
|
||||||
|
reader.exitGroupCheck();
|
||||||
|
if (!readItem(params, reader))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cout << "\nERROR:\n\n " << e.what() << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef OPENMW_ESMTOOL_TES4_H
|
||||||
|
#define OPENMW_ESMTOOL_TES4_H
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace EsmTool
|
||||||
|
{
|
||||||
|
struct Arguments;
|
||||||
|
|
||||||
|
int loadTes4(const Arguments& info, std::unique_ptr<std::ifstream>&& stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,60 @@
|
|||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <apps/openmw/mwsound/alext.h>
|
||||||
|
|
||||||
|
#include "openalutil.hpp"
|
||||||
|
|
||||||
|
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||||
|
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<std::string> Launcher::enumerateOpenALDevices()
|
||||||
|
{
|
||||||
|
std::vector<std::string> devlist;
|
||||||
|
const ALCchar *devnames;
|
||||||
|
|
||||||
|
if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"))
|
||||||
|
{
|
||||||
|
devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(devnames && *devnames)
|
||||||
|
{
|
||||||
|
devlist.emplace_back(devnames);
|
||||||
|
devnames += strlen(devnames)+1;
|
||||||
|
}
|
||||||
|
return devlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Launcher::enumerateOpenALDevicesHrtf()
|
||||||
|
{
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
|
||||||
|
ALCdevice *device = alcOpenDevice(nullptr);
|
||||||
|
if(device)
|
||||||
|
{
|
||||||
|
if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
|
||||||
|
{
|
||||||
|
LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
|
||||||
|
void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT");
|
||||||
|
memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));
|
||||||
|
ALCint num_hrtf;
|
||||||
|
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
|
||||||
|
ret.reserve(num_hrtf);
|
||||||
|
for(ALCint i = 0;i < num_hrtf;++i)
|
||||||
|
{
|
||||||
|
const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
|
||||||
|
if(strcmp(entry, "") == 0)
|
||||||
|
break;
|
||||||
|
ret.emplace_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alcCloseDevice(device);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Launcher
|
||||||
|
{
|
||||||
|
std::vector<std::string> enumerateOpenALDevices();
|
||||||
|
std::vector<std::string> enumerateOpenALDevicesHrtf();
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue