mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-07 03:45:33 +00:00
Merge branch 'master' into 'LTO-timing'
# Conflicts: # CI/before_script.msvc.sh
This commit is contained in:
commit
a8d4ba535f
2489 changed files with 175296 additions and 69459 deletions
8
.clang-tidy
Normal file
8
.clang-tidy
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
Checks: "-*,
|
||||
boost-*,
|
||||
portability-*,
|
||||
"
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
FormatStyle: none
|
87
.github/workflows/cmake.yml
vendored
Normal file
87
.github/workflows/cmake.yml
vendored
Normal file
|
@ -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
|
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -5,7 +5,7 @@ CMakeCache.txt
|
|||
cmake_install.cmake
|
||||
Makefile
|
||||
makefile
|
||||
build*
|
||||
build*/
|
||||
prebuilt
|
||||
|
||||
##windows build process
|
||||
|
@ -34,7 +34,6 @@ CMakeLists.txt.user*
|
|||
.vscode
|
||||
|
||||
## resources
|
||||
data
|
||||
resources
|
||||
/*.cfg
|
||||
/*.desktop
|
||||
|
@ -72,6 +71,7 @@ components/ui_contentselector.h
|
|||
docs/mainpage.hpp
|
||||
docs/Doxyfile
|
||||
docs/DoxyfilePages
|
||||
docs/source/reference/lua-scripting/generated_html
|
||||
moc_*.cxx
|
||||
*.cxx_parameters
|
||||
*qrc_launcher.cxx
|
||||
|
@ -84,13 +84,3 @@ moc_*.cxx
|
|||
*.[ao]
|
||||
*.so
|
||||
venv/
|
||||
|
||||
## recastnavigation unused files
|
||||
extern/recastnavigation/.travis.yml
|
||||
extern/recastnavigation/CONTRIBUTING.md
|
||||
extern/recastnavigation/Docs/
|
||||
extern/recastnavigation/Doxyfile
|
||||
extern/recastnavigation/README.md
|
||||
extern/recastnavigation/RecastDemo/
|
||||
extern/recastnavigation/Tests/
|
||||
extern/recastnavigation/appveyor.yml
|
||||
|
|
525
.gitlab-ci.yml
525
.gitlab-ci.yml
|
@ -1,130 +1,402 @@
|
|||
default:
|
||||
interruptible: true
|
||||
|
||||
# Note: We set `needs` on each job to control the job DAG.
|
||||
# See https://docs.gitlab.com/ee/ci/yaml/#needs
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
.Debian:
|
||||
# https://blog.nimbleways.com/let-s-make-faster-gitlab-ci-cd-pipelines/
|
||||
variables:
|
||||
FF_USE_NEW_SHELL_ESCAPE: "true"
|
||||
FF_USE_FASTZIP: "true"
|
||||
# These can be specified per job or per pipeline
|
||||
ARTIFACT_COMPRESSION_LEVEL: "fast"
|
||||
CACHE_COMPRESSION_LEVEL: "fast"
|
||||
|
||||
.Ubuntu_Image:
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
image: debian:bullseye
|
||||
image: ubuntu:focal
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
|
||||
.Ubuntu:
|
||||
extends: .Ubuntu_Image
|
||||
cache:
|
||||
paths:
|
||||
- apt-cache/
|
||||
- ccache/
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- apt-get update -yq
|
||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential 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 libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev liblz4-dev ccache git clang
|
||||
stage: build
|
||||
script:
|
||||
- df -h
|
||||
- export CCACHE_BASEDIR="`pwd`"
|
||||
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
||||
- ccache -z -M "${CCACHE_SIZE}"
|
||||
- CI/before_script.linux.sh
|
||||
- cd build
|
||||
- cmake --build . -- -j $(nproc)
|
||||
- df -h
|
||||
- du -sh .
|
||||
- find . | grep '\.o$' | xargs rm -f
|
||||
- df -h
|
||||
- du -sh .
|
||||
- cmake --install .
|
||||
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite; fi
|
||||
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite --gtest_output="xml:tests.xml"; fi
|
||||
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi
|
||||
- ccache -s
|
||||
- df -h
|
||||
- if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then gcovr --xml-pretty --exclude-unreachable-branches --print-summary --root "${CI_PROJECT_DIR}" -j $(nproc) -o ../coverage.xml; fi
|
||||
- ls | grep -v -e '^extern$' -e '^install$' -e '^tests.xml$' | xargs -I '{}' rm -rf './{}'
|
||||
- cd ..
|
||||
- df -h
|
||||
- du -sh build/
|
||||
- du -sh build/install/
|
||||
- du -sh apt-cache/
|
||||
- du -sh ccache/
|
||||
artifacts:
|
||||
paths:
|
||||
- build/install/
|
||||
|
||||
Debian_GCC:
|
||||
extends: .Debian
|
||||
cache:
|
||||
key: Debian_GCC.v2
|
||||
variables:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CCACHE_SIZE: 3G
|
||||
|
||||
Debian_GCC_tests:
|
||||
extends: .Debian
|
||||
cache:
|
||||
key: Debian_GCC_tests.v2
|
||||
variables:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
|
||||
Debian_Clang:
|
||||
extends: .Debian
|
||||
cache:
|
||||
key: Debian_Clang.v2
|
||||
Coverity:
|
||||
extends: .Ubuntu_Image
|
||||
stage: build
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic
|
||||
- curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
|
||||
- tar xfz /tmp/cov-analysis-linux64.tgz
|
||||
script:
|
||||
- CI/before_script.linux.sh
|
||||
# Remove the specific targets and build everything once we can do it under 3h
|
||||
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) openmw esmtool bsatool niftest openmw-wizard openmw-launcher openmw-iniimporter openmw-essimporter openmw-navmeshtool openmw-cs
|
||||
after_script:
|
||||
- tar cfz cov-int.tar.gz cov-int
|
||||
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
|
||||
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
|
||||
--form file=@cov-int.tar.gz --form version="$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA"
|
||||
--form description="CI_COMMIT_SHORT_SHA / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
|
||||
variables:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CXXFLAGS: -O0
|
||||
artifacts:
|
||||
paths:
|
||||
- /builds/OpenMW/openmw/cov-int/build-log.txt
|
||||
|
||||
Ubuntu_GCC:
|
||||
extends: .Ubuntu
|
||||
cache:
|
||||
key: Ubuntu_GCC.v3
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
||||
variables:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CCACHE_SIZE: 4G
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 2h
|
||||
|
||||
.Ubuntu_GCC_tests:
|
||||
extends: Ubuntu_GCC
|
||||
cache:
|
||||
key: Ubuntu_GCC_tests.v3
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
.Ubuntu_GCC_tests_Debug:
|
||||
extends: Ubuntu_GCC
|
||||
cache:
|
||||
key: Ubuntu_GCC_tests_Debug.v2
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_CXX_FLAGS_DEBUG: -g -O0 -D_GLIBCXX_ASSERTIONS
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
.Ubuntu_GCC_tests_asan:
|
||||
extends: Ubuntu_GCC
|
||||
cache:
|
||||
key: Ubuntu_GCC_asan.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_CXX_FLAGS_DEBUG: -g -O1 -fno-omit-frame-pointer -fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak
|
||||
CMAKE_EXE_LINKER_FLAGS: -fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak
|
||||
ASAN_OPTIONS: halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
.Ubuntu_GCC_tests_ubsan:
|
||||
extends: Ubuntu_GCC
|
||||
cache:
|
||||
key: Ubuntu_GCC_ubsan.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_CXX_FLAGS_DEBUG: -g -O0 -fsanitize=undefined
|
||||
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
.Ubuntu_GCC_tests_tsan:
|
||||
extends: Ubuntu_GCC
|
||||
cache:
|
||||
key: Ubuntu_GCC_tsan.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_CXX_FLAGS_DEBUG: -g -O2 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=thread -fPIE
|
||||
CMAKE_EXE_LINKER_FLAGS: -pthread -pie -fsanitize=thread
|
||||
TSAN_OPTIONS: second_deadlock_stack=1:halt_on_error=1
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
.Ubuntu_GCC_tests_coverage:
|
||||
extends: .Ubuntu_GCC_tests_Debug
|
||||
cache:
|
||||
key: Ubuntu_GCC_tests_coverage.v1
|
||||
variables:
|
||||
BUILD_WITH_CODE_COVERAGE: 1
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic openmw-coverage
|
||||
coverage: /^\s*lines:\s*\d+.\d+\%/
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
coverage_report:
|
||||
coverage_format: cobertura
|
||||
path: coverage.xml
|
||||
junit: build/tests.xml
|
||||
|
||||
.Ubuntu_Static_Deps:
|
||||
extends: Ubuntu_Clang
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
changes:
|
||||
- "**/CMakeLists.txt"
|
||||
- "cmake/**/*"
|
||||
- "CI/**/*"
|
||||
- ".gitlab-ci.yml"
|
||||
cache:
|
||||
key: Ubuntu_Static_Deps.V1
|
||||
paths:
|
||||
- apt-cache/
|
||||
- ccache/
|
||||
- build/extern/fetched/
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-static
|
||||
variables:
|
||||
CI_OPENMW_USE_STATIC_DEPS: 1
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CXXFLAGS: -O0
|
||||
timeout: 3h
|
||||
|
||||
.Ubuntu_Static_Deps_tests:
|
||||
extends: .Ubuntu_Static_Deps
|
||||
cache:
|
||||
key: Ubuntu_Static_Deps_tests.V1
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CXXFLAGS: -O0
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
Ubuntu_Clang:
|
||||
extends: .Ubuntu
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh clang clang-tidy openmw-deps openmw-deps-dynamic
|
||||
cache:
|
||||
key: Ubuntu_Clang.v2
|
||||
variables:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CI_CLANG_TIDY: 1
|
||||
CCACHE_SIZE: 2G
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 2h
|
||||
|
||||
Debian_Clang_tests:
|
||||
extends: .Debian
|
||||
.Ubuntu_Clang_tests:
|
||||
extends: Ubuntu_Clang
|
||||
cache:
|
||||
key: Debian_Clang_tests.v2
|
||||
key: Ubuntu_Clang_tests.v3
|
||||
variables:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
MacOS:
|
||||
Ubuntu_Clang_tests_Debug:
|
||||
extends: Ubuntu_Clang
|
||||
cache:
|
||||
key: Ubuntu_Clang_tests_Debug.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
artifacts:
|
||||
paths: []
|
||||
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
|
||||
when: always
|
||||
reports:
|
||||
junit: build/tests.xml
|
||||
|
||||
Ubuntu_Clang_integration_tests:
|
||||
extends: .Ubuntu_Image
|
||||
stage: test
|
||||
needs:
|
||||
- Ubuntu_Clang
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
cache:
|
||||
key: Ubuntu_Clang_integration_tests.v1
|
||||
paths:
|
||||
- .cache/pip
|
||||
- apt-cache/
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh openmw-integration-tests
|
||||
- pip3 install --user numpy matplotlib termtables click
|
||||
script:
|
||||
- CI/run_integration_tests.sh
|
||||
|
||||
.MacOS:
|
||||
image: macos-11-xcode-12
|
||||
tags:
|
||||
- macos
|
||||
- shared-macos-amd64
|
||||
stage: build
|
||||
only:
|
||||
variables:
|
||||
- $CI_PROJECT_ID == "7107382"
|
||||
cache:
|
||||
paths:
|
||||
- ccache/
|
||||
script:
|
||||
- rm -fr build/* # remove anything in the build directory
|
||||
- rm -fr build # remove the build directory
|
||||
- CI/before_install.osx.sh
|
||||
- export CCACHE_BASEDIR="$(pwd)"
|
||||
- export CCACHE_DIR="$(pwd)/ccache"
|
||||
- mkdir -pv "${CCACHE_DIR}"
|
||||
- ccache -z -M "${CCACHE_SIZE}"
|
||||
- CI/before_script.osx.sh
|
||||
- cd build; make -j2 package
|
||||
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done
|
||||
- cd build; make -j $(sysctl -n hw.logicalcpu) package
|
||||
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME##*/}_${CI_JOB_ID}.dmg"; done
|
||||
- ccache -s
|
||||
artifacts:
|
||||
paths:
|
||||
- build/OpenMW-*.dmg
|
||||
- "build/**/*.log"
|
||||
|
||||
variables: &engine-targets
|
||||
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
|
||||
|
||||
variables: &cs-targets
|
||||
targets: "openmw-cs,bsatool,esmtool,niftest"
|
||||
macOS12_Xcode13:
|
||||
extends: .MacOS
|
||||
image: macos-12-xcode-13
|
||||
cache:
|
||||
key: macOS12_Xcode13.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 3G
|
||||
|
||||
.Windows_Ninja_Base:
|
||||
tags:
|
||||
- windows
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
before_script:
|
||||
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
|
||||
- choco source disable -n=chocolatey
|
||||
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
|
||||
- choco install 7zip -y
|
||||
- choco install ccache -y
|
||||
- choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y
|
||||
- choco install vswhere -y
|
||||
- choco install ninja -y
|
||||
- choco install python -y
|
||||
- refreshenv
|
||||
- |
|
||||
function Make-SafeFileName {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String]
|
||||
$FileName
|
||||
)
|
||||
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object {
|
||||
$FileName = $FileName.Replace($_, '_')
|
||||
}
|
||||
return $FileName
|
||||
}
|
||||
stage: build
|
||||
script:
|
||||
- $time = (Get-Date -Format "HH:mm:ss")
|
||||
- echo ${time}
|
||||
- echo "started by ${GITLAB_USER_NAME}"
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N
|
||||
- $env:CCACHE_BASEDIR = Get-Location
|
||||
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
||||
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N -b -t -C $multiview -E
|
||||
- cd MSVC2019_64_Ninja
|
||||
- .\ActivateMSVC.ps1
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
- cmake --build . --config $config
|
||||
- ccache --show-stats
|
||||
- cd $config
|
||||
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
|
||||
- Get-ChildItem -Recurse *.ilk | Remove-Item
|
||||
- |
|
||||
if (Get-ChildItem -Recurse *.pdb) {
|
||||
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
|
||||
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
|
||||
Get-ChildItem -Recurse *.pdb | Remove-Item
|
||||
}
|
||||
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
|
||||
- 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" '*'
|
||||
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
|
||||
after_script:
|
||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||
cache:
|
||||
key: ninja-v2
|
||||
key: ninja-v3
|
||||
paths:
|
||||
- ccache
|
||||
- deps
|
||||
- MSVC2019_64_Ninja/deps/Qt
|
||||
artifacts:
|
||||
|
@ -140,81 +412,92 @@ variables: &cs-targets
|
|||
- MSVC2019_64_Ninja/*/*/*/*/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*/*/*/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*/*/*/*/*/*.log
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 2h
|
||||
|
||||
Windows_Ninja_Engine_Release:
|
||||
.Windows_Ninja_Release:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Release"
|
||||
|
||||
Windows_Ninja_Engine_Debug:
|
||||
.Windows_Ninja_Release_MultiView:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_Ninja_Engine_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "RelWithDebInfo"
|
||||
|
||||
Windows_Ninja_CS_Release:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
multiview: "-M"
|
||||
config: "Release"
|
||||
|
||||
Windows_Ninja_CS_Debug:
|
||||
.Windows_Ninja_Debug:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_Ninja_CS_RelWithDebInfo:
|
||||
.Windows_Ninja_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "RelWithDebInfo"
|
||||
# Gitlab can't successfully execute following binaries due to unknown reason
|
||||
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
|
||||
|
||||
.Windows_MSBuild_Base:
|
||||
tags:
|
||||
- windows
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
before_script:
|
||||
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
|
||||
- choco source disable -n=chocolatey
|
||||
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
|
||||
- choco install 7zip -y
|
||||
- choco install ccache -y
|
||||
- choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y
|
||||
- choco install vswhere -y
|
||||
- choco install python -y
|
||||
- refreshenv
|
||||
- |
|
||||
function Make-SafeFileName {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String]
|
||||
$FileName
|
||||
)
|
||||
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object {
|
||||
$FileName = $FileName.Replace($_, '_')
|
||||
}
|
||||
return $FileName
|
||||
}
|
||||
stage: build
|
||||
script:
|
||||
- $time = (Get-Date -Format "HH:mm:ss")
|
||||
- echo ${time}
|
||||
- echo "started by ${GITLAB_USER_NAME}"
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V
|
||||
- $env:CCACHE_BASEDIR = Get-Location
|
||||
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
||||
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -b -t -C $multiview -E
|
||||
- cd MSVC2019_64
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
- cmake --build . --config $config
|
||||
- ccache --show-stats
|
||||
- cd $config
|
||||
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
|
||||
- Get-ChildItem -Recurse *.ilk | Remove-Item
|
||||
- |
|
||||
if (Get-ChildItem -Recurse *.pdb) {
|
||||
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
|
||||
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
|
||||
Get-ChildItem -Recurse *.pdb | Remove-Item
|
||||
}
|
||||
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
|
||||
- 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" '*'
|
||||
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
|
||||
after_script:
|
||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||
cache:
|
||||
key: msbuild-v2
|
||||
key: msbuild-v3
|
||||
paths:
|
||||
- ccache
|
||||
- deps
|
||||
- MSVC2019_64/deps/Qt
|
||||
artifacts:
|
||||
|
@ -230,68 +513,59 @@ Windows_Ninja_CS_RelWithDebInfo:
|
|||
- MSVC2019_64/*/*/*/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*/*/*/*/*.log
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 2h
|
||||
|
||||
Windows_MSBuild_Engine_Release:
|
||||
.Windows_MSBuild_Release:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Release"
|
||||
# temporarily disabled while this isn't the thing we link on the downloads page
|
||||
# rules:
|
||||
# # run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it
|
||||
# - if: $CI_PIPELINE_SOURCE == "push"
|
||||
# - if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
|
||||
Windows_MSBuild_Engine_Debug:
|
||||
.Windows_MSBuild_Debug:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_MSBuild_Engine_RelWithDebInfo:
|
||||
Windows_MSBuild_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "RelWithDebInfo"
|
||||
# Gitlab can't successfully execute following binaries due to unknown reason
|
||||
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
|
||||
# temporarily enabled while we're linking these on the downloads page
|
||||
rules:
|
||||
# run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
|
||||
Windows_MSBuild_CS_Release:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Release"
|
||||
|
||||
Windows_MSBuild_CS_Debug:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_MSBuild_CS_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "RelWithDebInfo"
|
||||
|
||||
Debian_AndroidNDK_arm64-v8a:
|
||||
Ubuntu_AndroidNDK_arm64-v8a:
|
||||
tags:
|
||||
- linux
|
||||
image: debian:bullseye
|
||||
image: psi29a/android-ndk:focal-ndk22
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
variables:
|
||||
CCACHE_SIZE: 3G
|
||||
cache:
|
||||
key: Debian_AndroidNDK_arm64-v8a.v2
|
||||
key: Ubuntu__Focal_AndroidNDK_r22b_arm64-v8a.v2
|
||||
paths:
|
||||
- apt-cache/
|
||||
- ccache/
|
||||
- build/extern/fetched/
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- echo "deb http://deb.debian.org/debian unstable main contrib" > /etc/apt/sources.list
|
||||
- echo "google-android-ndk-installer google-android-installers/mirror select https://dl.google.com" | debconf-set-selections
|
||||
- apt-get update -yq
|
||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake ccache curl unzip git build-essential google-android-ndk-installer
|
||||
- CI/install_debian_deps.sh gcc
|
||||
stage: build
|
||||
script:
|
||||
- df -h
|
||||
- export CCACHE_BASEDIR="`pwd`"
|
||||
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
||||
- ccache -z -M "${CCACHE_SIZE}"
|
||||
|
@ -299,8 +573,35 @@ Debian_AndroidNDK_arm64-v8a:
|
|||
- CI/before_script.android.sh
|
||||
- cd build
|
||||
- cmake --build . -- -j $(nproc)
|
||||
- cmake --install .
|
||||
# - cmake --install . # no one uses builds anyway, disable until 'no space left' is resolved
|
||||
- ccache -s
|
||||
- df -h
|
||||
- ls | grep -v -e '^extern$' -e '^install$' | xargs -I '{}' rm -rf './{}'
|
||||
- cd ..
|
||||
- df -h
|
||||
- du -sh build/
|
||||
# - du -sh build/install/ # no install dir because it's commented out above
|
||||
- du -sh apt-cache/
|
||||
- du -sh ccache/
|
||||
- du -sh build/extern/fetched/
|
||||
artifacts:
|
||||
paths:
|
||||
- build/install/
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 1h30m
|
||||
|
||||
.FindMissingMergeRequests:
|
||||
image: python:latest
|
||||
stage: build
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
cache:
|
||||
key: FindMissingMergeRequests.v1
|
||||
paths:
|
||||
- .cache/pip
|
||||
before_script:
|
||||
- pip3 install --user requests click discord_webhook
|
||||
script:
|
||||
- scripts/find_missing_merge_requests.py --project_id=$CI_PROJECT_ID --ignored_mrs_path=$CI_PROJECT_DIR/.resubmitted_merge_requests.txt
|
||||
|
|
10
.readthedocs.yaml
Normal file
10
.readthedocs.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
version: 2
|
||||
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
python:
|
||||
version: 3.8
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
8
.resubmitted_merge_requests.txt
Normal file
8
.resubmitted_merge_requests.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
1471
|
||||
1450
|
||||
1420
|
||||
1314
|
||||
1216
|
||||
1172
|
||||
1160
|
||||
1051
|
105
.travis.yml
105
.travis.yml
|
@ -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
|
37
AUTHORS.md
37
AUTHORS.md
|
@ -4,7 +4,7 @@ Contributors
|
|||
The OpenMW project was started in 2008 by Nicolay Korslund.
|
||||
In the course of years many people have contributed to the project.
|
||||
|
||||
If you feel your name is missing from this list, please notify a developer.
|
||||
If you feel your name is missing from this list, please add it to `AUTHORS.md`.
|
||||
|
||||
|
||||
Programmers
|
||||
|
@ -24,11 +24,14 @@ Programmers
|
|||
Alex McKibben
|
||||
alexanderkjall
|
||||
Alexander Nadeau (wareya)
|
||||
Alexander Olofsson (Ace)
|
||||
Alexander Olofsson (Ananace)
|
||||
Alex Rice
|
||||
Alex S (docwest)
|
||||
Alexey Yaryshev (skeevert)
|
||||
Allofich
|
||||
Andreas Stöckel
|
||||
Andrei Kortunov (akortunov)
|
||||
Andrew Appuhamy (andrew-app)
|
||||
AnyOldName3
|
||||
Ardekantur
|
||||
Armin Preiml
|
||||
|
@ -42,6 +45,7 @@ Programmers
|
|||
Austin Salgat (Salgat)
|
||||
Ben Shealy (bentsherman)
|
||||
Berulacks
|
||||
Bo Svensson
|
||||
Britt Mathis (galdor557)
|
||||
Capostrophic
|
||||
Carl Maxwell
|
||||
|
@ -49,6 +53,8 @@ Programmers
|
|||
Cédric Mocquillon
|
||||
Chris Boyce (slothlife)
|
||||
Chris Robinson (KittyCat)
|
||||
Cody Glassman (Wazabear)
|
||||
Coleman Smith (olcoal)
|
||||
Cory F. Cohen (cfcohen)
|
||||
Cris Mihalache (Mirceam)
|
||||
crussell187
|
||||
|
@ -65,16 +71,19 @@ Programmers
|
|||
David Teviotdale (dteviot)
|
||||
Diggory Hardy
|
||||
Dmitry Marakasov (AMDmi3)
|
||||
Duncan Frost (duncans_pumpkin)
|
||||
Edmondo Tommasina (edmondo)
|
||||
Eduard Cot (trombonecot)
|
||||
Eli2
|
||||
Emanuel Guével (potatoesmaster)
|
||||
Eris Caffee (eris)
|
||||
eroen
|
||||
escondida
|
||||
Evgeniy Mineev (sandstranger)
|
||||
Federico Guerra (FedeWar)
|
||||
Fil Krynicki (filkry)
|
||||
Finbar Crago (finbar-crago)
|
||||
Florent Teppe (Tetramir)
|
||||
Florian Weber (Florianjw)
|
||||
Frédéric Chardon (fr3dz10)
|
||||
Gaëtan Dezeiraud (Brouilles)
|
||||
|
@ -86,16 +95,20 @@ Programmers
|
|||
Haoda Wang (h313)
|
||||
hristoast
|
||||
Internecine
|
||||
Ivan Beloborodov (myrix)
|
||||
Jackerty
|
||||
Jacob Essex (Yacoby)
|
||||
Jacob Turnbull (Tankinfrank)
|
||||
Jake Westrip (16bitint)
|
||||
James Carty (MrTopCat)
|
||||
James Moore (moore.work)
|
||||
James Stephens (james-h-stephens)
|
||||
Jan-Peter Nilsson (peppe)
|
||||
Jan Borsodi (am0s)
|
||||
JanuarySnow
|
||||
Jason Hooks (jhooks)
|
||||
jeaye
|
||||
jefetienne
|
||||
Jeffrey Haines (Jyby)
|
||||
Jengerer
|
||||
Jiří Kuneš (kunesj)
|
||||
|
@ -104,6 +117,7 @@ Programmers
|
|||
John Blomberg (fstp)
|
||||
Jordan Ayers
|
||||
Jordan Milne
|
||||
Josquin Frei
|
||||
Josua Grawitter
|
||||
Jules Blok (Armada651)
|
||||
julianko
|
||||
|
@ -114,6 +128,7 @@ Programmers
|
|||
Kurnevsky Evgeny (kurnevsky)
|
||||
Lars Söderberg (Lazaroth)
|
||||
lazydev
|
||||
Léo Peltier
|
||||
Leon Krieg (lkrieg)
|
||||
Leon Saunders (emoose)
|
||||
logzero
|
||||
|
@ -121,7 +136,6 @@ Programmers
|
|||
Lordrea
|
||||
Łukasz Gołębiewski (lukago)
|
||||
Lukasz Gromanowski (lgro)
|
||||
Manuel Edelmann (vorenon)
|
||||
Marc Bouvier (CramitDeFrog)
|
||||
Marcin Hulist (Gohan)
|
||||
Mark Siewert (mark76)
|
||||
|
@ -130,6 +144,7 @@ Programmers
|
|||
Martin Otto (MAtahualpa)
|
||||
Mateusz Kołaczek (PL_kolek)
|
||||
Mateusz Malisz (malice)
|
||||
Max Henzerling (SaintMercury)
|
||||
megaton
|
||||
Michael Hogan (Xethik)
|
||||
Michael Mc Donnell
|
||||
|
@ -148,9 +163,12 @@ Programmers
|
|||
Nathan Jeffords (blunted2night)
|
||||
NeveHanter
|
||||
Nialsy
|
||||
Nick Crawford (nighthawk469)
|
||||
Nikolay Kasyanov (corristo)
|
||||
Noah Gooder
|
||||
nobrakal
|
||||
Nolan Poe (nopoe)
|
||||
Nurivan Gomez (Nuri-G)
|
||||
Oleg Chkan (mrcheko)
|
||||
Paul Cercueil (pcercuei)
|
||||
Paul McElroy (Greendogo)
|
||||
|
@ -165,6 +183,7 @@ Programmers
|
|||
PlutonicOverkill
|
||||
Radu-Marius Popovici (rpopovici)
|
||||
Rafael Moura (dhustkoder)
|
||||
Randy Davin (Kindi)
|
||||
rdimesio
|
||||
rexelion
|
||||
riothamus
|
||||
|
@ -182,6 +201,7 @@ Programmers
|
|||
sergoz
|
||||
ShadowRadiance
|
||||
Siimacore
|
||||
Simon Meulenbeek (simonmb)
|
||||
sir_herrbatka
|
||||
smbas
|
||||
Sophie Kirschner (pineapplemachine)
|
||||
|
@ -195,21 +215,26 @@ Programmers
|
|||
Sylvain Thesnieres (Garvek)
|
||||
t6
|
||||
terrorfisch
|
||||
Tess (tescoShoppah)
|
||||
thegriglat
|
||||
Thomas Luppi (Digmaster)
|
||||
tlmullis
|
||||
tri4ng1e
|
||||
Thoronador
|
||||
Tobias Tribble (zackogenic)
|
||||
Tom Lowe (Vulpen)
|
||||
Tom Mason (wheybags)
|
||||
Torben Leif Carrington (TorbenC)
|
||||
unelsson
|
||||
uramer
|
||||
viadanna
|
||||
Vidi_Aquam
|
||||
Vincent Heuken
|
||||
Vladimir Panteleev (CyberShadow)
|
||||
vocollapse
|
||||
Wang Ryu (bzzt)
|
||||
Will Herrmann (Thunderforge)
|
||||
vocollapse
|
||||
Wolfgang Lieff
|
||||
xyzz
|
||||
Yohaulticetl
|
||||
Yuri Krupenin
|
||||
|
@ -228,11 +253,13 @@ Documentation
|
|||
Joakim Berg (lysol90)
|
||||
Ryan Tucker (Ravenwing)
|
||||
sir_herrbatka
|
||||
David Nagy (zuzaman)
|
||||
|
||||
Packagers
|
||||
---------
|
||||
|
||||
Alexander Olofsson (Ace) - Windows
|
||||
Alexander Olofsson (Ananace) - Windows and Flatpak
|
||||
Alexey Sokolov (DarthGandalf) - Gentoo Linux
|
||||
Bret Curtis (psi29a) - Debian and Ubuntu Linux
|
||||
Edmondo Tommasina (edmondo) - Gentoo Linux
|
||||
Julian Ospald (hasufell) - Gentoo Linux
|
||||
|
|
306
CHANGELOG.md
306
CHANGELOG.md
|
@ -1,50 +1,262 @@
|
|||
0.48.0
|
||||
------
|
||||
|
||||
Bug #1751: Birthsign abilities increase modified attribute values instead of base ones
|
||||
Bug #1930: Followers are still fighting if a target stops combat with a leader
|
||||
Bug #2036: SetStat and ModStat instructions aren't implemented the same way as in Morrowind
|
||||
Bug #3246: ESSImporter: Most NPCs are dead on save load
|
||||
Bug #3488: AI combat aiming is too slow
|
||||
Bug #3514: Editing a reference's position after loading an esp file makes the reference disappear
|
||||
Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions)
|
||||
Bug #3792: 1 frame late magicka recalc breaks early scripted magicka reactions to Intelligence change
|
||||
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
|
||||
Bug #3855: AI sometimes spams defensive spells
|
||||
Bug #3867: All followers attack player when one follower enters combat with player
|
||||
Bug #3905: Great House Dagoth issues
|
||||
Bug #4203: Resurrecting an actor doesn't close the loot GUI
|
||||
Bug #4376: Moved actors don't respawn in their original cells
|
||||
Bug #4389: NPC's lips do not move if his head model has the NiBSAnimationNode root node
|
||||
Bug #4602: Robert's Bodies: crash inside createInstance()
|
||||
Bug #4700: Editor: Incorrect command implementation
|
||||
Bug #4744: Invisible particles aren't always processed
|
||||
Bug #4949: Incorrect particle lighting
|
||||
Bug #5054: Non-biped creatures don't use spellcast equip/unequip animations
|
||||
Bug #5088: Sky abruptly changes direction during certain weather transitions
|
||||
Bug #5100: Persuasion doesn't always clamp the resulting disposition
|
||||
Bug #5120: Scripted object spawning updates physics system
|
||||
Bug #5192: Actor turn rate is too slow
|
||||
Bug #5207: Loose summons can be present in scene
|
||||
Bug #5279: Ingame console stops auto-scrolling after clicking output
|
||||
Bug #5318: Aiescort behaves differently from vanilla
|
||||
Bug #5371: 'Dead' slaughterfish added by mod are animated/alive
|
||||
Bug #5377: Console does not appear after using menutest in inventory
|
||||
Bug #5379: Wandering NPCs falling through cantons
|
||||
Bug #5394: Windows snapping no longer works
|
||||
Bug #5434: Pinned windows shouldn't cover breath progress bar
|
||||
Bug #5453: Magic effect VFX are offset for creatures
|
||||
Bug #5483: AutoCalc flag is not used to calculate spells cost
|
||||
Bug #5508: Engine binary links to Qt without using it
|
||||
Bug #5592: Weapon idle animations do not work properly
|
||||
Bug #5596: Effects in constant spells should not be merged
|
||||
Bug #5621: Drained stats cannot be restored
|
||||
Bug #5766: Active grid object paging - disappearing textures
|
||||
Bug #5788: Texture editing parses the selected indexes wrongly
|
||||
Bug #5801: A multi-effect spell with the intervention effects and recall always favors Almsivi intervention
|
||||
Bug #5842: GetDisposition adds temporary disposition change from different actors
|
||||
Bug #5858: Visible modal windows and dropdowns crashing game on exit
|
||||
Bug #5863: GetEffect should return true after the player has teleported
|
||||
Bug #5913: Failed assertion during Ritual of Trees quest
|
||||
Bug #5937: Lights always need to be rotated by 90 degrees
|
||||
Bug #5989: Simple water isn't affected by texture filter settings
|
||||
Bug #6037: Launcher: Morrowind content language cannot be set to English
|
||||
Bug #6051: NaN water height in ESM file is not handled gracefully
|
||||
Bug #6066: Addtopic "return" does not work from within script. No errors thrown
|
||||
Bug #6067: ESP loader fails for certain subrecord orders
|
||||
Bug #6087: Bound items added directly to the inventory disappear if their corresponding spell effect ends
|
||||
Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime
|
||||
Bug #6107: Fatigue is incorrectly recalculated when fortify effect is applied or removed
|
||||
Bug #6115: Showmap overzealous matching
|
||||
Bug #6118: Creature landing sound counts as a footstep
|
||||
Bug #6123: NPC with broken script freezes the game on hello
|
||||
Bug #6129: Player avatar not displayed correctly for large window sizes when GUI scaling active
|
||||
Bug #6131: Item selection in the avatar window not working correctly for large window sizes
|
||||
Bug #6133: Cannot reliably sneak or steal in the sight of the NPCs siding with player
|
||||
Bug #6142: Groundcover plugins change cells flags
|
||||
Bug #6143: Capturing a screenshot renders the engine temporarily unresponsive
|
||||
Bug #6165: Paralyzed player character can pickup items when the inventory is open
|
||||
Bug #6168: Weather particles flicker for a frame at start of storms
|
||||
Bug #6172: Some creatures can't open doors
|
||||
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
|
||||
Bug #6177: Followers of player follower stop following after waiting for a day
|
||||
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
|
||||
Bug #6191: Encumbrance messagebox timer works incorrectly
|
||||
Bug #6197: Infinite Casting Loop
|
||||
Bug #6253: Multiple instances of Reflect stack additively
|
||||
Bug #6255: Reflect is different from vanilla
|
||||
Bug #6256: Crash on exit with enabled shadows and statically linked OpenSceneGraph
|
||||
Bug #6258: Barter menu glitches out when modifying prices
|
||||
Bug #6273: Respawning NPCs rotation is inconsistent
|
||||
Bug #6276: Deleted groundcover instances are not deleted in game
|
||||
Bug #6282: Laura craft doesn't follow the player character
|
||||
Bug #6283: Avis Dorsey follows you after her death
|
||||
Bug #6285: Brush template drawing and terrain selection drawing performance is very bad
|
||||
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
|
||||
Bug #6291: Can't pickup the dead mage's journal from the mysterious hunter mod
|
||||
Bug #6302: Teleporting disabled actor breaks its disabled state
|
||||
Bug #6303: After "go to jail" weapon can be stuck in the ready to attack state
|
||||
Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken
|
||||
Bug #6321: Arrow enchantments should always be applied to the target
|
||||
Bug #6322: Total sold/cost should reset to 0 when there are no items offered
|
||||
Bug #6323: Wyrmhaven: Alboin doesn't follower the player character out of his house
|
||||
Bug #6324: Special Slave Companions: Can't buy the slave companions
|
||||
Bug #6326: Detect Enchantment/Key should detect items in unresolved containers
|
||||
Bug #6327: Blocking roots the character in place
|
||||
Bug #6333: Werewolf stat changes should be implemented as damage/fortifications
|
||||
Bug #6343: Magic projectile speed doesn't take race weight into account
|
||||
Bug #6347: PlaceItem/PlaceItemCell/PlaceAt should work with levelled creatures
|
||||
Bug #6354: SFX abruptly cut off after crossing max distance
|
||||
Bug #6358: Changeweather command does not report an error when entering non-existent region
|
||||
Bug #6363: Some scripts in Morrowland fail to work
|
||||
Bug #6376: Creatures should be able to use torches
|
||||
Bug #6386: Artifacts in water reflection due to imprecise screen-space coordinate computation
|
||||
Bug #6389: Maximum light distance setting doesn't affect water reflections
|
||||
Bug #6395: Translations with longer tab titles may cause tabs to disappear from the options menu
|
||||
Bug #6396: Inputting certain Unicode characters triggers an assertion
|
||||
Bug #6416: Morphs are applied to the wrong target
|
||||
Bug #6417: OpenMW doesn't always use the right node to accumulate movement
|
||||
Bug #6429: Wyrmhaven: Can't add AI packages to player
|
||||
Bug #6433: Items bound to Quick Keys sometimes do not appear until the Quick Key menu is opened
|
||||
Bug #6451: Weapon summoned from Cast When Used item will have the name "None"
|
||||
Bug #6473: Strings from NIF should be parsed only to first null terminator
|
||||
Bug #6493: Unlocking owned but not locked or unlocked containers is considered a crime
|
||||
Bug #6517: Rotations for KeyframeData in NIFs should be optional
|
||||
Bug #6519: Effects tooltips for ingredients work incorrectly
|
||||
Bug #6523: Disintegrate Weapon is resisted by Resist Magicka instead of Sanctuary
|
||||
Bug #6544: Far from world origin objects jitter when camera is still
|
||||
Bug #6559: Weapon condition inconsistency between melee and ranged critical / sneak / KO attacks
|
||||
Bug #6579: OpenMW compilation error when using OSG doubles for BoundingSphere
|
||||
Bug #6606: Quests with multiple IDs cannot always be restarted
|
||||
Bug #6653: With default settings the in-game console doesn't fit into screen
|
||||
Bug #6655: Constant effect absorb attribute causes the game to break
|
||||
Bug #6667: Pressing the Esc key while resting or waiting causes black screen.
|
||||
Bug #6670: Dialogue order is incorrect
|
||||
Bug #6672: Garbage object refs in groundcover plugins like Vurt's grass plugins
|
||||
Bug #6680: object.cpp handles nodetree unsafely, memory access with dangling pointer
|
||||
Bug #6682: HitOnMe doesn't fire as intended
|
||||
Bug #6697: Shaders vertex lighting incorrectly clamped
|
||||
Bug #6711: Log time differs from real time
|
||||
Bug #6717: Broken script causes interpreter stack corruption
|
||||
Bug #6718: Throwable weapons cause arrow enchantment effect to be applied to the whole body
|
||||
Bug #6730: LoopGroup stalls animation after playing :Stop frame until another animation is played
|
||||
Bug #6753: Info records without a DATA subrecords are loaded incorrectly
|
||||
Bug #6794: Light sources are attached to mesh bounds centers instead of mesh origins when AttachLight NiNode is missing
|
||||
Bug #6799: Game crashes if an NPC has no Class attached
|
||||
Bug #6849: ImageButton texture is not scaled properly
|
||||
Feature #890: OpenMW-CS: Column filtering
|
||||
Feature #1465: "Reset" argument for AI functions
|
||||
Feature #2491: Ability to make OpenMW "portable"
|
||||
Feature #2554: OpenMW-CS: Modifying an object in the cell view should trigger the instances table to scroll to the corresponding record
|
||||
Feature #2766: Warn user if their version of Morrowind is not the latest.
|
||||
Feature #2780: A way to see current OpenMW version in the console
|
||||
Feature #2858: Add a tab to the launcher for handling datafolders
|
||||
Feature #3245: Grid and angle snapping for the OpenMW-CS
|
||||
Feature #3616: Allow Zoom levels on the World Map
|
||||
Feature #4067: Post Processing
|
||||
Feature #4297: Implement APPLIED_ONCE flag for magic effects
|
||||
Feature #4414: Handle duration of EXTRA SPELL magic effect
|
||||
Feature #4595: Unique object identifier
|
||||
Feature #4974: Overridable MyGUI layout
|
||||
Feature #4975: Built-in TrueType fonts
|
||||
Feature #5198: Implement "Magic effect expired" event
|
||||
Feature #5454: Clear active spells from actor when he disappears from scene
|
||||
Feature #5489: MCP: Telekinesis fix for activators
|
||||
Feature #5701: Convert osgAnimation::RigGeometry to double-buffered custom version
|
||||
Feature #5737: Handle instance move from one cell to another
|
||||
Feature #5928: Allow Glow in the Dahrk to be disabled
|
||||
Feature #5996: Support Lua scripts in OpenMW
|
||||
Feature #6017: Separate persistent and temporary cell references when saving
|
||||
Feature #6019: Add antialias alpha test to the launcher or enable by default if possible
|
||||
Feature #6032: Reverse-z depth buffer
|
||||
Feature #6128: Soft Particles
|
||||
Feature #6171: In-game log viewer
|
||||
Feature #6189: Navigation mesh disk cache
|
||||
Feature #6199: Support FBO Rendering
|
||||
Feature #6248: Embedded error marker mesh
|
||||
Feature #6249: Alpha testing support for Collada
|
||||
Feature #6251: OpenMW-CS: Set instance movement based on camera zoom
|
||||
Feature #6288: Preserve the "blocked" record flag for referenceable objects.
|
||||
Feature #6360: More realistic raindrop ripples
|
||||
Feature #6380: Treat commas as whitespace in scripts
|
||||
Feature #6419: Don't grey out topics if they can produce another topic reference
|
||||
Feature #6443: Support NiStencilProperty
|
||||
Feature #6496: Handle NCC flag in NIF files
|
||||
Feature #6534: Shader-based object texture blending
|
||||
Feature #6541: Gloss-mapping
|
||||
Feature #6557: Add support for controller gyroscope
|
||||
Feature #6592: Support for NiTriShape particle emitters
|
||||
Feature #6600: Support NiSortAdjustNode
|
||||
Feature #6684: Support NiFltAnimationNode
|
||||
Feature #6699: Support Ignored flag
|
||||
Feature #6700: Support windowed fullscreen
|
||||
Feature #6706: Save the size of the Options window
|
||||
Feature #6721: [OpenMW-CS] Add option to open records in new window
|
||||
Feature #6867: Add a way to localize hardcoded strings in GUI
|
||||
Task #6078: First person should not clear depth buffer
|
||||
Task #6161: Refactor Sky to use shaders and be GLES/GL3 friendly
|
||||
Task #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
|
||||
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
|
||||
Task #6264: Remove the old classes in animation.cpp
|
||||
Task #6553: Simplify interpreter instruction registration
|
||||
Task #6564: Remove predefined data paths `data="?global?data"`, `data=./data`
|
||||
Task #6631: Fix ffmpeg avio API usage causing hangs in ffmpeg version 5
|
||||
Task #6709: Move KeyframeController transformation magic to NifOsg::MatrixTransform
|
||||
Task #6763: gcc 12.1 multiple compiler warnings
|
||||
|
||||
0.47.0
|
||||
------
|
||||
|
||||
Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path
|
||||
Bug #1901: Actors colliding behaviour is different from vanilla
|
||||
Bug #1952: Incorrect particle lighting
|
||||
Bug #2069: Fireflies in Fireflies invade Morrowind look wrong
|
||||
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
||||
Bug #2473: Unable to overstock merchants
|
||||
Bug #2798: Mutable ESM records
|
||||
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
|
||||
Bug #2976: [reopened]: Issues combining settings from the command line and both config files
|
||||
Bug #3137: Walking into a wall prevents jumping
|
||||
Bug #3372: Projectiles and magic bolts go through moving targets
|
||||
Bug #3676: NiParticleColorModifier isn't applied properly
|
||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||
Bug #3789: Crash in visitEffectSources while in battle
|
||||
Bug #3862: Random container contents behave differently than vanilla
|
||||
Bug #3929: Leveled list merchant containers respawn on barter
|
||||
Bug #4021: Attributes and skills are not stored as floats
|
||||
Bug #4039: Multiple followers should have the same following distance
|
||||
Bug #4055: Local scripts don't inherit variables from their base record
|
||||
Bug #4083: Door animation freezes when colliding with actors
|
||||
Bug #4247: Cannot walk up stairs in Ebonheart docks
|
||||
Bug #4357: OpenMW-CS: TopicInfos index sorting and rearranging isn't fully functional
|
||||
Bug #4363: OpenMW-CS: Defect in Clone Function for Dialogue Info records
|
||||
Bug #4447: Actor collision capsule shape allows looking through some walls
|
||||
Bug #4465: Collision shape overlapping causes twitching
|
||||
Bug #4476: Abot Gondoliers: player hangs in air during scenic travel
|
||||
Bug #4568: Too many actors in one spot can push other actors out of bounds
|
||||
Bug #4623: Corprus implementation is incorrect
|
||||
Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level
|
||||
Bug #4764: Data race in osg ParticleSystem
|
||||
Bug #4765: Data race in ChunkManager -> Array::setBinding
|
||||
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
|
||||
Bug #5026: Data races with rain intensity uniform set by sky and used by water
|
||||
Bug #5101: Hostile followers travel with the player
|
||||
Bug #5108: Savegame bloating due to inefficient fog textures format
|
||||
Bug #5165: Active spells should use real time intead of timestamps
|
||||
Bug #5300: NPCs don't switch from torch to shield when starting combat
|
||||
Bug #5358: ForceGreeting always resets the dialogue window completely
|
||||
Bug #5363: Enchantment autocalc not always 0/1
|
||||
Bug #5364: Script fails/stops if trying to startscript an unknown script
|
||||
Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound
|
||||
Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures
|
||||
Bug #5370: Opening an unlocked but trapped door uses the key
|
||||
Bug #5384: openmw-cs: deleting an instance requires reload of scene window to show in editor
|
||||
Bug #5384: OpenMW-CS: Deleting an instance requires reload of scene window to show in editor
|
||||
Bug #5387: Move/MoveWorld don't update the object's cell properly
|
||||
Bug #5391: Races Redone 1.2 bodies don't show on the inventory
|
||||
Bug #5397: NPC greeting does not reset if you leave + reenter area
|
||||
Bug #5400: Editor: Verifier checks race of non-skin bodyparts
|
||||
Bug #5400: OpenMW-CS: Verifier checks race of non-skin bodyparts
|
||||
Bug #5403: Enchantment effect doesn't show on an enemy during death animation
|
||||
Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work
|
||||
Bug #5416: Junk non-node records before the root node are not handled gracefully
|
||||
Bug #5422: The player loses all spells when resurrected
|
||||
Bug #5423: Guar follows actors too closely
|
||||
Bug #5424: Creatures do not headtrack player
|
||||
Bug #5425: Poison effect only appears for one frame
|
||||
Bug #5427: GetDistance unknown ID error is misleading
|
||||
Bug #5431: Physics performance degradation after a specific number of actors on a scene
|
||||
Bug #5435: Enemies can't hurt the player when collision is off
|
||||
Bug #5441: Enemies can't push a player character when in critical strike stance
|
||||
Bug #5451: Magic projectiles don't disappear with the caster
|
||||
Bug #5452: Autowalk is being included in savegames
|
||||
Bug #5469: Local map is reset when re-entering certain cells
|
||||
Bug #5472: Mistify mod causes CTD in 0.46 on Mac
|
||||
Bug #5473: OpenMW-CS: Cell border lines don't update properly on terrain change
|
||||
Bug #5479: NPCs who should be walking around town are standing around without walking
|
||||
Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold
|
||||
Bug #5485: Intimidate doesn't increase disposition on marginal wins
|
||||
|
@ -52,33 +264,98 @@
|
|||
Bug #5499: Faction advance is available when requirements not met
|
||||
Bug #5502: Dead zone for analogue stick movement is too small
|
||||
Bug #5507: Sound volume is not clamped on ingame settings update
|
||||
Bug #5525: Case-insensitive search in the inventory window does not work with non-ASCII characters
|
||||
Bug #5531: Actors flee using current rotation by axis x
|
||||
Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution
|
||||
Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue
|
||||
Bug #5557: Diagonal movement is noticeably slower with analogue stick
|
||||
Bug #5588: Randomly clicking on the journal's right-side page when it's empty shows random topics
|
||||
Bug #5603: Setting constant effect cast style doesn't correct effects view
|
||||
Bug #5604: Only one valid NIF root node is loaded from a single file
|
||||
Bug #5611: Usable items with "0 Uses" should be used only once
|
||||
Bug #5619: Input events are queued during save loading
|
||||
Bug #5622: Can't properly interact with the console when in pause menu
|
||||
Bug #5627: Bookart not shown if it isn't followed by <BR> statement
|
||||
Bug #5633: Damage Spells in effect before god mode is enabled continue to hurt the player character and can kill them
|
||||
Bug #5639: Tooltips cover Messageboxes
|
||||
Bug #5644: Summon effects running on the player during game initialization cause crashes
|
||||
Bug #5656: Sneaking characters block hits while standing
|
||||
Bug #5661: Region sounds don't play at the right interval
|
||||
Bug #5675: OpenMW-CS: FRMR subrecords are saved with the wrong MastIdx
|
||||
Bug #5680: Bull Netches incorrectly aim over the player character's head and always miss
|
||||
Bug #5681: Player character can clip or pass through bridges instead of colliding against them
|
||||
Bug #5687: Bound items covering the same inventory slot expiring at the same time freezes the game
|
||||
Bug #5688: Water shader broken indoors with enable indoor shadows = false
|
||||
Bug #5695: ExplodeSpell for actors doesn't target the ground
|
||||
Bug #5703: OpenMW-CS menu system crashing on XFCE
|
||||
Bug #5706: AI sequences stop looping after the saved game is reloaded
|
||||
Bug #5713: OpenMW-CS: Collada models are corrupted in Qt-based scene view
|
||||
Bug #5731: OpenMW-CS: skirts are invisible on characters
|
||||
Bug #5739: Saving and loading the save a second or two before hitting the ground doesn't count fall damage
|
||||
Bug #5758: Paralyzed actors behavior is inconsistent with vanilla
|
||||
Bug #5762: Movement solver is insufficiently robust
|
||||
Bug #5800: Equipping a CE enchanted ring deselects an already equipped and selected enchanted ring from the spell menu
|
||||
Bug #5807: Video decoding crash on ARM
|
||||
Bug #5821: NPCs from mods getting removed if mod order was changed
|
||||
Bug #5835: OpenMW doesn't accept negative values for NPC's hello, alarm, fight, and flee
|
||||
Bug #5836: OpenMW dialogue/greeting/voice filter doesn't accept negative Ai values for NPC's hello, alarm, fight, and flee
|
||||
Bug #5838: Local map and other menus become blank in some locations while playing Wizards' Islands mod.
|
||||
Bug #5840: GetSoundPlaying "Health Damage" doesn't play when NPC hits target with shield effect ( vanilla engine behavior )
|
||||
Bug #5841: Can't Cast Zero Cost Spells When Magicka is < 0
|
||||
Bug #5869: Guards can initiate arrest dialogue behind locked doors
|
||||
Bug #5871: The console appears if you type the Russian letter "Ё" in the name of the enchantment
|
||||
Bug #5877: Effects appearing with empty icon
|
||||
Bug #5899: Visible modal windows and dropdowns crashing game on exit
|
||||
Bug #5902: NiZBufferProperty is unable to disable the depth test
|
||||
Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs
|
||||
Bug #5912: ImprovedBound mod doesn't work
|
||||
Bug #5914: BM: The Swimmer can't reach destination
|
||||
Bug #5923: Clicking on empty spaces between journal entries might show random topics
|
||||
Bug #5934: AddItem command doesn't accept negative values
|
||||
Bug #5975: NIF controllers from sheath meshes are used
|
||||
Bug #5991: Activate should always be allowed for inventory items
|
||||
Bug #5995: NiUVController doesn't calculate the UV offset properly
|
||||
Bug #6007: Crash when ending cutscene is playing
|
||||
Bug #6016: Greeting interrupts Fargoth's sneak-walk
|
||||
Bug #6022: OpenMW-CS: Terrain selection is not updated when undoing/redoing terrain changes
|
||||
Bug #6023: OpenMW-CS: Clicking on a reference in "Terrain land editing" mode discards corresponding select/edit action
|
||||
Bug #6028: Particle system controller values are incorrectly used
|
||||
Bug #6035: OpenMW-CS: Circle brush in "Terrain land editing" mode sometimes includes vertices outside its radius
|
||||
Bug #6036: OpenMW-CS: Terrain selection at the border of cells omits certain corner vertices
|
||||
Bug #6043: Actor can have torch missing when torch animation is played
|
||||
Bug #6047: Mouse bindings can be triggered during save loading
|
||||
Bug #6136: Game freezes when NPCs try to open doors that are about to be closed
|
||||
Bug #6294: Game crashes with empty pathgrid
|
||||
Feature #390: 3rd person look "over the shoulder"
|
||||
Feature #832: OpenMW-CS: Handle deleted references
|
||||
Feature #1536: Show more information about level on menu
|
||||
Feature #2159: "Graying out" exhausted dialogue topics
|
||||
Feature #2386: Distant Statics in the form of Object Paging
|
||||
Feature #2404: Levelled List can not be placed into a container
|
||||
Feature #2686: Timestamps in openmw.log
|
||||
Feature #2798: Mutable ESM records
|
||||
Feature #3171: OpenMW-CS: Instance drag selection
|
||||
Feature #3983: Wizard: Add link to buy Morrowind
|
||||
Feature #4201: Projectile-projectile collision
|
||||
Feature #4486: Handle crashes on Windows
|
||||
Feature #4894: Consider actors as obstacles for pathfinding
|
||||
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
|
||||
Feature #4917: Do not trigger NavMesh update when RecastMesh update should not change NavMesh
|
||||
Feature #4977: Use the "default icon.tga" when an item's icon is not found
|
||||
Feature #5043: Head Bobbing
|
||||
Feature #5199: OpenMW-CS: Improve scene view colors
|
||||
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
||||
Feature #5362: Show the soul gems' trapped soul in count dialog
|
||||
Feature #5445: Handle NiLines
|
||||
Feature #5456: Basic collada animation support
|
||||
Feature #5457: Realistic diagonal movement
|
||||
Feature #5486: Fixes trainers to choose their training skills based on their base skill points
|
||||
Feature #5500: Prepare enough navmesh tiles before scene loading ends
|
||||
Feature #5511: Add in game option to toggle HRTF support in OpenMW
|
||||
Feature #5519: Code Patch tab in launcher
|
||||
Feature #5524: Resume failed script execution after reload
|
||||
Feature #5525: Search fields tweaks (utf-8)
|
||||
Feature #5545: Option to allow stealing from an unconscious NPC during combat
|
||||
Feature #5551: Do not reboot PC after OpenMW installation on Windows
|
||||
Feature #5563: Run physics update in background thread
|
||||
Feature #5579: MCP SetAngle enhancement
|
||||
Feature #5580: Service refusal filtering
|
||||
|
@ -86,6 +363,17 @@
|
|||
Feature #5642: Ability to attach arrows to actor skeleton instead of bow mesh
|
||||
Feature #5649: Skyrim SE compressed BSA format support
|
||||
Feature #5672: Make stretch menu background configuration more accessible
|
||||
Feature #5692: Improve spell/magic item search to factor in magic effect names
|
||||
Feature #5730: Add graphic herbalism option to the launcher and documents
|
||||
Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.
|
||||
Feature #5813: Instanced groundcover support
|
||||
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
|
||||
Feature #5828: Support more than 8 lights
|
||||
Feature #5910: Fall back to delta time when physics can't keep up
|
||||
Feature #5980: Support Bullet with double precision instead of one with single precision
|
||||
Feature #6024: OpenMW-CS: Selecting terrain in "Terrain land editing" should support "Add to selection" and "Remove from selection" modes
|
||||
Feature #6033: Include pathgrid to navigation mesh
|
||||
Feature #6034: Find path based on area cost depending on NPC stats
|
||||
Task #5480: Drop Qt4 support
|
||||
Task #5520: Improve cell name autocompleter implementation
|
||||
|
||||
|
@ -97,7 +385,7 @@
|
|||
Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin
|
||||
Bug #2679: Unable to map mouse wheel under control settings
|
||||
Bug #2969: Scripted items can stack
|
||||
Bug #2976: Data lines in global openmw.cfg take priority over user openmw.cfg
|
||||
Bug #2976: [reopened in 0.47] Data lines in global openmw.cfg take priority over user openmw.cfg
|
||||
Bug #2987: Editor: some chance and AI data fields can overflow
|
||||
Bug #3006: 'else if' operator breaks script compilation
|
||||
Bug #3109: SetPos/Position handles actors differently
|
||||
|
@ -115,7 +403,6 @@
|
|||
Bug #4009: Launcher does not show data files on the first run after installing
|
||||
Bug #4077: Enchanted items are not recharged if they are not in the player's inventory
|
||||
Bug #4141: PCSkipEquip isn't set to 1 when reading books/scrolls
|
||||
Bug #4202: Open .omwaddon files without needing toopen openmw-cs first
|
||||
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
|
||||
Bug #4262: Rain settings are hardcoded
|
||||
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
|
||||
|
@ -205,7 +492,6 @@
|
|||
Bug #4964: Multiple effect spell projectile sounds play louder than vanilla
|
||||
Bug #4965: Global light attenuation settings setup is lacking
|
||||
Bug #4969: "Miss" sound plays for any actor
|
||||
Bug #4971: OpenMW-CS: Make rotations display as degrees instead of radians
|
||||
Bug #4972: Player is able to use quickkeys while disableplayerfighting is active
|
||||
Bug #4979: AiTravel maximum range depends on "actors processing range" setting
|
||||
Bug #4980: Drowning mechanics is applied for actors indifferently from distance to player
|
||||
|
@ -303,7 +589,6 @@
|
|||
Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error
|
||||
Bug #5352: Light source items' duration is decremented while they aren't visible
|
||||
Feature #1724: Handle AvoidNode
|
||||
Feature #2159: "Graying out" exhausted dialogue topics
|
||||
Feature #2229: Improve pathfinding AI
|
||||
Feature #3025: Analogue gamepad movement controls
|
||||
Feature #3442: Default values for fallbacks from ini file
|
||||
|
@ -316,6 +601,7 @@
|
|||
Feature #4001: Toggle sneak controller shortcut
|
||||
Feature #4068: OpenMW-CS: Add a button to reset key bindings to defaults
|
||||
Feature #4129: Beta Comment to File
|
||||
Feature #4202: Open .omwaddon files without needing to open openmw-cs first
|
||||
Feature #4209: Editor: Faction rank sub-table
|
||||
Feature #4255: Handle broken RepairedOnMe script function
|
||||
Feature #4316: Implement RaiseRank/LowerRank functions properly
|
||||
|
@ -338,6 +624,7 @@
|
|||
Feature #4958: Support eight blood types
|
||||
Feature #4962: Add casting animations for magic items
|
||||
Feature #4968: Scalable UI widget skins
|
||||
Feature #4971: OpenMW-CS: Make rotations display as degrees instead of radians
|
||||
Feature #4994: Persistent pinnable windows hiding
|
||||
Feature #5000: Compressed BSA format support
|
||||
Feature #5005: Editor: Instance window via Scene window
|
||||
|
@ -1814,6 +2101,7 @@
|
|||
Bug #2025: Missing mouse-over text for non affordable items
|
||||
Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall"
|
||||
Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding
|
||||
Bug #3066: Editor doesn't check if IDs and other strings are longer than their hardcoded field length
|
||||
Feature #471: Editor: Special case implementation for top-level window with single sub-window
|
||||
Feature #472: Editor: Sub-Window re-use settings
|
||||
Feature #704: Font colors import from fallback settings
|
||||
|
|
|
@ -18,9 +18,10 @@ Known Issues:
|
|||
|
||||
New Features:
|
||||
- Dialogue to split item stacks now displays the name of the trapped soul for stacks of soul gems (#5362)
|
||||
- Basics of Collada animations are now supported via osgAnimation plugin (#5456)
|
||||
|
||||
New Editor Features:
|
||||
- ?
|
||||
- Instance selection modes are now implemented (centred cube, corner-dragged cube, sphere) with four user-configurable actions (select only, add to selection, remove from selection, invert selection) (#3171)
|
||||
|
||||
Bug Fixes:
|
||||
- NiParticleColorModifier in NIF files is now properly handled which solves issues regarding particle effects, e.g., smoke and fire (#1952, #3676)
|
||||
|
@ -33,7 +34,19 @@ Bug Fixes:
|
|||
- Morrowind legacy madness: Using a key on a trapped door/container now only disarms the trap if the door/container is locked (#5370)
|
||||
|
||||
Editor Bug Fixes:
|
||||
- Deleted and moved objects within a cell are now saved properly (#832)
|
||||
- Disabled record sorting in Topic and Journal Info tables, implemented drag-move for records (#4357)
|
||||
- Topic and Journal Info records can now be cloned with a different parent Topic/Journal Id (#4363)
|
||||
- Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400)
|
||||
- Cell borders are now properly redrawn when undoing/redoing terrain changes (#5473)
|
||||
- Loading mods now keeps the master index (#5675)
|
||||
- Flicker and crashing on XFCE4 fixed (#5703)
|
||||
- Collada models render properly in the Editor (#5713)
|
||||
- Terrain-selection grid is now properly updated when undoing/redoing terrain changes (#6022)
|
||||
- Tool outline and select/edit actions in "Terrain land editing" mode now ignore references (#6023)
|
||||
- Primary-select and secondary-select actions in "Terrain land editing" mode now behave like in "Instance editing" mode (#6024)
|
||||
- Using the circle brush to select terrain in the "Terrain land editing" mode no longer selects vertices outside the circle (#6035)
|
||||
- Vertices at the NW and SE corners of a cell can now also be selected in "Terrain land editing" mode if the adjacent cells aren't loaded yet (#6036)
|
||||
|
||||
Miscellaneous:
|
||||
- Prevent save-game bloating by using an appropriate fog texture format (#5108)
|
||||
|
|
|
@ -30,55 +30,11 @@ command -v unixPathAsWindows >/dev/null 2>&1 || function unixPathAsWindows {
|
|||
fi
|
||||
}
|
||||
|
||||
function windowsSystemPathAsUnix {
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
cygpath -u -p $1
|
||||
else
|
||||
IFS=';' read -r -a paths <<< "$1"
|
||||
declare -a convertedPaths
|
||||
for entry in paths; do
|
||||
convertedPaths+=(windowsPathAsUnix $entry)
|
||||
done
|
||||
convertedPath=printf ":%s" ${convertedPaths[@]}
|
||||
echo ${convertedPath:1}
|
||||
fi
|
||||
}
|
||||
|
||||
# capture CMD environment so we know what's been changed
|
||||
declare -A originalCmdEnv
|
||||
originalIFS="$IFS"
|
||||
IFS=$'\n\r'
|
||||
for pair in $(cmd //c "set"); do
|
||||
IFS='=' read -r -a separatedPair <<< "${pair}"
|
||||
if [ ${#separatedPair[@]} -ne 2 ]; then
|
||||
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
|
||||
continue
|
||||
fi
|
||||
originalCmdEnv["${separatedPair[0]}"]="${separatedPair[1]}"
|
||||
done
|
||||
|
||||
# capture CMD environment in a shell with MSVC activated
|
||||
cmdEnv="$(cmd //c "$(unixPathAsWindows "$(dirname "${BASH_SOURCE[0]}")")\ActivateMSVC.bat" "&&" set)"
|
||||
|
||||
declare -A cmdEnvChanges
|
||||
for pair in $cmdEnv; do
|
||||
if [ -n "$pair" ]; then
|
||||
IFS='=' read -r -a separatedPair <<< "${pair}"
|
||||
if [ ${#separatedPair[@]} -ne 2 ]; then
|
||||
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
|
||||
continue
|
||||
fi
|
||||
key="${separatedPair[0]}"
|
||||
value="${separatedPair[1]}"
|
||||
if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then
|
||||
if [ $key != 'PATH' ] && [ $key != 'path' ] && [ $key != 'Path' ]; then
|
||||
export "$key=$value"
|
||||
else
|
||||
export PATH=$(windowsSystemPathAsUnix $value)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
cmd //c "$(unixPathAsWindows "$(dirname "${BASH_SOURCE[0]}")")\ActivateMSVC.bat" "&&" "bash" "-c" "declare -px > declared_env.sh"
|
||||
source ./declared_env.sh
|
||||
rm declared_env.sh
|
||||
|
||||
MISSINGTOOLS=0
|
||||
|
||||
|
@ -93,6 +49,4 @@ if [ $MISSINGTOOLS -ne 0 ]; then
|
|||
return 1
|
||||
fi
|
||||
|
||||
IFS="$originalIFS"
|
||||
|
||||
restoreOldSettings
|
|
@ -1,4 +1,4 @@
|
|||
#!/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
|
||||
unzip -o ~/openmw-android-deps -d /usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
|
||||
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 /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
|
||||
[ -z "${TRAVIS}" ] && brew reinstall xquartz
|
||||
[ -z "${TRAVIS}" ] && brew reinstall fontconfig
|
||||
command -v ccache >/dev/null 2>&1 || brew install ccache
|
||||
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
|
||||
|
||||
|
|
|
@ -3,13 +3,31 @@
|
|||
# hack to work around: FFmpeg version is too old, 3.2 is required
|
||||
sed -i s/"NOT FFVER_OK"/"FALSE"/ CMakeLists.txt
|
||||
|
||||
mkdir build
|
||||
# Silence a git warning
|
||||
git config --global advice.detachedHead false
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
# Build a version of ICU for the host so that it can use the tools during the cross-compilation
|
||||
mkdir -p icu-host-build
|
||||
cd icu-host-build
|
||||
if [ -r ../extern/fetched/icu/icu4c/source/configure ]; then
|
||||
ICU_SOURCE_DIR=../extern/fetched/icu/icu4c/source
|
||||
else
|
||||
wget https://github.com/unicode-org/icu/archive/refs/tags/release-70-1.zip
|
||||
unzip release-70-1.zip
|
||||
ICU_SOURCE_DIR=./icu-release-70-1/icu4c/source
|
||||
fi
|
||||
${ICU_SOURCE_DIR}/configure --disable-tests --disable-samples --disable-icuio --disable-extras CC="ccache gcc" CXX="ccache g++"
|
||||
make -j $(nproc)
|
||||
cd ..
|
||||
|
||||
cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/usr/lib/android-sdk/ndk-bundle/build/cmake/android.toolchain.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/android-ndk-r22/build/cmake/android.toolchain.cmake \
|
||||
-DANDROID_ABI=arm64-v8a \
|
||||
-DANDROID_PLATFORM=android-21 \
|
||||
-DANDROID_LD=deprecated \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_INSTALL_PREFIX=install \
|
||||
|
@ -21,5 +39,11 @@ cmake \
|
|||
-DBUILD_ESSIMPORTER=0 \
|
||||
-DBUILD_OPENCS=0 \
|
||||
-DBUILD_WIZARD=0 \
|
||||
-DMyGUI_LIBRARY="/usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/libMyGUIEngineStatic.a" \
|
||||
-DBUILD_NAVMESHTOOL=OFF \
|
||||
-DBUILD_BULLETOBJECTTOOL=OFF \
|
||||
-DOPENMW_USE_SYSTEM_MYGUI=OFF \
|
||||
-DOPENMW_USE_SYSTEM_SQLITE3=OFF \
|
||||
-DOPENMW_USE_SYSTEM_YAML_CPP=OFF \
|
||||
-DOPENMW_USE_SYSTEM_ICU=OFF \
|
||||
-DOPENMW_ICU_HOST_BUILD_DIR="$(pwd)/icu-host-build" \
|
||||
..
|
||||
|
|
|
@ -1,44 +1,109 @@
|
|||
#!/bin/bash -ex
|
||||
#!/bin/bash
|
||||
|
||||
set -xeo pipefail
|
||||
|
||||
free -m
|
||||
|
||||
# Silence a git warning
|
||||
git config --global advice.detachedHead false
|
||||
|
||||
BUILD_UNITTESTS=OFF
|
||||
BUILD_BENCHMARKS=OFF
|
||||
|
||||
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
|
||||
BUILD_UNITTESTS=ON
|
||||
BUILD_BENCHMARKS=ON
|
||||
fi
|
||||
|
||||
mkdir build
|
||||
# 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
|
||||
|
||||
|
||||
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
|
||||
|
||||
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 \
|
||||
-D CMAKE_C_COMPILER="${CC}" \
|
||||
-D CMAKE_CXX_COMPILER="${CXX}" \
|
||||
-D CMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-D CMAKE_INSTALL_PREFIX=install \
|
||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-D USE_SYSTEM_TINYXML=TRUE \
|
||||
-D BUILD_OPENMW=OFF \
|
||||
-D BUILD_BSATOOL=OFF \
|
||||
-D BUILD_ESMTOOL=OFF \
|
||||
-D BUILD_LAUNCHER=OFF \
|
||||
-D BUILD_MWINIIMPORTER=OFF \
|
||||
-D BUILD_ESSIMPORTER=OFF \
|
||||
-D BUILD_OPENCS=OFF \
|
||||
-D BUILD_WIZARD=OFF \
|
||||
-D BUILD_UNITTESTS=ON \
|
||||
-D GTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
-D GMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||
"${CMAKE_CONF_OPTS[@]}" \
|
||||
-DBUILD_OPENMW=OFF \
|
||||
-DBUILD_BSATOOL=OFF \
|
||||
-DBUILD_ESMTOOL=OFF \
|
||||
-DBUILD_LAUNCHER=OFF \
|
||||
-DBUILD_MWINIIMPORTER=OFF \
|
||||
-DBUILD_ESSIMPORTER=OFF \
|
||||
-DBUILD_OPENCS=OFF \
|
||||
-DBUILD_WIZARD=OFF \
|
||||
-DBUILD_NAVMESHTOOL=OFF \
|
||||
-DBUILD_UNITTESTS=${BUILD_UNITTESTS} \
|
||||
-DBUILD_BENCHMARKS=${BUILD_BENCHMARKS} \
|
||||
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||
..
|
||||
else
|
||||
${ANALYZE} cmake \
|
||||
-D CMAKE_C_COMPILER="${CC}" \
|
||||
-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 \
|
||||
"${CMAKE_CONF_OPTS[@]}" \
|
||||
..
|
||||
fi
|
||||
|
|
|
@ -62,6 +62,7 @@ VERBOSE=""
|
|||
STRIP=""
|
||||
SKIP_DOWNLOAD=""
|
||||
SKIP_EXTRACT=""
|
||||
USE_CCACHE=""
|
||||
KEEP=""
|
||||
UNITY_BUILD=""
|
||||
VS_VERSION=""
|
||||
|
@ -73,9 +74,10 @@ CONFIGURATIONS=()
|
|||
TEST_FRAMEWORK=""
|
||||
GOOGLE_INSTALL_ROOT=""
|
||||
INSTALL_PREFIX="."
|
||||
BULLET_DOUBLE=""
|
||||
BULLET_DBL=""
|
||||
BULLET_DBL_DISPLAY="Single precision"
|
||||
BUILD_BENCHMARKS=""
|
||||
OSG_MULTIVIEW_BUILD=""
|
||||
USE_WERROR=""
|
||||
USE_CLANG_TIDY=""
|
||||
|
||||
ACTIVATE_MSVC=""
|
||||
SINGLE_CONFIG=""
|
||||
|
@ -99,12 +101,11 @@ while [ $# -gt 0 ]; do
|
|||
d )
|
||||
SKIP_DOWNLOAD=true ;;
|
||||
|
||||
D )
|
||||
BULLET_DOUBLE=true ;;
|
||||
|
||||
e )
|
||||
SKIP_EXTRACT=true ;;
|
||||
|
||||
C )
|
||||
USE_CCACHE=true ;;
|
||||
k )
|
||||
KEEP=true ;;
|
||||
|
||||
|
@ -139,6 +140,18 @@ while [ $# -gt 0 ]; do
|
|||
INSTALL_PREFIX=$(echo "$1" | sed 's;\\;/;g' | sed -E 's;/+;/;g')
|
||||
shift ;;
|
||||
|
||||
b )
|
||||
BUILD_BENCHMARKS=true ;;
|
||||
|
||||
M )
|
||||
OSG_MULTIVIEW_BUILD=true ;;
|
||||
|
||||
E )
|
||||
USE_WERROR=true ;;
|
||||
|
||||
T )
|
||||
USE_CLANG_TIDY=true ;;
|
||||
|
||||
h )
|
||||
cat <<EOF
|
||||
Usage: $0 [-cdehkpuvVi]
|
||||
|
@ -147,10 +160,10 @@ Options:
|
|||
Set the configuration, can also be set with environment variable CONFIGURATION.
|
||||
For mutli-config generators, this is ignored, and all configurations are set up.
|
||||
For single-config generators, several configurations can be set up at once by specifying -c multiple times.
|
||||
-C
|
||||
Use ccache.
|
||||
-d
|
||||
Skip checking the downloads.
|
||||
-D
|
||||
Use double-precision Bullet
|
||||
-e
|
||||
Skip extracting dependencies.
|
||||
-h
|
||||
|
@ -175,6 +188,14 @@ Options:
|
|||
Run verbosely
|
||||
-i
|
||||
CMake install prefix
|
||||
-b
|
||||
Build benchmarks
|
||||
-M
|
||||
Use a multiview build of OSG
|
||||
-E
|
||||
Use warnings as errors (/WX)
|
||||
-T
|
||||
Run clang-tidy
|
||||
EOF
|
||||
wrappedExit 0
|
||||
;;
|
||||
|
@ -260,10 +281,10 @@ download() {
|
|||
|
||||
if [ -z $VERBOSE ]; then
|
||||
RET=0
|
||||
curl --silent --retry 10 -Ly 5 -o $FILE $URL || RET=$?
|
||||
curl --silent --fail --retry 10 -Ly 5 -o $FILE $URL || RET=$?
|
||||
else
|
||||
RET=0
|
||||
curl --retry 10 -Ly 5 -o $FILE $URL || RET=$?
|
||||
curl --fail --retry 10 -Ly 5 -o $FILE $URL || RET=$?
|
||||
fi
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
|
@ -325,6 +346,16 @@ add_qt_platform_dlls() {
|
|||
QT_PLATFORMS[$CONFIG]="${QT_PLATFORMS[$CONFIG]} $@"
|
||||
}
|
||||
|
||||
declare -A QT_STYLES
|
||||
QT_STYLES["Release"]=""
|
||||
QT_STYLES["Debug"]=""
|
||||
QT_STYLES["RelWithDebInfo"]=""
|
||||
add_qt_style_dlls() {
|
||||
local CONFIG=$1
|
||||
shift
|
||||
QT_STYLES[$CONFIG]="${QT_STYLES[$CONFIG]} $@"
|
||||
}
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM="$(uname -m)"
|
||||
fi
|
||||
|
@ -342,9 +373,9 @@ case $VS_VERSION in
|
|||
MSVC_YEAR="2015"
|
||||
MSVC_REAL_YEAR="2019"
|
||||
MSVC_DISPLAY_YEAR="2019"
|
||||
BOOST_VER="1.71.0"
|
||||
BOOST_VER_URL="1_71_0"
|
||||
BOOST_VER_SDK="107100"
|
||||
BOOST_VER="1.79.0"
|
||||
BOOST_VER_URL="1_79_0"
|
||||
BOOST_VER_SDK="107900"
|
||||
;;
|
||||
|
||||
15|15.0|2017 )
|
||||
|
@ -423,9 +454,6 @@ if [ -n "$SINGLE_CONFIG" ]; then
|
|||
if [ -n "$SKIP_DOWNLOAD" ]; then
|
||||
RECURSIVE_OPTIONS+=("-d")
|
||||
fi
|
||||
if [ -n "$BULLET_DOUBLE" ]; then
|
||||
RECURSIVE_OPTIONS+=("-D")
|
||||
fi
|
||||
if [ -n "$SKIP_EXTRACT" ]; then
|
||||
RECURSIVE_OPTIONS+=("-e")
|
||||
fi
|
||||
|
@ -498,14 +526,33 @@ if ! [ -z $UNITY_BUILD ]; then
|
|||
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
|
||||
fi
|
||||
|
||||
if [ -n "$BULLET_DOUBLE" ]; then
|
||||
BULLET_DBL="-double"
|
||||
BULLET_DBL_DISPLAY="Double precision"
|
||||
add_cmake_opts "-DBULLET_USE_DOUBLES=True"
|
||||
if ! [ -z $USE_CCACHE ]; then
|
||||
add_cmake_opts "-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
|
||||
fi
|
||||
|
||||
# turn on LTO by default
|
||||
add_cmake_opts "-DOPENMW_LTO_BUILD=True"
|
||||
|
||||
if ! [ -z "$USE_WERROR" ]; then
|
||||
add_cmake_opts "-DOPENMW_MSVC_WERROR=ON"
|
||||
fi
|
||||
|
||||
if ! [ -z "$USE_CLANG_TIDY" ]; then
|
||||
add_cmake_opts "-DCMAKE_CXX_CLANG_TIDY=\"clang-tidy --warnings-as-errors=*\""
|
||||
fi
|
||||
|
||||
ICU_VER="70_1"
|
||||
|
||||
OSG_ARCHIVE_NAME="OSGoS 3.6.5"
|
||||
OSG_ARCHIVE="OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}"
|
||||
OSG_ARCHIVE_REPO_URL="https://gitlab.com/OpenMW/openmw-deps/-/raw/main"
|
||||
if ! [ -z $OSG_MULTIVIEW_BUILD ]; then
|
||||
OSG_ARCHIVE_NAME="OSG-3.6-multiview"
|
||||
OSG_ARCHIVE="OSG-3.6-multiview-ee297dce0-msvc${MSVC_REAL_YEAR}-win${BITS}"
|
||||
OSG_ARCHIVE_REPO_URL="https://gitlab.com/madsbuvi/openmw-deps/-/raw/openmw-vr-ovr_multiview"
|
||||
fi
|
||||
|
||||
|
||||
echo
|
||||
echo "==================================="
|
||||
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
|
||||
|
@ -530,9 +577,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
fi
|
||||
|
||||
# Bullet
|
||||
download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \
|
||||
"Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z"
|
||||
download "Bullet 2.89" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z" \
|
||||
"Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z"
|
||||
|
||||
# FFmpeg
|
||||
download "FFmpeg 4.2.2" \
|
||||
|
@ -542,14 +589,14 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
"ffmpeg-4.2.2-dev-win${BITS}.zip"
|
||||
|
||||
# MyGUI
|
||||
download "MyGUI 3.4.0" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||
download "MyGUI 3.4.1" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.1-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||
"MyGUI-3.4.1-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||
|
||||
if [ -n "$PDBS" ]; then
|
||||
download "MyGUI symbols" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
||||
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/MyGUI-3.4.1-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
||||
"MyGUI-3.4.1-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||
fi
|
||||
|
||||
# OpenAL
|
||||
|
@ -557,36 +604,46 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OpenAL-Soft-1.20.1.zip" \
|
||||
"OpenAL-Soft-1.20.1.zip"
|
||||
|
||||
# OSG
|
||||
download "OpenSceneGraph 3.6.5" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||
# OSGoS https://gitlab.com/madsbuvi/openmw-deps/-/raw/openmw-vr-ovr_multiview/windows/OSG-3.6-multiview-ee297dce0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z
|
||||
download "${OSG_ARCHIVE_NAME}" \
|
||||
"${OSG_ARCHIVE_REPO_URL}/windows/${OSG_ARCHIVE}.7z" \
|
||||
"${OSG_ARCHIVE}.7z"
|
||||
|
||||
if [ -n "$PDBS" ]; then
|
||||
download "OpenSceneGraph symbols" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
||||
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||
download "${OSG_ARCHIVE_NAME} symbols" \
|
||||
"${OSG_ARCHIVE_REPO_URL}/windows/${OSG_ARCHIVE}-sym.7z" \
|
||||
"${OSG_ARCHIVE}-sym.7z"
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
download "SDL 2.0.12" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-2.0.12.zip" \
|
||||
"SDL2-2.0.12.zip"
|
||||
download "SDL 2.0.22" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-2.0.22.zip" \
|
||||
"SDL2-2.0.22.zip"
|
||||
|
||||
# LZ4
|
||||
download "LZ4 1.9.2" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/lz4_win${BITS}_v1_9_2.7z" \
|
||||
"lz4_win${BITS}_v1_9_2.7z"
|
||||
|
||||
# LuaJIT
|
||||
download "LuaJIT 2.1.0-beta3" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/LuaJIT-2.1.0-beta3-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||
"LuaJIT-2.1.0-beta3-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||
|
||||
# Google test and mock
|
||||
if [ ! -z $TEST_FRAMEWORK ]; then
|
||||
echo "Google test 1.10.0..."
|
||||
if [ -n "$TEST_FRAMEWORK" ]; then
|
||||
echo "Google test 1.11.0..."
|
||||
if [ -d googletest ]; then
|
||||
printf " Google test exists, skipping."
|
||||
else
|
||||
git clone -b release-1.10.0 https://github.com/google/googletest.git
|
||||
git clone -b release-1.11.0 https://github.com/google/googletest.git
|
||||
fi
|
||||
fi
|
||||
|
||||
# ICU
|
||||
download "ICU ${ICU_VER/_/.}"\
|
||||
"https://github.com/unicode-org/icu/releases/download/release-${ICU_VER/_/-}/icu4c-${ICU_VER}-Win${BITS}-MSVC2019.zip" \
|
||||
"icu4c-${ICU_VER}-Win${BITS}-MSVC2019.zip"
|
||||
fi
|
||||
|
||||
cd .. #/..
|
||||
|
@ -672,15 +729,15 @@ fi
|
|||
cd $DEPS
|
||||
echo
|
||||
# Bullet
|
||||
printf "Bullet 2.89 (${BULLET_DBL_DISPLAY})... "
|
||||
printf "Bullet 2.89... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d Bullet ]; then
|
||||
printf -- "Exists. (No version checking) "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Bullet
|
||||
eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" $STRIP
|
||||
mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}" Bullet
|
||||
eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z" $STRIP
|
||||
mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double" Bullet
|
||||
fi
|
||||
add_cmake_opts -DBULLET_ROOT="$(real_pwd)/Bullet"
|
||||
echo Done.
|
||||
|
@ -713,20 +770,20 @@ printf "FFmpeg 4.2.2... "
|
|||
cd $DEPS
|
||||
echo
|
||||
# MyGUI
|
||||
printf "MyGUI 3.4.0... "
|
||||
printf "MyGUI 3.4.1... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d MyGUI ] && \
|
||||
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
grep "MYGUI_VERSION_MINOR 4" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
grep "MYGUI_VERSION_PATCH 0" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
|
||||
grep "MYGUI_VERSION_PATCH 1" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
|
||||
then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf MyGUI
|
||||
eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
|
||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
|
||||
mv "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}" MyGUI
|
||||
eval 7z x -y "${DEPS}/MyGUI-3.4.1-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
|
||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/MyGUI-3.4.1-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
|
||||
mv "MyGUI-3.4.1-msvc${MSVC_REAL_YEAR}-win${BITS}" MyGUI
|
||||
fi
|
||||
export MYGUI_HOME="$(real_pwd)/MyGUI"
|
||||
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
||||
|
@ -762,8 +819,8 @@ printf "OpenAL-Soft 1.20.1... "
|
|||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# OSG
|
||||
printf "OSG 3.6.5... "
|
||||
# OSGoS
|
||||
printf "${OSG_ARCHIVE_NAME}... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d OSG ] && \
|
||||
|
@ -774,9 +831,9 @@ printf "OSG 3.6.5... "
|
|||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf OSG
|
||||
eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
|
||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
|
||||
mv "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}" OSG
|
||||
eval 7z x -y "${DEPS}/${OSG_ARCHIVE}.7z" $STRIP
|
||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/${OSG_ARCHIVE}-sym.7z" $STRIP
|
||||
mv "${OSG_ARCHIVE}" OSG
|
||||
fi
|
||||
OSG_SDK="$(real_pwd)/OSG"
|
||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||
|
@ -786,8 +843,13 @@ printf "OSG 3.6.5... "
|
|||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \
|
||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
||||
if ! [ -z $OSG_MULTIVIEW_BUILD ]; then
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{ot21-OpenThreads,zlib,libpng16}${SUFFIX}.dll \
|
||||
"$(pwd)/OSG/bin/osg162-osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
||||
else
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \
|
||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
||||
fi
|
||||
add_osg_dlls $CONFIGURATION "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll
|
||||
add_osg_dlls $CONFIGURATION "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
|
||||
done
|
||||
|
@ -796,113 +858,92 @@ printf "OSG 3.6.5... "
|
|||
cd $DEPS
|
||||
echo
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Qt 5.15.0... "
|
||||
else
|
||||
printf "Qt 5.13 AppVeyor... "
|
||||
fi
|
||||
printf "Qt 5.15.2... "
|
||||
{
|
||||
if [ $BITS -eq 64 ]; then
|
||||
SUFFIX="_64"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
qt_version="5.15.0"
|
||||
if [ "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}" == "win64_msvc2017_64" ]; then
|
||||
echo "This combination of options is known not to work. Falling back to Qt 5.14.2."
|
||||
qt_version="5.14.2"
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
qt_version="5.15.2"
|
||||
|
||||
QT_SDK="$(real_pwd)/Qt/${qt_version}/msvc${MSVC_REAL_YEAR}${SUFFIX}"
|
||||
|
||||
if [ -d "Qt/${qt_version}" ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
if [ $MISSINGPYTHON -ne 0 ]; then
|
||||
echo "Can't be automatically installed without Python."
|
||||
wrappedExit 1
|
||||
fi
|
||||
|
||||
QT_SDK="$(real_pwd)/Qt/${qt_version}/msvc${MSVC_REAL_YEAR}${SUFFIX}"
|
||||
|
||||
if [ -d "Qt/${qt_version}" ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
if [ $MISSINGPYTHON -ne 0 ]; then
|
||||
echo "Can't be automatically installed without Python."
|
||||
wrappedExit 1
|
||||
fi
|
||||
|
||||
pushd "$DEPS" > /dev/null
|
||||
if ! [ -d 'aqt-venv' ]; then
|
||||
echo " Creating Virtualenv for aqt..."
|
||||
run_cmd python -m venv aqt-venv
|
||||
fi
|
||||
if [ -d 'aqt-venv/bin' ]; then
|
||||
VENV_BIN_DIR='bin'
|
||||
elif [ -d 'aqt-venv/Scripts' ]; then
|
||||
VENV_BIN_DIR='Scripts'
|
||||
else
|
||||
echo "Error: Failed to create virtualenv in expected location."
|
||||
wrappedExit 1
|
||||
fi
|
||||
|
||||
if ! [ -e "aqt-venv/${VENV_BIN_DIR}/aqt" ]; then
|
||||
echo " Installing aqt wheel into virtualenv..."
|
||||
run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==0.9.2
|
||||
fi
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf Qt
|
||||
|
||||
mkdir Qt
|
||||
cd Qt
|
||||
|
||||
run_cmd "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install $qt_version windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}"
|
||||
|
||||
printf " Cleaning up extraneous data... "
|
||||
rm -rf Qt/{aqtinstall.log,Tools}
|
||||
|
||||
echo Done.
|
||||
pushd "$DEPS" > /dev/null
|
||||
if ! [ -d 'aqt-venv' ]; then
|
||||
echo " Creating Virtualenv for aqt..."
|
||||
run_cmd python -m venv aqt-venv
|
||||
fi
|
||||
if [ -d 'aqt-venv/bin' ]; then
|
||||
VENV_BIN_DIR='bin'
|
||||
elif [ -d 'aqt-venv/Scripts' ]; then
|
||||
VENV_BIN_DIR='Scripts'
|
||||
else
|
||||
echo "Error: Failed to create virtualenv in expected location."
|
||||
wrappedExit 1
|
||||
fi
|
||||
|
||||
cd $QT_SDK
|
||||
add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
DLLSUFFIX="d"
|
||||
else
|
||||
DLLSUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll
|
||||
add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll"
|
||||
done
|
||||
echo Done.
|
||||
else
|
||||
QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}"
|
||||
add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
DLLSUFFIX="d"
|
||||
else
|
||||
DLLSUFFIX=""
|
||||
fi
|
||||
DIR=$(windowsPathAsUnix "${QT_SDK}")
|
||||
add_runtime_dlls $CONFIGURATION "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll
|
||||
add_qt_platform_dlls $CONFIGURATION "${DIR}/plugins/platforms/qwindows${DLLSUFFIX}.dll"
|
||||
done
|
||||
# check version
|
||||
aqt-venv/${VENV_BIN_DIR}/pip list | grep 'aqtinstall\s*1.1.3' || [ $? -ne 0 ]
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " Installing aqt wheel into virtualenv..."
|
||||
run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==1.1.3
|
||||
fi
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf Qt
|
||||
|
||||
mkdir Qt
|
||||
cd Qt
|
||||
|
||||
run_cmd "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install $qt_version windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}"
|
||||
|
||||
printf " Cleaning up extraneous data... "
|
||||
rm -rf Qt/{aqtinstall.log,Tools}
|
||||
|
||||
echo Done.
|
||||
fi
|
||||
|
||||
cd $QT_SDK
|
||||
add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
DLLSUFFIX="d"
|
||||
else
|
||||
DLLSUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll
|
||||
add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll"
|
||||
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
|
||||
done
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# SDL2
|
||||
printf "SDL 2.0.12... "
|
||||
printf "SDL 2.0.22... "
|
||||
{
|
||||
if [ -d SDL2-2.0.12 ]; then
|
||||
if [ -d SDL2-2.0.22 ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf SDL2-2.0.12
|
||||
eval 7z x -y SDL2-2.0.12.zip $STRIP
|
||||
rm -rf SDL2-2.0.22
|
||||
eval 7z x -y SDL2-2.0.22.zip $STRIP
|
||||
fi
|
||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.12"
|
||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.22"
|
||||
for config in ${CONFIGURATIONS[@]}; do
|
||||
add_runtime_dlls $config "$(pwd)/SDL2-2.0.12/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||
add_runtime_dlls $config "$(pwd)/SDL2-2.0.22/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||
done
|
||||
echo Done.
|
||||
}
|
||||
|
@ -915,7 +956,7 @@ printf "LZ4 1.9.2... "
|
|||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf LZ4_1.9.2
|
||||
eval 7z x -y lz4_win${BITS}_v1_9_2.7z -o./LZ4_1.9.2 $STRIP
|
||||
eval 7z x -y lz4_win${BITS}_v1_9_2.7z -o$(real_pwd)/LZ4_1.9.2 $STRIP
|
||||
fi
|
||||
export LZ4DIR="$(real_pwd)/LZ4_1.9.2"
|
||||
add_cmake_opts -DLZ4_INCLUDE_DIR="${LZ4DIR}/include" \
|
||||
|
@ -933,9 +974,28 @@ printf "LZ4 1.9.2... "
|
|||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# LuaJIT 2.1.0-beta3
|
||||
printf "LuaJIT 2.1.0-beta3... "
|
||||
{
|
||||
if [ -d LuaJIT ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf LuaJIT
|
||||
eval 7z x -y LuaJIT-2.1.0-beta3-msvc${MSVC_REAL_YEAR}-win${BITS}.7z -o$(real_pwd)/LuaJIT $STRIP
|
||||
fi
|
||||
export LUAJIT_DIR="$(real_pwd)/LuaJIT"
|
||||
add_cmake_opts -DLuaJit_INCLUDE_DIR="${LUAJIT_DIR}/include" \
|
||||
-DLuaJit_LIBRARY="${LUAJIT_DIR}/lib/lua51.lib"
|
||||
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/LuaJIT/bin/lua51.dll"
|
||||
done
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# Google Test and Google Mock
|
||||
if [ ! -z $TEST_FRAMEWORK ]; then
|
||||
printf "Google test 1.10.0 ..."
|
||||
if [ -n "$TEST_FRAMEWORK" ]; then
|
||||
printf "Google test 1.11.0 ..."
|
||||
|
||||
cd googletest
|
||||
mkdir -p build${MSVC_REAL_YEAR}
|
||||
|
@ -987,12 +1047,40 @@ if [ ! -z $TEST_FRAMEWORK ]; then
|
|||
|
||||
fi
|
||||
|
||||
cd $DEPS
|
||||
echo
|
||||
# ICU
|
||||
printf "ICU ${ICU_VER/_/.}... "
|
||||
{
|
||||
if [ -d ICU-${ICU_VER} ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf ICU-${ICU_VER}
|
||||
eval 7z x -y icu4c-${ICU_VER}-Win${BITS}-MSVC2019.zip -o$(real_pwd)/ICU-${ICU_VER} $STRIP
|
||||
fi
|
||||
ICU_ROOT="$(real_pwd)/ICU-${ICU_VER}"
|
||||
add_cmake_opts -DICU_ROOT="${ICU_ROOT}" \
|
||||
-DICU_INCLUDE_DIR="${ICU_ROOT}/include" \
|
||||
-DICU_I18N_LIBRARY="${ICU_ROOT}/lib${BITS}/icuin.lib " \
|
||||
-DICU_UC_LIBRARY="${ICU_ROOT}/lib${BITS}/icuuc.lib " \
|
||||
-DICU_DEBUG=ON
|
||||
|
||||
for config in ${CONFIGURATIONS[@]}; do
|
||||
add_runtime_dlls $config "$(pwd)/ICU-${ICU_VER}/bin${BITS}/icudt${ICU_VER/_*/}.dll"
|
||||
add_runtime_dlls $config "$(pwd)/ICU-${ICU_VER}/bin${BITS}/icuin${ICU_VER/_*/}.dll"
|
||||
add_runtime_dlls $config "$(pwd)/ICU-${ICU_VER}/bin${BITS}/icuuc${ICU_VER/_*/}.dll"
|
||||
done
|
||||
echo Done.
|
||||
}
|
||||
|
||||
echo
|
||||
cd $DEPS_INSTALL/..
|
||||
echo
|
||||
echo "Setting up OpenMW build..."
|
||||
add_cmake_opts -DOPENMW_MP_BUILD=on
|
||||
add_cmake_opts -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}"
|
||||
add_cmake_opts -DOPENMW_USE_SYSTEM_SQLITE3=OFF
|
||||
add_cmake_opts -DOPENMW_USE_SYSTEM_YAML_CPP=OFF
|
||||
if [ ! -z $CI ]; then
|
||||
case $STEP in
|
||||
components )
|
||||
|
@ -1062,14 +1150,31 @@ fi
|
|||
cp "$DLL" "${DLL_PREFIX}platforms"
|
||||
done
|
||||
echo
|
||||
echo "- Qt Style DLLs..."
|
||||
mkdir -p ${DLL_PREFIX}styles
|
||||
for DLL in ${QT_STYLES[$CONFIGURATION]}; do
|
||||
echo " $(basename $DLL)"
|
||||
cp "$DLL" "${DLL_PREFIX}styles"
|
||||
done
|
||||
echo
|
||||
done
|
||||
#fi
|
||||
|
||||
if [ "${BUILD_BENCHMARKS}" ]; then
|
||||
add_cmake_opts -DBUILD_BENCHMARKS=ON
|
||||
fi
|
||||
|
||||
if [ -n "$ACTIVATE_MSVC" ]; then
|
||||
echo -n "- Activating MSVC in the current shell... "
|
||||
command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; }
|
||||
|
||||
MSVC_INSTALLATION_PATH=$(vswhere -products '*' -version "[$MSVC_REAL_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath)
|
||||
# There are so many arguments now that I'm going to document them:
|
||||
# * products: allow Visual Studio or standalone build tools
|
||||
# * version: obvious. Awk helps make a version range by adding one.
|
||||
# * property installationPath: only give the installation path.
|
||||
# * latest: return only one result if several candidates exist. Prefer the last installed/updated
|
||||
# * requires: make sure it's got the MSVC compiler instead of, for example, just the .NET compiler. The .x86.x64 suffix means it's for either, not that it's the x64 on x86 cross compiler as you always get both
|
||||
MSVC_INSTALLATION_PATH=$(vswhere -products '*' -version "[$MSVC_REAL_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64)
|
||||
if [ -z "$MSVC_INSTALLATION_PATH" ]; then
|
||||
echo "vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR"
|
||||
wrappedExit 1
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
# Silence a git warning
|
||||
git config --global advice.detachedHead false
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH=$(brew --prefix qt)
|
||||
QT_PATH=$(brew --prefix qt@5)
|
||||
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
|
||||
mkdir build
|
||||
cd build
|
||||
|
@ -16,15 +19,16 @@ cmake \
|
|||
-D CMAKE_CXX_FLAGS="-stdlib=libc++" \
|
||||
-D CMAKE_C_FLAGS_RELEASE="-g -O0" \
|
||||
-D CMAKE_CXX_FLAGS_RELEASE="-g -O0" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.12" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.15" \
|
||||
-D CMAKE_BUILD_TYPE=RELEASE \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D OPENMW_USE_SYSTEM_SQLITE3=OFF \
|
||||
-D BUILD_OPENMW=TRUE \
|
||||
-D BUILD_OPENCS=TRUE \
|
||||
-D BUILD_ESMTOOL=TRUE \
|
||||
-D BUILD_BSATOOL=TRUE \
|
||||
-D BUILD_ESSIMPORTER=TRUE \
|
||||
-D BUILD_NIFTEST=TRUE \
|
||||
-D BULLET_USE_DOUBLES=TRUE \
|
||||
-D ICU_ROOT="/usr/local/opt/icu4c" \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
git clone -b release-1.10.0 https://github.com/google/googletest.git
|
||||
git clone -b release-1.11.0 https://github.com/google/googletest.git
|
||||
cd googletest
|
||||
mkdir build
|
||||
cd build
|
||||
|
|
100
CI/install_debian_deps.sh
Executable file
100
CI/install_debian_deps.sh
Executable file
|
@ -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
|
10
CI/run_integration_tests.sh
Executable file
10
CI/run_integration_tests.sh
Executable file
|
@ -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
|
460
CMakeLists.txt
460
CMakeLists.txt
|
@ -1,13 +1,37 @@
|
|||
project(OpenMW)
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# for link time optimization, remove if cmake version is >= 3.9
|
||||
if(POLICY CMP0069)
|
||||
if(POLICY CMP0069) # LTO
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
endif()
|
||||
|
||||
# for position-independent executable, remove if cmake version is >= 3.14
|
||||
if(POLICY CMP0083)
|
||||
cmake_policy(SET CMP0083 NEW)
|
||||
endif()
|
||||
|
||||
# to link with freetype library
|
||||
if(POLICY CMP0079)
|
||||
cmake_policy(SET CMP0079 NEW)
|
||||
endif()
|
||||
|
||||
# don't add /W3 flag by default for MSVC
|
||||
if(POLICY CMP0092)
|
||||
cmake_policy(SET CMP0092 NEW)
|
||||
endif()
|
||||
|
||||
project(OpenMW)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
option(OPENMW_GL4ES_MANUAL_INIT "Manually initialize gl4es. This is more reliable on platforms without a windowing system. Requires gl4es to be configured with -DNOEGL=ON -DNO_LOADER=ON -DNO_INIT_CONSTRUCTOR=ON." OFF)
|
||||
if(OPENMW_GL4ES_MANUAL_INIT)
|
||||
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
|
||||
endif()
|
||||
|
||||
# Apps and tools
|
||||
option(BUILD_OPENMW "Build OpenMW" ON)
|
||||
option(BUILD_LAUNCHER "Build Launcher" ON)
|
||||
|
@ -21,7 +45,9 @@ option(BUILD_NIFTEST "Build nif file tester" ON)
|
|||
option(BUILD_DOCS "Build documentation." OFF )
|
||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
||||
option(BULLET_USE_DOUBLES "Use double precision for Bullet" OFF)
|
||||
option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
|
||||
option(BUILD_NAVMESHTOOL "Build navmesh tool" ON)
|
||||
option(BUILD_BULLETOBJECTTOOL "Build Bullet object tool" ON)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up.
|
||||
|
||||
|
@ -49,14 +75,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
|||
|
||||
if (ANDROID)
|
||||
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
|
||||
set (OSG_PLUGINS_DIR CACHE STRING "")
|
||||
endif()
|
||||
|
||||
# Version
|
||||
message(STATUS "Configuring OpenMW...")
|
||||
|
||||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 47)
|
||||
set(OPENMW_VERSION_MINOR 48)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
|
@ -92,17 +117,53 @@ endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
|||
|
||||
# Macros
|
||||
include(OpenMWMacros)
|
||||
include(WholeArchive)
|
||||
|
||||
# doxygen main page
|
||||
|
||||
configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_BINARY_DIR}/docs/mainpage.hpp")
|
||||
|
||||
option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE)
|
||||
option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE)
|
||||
option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE)
|
||||
option(OSG_STATIC "Link static build of OpenSceneGraph into the binaries" FALSE)
|
||||
option(QT_STATIC "Link static build of QT into the binaries" FALSE)
|
||||
|
||||
option(OPENMW_USE_SYSTEM_BULLET "Use system provided bullet physics library" ON)
|
||||
if(OPENMW_USE_SYSTEM_BULLET)
|
||||
set(_bullet_static_default OFF)
|
||||
else()
|
||||
set(_bullet_static_default ON)
|
||||
endif()
|
||||
option(BULLET_STATIC "Link static build of Bullet into the binaries" ${_bullet_static_default})
|
||||
|
||||
option(OPENMW_USE_SYSTEM_OSG "Use system provided OpenSceneGraph libraries" ON)
|
||||
if(OPENMW_USE_SYSTEM_OSG)
|
||||
set(_osg_static_default OFF)
|
||||
else()
|
||||
set(_osg_static_default ON)
|
||||
endif()
|
||||
option(OSG_STATIC "Link static build of OpenSceneGraph into the binaries" ${_osg_static_default})
|
||||
|
||||
option(OPENMW_USE_SYSTEM_MYGUI "Use system provided mygui library" ON)
|
||||
if(OPENMW_USE_SYSTEM_MYGUI)
|
||||
set(_mygui_static_default OFF)
|
||||
else()
|
||||
set(_mygui_static_default ON)
|
||||
endif()
|
||||
option(MYGUI_STATIC "Link static build of Mygui into the binaries" ${_mygui_static_default})
|
||||
|
||||
option(OPENMW_USE_SYSTEM_RECASTNAVIGATION "Use system provided recastnavigation library" OFF)
|
||||
if(OPENMW_USE_SYSTEM_RECASTNAVIGATION)
|
||||
set(_recastnavigation_static_default OFF)
|
||||
find_package(RecastNavigation REQUIRED)
|
||||
else()
|
||||
set(_recastnavigation_static_default ON)
|
||||
endif()
|
||||
option(RECASTNAVIGATION_STATIC "Build recastnavigation static libraries" ${_recastnavigation_static_default})
|
||||
|
||||
option(OPENMW_USE_SYSTEM_SQLITE3 "Use system provided SQLite3 library" ON)
|
||||
|
||||
option(OPENMW_USE_SYSTEM_BENCHMARK "Use system Google Benchmark library." OFF)
|
||||
|
||||
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
|
||||
option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF)
|
||||
|
||||
|
@ -118,18 +179,27 @@ option(OPENMW_OSX_DEPLOYMENT OFF)
|
|||
|
||||
if (MSVC)
|
||||
option(OPENMW_MP_BUILD "Build OpenMW with /MP flag" OFF)
|
||||
if (OPENMW_MP_BUILD)
|
||||
add_compile_options(/MP)
|
||||
endif()
|
||||
|
||||
# \bigobj is required:
|
||||
# 1) for OPENMW_UNITY_BUILD;
|
||||
# 2) to compile lua bindings in components, openmw and tests, because sol3 is heavily templated.
|
||||
# there should be no relevant downsides to having it on:
|
||||
# https://docs.microsoft.com/en-us/cpp/build/reference/bigobj-increase-number-of-sections-in-dot-obj-file
|
||||
add_compile_options(/bigobj)
|
||||
endif()
|
||||
|
||||
# Set up common paths
|
||||
if (APPLE)
|
||||
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
|
||||
set(OPENMW_RESOURCE_FILES "../Resources/resources" CACHE PATH "location of OpenMW resources files")
|
||||
elseif(UNIX)
|
||||
# Paths
|
||||
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
|
||||
SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries")
|
||||
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
|
||||
SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix")
|
||||
SET(DATAROOTDIR "${CMAKE_INSTALL_DATAROOTDIR}" CACHE PATH "Sets the root of data directories to a non-default location")
|
||||
SET(GLOBAL_DATA_PATH "${CMAKE_INSTALL_PREFIX}/share/games/" CACHE PATH "Set data path prefix")
|
||||
SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
|
||||
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
|
||||
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
|
||||
|
@ -140,10 +210,8 @@ elseif(UNIX)
|
|||
ENDIF()
|
||||
SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir")
|
||||
|
||||
set(MORROWIND_DATA_FILES "${DATADIR}/data" CACHE PATH "location of Morrowind data files")
|
||||
set(OPENMW_RESOURCE_FILES "${DATADIR}/resources" CACHE PATH "location of OpenMW resources files")
|
||||
else()
|
||||
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
|
||||
set(OPENMW_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
|
||||
endif(APPLE)
|
||||
|
||||
|
@ -151,6 +219,10 @@ if (WIN32)
|
|||
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options("/utf-8")
|
||||
endif()
|
||||
|
||||
# Dependencies
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
|
@ -161,10 +233,47 @@ if (USE_QT)
|
|||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5OpenGL REQUIRED)
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
#set(CMAKE_AUTOMOC ON)
|
||||
endif()
|
||||
|
||||
set(USED_OSG_COMPONENTS
|
||||
osgDB
|
||||
osgViewer
|
||||
osgText
|
||||
osgGA
|
||||
osgParticle
|
||||
osgUtil
|
||||
osgFX
|
||||
osgShadow
|
||||
osgAnimation)
|
||||
set(USED_OSG_PLUGINS
|
||||
osgdb_bmp
|
||||
osgdb_dds
|
||||
osgdb_freetype
|
||||
osgdb_jpeg
|
||||
osgdb_osg
|
||||
osgdb_png
|
||||
osgdb_serializers_osg
|
||||
osgdb_tga)
|
||||
|
||||
|
||||
option(OPENMW_USE_SYSTEM_ICU "Use system ICU library instead of internal. If disabled, requires autotools" ON)
|
||||
if(OPENMW_USE_SYSTEM_ICU)
|
||||
find_package(ICU REQUIRED COMPONENTS uc i18n data)
|
||||
endif()
|
||||
|
||||
option(OPENMW_USE_SYSTEM_YAML_CPP "Use system yaml-cpp library instead of internal." ON)
|
||||
if(OPENMW_USE_SYSTEM_YAML_CPP)
|
||||
set(_yaml_cpp_static_default OFF)
|
||||
else()
|
||||
set(_yaml_cpp_static_default ON)
|
||||
endif()
|
||||
option(YAML_CPP_STATIC "Link static build of yaml-cpp into the binaries" ${_yaml_cpp_static_default})
|
||||
if (OPENMW_USE_SYSTEM_YAML_CPP)
|
||||
find_package(yaml-cpp REQUIRED)
|
||||
endif()
|
||||
|
||||
add_subdirectory(extern)
|
||||
|
||||
# Sound setup
|
||||
|
||||
# Require at least ffmpeg 3.2 for now
|
||||
|
@ -198,16 +307,16 @@ if(FFmpeg_FOUND)
|
|||
set(FFVER_OK FALSE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT FFVER_OK AND NOT APPLE) # unable to detect on version on MacOS < 11.0
|
||||
message(FATAL_ERROR "FFmpeg version is too old, 3.2 is required" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT FFmpeg_FOUND)
|
||||
message(FATAL_ERROR "FFmpeg was not found" )
|
||||
endif()
|
||||
|
||||
if(NOT FFVER_OK)
|
||||
message(FATAL_ERROR "FFmpeg version is too old, 3.2 is required" )
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
message("Can not detect FFmpeg version, at least the 3.2 is required" )
|
||||
endif()
|
||||
|
@ -237,7 +346,45 @@ if (WIN32)
|
|||
add_definitions(-DSDL_MAIN_HANDLED)
|
||||
|
||||
# Get rid of useless crud from windows.h
|
||||
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
||||
add_definitions(
|
||||
-DNOMINMAX # name conflict with std::min, std::max
|
||||
-DWIN32_LEAN_AND_MEAN
|
||||
-DNOMB # name conflict with MWGui::MessageBox
|
||||
-DNOGDI # name conflict with osgAnimation::MorphGeometry::RELATIVE
|
||||
)
|
||||
endif()
|
||||
|
||||
if(OPENMW_USE_SYSTEM_BULLET)
|
||||
set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape
|
||||
if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR})
|
||||
set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine
|
||||
endif()
|
||||
|
||||
# First, try BulletConfig-float64.cmake which comes with Debian derivatives.
|
||||
# This file does not define the Bullet version in a CMake-friendly way.
|
||||
find_package(Bullet CONFIGS BulletConfig-float64.cmake QUIET COMPONENTS BulletCollision LinearMath)
|
||||
if (BULLET_FOUND)
|
||||
string(REPLACE "." "" _bullet_version_num ${BULLET_VERSION_STRING})
|
||||
if (_bullet_version_num VERSION_LESS REQUIRED_BULLET_VERSION)
|
||||
message(FATAL_ERROR "System bullet version too old, OpenMW requires at least ${REQUIRED_BULLET_VERSION}, got ${_bullet_version_num}")
|
||||
endif()
|
||||
# Fix the relative include:
|
||||
set(BULLET_INCLUDE_DIRS "${BULLET_ROOT_DIR}/${BULLET_INCLUDE_DIRS}")
|
||||
include(FindPackageMessage)
|
||||
find_package_message(Bullet "Found Bullet: ${BULLET_LIBRARIES} ${BULLET_VERSION_STRING}" "${BULLET_VERSION_STRING}-float64")
|
||||
else()
|
||||
find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
|
||||
endif()
|
||||
|
||||
# Only link the Bullet libraries that we need:
|
||||
string(REGEX MATCHALL "((optimized|debug);)?[^;]*(BulletCollision|LinearMath)[^;]*" BULLET_LIBRARIES "${BULLET_LIBRARIES}")
|
||||
|
||||
include(cmake/CheckBulletPrecision.cmake)
|
||||
if (HAS_DOUBLE_PRECISION_BULLET)
|
||||
message(STATUS "Bullet uses double precision")
|
||||
else()
|
||||
message(FATAL_ERROR "Bullet does not uses double precision")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
|
||||
|
@ -258,42 +405,35 @@ if(NOT HAVE_STDINT_H)
|
|||
message(FATAL_ERROR "stdint.h was not found" )
|
||||
endif()
|
||||
|
||||
if(OPENMW_USE_SYSTEM_OSG)
|
||||
find_package(OpenSceneGraph 3.4.0 REQUIRED ${USED_OSG_COMPONENTS})
|
||||
if (${OPENSCENEGRAPH_VERSION} VERSION_GREATER 3.6.2 AND ${OPENSCENEGRAPH_VERSION} VERSION_LESS 3.6.5)
|
||||
message(FATAL_ERROR "OpenSceneGraph version ${OPENSCENEGRAPH_VERSION} has critical regressions which cause crashes. Please upgrade to 3.6.5 or later. We strongly recommend using the tip of the official 'OpenSceneGraph-3.6' branch or the tip of '3.6' OpenMW/osg (OSGoS).")
|
||||
endif()
|
||||
|
||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX osgShadow)
|
||||
include_directories(SYSTEM ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
if(OSG_STATIC)
|
||||
find_package(OSGPlugins REQUIRED COMPONENTS ${USED_OSG_PLUGINS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(USED_OSG_PLUGINS
|
||||
osgdb_bmp
|
||||
osgdb_dds
|
||||
osgdb_freetype
|
||||
osgdb_jpeg
|
||||
osgdb_osg
|
||||
osgdb_png
|
||||
osgdb_serializers_osg
|
||||
osgdb_tga
|
||||
)
|
||||
|
||||
set(OSGPlugins_LIB_DIR "")
|
||||
foreach(OSGDB_LIB ${OSGDB_LIBRARY})
|
||||
# Skip library type names
|
||||
if(EXISTS ${OSGDB_LIB} AND NOT IS_DIRECTORY ${OSGDB_LIB})
|
||||
get_filename_component(OSG_LIB_DIR ${OSGDB_LIB} DIRECTORY)
|
||||
list(APPEND OSGPlugins_LIB_DIR "${OSG_LIB_DIR}/osgPlugins-${OPENSCENEGRAPH_VERSION}")
|
||||
endif()
|
||||
endforeach(OSGDB_LIB)
|
||||
include_directories(BEFORE SYSTEM ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
if(OSG_STATIC)
|
||||
add_definitions(-DOSG_LIBRARY_STATIC)
|
||||
|
||||
find_package(OSGPlugins REQUIRED COMPONENTS ${USED_OSG_PLUGINS})
|
||||
list(APPEND OPENSCENEGRAPH_LIBRARIES ${OSGPlugins_LIBRARIES})
|
||||
endif()
|
||||
|
||||
include(cmake/CheckOsgMultiview.cmake)
|
||||
if(HAVE_MULTIVIEW)
|
||||
add_definitions(-DOSG_HAS_MULTIVIEW)
|
||||
endif(HAVE_MULTIVIEW)
|
||||
|
||||
set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
||||
if(WIN32)
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
||||
if(MSVC)
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} zlib)
|
||||
# boost-zlib is not present (nor needed) in vcpkg version of boost.
|
||||
# there, it is part of boost-iostreams instead.
|
||||
set(BOOST_OPTIONAL_COMPONENTS zlib)
|
||||
endif(MSVC)
|
||||
endif(WIN32)
|
||||
|
||||
|
@ -301,26 +441,47 @@ IF(BOOST_STATIC)
|
|||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif()
|
||||
|
||||
set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape
|
||||
if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR})
|
||||
set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine
|
||||
endif()
|
||||
|
||||
set(Boost_NO_BOOST_CMAKE ON)
|
||||
|
||||
find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
find_package(MyGUI 3.2.2 REQUIRED)
|
||||
find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS})
|
||||
|
||||
if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.77.0)
|
||||
find_package(Boost 1.77.0 REQUIRED COMPONENTS atomic)
|
||||
endif()
|
||||
|
||||
if(OPENMW_USE_SYSTEM_MYGUI)
|
||||
find_package(MyGUI 3.4.1 REQUIRED)
|
||||
endif()
|
||||
find_package(SDL2 2.0.9 REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
|
||||
|
||||
include_directories("."
|
||||
SYSTEM
|
||||
option(USE_LUAJIT "Switch Lua/LuaJit (TRUE is highly recommended)" TRUE)
|
||||
if(USE_LUAJIT)
|
||||
find_package(LuaJit REQUIRED)
|
||||
set(LUA_INCLUDE_DIR ${LuaJit_INCLUDE_DIR})
|
||||
set(LUA_LIBRARIES ${LuaJit_LIBRARIES})
|
||||
else(USE_LUAJIT)
|
||||
find_package(Lua REQUIRED)
|
||||
add_compile_definitions(NO_LUAJIT)
|
||||
endif(USE_LUAJIT)
|
||||
|
||||
# C++ library binding to Lua
|
||||
set(SOL_INCLUDE_DIR ${OpenMW_SOURCE_DIR}/extern/sol3)
|
||||
set(SOL_CONFIG_DIR ${OpenMW_SOURCE_DIR}/extern/sol_config)
|
||||
|
||||
include_directories(
|
||||
BEFORE SYSTEM
|
||||
"."
|
||||
${SDL2_INCLUDE_DIR}
|
||||
${Boost_INCLUDE_DIR}
|
||||
${MyGUI_INCLUDE_DIRS}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${BULLET_INCLUDE_DIRS}
|
||||
${LUA_INCLUDE_DIR}
|
||||
${SOL_INCLUDE_DIR}
|
||||
${SOL_CONFIG_DIR}
|
||||
${ICU_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
|
||||
|
@ -337,9 +498,8 @@ if (APPLE)
|
|||
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
|
||||
endif (APPLE)
|
||||
|
||||
if (NOT APPLE)
|
||||
set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR})
|
||||
set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})
|
||||
if (NOT APPLE) # this is modified for macOS use later in "apps/open[mw|cs]/CMakeLists.txt"
|
||||
set(OPENMW_RESOURCES_ROOT ${OpenMW_BINARY_DIR})
|
||||
endif ()
|
||||
|
||||
add_subdirectory(files/)
|
||||
|
@ -360,8 +520,8 @@ endif (APPLE)
|
|||
|
||||
# Other files
|
||||
|
||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
|
||||
"${OpenMW_BINARY_DIR}" "settings-default.cfg")
|
||||
pack_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
|
||||
"${OpenMW_BINARY_DIR}" "defaults.bin")
|
||||
|
||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml
|
||||
"${OpenMW_BINARY_DIR}" "openmw.appdata.xml")
|
||||
|
@ -376,8 +536,8 @@ else ()
|
|||
"${OpenMW_BINARY_DIR}/openmw.cfg")
|
||||
endif ()
|
||||
|
||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
|
||||
"${OpenMW_BINARY_DIR}" "openmw-cs.cfg")
|
||||
pack_resource_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
|
||||
"${OpenMW_BINARY_DIR}" "defaults-cs.bin")
|
||||
|
||||
# Needs the copy version because the configure version assumes the end of the file has been reached when a null character is reached and there are no CMake expressions to evaluate.
|
||||
copy_resource_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
|
||||
|
@ -415,7 +575,7 @@ endif()
|
|||
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long")
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long ${CMAKE_CXX_FLAGS}")
|
||||
add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )
|
||||
|
||||
if (APPLE)
|
||||
|
@ -432,27 +592,23 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.6)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 5.0)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override")
|
||||
endif()
|
||||
elseif (MSVC)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
|
||||
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
|
||||
# Extern
|
||||
set(RECASTNAVIGATION_STATIC ON CACHE BOOL "Build recastnavigation static libraries")
|
||||
|
||||
add_subdirectory (extern/recastnavigation EXCLUDE_FROM_ALL)
|
||||
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
||||
add_subdirectory (extern/oics)
|
||||
add_subdirectory (extern/Base64)
|
||||
if (BUILD_OPENCS)
|
||||
add_subdirectory (extern/osgQt)
|
||||
endif()
|
||||
|
||||
if (OPENMW_CXX_FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMW_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
# Components
|
||||
add_subdirectory (components)
|
||||
target_compile_definitions(components PRIVATE OPENMW_DOC_BASEURL="${OPENMW_DOC_BASEURL}")
|
||||
|
||||
# Apps and tools
|
||||
if (BUILD_OPENMW)
|
||||
|
@ -496,12 +652,20 @@ if (BUILD_UNITTESTS)
|
|||
add_subdirectory( apps/openmw_test_suite )
|
||||
endif()
|
||||
|
||||
if (BUILD_BENCHMARKS)
|
||||
add_subdirectory(apps/benchmarks)
|
||||
endif()
|
||||
|
||||
if (BUILD_NAVMESHTOOL)
|
||||
add_subdirectory(apps/navmeshtool)
|
||||
endif()
|
||||
|
||||
if (BUILD_BULLETOBJECTTOOL)
|
||||
add_subdirectory( apps/bulletobjecttool )
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (MSVC)
|
||||
if (OPENMW_MP_BUILD)
|
||||
set( MT_BUILD "/MP")
|
||||
endif()
|
||||
|
||||
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
|
||||
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
|
||||
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "$(SolutionDir)$(Configuration)" )
|
||||
|
@ -526,62 +690,24 @@ if (WIN32)
|
|||
|
||||
# Play a bit with the warning levels
|
||||
|
||||
set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them
|
||||
set(WARNINGS "/W4")
|
||||
|
||||
set(WARNINGS_DISABLE
|
||||
# Warnings that aren't enabled normally and don't need to be enabled
|
||||
# They're unneeded and sometimes completely retarded warnings that /Wall enables
|
||||
# Not going to bother commenting them as they tend to warn on every standard library file
|
||||
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
|
||||
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
|
||||
|
||||
# Warnings that are thrown on standard libraries and not OpenMW
|
||||
4347 # Non-template function with same name and parameter count as template function
|
||||
4365 # Variable signed/unsigned mismatch
|
||||
4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base
|
||||
4706 # Assignment in conditional expression
|
||||
4738 # Storing 32-bit float result in memory, possible loss of performance
|
||||
4774 # Format string expected in argument is not a string literal
|
||||
4986 # Undocumented warning that occurs in the crtdbg.h file
|
||||
4987 # nonstandard extension used (triggered by setjmp.h)
|
||||
4996 # Function was declared deprecated
|
||||
|
||||
# caused by OSG
|
||||
4589 # Constructor of abstract class 'osg::Operation' ignores initializer for virtual base class 'osg::Referenced' (False warning)
|
||||
|
||||
# caused by boost
|
||||
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
|
||||
4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files)
|
||||
5204 # Class has virtual functions, but its trivial destructor is not virtual
|
||||
|
||||
# caused by MyGUI
|
||||
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
|
||||
4297 # function assumed not to throw an exception but does
|
||||
|
||||
# OpenMW specific warnings
|
||||
4099 # Type mismatch, declared class or struct is defined with other type
|
||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||
4101 # Unreferenced local variable (-Wunused-variable)
|
||||
4127 # Conditional expression is constant
|
||||
4242 # Storing value in a variable of a smaller type, possible loss of data
|
||||
4244 # Storing value of one type in variable of another (size_t in int, for example)
|
||||
4245 # Signed/unsigned mismatch
|
||||
4267 # Conversion from 'size_t' to 'int', possible loss of data
|
||||
4305 # Truncating value (double to float, for example)
|
||||
4309 # Variable overflow, trying to store 128 in a signed char for example
|
||||
4351 # New behavior: elements of array 'array' will be default initialized (desired behavior)
|
||||
4355 # Using 'this' in member initialization list
|
||||
4464 # relative include path contains '..'
|
||||
4505 # Unreferenced local function has been removed
|
||||
4701 # Potentially uninitialized local variable used
|
||||
4702 # Unreachable code
|
||||
4714 # function 'QString QString::trimmed(void) &&' marked as __forceinline not inlined
|
||||
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
|
||||
4996 # Function was declared deprecated
|
||||
5054 # Deprecated operations between enumerations of different types caused by Qt headers
|
||||
)
|
||||
|
||||
if (MSVC_VERSION GREATER 1800)
|
||||
set(WARNINGS_DISABLE ${WARNINGS_DISABLE} 5026 5027
|
||||
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file (config_begin.hpp, config_end.hpp)
|
||||
if( "${MyGUI_VERSION}" VERSION_LESS_EQUAL "3.4.0" )
|
||||
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
|
||||
4866 # compiler may not enforce left-to-right evaluation order for call
|
||||
)
|
||||
endif()
|
||||
|
||||
if( "${MyGUI_VERSION}" VERSION_LESS_EQUAL "3.4.1" )
|
||||
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
|
||||
4275 # non dll-interface class 'MyGUI::delegates::IDelegateUnlink' used as base for dll-interface class 'MyGUI::Widget'
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -589,47 +715,64 @@ if (WIN32)
|
|||
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||
endforeach(d)
|
||||
|
||||
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
if(OPENMW_MSVC_WERROR)
|
||||
set(WARNINGS "${WARNINGS} /WX")
|
||||
endif()
|
||||
|
||||
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
|
||||
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
|
||||
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||
endif()
|
||||
|
||||
if (BUILD_BSATOOL)
|
||||
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_ESMTOOL)
|
||||
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_ESSIMPORTER)
|
||||
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_LAUNCHER)
|
||||
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_MWINIIMPORTER)
|
||||
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENCS)
|
||||
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENMW)
|
||||
if (OPENMW_UNITY_BUILD)
|
||||
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD} /bigobj")
|
||||
else()
|
||||
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
endif()
|
||||
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_WIZARD)
|
||||
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_UNITTESTS)
|
||||
set_target_properties(openmw_test_suite PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_BENCHMARKS)
|
||||
set_target_properties(openmw_detournavigator_navmeshtilescache_benchmark PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_NAVMESHTOOL)
|
||||
set_target_properties(openmw-navmeshtool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
|
||||
if (BUILD_BULLETOBJECTTOOL)
|
||||
set(WARNINGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(openmw-bulletobjecttool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
|
@ -638,6 +781,15 @@ if (WIN32)
|
|||
#set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENMW AND APPLE)
|
||||
if (USE_LUAJIT)
|
||||
# Without these flags LuaJit crashes on startup on OSX
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000")
|
||||
endif(USE_LUAJIT)
|
||||
target_compile_definitions(components PRIVATE GL_SILENCE_DEPRECATION=1)
|
||||
target_compile_definitions(openmw PRIVATE GL_SILENCE_DEPRECATION=1)
|
||||
endif()
|
||||
|
||||
# Apple bundling
|
||||
if (OPENMW_OSX_DEPLOYMENT AND APPLE)
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CMAKE_VERSION VERSION_LESS 3.13.4)
|
||||
|
@ -769,8 +921,7 @@ elseif(NOT APPLE)
|
|||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/LICENSE" DESTINATION "." RENAME "LICENSE.txt")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/mygui/DejaVuFontLicense.txt" DESTINATION ".")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/settings-default.cfg" DESTINATION ".")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/defaults.bin" DESTINATION ".")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION ".")
|
||||
|
||||
INSTALL(DIRECTORY "${INSTALL_SOURCE}/resources" DESTINATION ".")
|
||||
|
@ -812,13 +963,13 @@ elseif(NOT APPLE)
|
|||
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
|
||||
if(EXISTS ${VCREDIST32})
|
||||
INSTALL(FILES ${VCREDIST32} DESTINATION "redist")
|
||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" )
|
||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q /norestart'" )
|
||||
endif(EXISTS ${VCREDIST32})
|
||||
|
||||
SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe")
|
||||
if(EXISTS ${VCREDIST64})
|
||||
INSTALL(FILES ${VCREDIST64} DESTINATION "redist")
|
||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" )
|
||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q /norestart'" )
|
||||
endif(EXISTS ${VCREDIST64})
|
||||
|
||||
SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe")
|
||||
|
@ -838,11 +989,7 @@ elseif(NOT APPLE)
|
|||
|
||||
# Install binaries
|
||||
IF(BUILD_OPENMW)
|
||||
IF(ANDROID)
|
||||
INSTALL(PROGRAMS "${INSTALL_SOURCE}/libopenmw.so" DESTINATION "${BINDIR}" )
|
||||
ELSE(ANDROID)
|
||||
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw" DESTINATION "${BINDIR}" )
|
||||
ENDIF(ANDROID)
|
||||
INSTALL(PROGRAMS "$<TARGET_FILE:openmw>" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_OPENMW)
|
||||
IF(BUILD_LAUNCHER)
|
||||
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-launcher" DESTINATION "${BINDIR}" )
|
||||
|
@ -868,9 +1015,12 @@ elseif(NOT APPLE)
|
|||
IF(BUILD_WIZARD)
|
||||
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-wizard" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_WIZARD)
|
||||
|
||||
# Install licenses
|
||||
INSTALL(FILES "files/mygui/DejaVuFontLicense.txt" DESTINATION "${LICDIR}" )
|
||||
if(BUILD_NAVMESHTOOL)
|
||||
install(PROGRAMS "${INSTALL_SOURCE}/openmw-navmeshtool" DESTINATION "${BINDIR}" )
|
||||
endif()
|
||||
IF(BUILD_BULLETOBJECTTOOL)
|
||||
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-bulletobjecttool" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_BULLETOBJECTTOOL)
|
||||
|
||||
# Install icon and desktop file
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/org.openmw.launcher.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
|
||||
|
@ -882,13 +1032,13 @@ elseif(NOT APPLE)
|
|||
ENDIF(BUILD_OPENCS)
|
||||
|
||||
# Install global configuration files
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/defaults.bin" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||
|
||||
IF(BUILD_OPENCS)
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
|
||||
INSTALL(FILES "${INSTALL_SOURCE}/defaults-cs.bin" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
|
||||
ENDIF(BUILD_OPENCS)
|
||||
|
||||
# Install resources
|
||||
|
|
|
@ -22,9 +22,9 @@ Pull Request Guidelines
|
|||
To facilitate the review process, your pull request description should include the following, if applicable:
|
||||
|
||||
* A link back to the bug report or forum discussion that prompted the change. Note: when linking bugs, use the syntax ```[Bug #xyz](https://gitlab.com/OpenMW/openmw/issues/#xyz)``` to create a clickable link. Writing only 'Bug #xyz' will unfortunately create a link to the Github pull request with that number instead.
|
||||
* Summary of the changes made
|
||||
* Reasoning / motivation behind the change
|
||||
* What testing you have carried out to verify the change
|
||||
* Summary of the changes made.
|
||||
* Reasoning / motivation behind the change.
|
||||
* What testing you have carried out to verify the change.
|
||||
|
||||
Furthermore, we advise to:
|
||||
|
||||
|
@ -51,9 +51,9 @@ OpenMW, in its default configuration, is meant to be a faithful reimplementation
|
|||
|
||||
That said, we may sometimes evaluate such issues on an individual basis. Common exceptions to the above would be:
|
||||
|
||||
* Issues so glaring that they would severely limit the capabilities of the engine in the future (for example, the scripting engine not being allowed to access objects in remote cells)
|
||||
* Bugs where the intent is very obvious, and that have little to no balancing impact (e.g. the bug were being tired made it easier to repair items, instead of harder)
|
||||
* Bugs that were fixed in an official patch for Morrowind
|
||||
* Issues so glaring that they would severely limit the capabilities of the engine in the future (for example, the scripting engine not being allowed to access objects in remote cells).
|
||||
* Bugs where the intent is very obvious, and that have little to no balancing impact (e.g. the bug were being tired made it easier to repair items, instead of harder).
|
||||
* Bugs that were fixed in an official patch for Morrowind.
|
||||
|
||||
Feature additions policy
|
||||
=====================
|
||||
|
@ -99,7 +99,7 @@ Code Review
|
|||
Merging
|
||||
=======
|
||||
|
||||
To be able to merge PRs, commit priviledges are required. If you do not have the priviledges, just ping someone that does have them with a short comment like "Looks good to me @user".
|
||||
To be able to merge PRs, commit privileges are required. If you do not have the privileges, just ping someone that does have them with a short comment like "Looks good to me @user".
|
||||
|
||||
The person to merge the PR may either use github's Merge button or if using the command line, use the ```--no-ff``` flag (so a merge commit is created, just like with Github's merge button) and include the pull request number in the commit description.
|
||||
|
||||
|
|
18
README.md
18
README.md
|
@ -1,19 +1,21 @@
|
|||
OpenMW
|
||||
======
|
||||
|
||||
[![Build Status](https://api.travis-ci.org/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Build status](https://ci.appveyor.com/api/projects/status/github/openmw/openmw?svg=true)](https://ci.appveyor.com/project/psi29a/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) [![pipeline status](https://gitlab.com/OpenMW/openmw/badges/master/pipeline.svg)](https://gitlab.com/OpenMW/openmw/commits/master)
|
||||
|
||||
OpenMW is an open-source game engine that supports playing Morrowind by Bethesda Softworks. You need to own the game for OpenMW to play Morrowind.
|
||||
OpenMW is an open-source open-world RPG game engine that supports playing Morrowind by Bethesda Softworks. You need to own the game for OpenMW to play Morrowind.
|
||||
|
||||
OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set.
|
||||
|
||||
* Version: 0.47.0
|
||||
* License: GPLv3 (see [LICENSE](https://github.com/OpenMW/openmw/blob/master/LICENSE) for more information)
|
||||
* Version: 0.48.0
|
||||
* License: GPLv3 (see [LICENSE](https://gitlab.com/OpenMW/openmw/-/raw/master/LICENSE) for more information)
|
||||
* Website: https://www.openmw.org
|
||||
* IRC: #openmw on irc.freenode.net
|
||||
* IRC: #openmw on irc.libera.chat
|
||||
* Discord: https://discord.gg/bWuqq2e
|
||||
|
||||
|
||||
Font Licenses:
|
||||
* DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVuFontLicense.txt](https://github.com/OpenMW/openmw/blob/master/files/mygui/DejaVuFontLicense.txt) for more information)
|
||||
* DejaVuLGCSansMono.ttf: custom (see [files/data/fonts/DejaVuFontLicense.txt](https://gitlab.com/OpenMW/openmw/-/raw/master/files/data/fonts/DejaVuFontLicense.txt) for more information)
|
||||
* OMWAyembedt.ttf: SIL Open Font License (see [files/data/fonts/OMWAyembedtFontLicense.txt](https://gitlab.com/OpenMW/openmw/-/raw/master/files/data/fonts/OMWAyembedtFontLicense.txt) for more information)
|
||||
* Pelagiad.ttf: SIL Open Font License (see [files/data/fonts/PelagiadFontLicense.txt](https://gitlab.com/OpenMW/openmw/-/raw/master/files/data/fonts/PelagiadFontLicense.txt) for more information)
|
||||
|
||||
Current Status
|
||||
--------------
|
||||
|
@ -26,7 +28,7 @@ Getting Started
|
|||
---------------
|
||||
|
||||
* [Official forums](https://forum.openmw.org/)
|
||||
* [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions)
|
||||
* [Installation instructions](https://openmw.readthedocs.io/en/latest/manuals/installation/index.html)
|
||||
* [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup)
|
||||
* [Testing the game](https://wiki.openmw.org/index.php?title=Testing)
|
||||
* [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted)
|
||||
|
|
15
apps/benchmarks/CMakeLists.txt
Normal file
15
apps/benchmarks/CMakeLists.txt
Normal file
|
@ -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()
|
290
apps/benchmarks/detournavigator/navmeshtilescache.cpp
Normal file
290
apps/benchmarks/detournavigator/navmeshtilescache.cpp
Normal file
|
@ -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();
|
|
@ -10,7 +10,6 @@ openmw_add_executable(bsatool
|
|||
|
||||
target_link_libraries(bsatool
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
|
@ -18,3 +17,11 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
add_definitions (--coverage)
|
||||
target_link_libraries(bsatool gcov)
|
||||
endif()
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||
target_precompile_headers(bsatool PRIVATE
|
||||
<filesystem>
|
||||
<fstream>
|
||||
<vector>
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <components/bsa/compressedbsafile.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
@ -13,13 +13,13 @@
|
|||
|
||||
// Create local aliases for brevity
|
||||
namespace bpo = boost::program_options;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
struct Arguments
|
||||
{
|
||||
std::string mode;
|
||||
std::string filename;
|
||||
std::string extractfile;
|
||||
std::string addfile;
|
||||
std::string outdir;
|
||||
|
||||
bool longformat;
|
||||
|
@ -36,6 +36,10 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
" Extract a file from the input archive.\n\n"
|
||||
" bsatool extractall archivefile [output_directory]\n"
|
||||
" Extract all files from the input archive.\n\n"
|
||||
" bsatool add [-a] archivefile file_to_add\n"
|
||||
" Add a file to the input archive.\n\n"
|
||||
" bsatool create [-c] archivefile\n"
|
||||
" Create an archive.\n\n"
|
||||
"Allowed options");
|
||||
|
||||
desc.add_options()
|
||||
|
@ -95,7 +99,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
}
|
||||
|
||||
info.mode = variables["mode"].as<std::string>();
|
||||
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall"))
|
||||
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall" || info.mode == "add" || info.mode == "create"))
|
||||
{
|
||||
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n"
|
||||
<< desc << std::endl;
|
||||
|
@ -126,6 +130,17 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
if (variables["input-file"].as< std::vector<std::string> >().size() > 2)
|
||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2];
|
||||
}
|
||||
else if (info.mode == "add")
|
||||
{
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() < 1)
|
||||
{
|
||||
std::cout << "\nERROR: file to add unspecified\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.addfile = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
}
|
||||
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
|
||||
|
@ -135,72 +150,31 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
return true;
|
||||
}
|
||||
|
||||
int list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
||||
int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
||||
int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
Arguments info;
|
||||
if(!parseOptions (argc, argv, info))
|
||||
return 1;
|
||||
|
||||
// Open file
|
||||
std::unique_ptr<Bsa::BSAFile> bsa;
|
||||
|
||||
Bsa::BsaVersion bsaVersion = Bsa::CompressedBSAFile::detectVersion(info.filename);
|
||||
|
||||
if (bsaVersion == Bsa::BSAVER_COMPRESSED)
|
||||
bsa = std::make_unique<Bsa::CompressedBSAFile>(Bsa::CompressedBSAFile());
|
||||
else
|
||||
bsa = std::make_unique<Bsa::BSAFile>(Bsa::BSAFile());
|
||||
|
||||
bsa->open(info.filename);
|
||||
|
||||
if (info.mode == "list")
|
||||
return list(bsa, info);
|
||||
else if (info.mode == "extract")
|
||||
return extract(bsa, info);
|
||||
else if (info.mode == "extractall")
|
||||
return extractAll(bsa, info);
|
||||
else
|
||||
{
|
||||
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "ERROR reading BSA archive\nDetails:\n" << e.what() << std::endl;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
int list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||
template<typename File>
|
||||
int list(std::unique_ptr<File>& bsa, Arguments& info)
|
||||
{
|
||||
// List all files
|
||||
const Bsa::BSAFile::FileList &files = bsa->getList();
|
||||
const auto &files = bsa->getList();
|
||||
for (const auto& file : files)
|
||||
{
|
||||
if(info.longformat)
|
||||
{
|
||||
// Long format
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
std::cout << std::setw(50) << std::left << file.name;
|
||||
std::cout << std::setw(50) << std::left << file.name();
|
||||
std::cout << std::setw(8) << std::left << std::dec << file.fileSize;
|
||||
std::cout << "@ 0x" << std::hex << file.offset << std::endl;
|
||||
std::cout.flags(f);
|
||||
}
|
||||
else
|
||||
std::cout << file.name << std::endl;
|
||||
std::cout << file.name() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||
template<typename File>
|
||||
int extract(std::unique_ptr<File>& bsa, Arguments& info)
|
||||
{
|
||||
std::string archivePath = info.extractfile;
|
||||
Misc::StringUtils::replaceAll(archivePath, "/", "\\");
|
||||
|
@ -208,7 +182,17 @@ int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
|||
std::string extractPath = info.extractfile;
|
||||
Misc::StringUtils::replaceAll(extractPath, "\\", "/");
|
||||
|
||||
if (!bsa->exists(archivePath.c_str()))
|
||||
Files::IStreamPtr stream;
|
||||
// Get a stream for the file to extract
|
||||
for (auto it = bsa->getList().rbegin(); it != bsa->getList().rend(); ++it)
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(std::string(it->name()), archivePath))
|
||||
{
|
||||
stream = bsa->getFile(&*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stream)
|
||||
{
|
||||
std::cout << "ERROR: file '" << archivePath << "' not found\n";
|
||||
std::cout << "In archive: " << info.filename << std::endl;
|
||||
|
@ -216,29 +200,26 @@ int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
|||
}
|
||||
|
||||
// Get the target path (the path the file will be extracted to)
|
||||
bfs::path relPath (extractPath);
|
||||
bfs::path outdir (info.outdir);
|
||||
std::filesystem::path relPath (extractPath);
|
||||
std::filesystem::path outdir (info.outdir);
|
||||
|
||||
bfs::path target;
|
||||
std::filesystem::path target;
|
||||
if (info.fullpath)
|
||||
target = outdir / relPath;
|
||||
else
|
||||
target = outdir / relPath.filename();
|
||||
|
||||
// Create the directory hierarchy
|
||||
bfs::create_directories(target.parent_path());
|
||||
std::filesystem::create_directories(target.parent_path());
|
||||
|
||||
bfs::file_status s = bfs::status(target.parent_path());
|
||||
if (!bfs::is_directory(s))
|
||||
std::filesystem::file_status s = std::filesystem::status(target.parent_path());
|
||||
if (!std::filesystem::is_directory(s))
|
||||
{
|
||||
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get a stream for the file to extract
|
||||
Files::IStreamPtr stream = bsa->getFile(archivePath.c_str());
|
||||
|
||||
bfs::ofstream out(target, std::ios::binary);
|
||||
std::ofstream out(target, std::ios::binary);
|
||||
|
||||
// Write the file to disk
|
||||
std::cout << "Extracting " << info.extractfile << " to " << target << std::endl;
|
||||
|
@ -249,31 +230,31 @@ int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||
template<typename File>
|
||||
int extractAll(std::unique_ptr<File>& bsa, Arguments& info)
|
||||
{
|
||||
for (const auto &file : bsa->getList())
|
||||
{
|
||||
std::string extractPath(file.name);
|
||||
std::string extractPath(file.name());
|
||||
Misc::StringUtils::replaceAll(extractPath, "\\", "/");
|
||||
|
||||
// Get the target path (the path the file will be extracted to)
|
||||
bfs::path target (info.outdir);
|
||||
std::filesystem::path target (info.outdir);
|
||||
target /= extractPath;
|
||||
|
||||
// Create the directory hierarchy
|
||||
bfs::create_directories(target.parent_path());
|
||||
std::filesystem::create_directories(target.parent_path());
|
||||
|
||||
bfs::file_status s = bfs::status(target.parent_path());
|
||||
if (!bfs::is_directory(s))
|
||||
std::filesystem::file_status s = std::filesystem::status(target.parent_path());
|
||||
if (!std::filesystem::is_directory(s))
|
||||
{
|
||||
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get a stream for the file to extract
|
||||
// (inefficient because getFile iter on the list again)
|
||||
Files::IStreamPtr data = bsa->getFile(file.name);
|
||||
bfs::ofstream out(target, std::ios::binary);
|
||||
Files::IStreamPtr data = bsa->getFile(&file);
|
||||
std::ofstream out(target, std::ios::binary);
|
||||
|
||||
// Write the file to disk
|
||||
std::cout << "Extracting " << target << std::endl;
|
||||
|
@ -283,3 +264,63 @@ int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename File>
|
||||
int add(std::unique_ptr<File>& bsa, Arguments& info)
|
||||
{
|
||||
std::fstream stream(info.addfile, std::ios_base::binary | std::ios_base::out | std::ios_base::in);
|
||||
bsa->addFile(info.addfile, stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename File>
|
||||
int call(Arguments& info)
|
||||
{
|
||||
std::unique_ptr<File> bsa = std::make_unique<File>();
|
||||
if (info.mode == "create")
|
||||
{
|
||||
bsa->open(info.filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bsa->open(info.filename);
|
||||
|
||||
if (info.mode == "list")
|
||||
return list(bsa, info);
|
||||
else if (info.mode == "extract")
|
||||
return extract(bsa, info);
|
||||
else if (info.mode == "extractall")
|
||||
return extractAll(bsa, info);
|
||||
else if (info.mode == "add")
|
||||
return add(bsa, info);
|
||||
else
|
||||
{
|
||||
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
Arguments info;
|
||||
if (!parseOptions(argc, argv, info))
|
||||
return 1;
|
||||
|
||||
// Open file
|
||||
|
||||
Bsa::BsaVersion bsaVersion = Bsa::CompressedBSAFile::detectVersion(info.filename);
|
||||
|
||||
if (bsaVersion == Bsa::BSAVER_COMPRESSED)
|
||||
return call<Bsa::CompressedBSAFile>(info);
|
||||
else
|
||||
return call<Bsa::BSAFile>(info);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "ERROR reading BSA archive\nDetails:\n" << e.what() << std::endl;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
|
27
apps/bulletobjecttool/CMakeLists.txt
Normal file
27
apps/bulletobjecttool/CMakeLists.txt
Normal file
|
@ -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()
|
203
apps/bulletobjecttool/main.cpp
Normal file
203
apps/bulletobjecttool/main.cpp
Normal file
|
@ -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");
|
||||
}
|
|
@ -4,6 +4,9 @@ set(ESMTOOL
|
|||
labels.cpp
|
||||
record.hpp
|
||||
record.cpp
|
||||
arguments.hpp
|
||||
tes4.hpp
|
||||
tes4.cpp
|
||||
)
|
||||
source_group(apps\\esmtool FILES ${ESMTOOL})
|
||||
|
||||
|
@ -21,3 +24,11 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
add_definitions (--coverage)
|
||||
target_link_libraries(esmtool gcov)
|
||||
endif()
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||
target_precompile_headers(esmtool PRIVATE
|
||||
<fstream>
|
||||
<string>
|
||||
<vector>
|
||||
)
|
||||
endif()
|
||||
|
|
28
apps/esmtool/arguments.hpp
Normal file
28
apps/esmtool/arguments.hpp
Normal file
|
@ -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
|
|
@ -2,71 +2,46 @@
|
|||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <iomanip>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/esm/records.hpp>
|
||||
#include <components/esm/format.hpp>
|
||||
#include <components/files/openfile.hpp>
|
||||
|
||||
#include "record.hpp"
|
||||
#include "labels.hpp"
|
||||
#include "arguments.hpp"
|
||||
#include "tes4.hpp"
|
||||
|
||||
#define ESMTOOL_VERSION 1.2
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace EsmTool;
|
||||
|
||||
constexpr unsigned majorVersion = 1;
|
||||
constexpr unsigned minorVersion = 3;
|
||||
|
||||
// Create a local alias for brevity
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
struct ESMData
|
||||
{
|
||||
std::string author;
|
||||
std::string description;
|
||||
unsigned int version;
|
||||
std::vector<ESM::Header::MasterData> masters;
|
||||
|
||||
std::deque<EsmTool::RecordBase *> mRecords;
|
||||
ESM::Header mHeader;
|
||||
std::deque<std::unique_ptr<EsmTool::RecordBase>> mRecords;
|
||||
// Value: (Reference, Deleted flag)
|
||||
std::map<ESM::Cell *, std::deque<std::pair<ESM::CellRef, bool> > > mCellRefs;
|
||||
std::map<int, int> mRecordStats;
|
||||
|
||||
static const std::set<int> sLabeledRec;
|
||||
};
|
||||
|
||||
static const int sLabeledRecIds[] = {
|
||||
ESM::REC_GLOB, ESM::REC_CLAS, ESM::REC_FACT, ESM::REC_RACE, ESM::REC_SOUN,
|
||||
ESM::REC_REGN, ESM::REC_BSGN, ESM::REC_LTEX, ESM::REC_STAT, ESM::REC_DOOR,
|
||||
ESM::REC_MISC, ESM::REC_WEAP, ESM::REC_CONT, ESM::REC_SPEL, ESM::REC_CREA,
|
||||
ESM::REC_BODY, ESM::REC_LIGH, ESM::REC_ENCH, ESM::REC_NPC_, ESM::REC_ARMO,
|
||||
ESM::REC_CLOT, ESM::REC_REPA, ESM::REC_ACTI, ESM::REC_APPA, ESM::REC_LOCK,
|
||||
ESM::REC_PROB, ESM::REC_INGR, ESM::REC_BOOK, ESM::REC_ALCH, ESM::REC_LEVI,
|
||||
ESM::REC_LEVC, ESM::REC_SNDG, ESM::REC_CELL, ESM::REC_DIAL
|
||||
};
|
||||
|
||||
const std::set<int> ESMData::sLabeledRec =
|
||||
std::set<int>(sLabeledRecIds, sLabeledRecIds + 34);
|
||||
|
||||
// Based on the legacy struct
|
||||
struct Arguments
|
||||
{
|
||||
bool raw_given;
|
||||
bool quiet_given;
|
||||
bool loadcells_given;
|
||||
bool plain_given;
|
||||
|
||||
std::string mode;
|
||||
std::string encoding;
|
||||
std::string filename;
|
||||
std::string outname;
|
||||
|
||||
std::vector<std::string> types;
|
||||
std::string name;
|
||||
|
||||
ESMData data;
|
||||
ESM::ESMReader reader;
|
||||
ESM::ESMWriter writer;
|
||||
};
|
||||
|
||||
bool parseOptions (int argc, char** argv, Arguments &info)
|
||||
|
@ -76,7 +51,10 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
desc.add_options()
|
||||
("help,h", "print help message.")
|
||||
("version,v", "print version information and quit.")
|
||||
("raw,r", "Show an unformatted list of all records and subrecords.")
|
||||
("raw,r", bpo::value<std::string>(),
|
||||
"Show an unformatted list of all records and subrecords of given format:\n"
|
||||
"\n\tTES3"
|
||||
"\n\tTES4")
|
||||
// The intention is that this option would interact better
|
||||
// with other modes including clone, dump, and raw.
|
||||
("type,t", bpo::value< std::vector<std::string> >(),
|
||||
|
@ -138,12 +116,12 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
}
|
||||
if (variables.count ("version"))
|
||||
{
|
||||
std::cout << "ESMTool version " << ESMTOOL_VERSION << std::endl;
|
||||
std::cout << "ESMTool version " << majorVersion << '.' << minorVersion << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!variables.count("mode"))
|
||||
{
|
||||
std::cout << "No mode specified!" << std::endl << std::endl
|
||||
std::cout << "No mode specified!\n\n"
|
||||
<< desc << finalText << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
@ -156,7 +134,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
info.mode = variables["mode"].as<std::string>();
|
||||
if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp"))
|
||||
{
|
||||
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"" << std::endl << std::endl
|
||||
std::cout << "\nERROR: invalid mode \"" << info.mode << "\"\n\n"
|
||||
<< desc << finalText << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
@ -180,7 +158,9 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.outname = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
|
||||
info.raw_given = variables.count ("raw") != 0;
|
||||
if (const auto it = variables.find("raw"); it != variables.end())
|
||||
info.mRawFormat = ESM::parseFormat(it->second.as<std::string>());
|
||||
|
||||
info.quiet_given = variables.count ("quiet") != 0;
|
||||
info.loadcells_given = variables.count ("loadcells") != 0;
|
||||
info.plain_given = variables.count("plain") != 0;
|
||||
|
@ -189,7 +169,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
info.encoding = variables["encoding"].as<std::string>();
|
||||
if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252")
|
||||
{
|
||||
std::cout << info.encoding << " is not a valid encoding option." << std::endl;
|
||||
std::cout << info.encoding << " is not a valid encoding option.\n";
|
||||
info.encoding = "win1252";
|
||||
}
|
||||
std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl;
|
||||
|
@ -197,12 +177,13 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
return true;
|
||||
}
|
||||
|
||||
void printRaw(ESM::ESMReader &esm);
|
||||
void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info);
|
||||
void loadCell(const Arguments& info, ESM::Cell &cell, ESM::ESMReader &esm, ESMData* data);
|
||||
|
||||
int load(Arguments& info);
|
||||
int clone(Arguments& info);
|
||||
int comp(Arguments& info);
|
||||
int load(const Arguments& info, ESMData* data);
|
||||
int clone(const Arguments& info);
|
||||
int comp(const Arguments& info);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char**argv)
|
||||
{
|
||||
|
@ -213,7 +194,7 @@ int main(int argc, char**argv)
|
|||
return 1;
|
||||
|
||||
if (info.mode == "dump")
|
||||
return load(info);
|
||||
return load(info, nullptr);
|
||||
else if (info.mode == "clone")
|
||||
return clone(info);
|
||||
else if (info.mode == "comp")
|
||||
|
@ -233,7 +214,10 @@ int main(int argc, char**argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
||||
namespace
|
||||
{
|
||||
|
||||
void loadCell(const Arguments& info, ESM::Cell &cell, ESM::ESMReader &esm, ESMData* data)
|
||||
{
|
||||
bool quiet = (info.quiet_given || info.mode == "clone");
|
||||
bool save = (info.mode == "clone");
|
||||
|
@ -249,55 +233,66 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
|||
if(!quiet) std::cout << " References:\n";
|
||||
|
||||
bool deleted = false;
|
||||
while(cell.getNextRef(esm, ref, deleted))
|
||||
ESM::MovedCellRef movedCellRef;
|
||||
bool moved = false;
|
||||
while(cell.getNextRef(esm, ref, deleted, movedCellRef, moved))
|
||||
{
|
||||
if (save) {
|
||||
info.data.mCellRefs[&cell].push_back(std::make_pair(ref, deleted));
|
||||
}
|
||||
if (data != nullptr && save)
|
||||
data->mCellRefs[&cell].push_back(std::make_pair(ref, deleted));
|
||||
|
||||
if(quiet) continue;
|
||||
|
||||
std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl;
|
||||
std::cout << " ID: " << ref.mRefID << std::endl;
|
||||
std::cout << " Position: (" << ref.mPos.pos[0] << ", " << ref.mPos.pos[1] << ", " << ref.mPos.pos[2] << ")" << std::endl;
|
||||
std::cout << " - Refnum: " << ref.mRefNum.mIndex << '\n';
|
||||
std::cout << " ID: " << ref.mRefID << '\n';
|
||||
std::cout << " Position: (" << ref.mPos.pos[0] << ", " << ref.mPos.pos[1] << ", " << ref.mPos.pos[2] << ")\n";
|
||||
if (ref.mScale != 1.f)
|
||||
std::cout << " Scale: " << ref.mScale << std::endl;
|
||||
std::cout << " Scale: " << ref.mScale << '\n';
|
||||
if (!ref.mOwner.empty())
|
||||
std::cout << " Owner: " << ref.mOwner << std::endl;
|
||||
std::cout << " Owner: " << ref.mOwner << '\n';
|
||||
if (!ref.mGlobalVariable.empty())
|
||||
std::cout << " Global: " << ref.mGlobalVariable << std::endl;
|
||||
std::cout << " Global: " << ref.mGlobalVariable << '\n';
|
||||
if (!ref.mFaction.empty())
|
||||
std::cout << " Faction: " << ref.mFaction << std::endl;
|
||||
std::cout << " Faction: " << ref.mFaction << '\n';
|
||||
if (!ref.mFaction.empty() || ref.mFactionRank != -2)
|
||||
std::cout << " Faction rank: " << ref.mFactionRank << std::endl;
|
||||
std::cout << " Enchantment charge: " << ref.mEnchantmentCharge << std::endl;
|
||||
std::cout << " Uses/health: " << ref.mChargeInt << std::endl;
|
||||
std::cout << " Gold value: " << ref.mGoldValue << std::endl;
|
||||
std::cout << " Blocked: " << static_cast<int>(ref.mReferenceBlocked) << std::endl;
|
||||
std::cout << " Deleted: " << deleted << std::endl;
|
||||
std::cout << " Faction rank: " << ref.mFactionRank << '\n';
|
||||
std::cout << " Enchantment charge: " << ref.mEnchantmentCharge << '\n';
|
||||
std::cout << " Uses/health: " << ref.mChargeInt << '\n';
|
||||
std::cout << " Gold value: " << ref.mGoldValue << '\n';
|
||||
std::cout << " Blocked: " << static_cast<int>(ref.mReferenceBlocked) << '\n';
|
||||
std::cout << " Deleted: " << deleted << '\n';
|
||||
if (!ref.mKey.empty())
|
||||
std::cout << " Key: " << ref.mKey << std::endl;
|
||||
std::cout << " Lock level: " << ref.mLockLevel << std::endl;
|
||||
std::cout << " Key: " << ref.mKey << '\n';
|
||||
std::cout << " Lock level: " << ref.mLockLevel << '\n';
|
||||
if (!ref.mTrap.empty())
|
||||
std::cout << " Trap: " << ref.mTrap << std::endl;
|
||||
std::cout << " Trap: " << ref.mTrap << '\n';
|
||||
if (!ref.mSoul.empty())
|
||||
std::cout << " Soul: " << ref.mSoul << std::endl;
|
||||
std::cout << " Soul: " << ref.mSoul << '\n';
|
||||
if (ref.mTeleport)
|
||||
{
|
||||
std::cout << " Destination position: (" << ref.mDoorDest.pos[0] << ", "
|
||||
<< ref.mDoorDest.pos[1] << ", " << ref.mDoorDest.pos[2] << ")" << std::endl;
|
||||
<< ref.mDoorDest.pos[1] << ", " << ref.mDoorDest.pos[2] << ")\n";
|
||||
if (!ref.mDestCell.empty())
|
||||
std::cout << " Destination cell: " << ref.mDestCell << std::endl;
|
||||
std::cout << " Destination cell: " << ref.mDestCell << '\n';
|
||||
}
|
||||
std::cout << " Moved: " << std::boolalpha << moved << std::noboolalpha << '\n';
|
||||
if (moved)
|
||||
{
|
||||
std::cout << " Moved refnum: " << movedCellRef.mRefNum.mIndex << '\n';
|
||||
std::cout << " Moved content file: " << movedCellRef.mRefNum.mContentFile << '\n';
|
||||
std::cout << " Target: " << movedCellRef.mTarget[0] << ", " << movedCellRef.mTarget[1] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printRaw(ESM::ESMReader &esm)
|
||||
void printRawTes3(std::string_view path)
|
||||
{
|
||||
std::cout << "TES3 RAW file listing: " << path << '\n';
|
||||
ESM::ESMReader esm;
|
||||
esm.openRaw(path);
|
||||
while(esm.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = esm.getRecName();
|
||||
std::cout << "Record: " << n.toString() << std::endl;
|
||||
std::cout << "Record: " << n.toStringView() << '\n';
|
||||
esm.getRecHeader();
|
||||
while(esm.hasMoreSubs())
|
||||
{
|
||||
|
@ -306,75 +301,62 @@ void printRaw(ESM::ESMReader &esm)
|
|||
esm.skipHSub();
|
||||
n = esm.retSubName();
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
std::cout << " " << n.toString() << " - " << esm.getSubSize()
|
||||
<< " bytes @ 0x" << std::hex << offs << "\n";
|
||||
std::cout << " " << n.toStringView() << " - " << esm.getSubSize()
|
||||
<< " bytes @ 0x" << std::hex << offs << '\n';
|
||||
std::cout.flags(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int load(Arguments& info)
|
||||
int loadTes3(const Arguments& info, std::unique_ptr<std::ifstream>&& stream, ESMData* data)
|
||||
{
|
||||
ESM::ESMReader& esm = info.reader;
|
||||
std::cout << "Loading TES3 file: " << info.filename << '\n';
|
||||
|
||||
ESM::ESMReader esm;
|
||||
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
|
||||
esm.setEncoder(&encoder);
|
||||
|
||||
std::string filename = info.filename;
|
||||
std::cout << "Loading file: " << filename << std::endl;
|
||||
|
||||
std::list<int> skipped;
|
||||
|
||||
try {
|
||||
|
||||
if(info.raw_given && info.mode == "dump")
|
||||
{
|
||||
std::cout << "RAW file listing:\n";
|
||||
|
||||
esm.openRaw(filename);
|
||||
|
||||
printRaw(esm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
std::unordered_set<uint32_t> skipped;
|
||||
|
||||
try
|
||||
{
|
||||
bool quiet = (info.quiet_given || info.mode == "clone");
|
||||
bool loadCells = (info.loadcells_given || info.mode == "clone");
|
||||
bool save = (info.mode == "clone");
|
||||
|
||||
esm.open(filename);
|
||||
esm.open(std::move(stream), info.filename);
|
||||
|
||||
info.data.author = esm.getAuthor();
|
||||
info.data.description = esm.getDesc();
|
||||
info.data.masters = esm.getGameFiles();
|
||||
if (data != nullptr)
|
||||
data->mHeader = esm.getHeader();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
std::cout << "Author: " << esm.getAuthor() << std::endl
|
||||
<< "Description: " << esm.getDesc() << std::endl
|
||||
<< "File format version: " << esm.getFVer() << std::endl;
|
||||
std::cout << "Author: " << esm.getAuthor() << '\n'
|
||||
<< "Description: " << esm.getDesc() << '\n'
|
||||
<< "File format version: " << esm.getFVer() << '\n';
|
||||
std::vector<ESM::Header::MasterData> masterData = esm.getGameFiles();
|
||||
if (!masterData.empty())
|
||||
{
|
||||
std::cout << "Masters:" << std::endl;
|
||||
std::cout << "Masters:" << '\n';
|
||||
for(const auto& master : masterData)
|
||||
std::cout << " " << master.name << ", " << master.size << " bytes" << std::endl;
|
||||
std::cout << " " << master.name << ", " << master.size << " bytes\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through all records
|
||||
while(esm.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = esm.getRecName();
|
||||
const ESM::NAME n = esm.getRecName();
|
||||
uint32_t flags;
|
||||
esm.getRecHeader(flags);
|
||||
|
||||
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
|
||||
auto record = EsmTool::RecordBase::create(n);
|
||||
if (record == nullptr)
|
||||
{
|
||||
if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end())
|
||||
if (!quiet && skipped.count(n.toInt()) == 0)
|
||||
{
|
||||
std::cout << "Skipping " << n.toString() << " records." << std::endl;
|
||||
skipped.push_back(n.intval);
|
||||
std::cout << "Skipping " << n.toStringView() << " records.\n";
|
||||
skipped.emplace(n.toInt());
|
||||
}
|
||||
|
||||
esm.skipRecord();
|
||||
|
@ -390,54 +372,88 @@ int load(Arguments& info)
|
|||
|
||||
// Is the user interested in this record type?
|
||||
bool interested = true;
|
||||
if (!info.types.empty())
|
||||
{
|
||||
std::vector<std::string>::iterator match;
|
||||
match = std::find(info.types.begin(), info.types.end(), n.toString());
|
||||
if (match == info.types.end()) interested = false;
|
||||
}
|
||||
if (!info.types.empty() && std::find(info.types.begin(), info.types.end(), n.toStringView()) == info.types.end())
|
||||
interested = false;
|
||||
|
||||
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, record->getId()))
|
||||
interested = false;
|
||||
|
||||
if(!quiet && interested)
|
||||
{
|
||||
std::cout << "\nRecord: " << n.toString() << " '" << record->getId() << "'\n";
|
||||
std::cout << "\nRecord: " << n.toStringView() << " '" << record->getId() << "'\n"
|
||||
<< "Record flags: " << recordFlags(record->getFlags()) << '\n';
|
||||
record->print();
|
||||
}
|
||||
|
||||
if (record->getType().intval == ESM::REC_CELL && loadCells && interested)
|
||||
if (record->getType().toInt() == ESM::REC_CELL && loadCells && interested)
|
||||
{
|
||||
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
|
||||
loadCell(info, record->cast<ESM::Cell>()->get(), esm, data);
|
||||
}
|
||||
|
||||
if (save)
|
||||
if (data != nullptr)
|
||||
{
|
||||
info.data.mRecords.push_back(record);
|
||||
if (save)
|
||||
data->mRecords.push_back(std::move(record));
|
||||
++data->mRecordStats[n.toInt()];
|
||||
}
|
||||
else
|
||||
{
|
||||
delete record;
|
||||
}
|
||||
++info.data.mRecordStats[n.intval];
|
||||
}
|
||||
|
||||
} catch(std::exception &e) {
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cout << "\nERROR:\n\n " << e.what() << std::endl;
|
||||
|
||||
for (const EsmTool::RecordBase* record : info.data.mRecords)
|
||||
delete record;
|
||||
|
||||
info.data.mRecords.clear();
|
||||
if (data != nullptr)
|
||||
data->mRecords.clear();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <iomanip>
|
||||
int load(const Arguments& info, ESMData* data)
|
||||
{
|
||||
if (info.mRawFormat.has_value() && info.mode == "dump")
|
||||
{
|
||||
switch (*info.mRawFormat)
|
||||
{
|
||||
case ESM::Format::Tes3:
|
||||
printRawTes3(info.filename);
|
||||
break;
|
||||
case ESM::Format::Tes4:
|
||||
std::cout << "Printing raw TES4 file is not supported: " << info.filename << "\n";
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clone(Arguments& info)
|
||||
auto stream = Files::openBinaryInputFileStream(info.filename);
|
||||
if (!stream->is_open())
|
||||
{
|
||||
std::cout << "Failed to open file: " << std::strerror(errno) << '\n';
|
||||
return -1;
|
||||
}
|
||||
|
||||
const ESM::Format format = ESM::readFormat(*stream);
|
||||
stream->seekg(0);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case ESM::Format::Tes3:
|
||||
return loadTes3(info, std::move(stream), data);
|
||||
case ESM::Format::Tes4:
|
||||
if (data != nullptr)
|
||||
{
|
||||
std::cout << "Collecting data from esm file is not supported for TES4\n";
|
||||
return -1;
|
||||
}
|
||||
return loadTes4(info, std::move(stream));
|
||||
}
|
||||
|
||||
std::cout << "Unsupported ESM format: " << ESM::NAME(format).toStringView() << '\n';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int clone(const Arguments& info)
|
||||
{
|
||||
if (info.outname.empty())
|
||||
{
|
||||
|
@ -445,71 +461,68 @@ int clone(Arguments& info)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (load(info) != 0)
|
||||
ESMData data;
|
||||
if (load(info, &data) != 0)
|
||||
{
|
||||
std::cout << "Failed to load, aborting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t recordCount = info.data.mRecords.size();
|
||||
size_t recordCount = data.mRecords.size();
|
||||
|
||||
int digitCount = 1; // For a nicer output
|
||||
if (recordCount > 0)
|
||||
digitCount = (int)std::log10(recordCount) + 1;
|
||||
|
||||
std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl;
|
||||
std::cout << "Loaded " << recordCount << " records:\n\n";
|
||||
|
||||
int i = 0;
|
||||
for (std::pair<int, int> stat : info.data.mRecordStats)
|
||||
for (std::pair<int, int> stat : data.mRecordStats)
|
||||
{
|
||||
ESM::NAME name;
|
||||
name.intval = stat.first;
|
||||
name = stat.first;
|
||||
int amount = stat.second;
|
||||
std::cout << std::setw(digitCount) << amount << " " << name.toString() << " ";
|
||||
std::cout << std::setw(digitCount) << amount << " " << name.toStringView() << " ";
|
||||
if (++i % 3 == 0)
|
||||
std::cout << std::endl;
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
if (i % 3 != 0)
|
||||
std::cout << std::endl;
|
||||
std::cout << '\n';
|
||||
|
||||
std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl;
|
||||
std::cout << "\nSaving records to: " << info.outname << "...\n";
|
||||
|
||||
ESM::ESMWriter& esm = info.writer;
|
||||
ESM::ESMWriter esm;
|
||||
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
|
||||
esm.setEncoder(&encoder);
|
||||
esm.setAuthor(info.data.author);
|
||||
esm.setDescription(info.data.description);
|
||||
esm.setVersion(info.data.version);
|
||||
esm.setHeader(data.mHeader);
|
||||
esm.setVersion(ESM::VER_13);
|
||||
esm.setRecordCount (recordCount);
|
||||
|
||||
for (const ESM::Header::MasterData &master : info.data.masters)
|
||||
esm.addMaster(master.name, master.size);
|
||||
|
||||
std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary);
|
||||
esm.save(save);
|
||||
|
||||
int saved = 0;
|
||||
for (EsmTool::RecordBase* record : info.data.mRecords)
|
||||
for (auto& record : data.mRecords)
|
||||
{
|
||||
if (i <= 0)
|
||||
break;
|
||||
|
||||
const ESM::NAME& typeName = record->getType();
|
||||
const ESM::NAME typeName = record->getType();
|
||||
|
||||
esm.startRecord(typeName.toString(), record->getFlags());
|
||||
esm.startRecord(typeName, record->getFlags());
|
||||
|
||||
record->save(esm);
|
||||
if (typeName.intval == ESM::REC_CELL) {
|
||||
if (typeName.toInt() == ESM::REC_CELL) {
|
||||
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
|
||||
if (!info.data.mCellRefs[ptr].empty())
|
||||
if (!data.mCellRefs[ptr].empty())
|
||||
{
|
||||
for (std::pair<ESM::CellRef, bool> &ref : info.data.mCellRefs[ptr])
|
||||
for (std::pair<ESM::CellRef, bool> &ref : data.mCellRefs[ptr])
|
||||
ref.first.save(esm, ref.second);
|
||||
}
|
||||
}
|
||||
|
||||
esm.endRecord(typeName.toString());
|
||||
esm.endRecord(typeName);
|
||||
|
||||
saved++;
|
||||
int perc = recordCount == 0 ? 100 : (int)((saved / (float)recordCount)*100);
|
||||
|
@ -527,7 +540,7 @@ int clone(Arguments& info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int comp(Arguments& info)
|
||||
int comp(const Arguments& info)
|
||||
{
|
||||
if (info.filename.empty() || info.outname.empty())
|
||||
{
|
||||
|
@ -538,9 +551,6 @@ int comp(Arguments& info)
|
|||
Arguments fileOne;
|
||||
Arguments fileTwo;
|
||||
|
||||
fileOne.raw_given = false;
|
||||
fileTwo.raw_given = false;
|
||||
|
||||
fileOne.mode = "clone";
|
||||
fileTwo.mode = "clone";
|
||||
|
||||
|
@ -550,19 +560,21 @@ int comp(Arguments& info)
|
|||
fileOne.filename = info.filename;
|
||||
fileTwo.filename = info.outname;
|
||||
|
||||
if (load(fileOne) != 0)
|
||||
ESMData dataOne;
|
||||
if (load(fileOne, &dataOne) != 0)
|
||||
{
|
||||
std::cout << "Failed to load " << info.filename << ", aborting comparison." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (load(fileTwo) != 0)
|
||||
ESMData dataTwo;
|
||||
if (load(fileTwo, &dataTwo) != 0)
|
||||
{
|
||||
std::cout << "Failed to load " << info.outname << ", aborting comparison." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fileOne.data.mRecords.size() != fileTwo.data.mRecords.size())
|
||||
if (dataOne.mRecords.size() != dataTwo.mRecords.size())
|
||||
{
|
||||
std::cout << "Not equal, different amount of records." << std::endl;
|
||||
return 1;
|
||||
|
@ -570,3 +582,5 @@ int comp(Arguments& info)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
#include "labels.hpp"
|
||||
|
||||
#include <components/esm/loadbody.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/esm/loadcont.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadlevlist.hpp>
|
||||
#include <components/esm/loadligh.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/loadrace.hpp>
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm3/loadbody.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/esm3/loadcont.hpp>
|
||||
#include <components/esm3/loadcrea.hpp>
|
||||
#include <components/esm3/loadench.hpp>
|
||||
#include <components/esm3/loadlevlist.hpp>
|
||||
#include <components/esm3/loadligh.hpp>
|
||||
#include <components/esm3/loadmgef.hpp>
|
||||
#include <components/esm3/loadnpc.hpp>
|
||||
#include <components/esm3/loadrace.hpp>
|
||||
#include <components/esm3/loadspel.hpp>
|
||||
#include <components/esm3/loadweap.hpp>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
|
@ -902,3 +902,17 @@ std::string weaponFlags(int flags)
|
|||
properties += Misc::StringUtils::format("(0x%08X)", flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string recordFlags(uint32_t flags)
|
||||
{
|
||||
std::string properties;
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::FLAG_Deleted) properties += "Deleted ";
|
||||
if (flags & ESM::FLAG_Persistent) properties += "Persistent ";
|
||||
if (flags & ESM::FLAG_Ignored) properties += "Ignored ";
|
||||
if (flags & ESM::FLAG_Blocked) properties += "Blocked ";
|
||||
int unused = ~(ESM::FLAG_Deleted | ESM::FLAG_Persistent | ESM::FLAG_Ignored | ESM::FLAG_Blocked);
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += Misc::StringUtils::format("(0x%08X)", flags);
|
||||
return properties;
|
||||
}
|
|
@ -60,6 +60,8 @@ std::string raceFlags(int flags);
|
|||
std::string spellFlags(int flags);
|
||||
std::string weaponFlags(int flags);
|
||||
|
||||
std::string recordFlags(uint32_t flags);
|
||||
|
||||
// Missing flags functions:
|
||||
// aiServicesFlags, possibly more
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ void printAIPackage(const ESM::AIPackage& p)
|
|||
{
|
||||
std::cout << " Travel Coordinates: (" << p.mTravel.mX << ","
|
||||
<< p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl;
|
||||
std::cout << " Travel Unknown: " << p.mTravel.mUnk << std::endl;
|
||||
std::cout << " Should repeat: " << p.mTravel.mShouldRepeat << std::endl;
|
||||
}
|
||||
else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort)
|
||||
{
|
||||
|
@ -38,12 +38,12 @@ void printAIPackage(const ESM::AIPackage& p)
|
|||
<< p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl;
|
||||
std::cout << " Duration: " << p.mTarget.mDuration << std::endl;
|
||||
std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl;
|
||||
std::cout << " Unknown: " << p.mTarget.mUnk << std::endl;
|
||||
std::cout << " Should repeat: " << p.mTarget.mShouldRepeat << std::endl;
|
||||
}
|
||||
else if (p.mType == ESM::AI_Activate)
|
||||
{
|
||||
std::cout << " Name: " << p.mActivate.mName.toString() << std::endl;
|
||||
std::cout << " Activate Unknown: " << p.mActivate.mUnk << std::endl;
|
||||
std::cout << " Should repeat: " << p.mActivate.mShouldRepeat << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << " BadPackage: " << Misc::StringUtils::format("0x%08X", p.mType) << std::endl;
|
||||
|
@ -171,226 +171,227 @@ void printTransport(const std::vector<ESM::Transport::Dest>& transport)
|
|||
|
||||
namespace EsmTool {
|
||||
|
||||
RecordBase *
|
||||
RecordBase::create(ESM::NAME type)
|
||||
std::unique_ptr<RecordBase> RecordBase::create(const ESM::NAME type)
|
||||
{
|
||||
RecordBase *record = nullptr;
|
||||
std::unique_ptr<RecordBase> record;
|
||||
|
||||
switch (type.intval) {
|
||||
switch (type.toInt())
|
||||
{
|
||||
case ESM::REC_ACTI:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Activator>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Activator>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_ALCH:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Potion>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Potion>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_APPA:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Apparatus>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Apparatus>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_ARMO:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Armor>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Armor>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_BODY:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::BodyPart>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::BodyPart>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_BOOK:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Book>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Book>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_BSGN:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::BirthSign>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::BirthSign>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_CELL:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Cell>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Cell>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_CLAS:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Class>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Class>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_CLOT:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Clothing>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Clothing>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_CONT:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Container>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Container>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_CREA:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Creature>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Creature>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_DIAL:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Dialogue>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Dialogue>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_DOOR:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Door>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Door>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_ENCH:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Enchantment>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Enchantment>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_FACT:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Faction>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Faction>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_GLOB:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Global>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Global>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_GMST:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::GameSetting>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::GameSetting>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_INFO:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::DialInfo>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::DialInfo>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_INGR:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Ingredient>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Ingredient>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_LAND:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Land>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Land>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_LEVI:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::ItemLevList>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::ItemLevList>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_LEVC:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::CreatureLevList>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::CreatureLevList>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_LIGH:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Light>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Light>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_LOCK:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Lockpick>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Lockpick>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_LTEX:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::LandTexture>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::LandTexture>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_MISC:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Miscellaneous>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Miscellaneous>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_MGEF:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::MagicEffect>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::MagicEffect>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_NPC_:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::NPC>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::NPC>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_PGRD:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Pathgrid>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Pathgrid>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_PROB:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Probe>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Probe>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_RACE:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Race>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Race>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_REGN:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Region>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Region>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_REPA:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Repair>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Repair>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_SCPT:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Script>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Script>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_SKIL:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Skill>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Skill>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_SNDG:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::SoundGenerator>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::SoundGenerator>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_SOUN:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Sound>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Sound>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_SPEL:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Spell>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Spell>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_STAT:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Static>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Static>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_WEAP:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::Weapon>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::Weapon>>();
|
||||
break;
|
||||
}
|
||||
case ESM::REC_SSCR:
|
||||
{
|
||||
record = new EsmTool::Record<ESM::StartScript>;
|
||||
record = std::make_unique<EsmTool::Record<ESM::StartScript>>();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
record = nullptr;
|
||||
break;
|
||||
}
|
||||
if (record) {
|
||||
if (record)
|
||||
{
|
||||
record->mType = type;
|
||||
}
|
||||
return record;
|
||||
|
@ -1183,8 +1184,8 @@ void Record<ESM::Region>::print()
|
|||
std::cout << " Thunder: " << (int)mData.mData.mThunder << std::endl;
|
||||
std::cout << " Ash: " << (int)mData.mData.mAsh << std::endl;
|
||||
std::cout << " Blight: " << (int)mData.mData.mBlight << std::endl;
|
||||
std::cout << " UnknownA: " << (int)mData.mData.mA << std::endl;
|
||||
std::cout << " UnknownB: " << (int)mData.mData.mB << std::endl;
|
||||
std::cout << " Snow: " << (int)mData.mData.mSnow << std::endl;
|
||||
std::cout << " Blizzard: " << (int)mData.mData.mBlizzard << std::endl;
|
||||
std::cout << " Map Color: " << mData.mMapColor << std::endl;
|
||||
if (!mData.mSleepList.empty())
|
||||
std::cout << " Sleep List: " << mData.mSleepList << std::endl;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define OPENMW_ESMTOOL_RECORD_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <components/esm/records.hpp>
|
||||
|
||||
|
@ -54,7 +55,7 @@ namespace EsmTool
|
|||
virtual void save(ESM::ESMWriter &esm) = 0;
|
||||
virtual void print() = 0;
|
||||
|
||||
static RecordBase *create(ESM::NAME type);
|
||||
static std::unique_ptr<RecordBase> create(ESM::NAME type);
|
||||
|
||||
// just make it a bit shorter
|
||||
template <class T>
|
||||
|
|
329
apps/esmtool/tes4.cpp
Normal file
329
apps/esmtool/tes4.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
15
apps/esmtool/tes4.hpp
Normal file
15
apps/esmtool/tes4.hpp
Normal file
|
@ -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
|
|
@ -36,7 +36,6 @@ openmw_add_executable(openmw-essimporter
|
|||
|
||||
target_link_libraries(openmw-essimporter
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
|
@ -48,3 +47,13 @@ endif()
|
|||
if (WIN32)
|
||||
INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".")
|
||||
endif(WIN32)
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||
target_precompile_headers(openmw-essimporter PRIVATE
|
||||
<algorithm>
|
||||
<filesystem>
|
||||
<fstream>
|
||||
<string>
|
||||
<vector>
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#ifndef OPENMW_ESSIMPORT_CONVERTACDT_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTACDT_H
|
||||
|
||||
#include <components/esm/creaturestats.hpp>
|
||||
#include <components/esm/npcstats.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
#include <components/esm/animationstate.hpp>
|
||||
#include <components/esm3/creaturestats.hpp>
|
||||
#include <components/esm3/npcstats.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
#include <components/esm3/animationstate.hpp>
|
||||
|
||||
#include "importacdt.hpp"
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "importcntc.hpp"
|
||||
|
||||
#include <components/esm/containerstate.hpp>
|
||||
#include <components/esm3/containerstate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "importcrec.hpp"
|
||||
|
||||
#include <components/esm/creaturestate.hpp>
|
||||
#include <components/esm3/creaturestate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
#include <osgDB/WriteFile>
|
||||
|
||||
#include <components/esm/creaturestate.hpp>
|
||||
#include <components/esm/containerstate.hpp>
|
||||
#include <components/esm3/creaturestate.hpp>
|
||||
#include <components/esm3/containerstate.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace
|
|||
{
|
||||
if (isIndexedRefId(indexedRefId))
|
||||
{
|
||||
int refIndex;
|
||||
int refIndex = 0;
|
||||
std::string refId;
|
||||
splitIndexedRefId(indexedRefId, refIndex, refId);
|
||||
|
||||
|
@ -278,7 +278,7 @@ namespace ESSImport
|
|||
while (esm.isNextSub("MPCD"))
|
||||
{
|
||||
float notepos[3];
|
||||
esm.getHT(notepos, 3*sizeof(float));
|
||||
esm.getHTSized<3 * sizeof(float)>(notepos);
|
||||
|
||||
// Markers seem to be arranged in a 32*32 grid, notepos has grid-indices.
|
||||
// This seems to be the reason markers can't be placed everywhere in interior cells,
|
||||
|
@ -320,6 +320,8 @@ namespace ESSImport
|
|||
esm.startRecord(ESM::REC_CSTA);
|
||||
ESM::CellState csta;
|
||||
csta.mHasFogOfWar = 0;
|
||||
csta.mLastRespawn.mDay = 0;
|
||||
csta.mLastRespawn.mHour = 0;
|
||||
csta.mId = esmcell.getCellId();
|
||||
csta.mId.save(esm);
|
||||
// TODO csta.mLastRespawn;
|
||||
|
@ -352,12 +354,12 @@ namespace ESSImport
|
|||
}
|
||||
else
|
||||
{
|
||||
int refIndex;
|
||||
int refIndex = 0;
|
||||
splitIndexedRefId(cellref.mIndexedRefId, refIndex, out.mRefID);
|
||||
|
||||
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
|
||||
|
||||
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
|
||||
auto npccIt = mContext->mNpcChanges.find(
|
||||
std::make_pair(refIndex, out.mRefID));
|
||||
if (npccIt != mContext->mNpcChanges.end())
|
||||
{
|
||||
|
@ -369,6 +371,8 @@ namespace ESSImport
|
|||
// from the ESM with default values
|
||||
if (cellref.mHasACDT)
|
||||
convertACDT(cellref.mACDT, objstate.mCreatureStats);
|
||||
else
|
||||
objstate.mCreatureStats.mMissingACDT = true;
|
||||
if (cellref.mHasACSC)
|
||||
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
||||
convertNpcData(cellref, objstate.mNpcStats);
|
||||
|
@ -383,7 +387,7 @@ namespace ESSImport
|
|||
continue;
|
||||
}
|
||||
|
||||
std::map<std::pair<int, std::string>, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find(
|
||||
auto cntcIt = mContext->mContainerChanges.find(
|
||||
std::make_pair(refIndex, out.mRefID));
|
||||
if (cntcIt != mContext->mContainerChanges.end())
|
||||
{
|
||||
|
@ -398,7 +402,7 @@ namespace ESSImport
|
|||
continue;
|
||||
}
|
||||
|
||||
std::map<std::pair<int, std::string>, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find(
|
||||
auto crecIt = mContext->mCreatureChanges.find(
|
||||
std::make_pair(refIndex, out.mRefID));
|
||||
if (crecIt != mContext->mCreatureChanges.end())
|
||||
{
|
||||
|
@ -410,6 +414,8 @@ namespace ESSImport
|
|||
// from the ESM with default values
|
||||
if (cellref.mHasACDT)
|
||||
convertACDT(cellref.mACDT, objstate.mCreatureStats);
|
||||
else
|
||||
objstate.mCreatureStats.mMissingACDT = true;
|
||||
if (cellref.mHasACSC)
|
||||
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
||||
convertCREC(crecIt->second, objstate);
|
||||
|
@ -486,6 +492,7 @@ namespace ESSImport
|
|||
|
||||
out.mSpellId = it->mSPDT.mId.toString();
|
||||
out.mSpeed = pnam.mSpeed * 0.001f; // not sure where this factor comes from
|
||||
out.mSlot = 0;
|
||||
|
||||
esm.startRecord(ESM::REC_MPRJ);
|
||||
out.save(esm);
|
||||
|
|
|
@ -6,23 +6,23 @@
|
|||
#include <osg/Image>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/esm/loadbook.hpp>
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loadglob.hpp>
|
||||
#include <components/esm/cellstate.hpp>
|
||||
#include <components/esm/loadfact.hpp>
|
||||
#include <components/esm/dialoguestate.hpp>
|
||||
#include <components/esm/custommarkerstate.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/weatherstate.hpp>
|
||||
#include <components/esm/globalscript.hpp>
|
||||
#include <components/esm/queststate.hpp>
|
||||
#include <components/esm/stolenitems.hpp>
|
||||
#include <components/esm/projectilestate.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/esm3/loadbook.hpp>
|
||||
#include <components/esm3/loadclas.hpp>
|
||||
#include <components/esm3/loadglob.hpp>
|
||||
#include <components/esm3/cellstate.hpp>
|
||||
#include <components/esm3/loadfact.hpp>
|
||||
#include <components/esm3/dialoguestate.hpp>
|
||||
#include <components/esm3/custommarkerstate.hpp>
|
||||
#include <components/esm3/loadcrea.hpp>
|
||||
#include <components/esm3/weatherstate.hpp>
|
||||
#include <components/esm3/globalscript.hpp>
|
||||
#include <components/esm3/queststate.hpp>
|
||||
#include <components/esm3/stolenitems.hpp>
|
||||
#include <components/esm3/projectilestate.hpp>
|
||||
|
||||
#include "importcrec.hpp"
|
||||
#include "importcntc.hpp"
|
||||
|
@ -124,11 +124,9 @@ public:
|
|||
{
|
||||
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt.mLevel;
|
||||
mContext->mPlayerBase = npc;
|
||||
ESM::SpellState::SpellParams empty;
|
||||
// FIXME: player start spells and birthsign spells aren't listed here,
|
||||
// need to fix openmw to account for this
|
||||
for (const auto & spell : npc.mSpells.mList)
|
||||
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty;
|
||||
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells = npc.mSpells.mList;
|
||||
|
||||
// Clear the list now that we've written it, this prevents issues cropping up with
|
||||
// ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal.
|
||||
|
@ -374,7 +372,7 @@ public:
|
|||
void write(ESM::ESMWriter &esm) override
|
||||
{
|
||||
esm.startRecord(ESM::REC_DCOU);
|
||||
for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
|
||||
for (auto it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
|
||||
{
|
||||
esm.writeHNString("ID__", it->first);
|
||||
esm.writeHNT ("COUN", it->second);
|
||||
|
@ -397,7 +395,7 @@ public:
|
|||
faction.load(esm, isDeleted);
|
||||
std::string id = Misc::StringUtils::lowerCase(faction.mId);
|
||||
|
||||
for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
|
||||
for (auto it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
|
||||
{
|
||||
std::string faction2 = Misc::StringUtils::lowerCase(it->first);
|
||||
mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second));
|
||||
|
@ -431,7 +429,7 @@ public:
|
|||
void write(ESM::ESMWriter &esm) override
|
||||
{
|
||||
ESM::StolenItems items;
|
||||
for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
|
||||
for (auto it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
|
||||
{
|
||||
std::map<std::pair<std::string, bool>, int> owners;
|
||||
for (const auto & ownerIt : it->second)
|
||||
|
@ -487,7 +485,7 @@ public:
|
|||
}
|
||||
void write(ESM::ESMWriter &esm) override
|
||||
{
|
||||
for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)
|
||||
for (auto it = mDials.begin(); it != mDials.end(); ++it)
|
||||
{
|
||||
esm.startRecord(ESM::REC_QUES);
|
||||
ESM::QuestState state;
|
||||
|
@ -545,9 +543,7 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Invalid weather ID:" << weatherID << std::endl;
|
||||
throw std::runtime_error(error.str());
|
||||
throw std::runtime_error("Invalid weather ID: " + std::to_string(weatherID));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "importinventory.hpp"
|
||||
|
||||
#include <components/esm/inventorystate.hpp>
|
||||
#include <components/esm3/inventorystate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "importnpcc.hpp"
|
||||
|
||||
#include <components/esm/npcstate.hpp>
|
||||
#include <components/esm3/npcstate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#include "importplayer.hpp"
|
||||
|
||||
#include <components/esm/player.hpp>
|
||||
#include <components/esm/controlsstate.hpp>
|
||||
#include <components/esm3/player.hpp>
|
||||
#include <components/esm3/controlsstate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace ESSImport
|
|||
{
|
||||
out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString());
|
||||
out.mRunning = scpt.mRunning;
|
||||
out.mTargetRef.unset(); // TODO: convert target reference of global script
|
||||
convertSCRI(scpt.mSCRI, out.mLocals);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef OPENMW_ESSIMPORT_CONVERTSCPT_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTSCPT_H
|
||||
|
||||
#include <components/esm/globalscript.hpp>
|
||||
#include <components/esm3/globalscript.hpp>
|
||||
|
||||
#include "importscpt.hpp"
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "importscri.hpp"
|
||||
|
||||
#include <components/esm/locals.hpp>
|
||||
#include <components/esm3/locals.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#include "importacdt.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm3/cellref.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void ActorData::load(ESM::ESMReader &esm)
|
||||
{
|
||||
blank();
|
||||
|
||||
if (esm.isNextSub("ACTN"))
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm3/cellref.hpp>
|
||||
|
||||
#include "importscri.hpp"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importcellref.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
@ -35,8 +35,8 @@ namespace ESSImport
|
|||
// DATA should occur for all references, except levelled creature spawners
|
||||
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird
|
||||
// alarmvoi0000.ess
|
||||
esm.getHNOT(mPos, "DATA", 24);
|
||||
esm.getHNOT(mPos, "DATA", 24);
|
||||
esm.getHNOTSized<24>(mPos, "DATA");
|
||||
esm.getHNOTSized<24>(mPos, "DATA");
|
||||
|
||||
mDeleted = 0;
|
||||
if (esm.isNextSub("DELE"))
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm3/cellref.hpp>
|
||||
|
||||
#include "importacdt.hpp"
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace ESSImport
|
|||
|
||||
void load(ESM::ESMReader& esm) override;
|
||||
|
||||
virtual ~CellRef() = default;
|
||||
~CellRef() override = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importcntc.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importcrec.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define OPENMW_ESSIMPORT_CREC_H
|
||||
|
||||
#include "importinventory.hpp"
|
||||
#include <components/esm/aipackage.hpp>
|
||||
#include <components/esm3/aipackage.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importdial.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
#include "importer.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osg/ImageUtils>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/esm/defs.hpp>
|
||||
|
||||
#include <components/esm/savedgame.hpp>
|
||||
#include <components/esm/player.hpp>
|
||||
#include <components/esm3/savedgame.hpp>
|
||||
#include <components/esm3/player.hpp>
|
||||
|
||||
#include <components/esm/loadalch.hpp>
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loadarmo.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/loadclot.hpp>
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadlevlist.hpp>
|
||||
#include <components/esm3/loadalch.hpp>
|
||||
#include <components/esm3/loadspel.hpp>
|
||||
#include <components/esm3/loadarmo.hpp>
|
||||
#include <components/esm3/loadweap.hpp>
|
||||
#include <components/esm3/loadclot.hpp>
|
||||
#include <components/esm3/loadench.hpp>
|
||||
#include <components/esm3/loadlevlist.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
|
@ -264,48 +263,48 @@ namespace ESSImport
|
|||
const ESM::Header& header = esm.getHeader();
|
||||
context.mPlayerCellName = header.mGameData.mCurrentCell.toString();
|
||||
|
||||
const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value;
|
||||
const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value;
|
||||
const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value;
|
||||
const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value;
|
||||
const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;
|
||||
const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value;
|
||||
const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value;
|
||||
const unsigned int recSPLM = ESM::FourCC<'S','P','L','M'>::value;
|
||||
const unsigned int recREFR = ESM::fourCC("REFR");
|
||||
const unsigned int recPCDT = ESM::fourCC("PCDT");
|
||||
const unsigned int recFMAP = ESM::fourCC("FMAP");
|
||||
const unsigned int recKLST = ESM::fourCC("KLST");
|
||||
const unsigned int recSTLN = ESM::fourCC("STLN");
|
||||
const unsigned int recGAME = ESM::fourCC("GAME");
|
||||
const unsigned int recJOUR = ESM::fourCC("JOUR");
|
||||
const unsigned int recSPLM = ESM::fourCC("SPLM");
|
||||
|
||||
std::map<unsigned int, std::shared_ptr<Converter> > converters;
|
||||
converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal());
|
||||
converters[ESM::REC_BOOK] = std::shared_ptr<Converter>(new ConvertBook());
|
||||
converters[ESM::REC_NPC_] = std::shared_ptr<Converter>(new ConvertNPC());
|
||||
converters[ESM::REC_CREA] = std::shared_ptr<Converter>(new ConvertCREA());
|
||||
converters[ESM::REC_NPCC] = std::shared_ptr<Converter>(new ConvertNPCC());
|
||||
converters[ESM::REC_CREC] = std::shared_ptr<Converter>(new ConvertCREC());
|
||||
converters[recREFR ] = std::shared_ptr<Converter>(new ConvertREFR());
|
||||
converters[recPCDT ] = std::shared_ptr<Converter>(new ConvertPCDT());
|
||||
converters[recFMAP ] = std::shared_ptr<Converter>(new ConvertFMAP());
|
||||
converters[recKLST ] = std::shared_ptr<Converter>(new ConvertKLST());
|
||||
converters[recSTLN ] = std::shared_ptr<Converter>(new ConvertSTLN());
|
||||
converters[recGAME ] = std::shared_ptr<Converter>(new ConvertGAME());
|
||||
converters[ESM::REC_CELL] = std::shared_ptr<Converter>(new ConvertCell());
|
||||
converters[ESM::REC_ALCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
|
||||
converters[ESM::REC_CLAS] = std::shared_ptr<Converter>(new ConvertClass());
|
||||
converters[ESM::REC_SPEL] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Spell>());
|
||||
converters[ESM::REC_ARMO] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Armor>());
|
||||
converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||
converters[ESM::REC_CLOT] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Clothing>());
|
||||
converters[ESM::REC_ENCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Enchantment>());
|
||||
converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||
converters[ESM::REC_LEVC] = std::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());
|
||||
converters[ESM::REC_LEVI] = std::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());
|
||||
converters[ESM::REC_CNTC] = std::shared_ptr<Converter>(new ConvertCNTC());
|
||||
converters[ESM::REC_FACT] = std::shared_ptr<Converter>(new ConvertFACT());
|
||||
converters[ESM::REC_INFO] = std::shared_ptr<Converter>(new ConvertINFO());
|
||||
converters[ESM::REC_DIAL] = std::shared_ptr<Converter>(new ConvertDIAL());
|
||||
converters[ESM::REC_QUES] = std::shared_ptr<Converter>(new ConvertQUES());
|
||||
converters[recJOUR ] = std::shared_ptr<Converter>(new ConvertJOUR());
|
||||
converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT());
|
||||
converters[ESM::REC_PROJ] = std::shared_ptr<Converter>(new ConvertPROJ());
|
||||
converters[recSPLM] = std::shared_ptr<Converter>(new ConvertSPLM());
|
||||
std::map<unsigned int, std::unique_ptr<Converter>> converters;
|
||||
converters[ESM::REC_GLOB] = std::make_unique<ConvertGlobal>();
|
||||
converters[ESM::REC_BOOK] = std::make_unique<ConvertBook>();
|
||||
converters[ESM::REC_NPC_] = std::make_unique<ConvertNPC>();
|
||||
converters[ESM::REC_CREA] = std::make_unique<ConvertCREA>();
|
||||
converters[ESM::REC_NPCC] = std::make_unique<ConvertNPCC>();
|
||||
converters[ESM::REC_CREC] = std::make_unique<ConvertCREC>();
|
||||
converters[recREFR ] = std::make_unique<ConvertREFR>();
|
||||
converters[recPCDT ] = std::make_unique<ConvertPCDT>();
|
||||
converters[recFMAP ] = std::make_unique<ConvertFMAP>();
|
||||
converters[recKLST ] = std::make_unique<ConvertKLST>();
|
||||
converters[recSTLN ] = std::make_unique<ConvertSTLN>();
|
||||
converters[recGAME ] = std::make_unique<ConvertGAME>();
|
||||
converters[ESM::REC_CELL] = std::make_unique<ConvertCell>();
|
||||
converters[ESM::REC_ALCH] = std::make_unique<DefaultConverter<ESM::Potion>>();
|
||||
converters[ESM::REC_CLAS] = std::make_unique<ConvertClass>();
|
||||
converters[ESM::REC_SPEL] = std::make_unique<DefaultConverter<ESM::Spell>>();
|
||||
converters[ESM::REC_ARMO] = std::make_unique<DefaultConverter<ESM::Armor>>();
|
||||
converters[ESM::REC_WEAP] = std::make_unique<DefaultConverter<ESM::Weapon>>();
|
||||
converters[ESM::REC_CLOT] = std::make_unique<DefaultConverter<ESM::Clothing>>();
|
||||
converters[ESM::REC_ENCH] = std::make_unique<DefaultConverter<ESM::Enchantment>>();
|
||||
converters[ESM::REC_WEAP] = std::make_unique<DefaultConverter<ESM::Weapon>>();
|
||||
converters[ESM::REC_LEVC] = std::make_unique<DefaultConverter<ESM::CreatureLevList>>();
|
||||
converters[ESM::REC_LEVI] = std::make_unique<DefaultConverter<ESM::ItemLevList>>();
|
||||
converters[ESM::REC_CNTC] = std::make_unique<ConvertCNTC>();
|
||||
converters[ESM::REC_FACT] = std::make_unique<ConvertFACT>();
|
||||
converters[ESM::REC_INFO] = std::make_unique<ConvertINFO>();
|
||||
converters[ESM::REC_DIAL] = std::make_unique<ConvertDIAL>();
|
||||
converters[ESM::REC_QUES] = std::make_unique<ConvertQUES>();
|
||||
converters[recJOUR ] = std::make_unique<ConvertJOUR>();
|
||||
converters[ESM::REC_SCPT] = std::make_unique<ConvertSCPT>();
|
||||
converters[ESM::REC_PROJ] = std::make_unique<ConvertPROJ>();
|
||||
converters[recSPLM] = std::make_unique<ConvertSPLM>();
|
||||
|
||||
// TODO:
|
||||
// - REGN (weather in certain regions?)
|
||||
|
@ -324,14 +323,14 @@ namespace ESSImport
|
|||
ESM::NAME n = esm.getRecName();
|
||||
esm.getRecHeader();
|
||||
|
||||
auto it = converters.find(n.intval);
|
||||
auto it = converters.find(n.toInt());
|
||||
if (it != converters.end())
|
||||
{
|
||||
it->second->read(esm);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unknownRecords.insert(n.intval).second)
|
||||
if (unknownRecords.insert(n.toInt()).second)
|
||||
{
|
||||
std::ios::fmtflags f(std::cerr.flags());
|
||||
std::cerr << "Error: unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
|
||||
|
@ -346,7 +345,7 @@ namespace ESSImport
|
|||
|
||||
writer.setFormat (ESM::SavedGame::sCurrentFormat);
|
||||
|
||||
boost::filesystem::ofstream stream(boost::filesystem::path(mOutFile), std::ios::out | std::ios::binary);
|
||||
std::ofstream stream(std::filesystem::path(mOutFile), std::ios::out | std::ios::binary);
|
||||
// all unused
|
||||
writer.setVersion(0);
|
||||
writer.setType(0);
|
||||
|
@ -369,6 +368,7 @@ namespace ESSImport
|
|||
profile.mInGameTime.mGameHour = context.mHour;
|
||||
profile.mInGameTime.mMonth = context.mMonth;
|
||||
profile.mInGameTime.mYear = context.mYear;
|
||||
profile.mTimePlayed = 0;
|
||||
profile.mPlayerCell = header.mGameData.mCurrentCell.toString();
|
||||
if (context.mPlayerBase.mClass == "NEWCLASSID_CHARGEN")
|
||||
profile.mPlayerClassName = context.mCustomPlayerClassName;
|
||||
|
@ -385,7 +385,7 @@ namespace ESSImport
|
|||
|
||||
// Writing order should be Dynamic Store -> Cells -> Player,
|
||||
// so that references to dynamic records can be recognized when loading
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
for (auto it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
{
|
||||
if (it->second->getStage() != 0)
|
||||
|
@ -398,7 +398,7 @@ namespace ESSImport
|
|||
context.mPlayerBase.save(writer);
|
||||
writer.endRecord(ESM::REC_NPC_);
|
||||
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
for (auto it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
{
|
||||
if (it->second->getStage() != 1)
|
||||
|
@ -423,7 +423,7 @@ namespace ESSImport
|
|||
writer.endRecord(ESM::REC_ACTC);
|
||||
|
||||
// Stage 2 requires cell references to be written / actors IDs assigned
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
for (auto it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
{
|
||||
if (it->second->getStage() != 2)
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/player.hpp>
|
||||
#include <components/esm/dialoguestate.hpp>
|
||||
#include <components/esm/globalmap.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/controlsstate.hpp>
|
||||
#include <components/esm3/loadnpc.hpp>
|
||||
#include <components/esm3/player.hpp>
|
||||
#include <components/esm3/dialoguestate.hpp>
|
||||
#include <components/esm3/globalmap.hpp>
|
||||
#include <components/esm3/loadcrea.hpp>
|
||||
#include <components/esm3/loadnpc.hpp>
|
||||
#include <components/esm3/controlsstate.hpp>
|
||||
|
||||
#include "importnpcc.hpp"
|
||||
#include "importcrec.hpp"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importgame.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importinfo.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
@ -19,6 +19,7 @@ namespace ESSImport
|
|||
item.mCount = contItem.mCount;
|
||||
item.mRelativeEquipmentSlot = -1;
|
||||
item.mLockLevel = 0;
|
||||
item.mRefNum.unset();
|
||||
|
||||
unsigned int itemCount = std::abs(item.mCount);
|
||||
bool separateStacks = false;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm3/cellref.hpp>
|
||||
#include <components/esm/esmcommon.hpp>
|
||||
|
||||
#include "importscri.hpp"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importjour.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importklst.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importnpcc.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#ifndef OPENMW_ESSIMPORT_NPCC_H
|
||||
#define OPENMW_ESSIMPORT_NPCC_H
|
||||
|
||||
#include <components/esm/loadcont.hpp>
|
||||
#include <components/esm3/loadcont.hpp>
|
||||
|
||||
#include <components/esm/aipackage.hpp>
|
||||
#include <components/esm3/aipackage.hpp>
|
||||
|
||||
#include "importinventory.hpp"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importplayer.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace ESSImport
|
|||
|
||||
mActorData.load(esm);
|
||||
|
||||
esm.getHNOT(mPos, "DATA", 24);
|
||||
esm.getHNOTSized<24>(mPos, "DATA");
|
||||
}
|
||||
|
||||
void PCDT::load(ESM::ESMReader &esm)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm3/cellref.hpp>
|
||||
#include <components/esm/esmcommon.hpp>
|
||||
|
||||
#include "importacdt.hpp"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importproj.h"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importques.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "importscpt.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
|
||||
namespace ESSImport
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "importscri.hpp"
|
||||
|
||||
#include <components/esm/loadscpt.hpp>
|
||||
#include <components/esm3/loadscpt.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "importscri.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef OPENMW_ESSIMPORT_IMPORTSCRI_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTSCRI_H
|
||||
|
||||
#include <components/esm/variant.hpp>
|
||||
#include <components/esm3/variant.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "importsplm.h"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
|
|
@ -41,7 +41,7 @@ struct SPLM
|
|||
{
|
||||
int mUnknown;
|
||||
unsigned char mUnknown2;
|
||||
ESM::FIXED_STRING<35> mItemId; // disintegrated item / bound item / item to re-equip after expiration
|
||||
ESM::FixedString<35> mItemId; // disintegrated item / bound item / item to re-equip after expiration
|
||||
};
|
||||
|
||||
struct CNAM // 36 bytes
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include "importer.hpp"
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
@ -26,6 +24,7 @@ int main(int argc, char** argv)
|
|||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"), "encoding of the save file")
|
||||
;
|
||||
p_desc.add("mwsave", 1).add("output", 1);
|
||||
Files::ConfigurationManager::addCommonOptions(desc);
|
||||
|
||||
bpo::variables_map variables;
|
||||
|
||||
|
@ -57,7 +56,7 @@ int main(int argc, char** argv)
|
|||
else
|
||||
{
|
||||
const std::string& ext = ".omwsave";
|
||||
if (boost::filesystem::exists(boost::filesystem::path(outputFile))
|
||||
if (std::filesystem::exists(std::filesystem::path(outputFile))
|
||||
&& (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext))
|
||||
{
|
||||
throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?");
|
||||
|
|
|
@ -13,6 +13,7 @@ set(LAUNCHER
|
|||
utils/profilescombobox.cpp
|
||||
utils/textinputdialog.cpp
|
||||
utils/lineedit.cpp
|
||||
utils/openalutil.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/files/windows/launcher.rc
|
||||
)
|
||||
|
@ -31,25 +32,10 @@ set(LAUNCHER_HEADER
|
|||
utils/profilescombobox.hpp
|
||||
utils/textinputdialog.hpp
|
||||
utils/lineedit.hpp
|
||||
utils/openalutil.hpp
|
||||
)
|
||||
|
||||
# Headers that must be pre-processed
|
||||
set(LAUNCHER_HEADER_MOC
|
||||
datafilespage.hpp
|
||||
graphicspage.hpp
|
||||
maindialog.hpp
|
||||
playpage.hpp
|
||||
textslotmsgbox.hpp
|
||||
settingspage.hpp
|
||||
advancedpage.hpp
|
||||
|
||||
utils/cellnameloader.hpp
|
||||
utils/textinputdialog.hpp
|
||||
utils/profilescombobox.hpp
|
||||
utils/lineedit.hpp
|
||||
|
||||
)
|
||||
|
||||
set(LAUNCHER_UI
|
||||
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
|
||||
|
@ -58,6 +44,7 @@ set(LAUNCHER_UI
|
|||
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/advancedpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/directorypicker.ui
|
||||
)
|
||||
|
||||
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
|
||||
|
@ -71,7 +58,6 @@ if(WIN32)
|
|||
endif(WIN32)
|
||||
|
||||
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||
QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
|
||||
QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
@ -95,7 +81,8 @@ endif (WIN32)
|
|||
|
||||
target_link_libraries(openmw-launcher
|
||||
${SDL2_LIBRARY_ONLY}
|
||||
components
|
||||
${OPENAL_LIBRARY}
|
||||
components_qt
|
||||
)
|
||||
|
||||
target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)
|
||||
|
@ -105,4 +92,16 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(openmw-launcher gcov)
|
||||
endif()
|
||||
|
||||
if(USE_QT)
|
||||
set_property(TARGET openmw-launcher PROPERTY AUTOMOC ON)
|
||||
endif(USE_QT)
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
|
||||
target_precompile_headers(openmw-launcher PRIVATE
|
||||
<boost/program_options/options_description.hpp>
|
||||
|
||||
<algorithm>
|
||||
<string>
|
||||
<vector>
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -1,27 +1,37 @@
|
|||
#include "advancedpage.hpp"
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/config/launchersettings.hpp>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QCompleter>
|
||||
#include <QProxyStyle>
|
||||
#include <QString>
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/contentselector/view/contentselector.hpp>
|
||||
#include <components/contentselector/model/esmfile.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include "utils/openalutil.hpp"
|
||||
|
||||
Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg,
|
||||
Config::GameSettings &gameSettings,
|
||||
Settings::Manager &engineSettings, QWidget *parent)
|
||||
Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, mCfgMgr(cfg)
|
||||
, mGameSettings(gameSettings)
|
||||
, mEngineSettings(engineSettings)
|
||||
{
|
||||
setObjectName ("AdvancedPage");
|
||||
setupUi(this);
|
||||
|
||||
for(const std::string& name : Launcher::enumerateOpenALDevices())
|
||||
{
|
||||
audioDeviceSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
|
||||
}
|
||||
for(const std::string& name : Launcher::enumerateOpenALDevicesHrtf())
|
||||
{
|
||||
hrtfProfileSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
||||
mCellNameCompleter.setModel(&mCellNameCompleterModel);
|
||||
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
|
||||
}
|
||||
|
@ -64,12 +74,12 @@ namespace
|
|||
|
||||
double convertToCells(double unitRadius)
|
||||
{
|
||||
return std::round((unitRadius / 0.93 + 1024) / CellSizeInUnits);
|
||||
return unitRadius / CellSizeInUnits;
|
||||
}
|
||||
|
||||
double convertToUnits(double CellGridRadius)
|
||||
int convertToUnits(double CellGridRadius)
|
||||
{
|
||||
return (CellSizeInUnits * CellGridRadius - 1024) * 0.93;
|
||||
return static_cast<int>(CellSizeInUnits * CellGridRadius);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,14 +99,15 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
||||
loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
||||
loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
|
||||
int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game");
|
||||
int unarmedFactorsStrengthIndex = Settings::Manager::getInt("strength influences hand to hand", "Game");
|
||||
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
|
||||
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
|
||||
loadSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
|
||||
loadSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
|
||||
int numPhysicsThreads = mEngineSettings.getInt("async num threads", "Physics");
|
||||
int numPhysicsThreads = Settings::Manager::getInt("async num threads", "Physics");
|
||||
if (numPhysicsThreads >= 0)
|
||||
physicsThreadsSpinBox->setValue(numPhysicsThreads);
|
||||
loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game");
|
||||
}
|
||||
|
||||
// Visuals
|
||||
|
@ -106,11 +117,15 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders");
|
||||
loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
|
||||
loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
|
||||
loadSettingBool(radialFogCheckBox, "radial fog", "Shaders");
|
||||
loadSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
|
||||
loadSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
|
||||
if (Settings::Manager::getInt("antialiasing", "Video") == 0) {
|
||||
antialiasAlphaTestCheckBox->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
|
||||
connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool)));
|
||||
loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
|
||||
if (animSourcesCheckBox->checkState())
|
||||
if (animSourcesCheckBox->checkState() != Qt::Unchecked)
|
||||
{
|
||||
loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
|
||||
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
|
||||
|
@ -118,27 +133,56 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
||||
loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
|
||||
|
||||
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain");
|
||||
const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain");
|
||||
const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
|
||||
const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain");
|
||||
if (distantTerrain && objectPaging) {
|
||||
distantLandCheckBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
|
||||
loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
|
||||
viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera")));
|
||||
viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt("viewing distance", "Camera")));
|
||||
objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain"));
|
||||
|
||||
loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
||||
|
||||
connect(postprocessEnabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotPostProcessToggled(bool)));
|
||||
loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
||||
loadSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing");
|
||||
loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
||||
postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("hdr exposure time", "Post Processing"));
|
||||
|
||||
connect(skyBlendingCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotSkyBlendingToggled(bool)));
|
||||
loadSettingBool(radialFogCheckBox, "radial fog", "Fog");
|
||||
loadSettingBool(exponentialFogCheckBox, "exponential fog", "Fog");
|
||||
loadSettingBool(skyBlendingCheckBox, "sky blending", "Fog");
|
||||
skyBlendingStartComboBox->setValue(Settings::Manager::getDouble("sky blending start", "Fog"));
|
||||
}
|
||||
|
||||
// Camera
|
||||
// Audio
|
||||
{
|
||||
loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera");
|
||||
connect(viewOverShoulderCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotViewOverShoulderToggled(bool)));
|
||||
viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());
|
||||
loadSettingBool(autoSwitchShoulderCheckBox, "auto switch shoulder", "Camera");
|
||||
loadSettingBool(previewIfStandStillCheckBox, "preview if stand still", "Camera");
|
||||
loadSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera");
|
||||
loadSettingBool(headBobbingCheckBox, "head bobbing", "Camera");
|
||||
defaultShoulderComboBox->setCurrentIndex(
|
||||
mEngineSettings.getVector2("view over shoulder offset", "Camera").x() >= 0 ? 0 : 1);
|
||||
std::string selectedAudioDevice = Settings::Manager::getString("device", "Sound");
|
||||
if (selectedAudioDevice.empty() == false)
|
||||
{
|
||||
int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice));
|
||||
if (audioDeviceIndex != -1)
|
||||
{
|
||||
audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex);
|
||||
}
|
||||
}
|
||||
int hrtfEnabledIndex = Settings::Manager::getInt("hrtf enable", "Sound");
|
||||
if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1)
|
||||
{
|
||||
enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1);
|
||||
}
|
||||
std::string selectedHRTFProfile = Settings::Manager::getString("hrtf", "Sound");
|
||||
if (selectedHRTFProfile.empty() == false)
|
||||
{
|
||||
int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile));
|
||||
if (hrtfProfileIndex != -1)
|
||||
{
|
||||
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interface Changes
|
||||
|
@ -148,11 +192,14 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
||||
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
||||
loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
|
||||
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
|
||||
int showOwnedIndex = Settings::Manager::getInt("show owned", "Game");
|
||||
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
|
||||
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
|
||||
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
|
||||
loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
|
||||
loadSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
|
||||
loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
|
||||
scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI"));
|
||||
}
|
||||
|
||||
// Bug fixes
|
||||
|
@ -165,13 +212,15 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
{
|
||||
// Saves
|
||||
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
||||
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
|
||||
loadSettingInt(maximumQuicksavesComboBox,"max quicksaves", "Saves");
|
||||
|
||||
// Other Settings
|
||||
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
|
||||
QString screenshotFormatString = QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper();
|
||||
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
|
||||
screenshotFormatComboBox->addItem(screenshotFormatString);
|
||||
screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString));
|
||||
|
||||
loadSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General");
|
||||
}
|
||||
|
||||
// Testing
|
||||
|
@ -208,14 +257,11 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
||||
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
||||
saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
|
||||
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();
|
||||
if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game"))
|
||||
mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
|
||||
saveSettingInt(unarmedFactorsStrengthComboBox, "strength influences hand to hand", "Game");
|
||||
saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
|
||||
saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
|
||||
int numPhysicsThreads = physicsThreadsSpinBox->value();
|
||||
if (numPhysicsThreads != mEngineSettings.getInt("async num threads", "Physics"))
|
||||
mEngineSettings.setInt("async num threads", "Physics", numPhysicsThreads);
|
||||
saveSettingInt(physicsThreadsSpinBox, "async num threads", "Physics");
|
||||
saveSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game");
|
||||
}
|
||||
|
||||
// Visuals
|
||||
|
@ -225,7 +271,9 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders");
|
||||
saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
|
||||
saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
|
||||
saveSettingBool(radialFogCheckBox, "radial fog", "Shaders");
|
||||
saveSettingBool(radialFogCheckBox, "radial fog", "Fog");
|
||||
saveSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
|
||||
saveSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
|
||||
saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
|
||||
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
|
||||
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
|
||||
|
@ -233,38 +281,69 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
|
||||
saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
|
||||
|
||||
const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain");
|
||||
const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain");
|
||||
const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
|
||||
const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain");
|
||||
const bool wantDistantLand = distantLandCheckBox->checkState();
|
||||
if (wantDistantLand != (distantTerrain && objectPaging)) {
|
||||
mEngineSettings.setBool("distant terrain", "Terrain", wantDistantLand);
|
||||
mEngineSettings.setBool("object paging", "Terrain", wantDistantLand);
|
||||
Settings::Manager::setBool("distant terrain", "Terrain", wantDistantLand);
|
||||
Settings::Manager::setBool("object paging", "Terrain", wantDistantLand);
|
||||
}
|
||||
|
||||
saveSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
|
||||
double viewingDistance = viewingDistanceComboBox->value();
|
||||
if (viewingDistance != convertToCells(mEngineSettings.getInt("viewing distance", "Camera")))
|
||||
int viewingDistance = convertToUnits(viewingDistanceComboBox->value());
|
||||
if (viewingDistance != Settings::Manager::getInt("viewing distance", "Camera"))
|
||||
{
|
||||
mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance));
|
||||
Settings::Manager::setInt("viewing distance", "Camera", viewingDistance);
|
||||
}
|
||||
double objectPagingMinSize = objectPagingMinSizeComboBox->value();
|
||||
if (objectPagingMinSize != Settings::Manager::getDouble("object paging min size", "Terrain"))
|
||||
Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize);
|
||||
|
||||
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
||||
|
||||
saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
||||
saveSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing");
|
||||
saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
||||
double hdrExposureTime = postprocessHDRTimeComboBox->value();
|
||||
if (hdrExposureTime != Settings::Manager::getDouble("hdr exposure time", "Post Processing"))
|
||||
Settings::Manager::setDouble("hdr exposure time", "Post Processing", hdrExposureTime);
|
||||
|
||||
saveSettingBool(radialFogCheckBox, "radial fog", "Fog");
|
||||
saveSettingBool(exponentialFogCheckBox, "exponential fog", "Fog");
|
||||
saveSettingBool(skyBlendingCheckBox, "sky blending", "Fog");
|
||||
Settings::Manager::setDouble("sky blending start", "Fog", skyBlendingStartComboBox->value());
|
||||
}
|
||||
|
||||
// Camera
|
||||
// Audio
|
||||
{
|
||||
saveSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera");
|
||||
saveSettingBool(autoSwitchShoulderCheckBox, "auto switch shoulder", "Camera");
|
||||
saveSettingBool(previewIfStandStillCheckBox, "preview if stand still", "Camera");
|
||||
saveSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera");
|
||||
saveSettingBool(headBobbingCheckBox, "head bobbing", "Camera");
|
||||
|
||||
osg::Vec2f shoulderOffset = mEngineSettings.getVector2("view over shoulder offset", "Camera");
|
||||
if (defaultShoulderComboBox->currentIndex() != (shoulderOffset.x() >= 0 ? 0 : 1))
|
||||
int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex();
|
||||
std::string prevAudioDevice = Settings::Manager::getString("device", "Sound");
|
||||
if (audioDeviceIndex != 0)
|
||||
{
|
||||
if (defaultShoulderComboBox->currentIndex() == 0)
|
||||
shoulderOffset.x() = std::abs(shoulderOffset.x());
|
||||
else
|
||||
shoulderOffset.x() = -std::abs(shoulderOffset.x());
|
||||
mEngineSettings.setVector2("view over shoulder offset", "Camera", shoulderOffset);
|
||||
const std::string& newAudioDevice = audioDeviceSelectorComboBox->currentText().toUtf8().constData();
|
||||
if (newAudioDevice != prevAudioDevice)
|
||||
Settings::Manager::setString("device", "Sound", newAudioDevice);
|
||||
}
|
||||
else if (!prevAudioDevice.empty())
|
||||
{
|
||||
Settings::Manager::setString("device", "Sound", {});
|
||||
}
|
||||
int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1;
|
||||
if (hrtfEnabledIndex != Settings::Manager::getInt("hrtf enable", "Sound"))
|
||||
{
|
||||
Settings::Manager::setInt("hrtf enable", "Sound", hrtfEnabledIndex);
|
||||
}
|
||||
int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex();
|
||||
std::string prevHRTFProfile = Settings::Manager::getString("hrtf", "Sound");
|
||||
if (selectedHRTFProfileIndex != 0)
|
||||
{
|
||||
const std::string& newHRTFProfile = hrtfProfileSelectorComboBox->currentText().toUtf8().constData();
|
||||
if (newHRTFProfile != prevHRTFProfile)
|
||||
Settings::Manager::setString("hrtf", "Sound", newHRTFProfile);
|
||||
}
|
||||
else if (!prevHRTFProfile.empty())
|
||||
{
|
||||
Settings::Manager::setString("hrtf", "Sound", {});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,10 +354,13 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
||||
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
||||
saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
|
||||
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
|
||||
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
|
||||
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
|
||||
saveSettingInt(showOwnedComboBox,"show owned", "Game");
|
||||
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
|
||||
saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
|
||||
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
|
||||
float uiScalingFactor = scalingSpinBox->value();
|
||||
if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI"))
|
||||
Settings::Manager::setFloat("scaling factor", "GUI", uiScalingFactor);
|
||||
}
|
||||
|
||||
// Bug fixes
|
||||
|
@ -291,16 +373,14 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
{
|
||||
// Saves Settings
|
||||
saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
||||
int maximumQuicksaves = maximumQuicksavesComboBox->value();
|
||||
if (maximumQuicksaves != mEngineSettings.getInt("max quicksaves", "Saves"))
|
||||
{
|
||||
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
|
||||
}
|
||||
saveSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves");
|
||||
|
||||
// Other Settings
|
||||
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
|
||||
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
|
||||
mEngineSettings.setString("screenshot format", "General", screenshotFormatString);
|
||||
if (screenshotFormatString != Settings::Manager::getString("screenshot format", "General"))
|
||||
Settings::Manager::setString("screenshot format", "General", screenshotFormatString);
|
||||
|
||||
saveSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General");
|
||||
}
|
||||
|
||||
// Testing
|
||||
|
@ -324,15 +404,41 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
|
||||
void Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
if (mEngineSettings.getBool(setting, group))
|
||||
if (Settings::Manager::getBool(setting, group))
|
||||
checkbox->setCheckState(Qt::Checked);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
bool cValue = checkbox->checkState();
|
||||
if (cValue != mEngineSettings.getBool(setting, group))
|
||||
mEngineSettings.setBool(setting, group, cValue);
|
||||
if (cValue != Settings::Manager::getBool(setting, group))
|
||||
Settings::Manager::setBool(setting, group, cValue);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::loadSettingInt(QComboBox *comboBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int currentIndex = Settings::Manager::getInt(setting, group);
|
||||
comboBox->setCurrentIndex(currentIndex);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::saveSettingInt(QComboBox *comboBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int currentIndex = comboBox->currentIndex();
|
||||
if (currentIndex != Settings::Manager::getInt(setting, group))
|
||||
Settings::Manager::setInt(setting, group, currentIndex);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::loadSettingInt(QSpinBox *spinBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int value = Settings::Manager::getInt(setting, group);
|
||||
spinBox->setValue(value);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::saveSettingInt(QSpinBox *spinBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int value = spinBox->value();
|
||||
if (value != Settings::Manager::getInt(setting, group))
|
||||
Settings::Manager::setInt(setting, group, value);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
|
||||
|
@ -351,7 +457,16 @@ void Launcher::AdvancedPage::slotAnimSourcesToggled(bool checked)
|
|||
}
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::slotViewOverShoulderToggled(bool checked)
|
||||
void Launcher::AdvancedPage::slotPostProcessToggled(bool checked)
|
||||
{
|
||||
viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());
|
||||
postprocessLiveReloadCheckBox->setEnabled(checked);
|
||||
postprocessTransparentPostpassCheckBox->setEnabled(checked);
|
||||
postprocessHDRTimeComboBox->setEnabled(checked);
|
||||
postprocessHDRTimeLabel->setEnabled(checked);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::slotSkyBlendingToggled(bool checked)
|
||||
{
|
||||
skyBlendingStartComboBox->setEnabled(checked);
|
||||
skyBlendingStartLabel->setEnabled(checked);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef ADVANCEDPAGE_H
|
||||
#define ADVANCEDPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QCompleter>
|
||||
#include <QStringListModel>
|
||||
|
||||
|
@ -9,7 +8,6 @@
|
|||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace Files { struct ConfigurationManager; }
|
||||
namespace Config { class GameSettings; }
|
||||
|
||||
namespace Launcher
|
||||
|
@ -19,8 +17,7 @@ namespace Launcher
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AdvancedPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
||||
Settings::Manager &engineSettings, QWidget *parent = 0);
|
||||
explicit AdvancedPage(Config::GameSettings &gameSettings, QWidget *parent = nullptr);
|
||||
|
||||
bool loadSettings();
|
||||
void saveSettings();
|
||||
|
@ -32,12 +29,11 @@ namespace Launcher
|
|||
void on_skipMenuCheckBox_stateChanged(int state);
|
||||
void on_runScriptAfterStartupBrowseButton_clicked();
|
||||
void slotAnimSourcesToggled(bool checked);
|
||||
void slotViewOverShoulderToggled(bool checked);
|
||||
void slotPostProcessToggled(bool checked);
|
||||
void slotSkyBlendingToggled(bool checked);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
Config::GameSettings &mGameSettings;
|
||||
Settings::Manager &mEngineSettings;
|
||||
QCompleter mCellNameCompleter;
|
||||
QStringListModel mCellNameCompleterModel;
|
||||
|
||||
|
@ -46,8 +42,12 @@ namespace Launcher
|
|||
* @param filePaths the file paths of the content files to be examined
|
||||
*/
|
||||
void loadCellsForAutocomplete(QStringList filePaths);
|
||||
void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
static void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
static void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
static void loadSettingInt(QComboBox *comboBox, const std::string& setting, const std::string& group);
|
||||
static void saveSettingInt(QComboBox *comboBox, const std::string& setting, const std::string& group);
|
||||
static void loadSettingInt(QSpinBox *spinBox, const std::string& setting, const std::string& group);
|
||||
static void saveSettingInt(QSpinBox *spinBox, const std::string& setting, const std::string& group);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,41 +1,131 @@
|
|||
#include "datafilespage.hpp"
|
||||
#include "maindialog.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QCheckBox>
|
||||
#include <QMenu>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
|
||||
#include <apps/launcher/utils/cellnameloader.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <components/contentselector/model/esmfile.hpp>
|
||||
#include <components/contentselector/model/naturalsort.hpp>
|
||||
#include <components/contentselector/view/contentselector.hpp>
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/config/launchersettings.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/bsa/compressedbsafile.hpp>
|
||||
#include <components/navmeshtool/protocol.hpp>
|
||||
#include <components/vfs/bsaarchive.hpp>
|
||||
|
||||
#include "utils/textinputdialog.hpp"
|
||||
#include "utils/profilescombobox.hpp"
|
||||
|
||||
#include "ui_directorypicker.h"
|
||||
|
||||
const char *Launcher::DataFilesPage::mDefaultContentListName = "Default";
|
||||
|
||||
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
|
||||
namespace
|
||||
{
|
||||
void contentSubdirs(const QString& path, QStringList& dirs)
|
||||
{
|
||||
QStringList fileFilter {"*.esm", "*.esp", "*.omwaddon", "*.bsa"};
|
||||
QStringList dirFilter {"bookart", "icons", "meshes", "music", "sound", "textures"};
|
||||
|
||||
QDir currentDir(path);
|
||||
if (!currentDir.entryInfoList(fileFilter, QDir::Files).empty()
|
||||
|| !currentDir.entryInfoList(dirFilter, QDir::Dirs | QDir::NoDotAndDotDot).empty())
|
||||
dirs.push_back(currentDir.canonicalPath());
|
||||
|
||||
for (const auto& subdir : currentDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||
contentSubdirs(subdir.canonicalFilePath(), dirs);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct HandleNavMeshToolMessage
|
||||
{
|
||||
int mCellsCount;
|
||||
int mExpectedMaxProgress;
|
||||
int mMaxProgress;
|
||||
int mProgress;
|
||||
|
||||
HandleNavMeshToolMessage operator()(NavMeshTool::ExpectedCells&& message) const
|
||||
{
|
||||
return HandleNavMeshToolMessage {
|
||||
static_cast<int>(message.mCount),
|
||||
mExpectedMaxProgress,
|
||||
static_cast<int>(message.mCount) * 100,
|
||||
mProgress
|
||||
};
|
||||
}
|
||||
|
||||
HandleNavMeshToolMessage operator()(NavMeshTool::ProcessedCells&& message) const
|
||||
{
|
||||
return HandleNavMeshToolMessage {
|
||||
mCellsCount,
|
||||
mExpectedMaxProgress,
|
||||
mMaxProgress,
|
||||
std::max(mProgress, static_cast<int>(message.mCount))
|
||||
};
|
||||
}
|
||||
|
||||
HandleNavMeshToolMessage operator()(NavMeshTool::ExpectedTiles&& message) const
|
||||
{
|
||||
const int expectedMaxProgress = mCellsCount + static_cast<int>(message.mCount);
|
||||
return HandleNavMeshToolMessage {
|
||||
mCellsCount,
|
||||
expectedMaxProgress,
|
||||
std::max(mMaxProgress, expectedMaxProgress),
|
||||
mProgress
|
||||
};
|
||||
}
|
||||
|
||||
HandleNavMeshToolMessage operator()(NavMeshTool::GeneratedTiles&& message) const
|
||||
{
|
||||
int progress = mCellsCount + static_cast<int>(message.mCount);
|
||||
if (mExpectedMaxProgress < mMaxProgress)
|
||||
progress += static_cast<int>(std::round(
|
||||
(mMaxProgress - mExpectedMaxProgress)
|
||||
* (static_cast<float>(progress) / static_cast<float>(mExpectedMaxProgress))
|
||||
));
|
||||
return HandleNavMeshToolMessage {
|
||||
mCellsCount,
|
||||
mExpectedMaxProgress,
|
||||
mMaxProgress,
|
||||
std::max(mProgress, progress)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
int getMaxNavMeshDbFileSizeMiB()
|
||||
{
|
||||
return static_cast<int>(Settings::Manager::getInt64("max navmeshdb file size", "Navigator") / (1024 * 1024));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent)
|
||||
: QWidget(parent)
|
||||
, mMainDialog(parent)
|
||||
, mCfgMgr(cfg)
|
||||
, mGameSettings(gameSettings)
|
||||
, mLauncherSettings(launcherSettings)
|
||||
, mNavMeshToolInvoker(new Process::ProcessInvoker(this))
|
||||
{
|
||||
ui.setupUi (this);
|
||||
setObjectName ("DataFilesPage");
|
||||
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
|
||||
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget, /*showOMWScripts=*/true);
|
||||
const QString encoding = mGameSettings.value("encoding", "win1252");
|
||||
mSelector->setEncoding(encoding);
|
||||
|
||||
|
@ -46,6 +136,14 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
|
|||
this, SLOT(updateNewProfileOkButton(QString)));
|
||||
connect(mCloneProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),
|
||||
this, SLOT(updateCloneProfileOkButton(QString)));
|
||||
connect(ui.directoryAddSubdirsButton, &QPushButton::released, this, [this]() { this->addSubdirectories(true); });
|
||||
connect(ui.directoryInsertButton, &QPushButton::released, this, [this]() { this->addSubdirectories(false); });
|
||||
connect(ui.directoryUpButton, &QPushButton::released, this, [this]() { this->moveDirectory(-1); });
|
||||
connect(ui.directoryDownButton, &QPushButton::released, this, [this]() { this->moveDirectory(1); });
|
||||
connect(ui.directoryRemoveButton, &QPushButton::released, this, [this]() { this->removeDirectory(); });
|
||||
connect(ui.archiveUpButton, &QPushButton::released, this, [this]() { this->moveArchive(-1); });
|
||||
connect(ui.archiveDownButton, &QPushButton::released, this, [this]() { this->moveArchive(1); });
|
||||
connect(ui.directoryListWidget->model(), &QAbstractItemModel::rowsMoved, this, [this]() { this->sortDirectories(); });
|
||||
|
||||
buildView();
|
||||
loadSettings();
|
||||
|
@ -60,15 +158,12 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
|
|||
|
||||
void Launcher::DataFilesPage::buildView()
|
||||
{
|
||||
ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
|
||||
|
||||
QToolButton * refreshButton = mSelector->refreshButton();
|
||||
|
||||
//tool buttons
|
||||
ui.newProfileButton->setToolTip ("Create a new Content List");
|
||||
ui.cloneProfileButton->setToolTip ("Clone the current Content List");
|
||||
ui.deleteProfileButton->setToolTip ("Delete an existing Content List");
|
||||
refreshButton->setToolTip("Refresh Data Files");
|
||||
|
||||
//combo box
|
||||
ui.profilesComboBox->addItem(mDefaultContentListName);
|
||||
|
@ -92,10 +187,19 @@ void Launcher::DataFilesPage::buildView()
|
|||
this, SLOT (slotProfileChangedByUser(QString, QString)));
|
||||
|
||||
connect(ui.refreshDataFilesAction, SIGNAL(triggered()),this, SLOT(slotRefreshButtonClicked()));
|
||||
|
||||
connect(ui.updateNavMeshButton, SIGNAL(clicked()), this, SLOT(startNavMeshTool()));
|
||||
connect(ui.cancelNavMeshButton, SIGNAL(clicked()), this, SLOT(killNavMeshTool()));
|
||||
|
||||
connect(mNavMeshToolInvoker->getProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(readNavMeshToolStdout()));
|
||||
connect(mNavMeshToolInvoker->getProcess(), SIGNAL(readyReadStandardError()), this, SLOT(readNavMeshToolStderr()));
|
||||
connect(mNavMeshToolInvoker->getProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(navMeshToolFinished(int, QProcess::ExitStatus)));
|
||||
}
|
||||
|
||||
bool Launcher::DataFilesPage::loadSettings()
|
||||
{
|
||||
ui.navMeshMaxSizeSpinBox->setValue(getMaxNavMeshDbFileSizeMiB());
|
||||
|
||||
QStringList profiles = mLauncherSettings.getContentLists();
|
||||
QString currentProfile = mLauncherSettings.getCurrentContentListName();
|
||||
|
||||
|
@ -113,19 +217,96 @@ bool Launcher::DataFilesPage::loadSettings()
|
|||
|
||||
void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
||||
{
|
||||
QStringList paths = mGameSettings.getDataDirs();
|
||||
mSelector->clearFiles();
|
||||
ui.archiveListWidget->clear();
|
||||
ui.directoryListWidget->clear();
|
||||
|
||||
QStringList directories = mLauncherSettings.getDataDirectoryList(contentModelName);
|
||||
if (directories.isEmpty())
|
||||
directories = mGameSettings.getDataDirs();
|
||||
|
||||
mDataLocal = mGameSettings.getDataLocal();
|
||||
|
||||
if (!mDataLocal.isEmpty())
|
||||
paths.insert(0, mDataLocal);
|
||||
directories.insert(0, mDataLocal);
|
||||
|
||||
mSelector->clearFiles();
|
||||
const auto globalDataDir = QString(mGameSettings.getGlobalDataDir().c_str());
|
||||
if (!globalDataDir.isEmpty())
|
||||
directories.insert(0, globalDataDir);
|
||||
|
||||
for (const QString &path : paths)
|
||||
mSelector->addFiles(path);
|
||||
// normalize user supplied directories: resolve symlink, convert to native separator, make absolute
|
||||
for (auto& currentDir : directories)
|
||||
currentDir = QDir(QDir::cleanPath(currentDir)).canonicalPath();
|
||||
|
||||
PathIterator pathIterator(paths);
|
||||
// add directories, archives and content files
|
||||
directories.removeDuplicates();
|
||||
for (const auto& currentDir : directories)
|
||||
{
|
||||
// add new achives files presents in current directory
|
||||
addArchivesFromDir(currentDir);
|
||||
|
||||
QString tooltip;
|
||||
|
||||
// add content files presents in current directory
|
||||
mSelector->addFiles(currentDir, mNewDataDirs.contains(currentDir));
|
||||
|
||||
// add current directory to list
|
||||
ui.directoryListWidget->addItem(currentDir);
|
||||
auto row = ui.directoryListWidget->count() - 1;
|
||||
auto* item = ui.directoryListWidget->item(row);
|
||||
|
||||
// Display new content with green background
|
||||
if (mNewDataDirs.contains(currentDir))
|
||||
{
|
||||
tooltip += "Will be added to the current profile\n";
|
||||
item->setBackground(Qt::green);
|
||||
item->setForeground(Qt::black);
|
||||
}
|
||||
|
||||
// deactivate data-local and global data directory: they are always included
|
||||
if (currentDir == mDataLocal || currentDir == globalDataDir)
|
||||
{
|
||||
auto flags = item->flags();
|
||||
item->setFlags(flags & ~(Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled|Qt::ItemIsEnabled));
|
||||
}
|
||||
|
||||
// Add a "data file" icon if the directory contains a content file
|
||||
if (mSelector->containsDataFiles(currentDir))
|
||||
{
|
||||
item->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||
tooltip += "Contains content file(s)";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pad to correct vertical alignment
|
||||
QPixmap pixmap(QSize(200, 200)); // Arbitrary big number, will be scaled down to widget size
|
||||
pixmap.fill(ui.directoryListWidget->palette().base().color());
|
||||
auto emptyIcon = QIcon(pixmap);
|
||||
item->setIcon(emptyIcon);
|
||||
}
|
||||
item->setToolTip(tooltip);
|
||||
}
|
||||
mSelector->sortFiles();
|
||||
|
||||
QStringList selectedArchives = mLauncherSettings.getArchiveList(contentModelName);
|
||||
if (selectedArchives.isEmpty())
|
||||
selectedArchives = mGameSettings.getArchiveList();
|
||||
|
||||
// sort and tick BSA according to profile
|
||||
int row = 0;
|
||||
for (const auto& archive : selectedArchives)
|
||||
{
|
||||
const auto match = ui.archiveListWidget->findItems(archive, Qt::MatchExactly);
|
||||
if (match.isEmpty())
|
||||
continue;
|
||||
const auto name = match[0]->text();
|
||||
const auto oldrow = ui.archiveListWidget->row(match[0]);
|
||||
ui.archiveListWidget->takeItem(oldrow);
|
||||
ui.archiveListWidget->insertItem(row, name);
|
||||
ui.archiveListWidget->item(row)->setCheckState(Qt::Checked);
|
||||
row++;
|
||||
}
|
||||
|
||||
PathIterator pathIterator(directories);
|
||||
|
||||
mSelector->setProfileContent(filesInProfile(contentModelName, pathIterator));
|
||||
}
|
||||
|
@ -148,13 +329,19 @@ QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName,
|
|||
|
||||
void Launcher::DataFilesPage::saveSettings(const QString &profile)
|
||||
{
|
||||
QString profileName = profile;
|
||||
if (const int value = ui.navMeshMaxSizeSpinBox->value(); value != getMaxNavMeshDbFileSizeMiB())
|
||||
Settings::Manager::setInt64("max navmeshdb file size", "Navigator", static_cast<std::int64_t>(value) * 1024 * 1024);
|
||||
|
||||
if (profileName.isEmpty())
|
||||
profileName = ui.profilesComboBox->currentText();
|
||||
QString profileName = profile;
|
||||
|
||||
//retrieve the files selected for the profile
|
||||
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
||||
if (profileName.isEmpty())
|
||||
profileName = ui.profilesComboBox->currentText();
|
||||
|
||||
//retrieve the data paths
|
||||
auto dirList = selectedDirectoriesPaths();
|
||||
|
||||
//retrieve the files selected for the profile
|
||||
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
||||
|
||||
//set the value of the current profile (not necessarily the profile being saved!)
|
||||
mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText());
|
||||
|
@ -164,11 +351,36 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
|
|||
{
|
||||
fileNames.append(item->fileName());
|
||||
}
|
||||
mLauncherSettings.setContentList(profileName, fileNames);
|
||||
mGameSettings.setContentList(fileNames);
|
||||
mLauncherSettings.setContentList(profileName, dirList, selectedArchivePaths(), fileNames);
|
||||
mGameSettings.setContentList(dirList, selectedArchivePaths(), fileNames);
|
||||
}
|
||||
|
||||
QStringList Launcher::DataFilesPage::selectedFilePaths()
|
||||
QStringList Launcher::DataFilesPage::selectedDirectoriesPaths() const
|
||||
{
|
||||
QStringList dirList;
|
||||
for (int i = 0; i < ui.directoryListWidget->count(); ++i)
|
||||
{
|
||||
if (ui.directoryListWidget->item(i)->flags() & Qt::ItemIsEnabled)
|
||||
dirList.append(ui.directoryListWidget->item(i)->text());
|
||||
}
|
||||
return dirList;
|
||||
}
|
||||
|
||||
QStringList Launcher::DataFilesPage::selectedArchivePaths(bool all) const
|
||||
{
|
||||
QStringList archiveList;
|
||||
for (int i = 0; i < ui.archiveListWidget->count(); ++i)
|
||||
{
|
||||
const auto* item = ui.archiveListWidget->item(i);
|
||||
const auto archive = ui.archiveListWidget->item(i)->text();
|
||||
|
||||
if (all ||item->checkState() == Qt::Checked)
|
||||
archiveList.append(item->text());
|
||||
}
|
||||
return archiveList;
|
||||
}
|
||||
|
||||
QStringList Launcher::DataFilesPage::selectedFilePaths() const
|
||||
{
|
||||
//retrieve the files selected for the profile
|
||||
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
||||
|
@ -176,15 +388,8 @@ QStringList Launcher::DataFilesPage::selectedFilePaths()
|
|||
for (const ContentSelectorModel::EsmFile *item : items)
|
||||
{
|
||||
QFile file(item->filePath());
|
||||
|
||||
if(file.exists())
|
||||
{
|
||||
filePaths.append(item->filePath());
|
||||
}
|
||||
else
|
||||
{
|
||||
slotRefreshButtonClicked();
|
||||
}
|
||||
}
|
||||
return filePaths;
|
||||
}
|
||||
|
@ -228,8 +433,17 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString
|
|||
|
||||
ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current));
|
||||
|
||||
mNewDataDirs.clear();
|
||||
mKnownArchives.clear();
|
||||
populateFileViews(current);
|
||||
|
||||
// save list of "old" bsa to be able to display "new" bsa in a different colour
|
||||
for (int i = 0; i < ui.archiveListWidget->count(); ++i)
|
||||
{
|
||||
auto* item = ui.archiveListWidget->item(i);
|
||||
mKnownArchives.push_back(item->text());
|
||||
}
|
||||
|
||||
checkForDefaultProfile();
|
||||
}
|
||||
|
||||
|
@ -318,7 +532,7 @@ void Launcher::DataFilesPage::on_cloneProfileAction_triggered()
|
|||
if (profile.isEmpty())
|
||||
return;
|
||||
|
||||
mLauncherSettings.setContentList(profile, selectedFilePaths());
|
||||
mLauncherSettings.setContentList(profile, selectedDirectoriesPaths(), selectedArchivePaths(), selectedFilePaths());
|
||||
addProfile(profile, true);
|
||||
}
|
||||
|
||||
|
@ -356,6 +570,158 @@ void Launcher::DataFilesPage::updateCloneProfileOkButton(const QString &text)
|
|||
mCloneProfileDialog->setOkButtonEnabled(!text.isEmpty() && ui.profilesComboBox->findText(text) == -1);
|
||||
}
|
||||
|
||||
QString Launcher::DataFilesPage::selectDirectory()
|
||||
{
|
||||
QFileDialog fileDialog(this);
|
||||
fileDialog.setFileMode(QFileDialog::Directory);
|
||||
fileDialog.setOptions(QFileDialog::Option::ShowDirsOnly | QFileDialog::Option::ReadOnly);
|
||||
|
||||
if (fileDialog.exec() == QDialog::Rejected)
|
||||
return {};
|
||||
|
||||
return QDir(fileDialog.selectedFiles()[0]).canonicalPath();
|
||||
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::addSubdirectories(bool append)
|
||||
{
|
||||
int selectedRow = append ? ui.directoryListWidget->count() : ui.directoryListWidget->currentRow();
|
||||
|
||||
if (selectedRow == -1)
|
||||
return;
|
||||
|
||||
const auto rootDir = selectDirectory();
|
||||
if (rootDir.isEmpty())
|
||||
return;
|
||||
|
||||
QStringList subdirs;
|
||||
contentSubdirs(rootDir, subdirs);
|
||||
|
||||
if (subdirs.empty())
|
||||
{
|
||||
// we didn't find anything that looks like a content directory, add directory selected by user
|
||||
if (ui.directoryListWidget->findItems(rootDir, Qt::MatchFixedString).isEmpty())
|
||||
{
|
||||
ui.directoryListWidget->addItem(rootDir);
|
||||
mNewDataDirs.push_back(rootDir);
|
||||
refreshDataFilesView();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QDialog dialog;
|
||||
Ui::SelectSubdirs select;
|
||||
|
||||
select.setupUi(&dialog);
|
||||
|
||||
for (const auto& dir : subdirs)
|
||||
{
|
||||
if (!ui.directoryListWidget->findItems(dir, Qt::MatchFixedString).isEmpty())
|
||||
continue;
|
||||
const auto lastRow = select.dirListWidget->count();
|
||||
select.dirListWidget->addItem(dir);
|
||||
select.dirListWidget->item(lastRow)->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
|
||||
dialog.show();
|
||||
|
||||
if (dialog.exec() == QDialog::Rejected)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < select.dirListWidget->count(); ++i)
|
||||
{
|
||||
const auto* dir = select.dirListWidget->item(i);
|
||||
if (dir->checkState() == Qt::Checked)
|
||||
{
|
||||
ui.directoryListWidget->insertItem(selectedRow++, dir->text());
|
||||
mNewDataDirs.push_back(dir->text());
|
||||
}
|
||||
}
|
||||
|
||||
refreshDataFilesView();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::sortDirectories()
|
||||
{
|
||||
// Ensure disabled entries (aka default directories) are always at the top.
|
||||
for (auto i = 1; i < ui.directoryListWidget->count(); ++i)
|
||||
{
|
||||
if (!(ui.directoryListWidget->item(i)->flags() & Qt::ItemIsEnabled) &&
|
||||
(ui.directoryListWidget->item(i - 1)->flags() & Qt::ItemIsEnabled))
|
||||
{
|
||||
const auto item = ui.directoryListWidget->takeItem(i);
|
||||
ui.directoryListWidget->insertItem(i - 1, item);
|
||||
ui.directoryListWidget->setCurrentRow(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::moveDirectory(int step)
|
||||
{
|
||||
int selectedRow = ui.directoryListWidget->currentRow();
|
||||
int newRow = selectedRow + step;
|
||||
if (selectedRow == -1 || newRow < 0 || newRow > ui.directoryListWidget->count() - 1)
|
||||
return;
|
||||
|
||||
if (!(ui.directoryListWidget->item(newRow)->flags() & Qt::ItemIsEnabled))
|
||||
return;
|
||||
|
||||
const auto item = ui.directoryListWidget->takeItem(selectedRow);
|
||||
ui.directoryListWidget->insertItem(newRow, item);
|
||||
ui.directoryListWidget->setCurrentRow(newRow);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::removeDirectory()
|
||||
{
|
||||
for (const auto& path : ui.directoryListWidget->selectedItems())
|
||||
ui.directoryListWidget->takeItem(ui.directoryListWidget->row(path));
|
||||
refreshDataFilesView();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::moveArchive(int step)
|
||||
{
|
||||
int selectedRow = ui.archiveListWidget->currentRow();
|
||||
int newRow = selectedRow + step;
|
||||
if (selectedRow == -1 || newRow < 0 || newRow > ui.archiveListWidget->count() - 1)
|
||||
return;
|
||||
|
||||
const auto* item = ui.archiveListWidget->takeItem(selectedRow);
|
||||
|
||||
addArchive(item->text(), item->checkState(), newRow);
|
||||
ui.archiveListWidget->setCurrentRow(newRow);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::addArchive(const QString& name, Qt::CheckState selected, int row)
|
||||
{
|
||||
if (row == -1)
|
||||
row = ui.archiveListWidget->count();
|
||||
ui.archiveListWidget->insertItem(row, name);
|
||||
ui.archiveListWidget->item(row)->setCheckState(selected);
|
||||
if (mKnownArchives.filter(name).isEmpty()) // XXX why contains doesn't work here ???
|
||||
{
|
||||
ui.archiveListWidget->item(row)->setBackground(Qt::green);
|
||||
ui.archiveListWidget->item(row)->setForeground(Qt::black);
|
||||
}
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::addArchivesFromDir(const QString& path)
|
||||
{
|
||||
QDir dir(path, "*.bsa");
|
||||
|
||||
for (const auto& fileinfo : dir.entryInfoList())
|
||||
{
|
||||
const auto absPath = fileinfo.absoluteFilePath();
|
||||
if (Bsa::CompressedBSAFile::detectVersion(absPath.toStdString()) == Bsa::BSAVER_UNKNOWN)
|
||||
continue;
|
||||
|
||||
const auto fileName = fileinfo.fileName();
|
||||
const auto currentList = selectedArchivePaths(true);
|
||||
|
||||
if (!currentList.contains(fileName, Qt::CaseInsensitive))
|
||||
addArchive(fileName, Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::checkForDefaultProfile()
|
||||
{
|
||||
//don't allow deleting "Default" profile
|
||||
|
@ -394,13 +760,13 @@ void Launcher::DataFilesPage::slotAddonDataChanged()
|
|||
}
|
||||
|
||||
// Mutex lock to run reloadCells synchronously.
|
||||
std::mutex _reloadCellsMutex;
|
||||
static std::mutex reloadCellsMutex;
|
||||
|
||||
void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
|
||||
{
|
||||
// Use a mutex lock so that we can prevent two threads from executing the rest of this code at the same time
|
||||
// Based on https://stackoverflow.com/a/5429695/531762
|
||||
std::unique_lock<std::mutex> lock(_reloadCellsMutex);
|
||||
std::unique_lock<std::mutex> lock(reloadCellsMutex);
|
||||
|
||||
// The following code will run only if there is not another thread currently running it
|
||||
CellNameLoader cellNameLoader;
|
||||
|
@ -413,3 +779,89 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
|
|||
std::sort(cellNamesList.begin(), cellNamesList.end());
|
||||
emit signalLoadedCellsChanged(cellNamesList);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::startNavMeshTool()
|
||||
{
|
||||
mMainDialog->writeSettings();
|
||||
|
||||
ui.navMeshLogPlainTextEdit->clear();
|
||||
ui.navMeshProgressBar->setValue(0);
|
||||
ui.navMeshProgressBar->setMaximum(1);
|
||||
|
||||
mNavMeshToolProgress = NavMeshToolProgress {};
|
||||
|
||||
QStringList arguments({"--write-binary-log"});
|
||||
if (ui.navMeshRemoveUnusedTilesCheckBox->checkState() == Qt::Checked)
|
||||
arguments.append("--remove-unused-tiles");
|
||||
|
||||
if (!mNavMeshToolInvoker->startProcess(QLatin1String("openmw-navmeshtool"), arguments))
|
||||
return;
|
||||
|
||||
ui.cancelNavMeshButton->setEnabled(true);
|
||||
ui.navMeshProgressBar->setEnabled(true);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::killNavMeshTool()
|
||||
{
|
||||
mNavMeshToolInvoker->killProcess();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::readNavMeshToolStderr()
|
||||
{
|
||||
updateNavMeshProgress(4096);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::updateNavMeshProgress(int minDataSize)
|
||||
{
|
||||
QProcess& process = *mNavMeshToolInvoker->getProcess();
|
||||
mNavMeshToolProgress.mMessagesData.append(process.readAllStandardError());
|
||||
if (mNavMeshToolProgress.mMessagesData.size() < minDataSize)
|
||||
return;
|
||||
const std::byte* const begin = reinterpret_cast<const std::byte*>(mNavMeshToolProgress.mMessagesData.constData());
|
||||
const std::byte* const end = begin + mNavMeshToolProgress.mMessagesData.size();
|
||||
const std::byte* position = begin;
|
||||
HandleNavMeshToolMessage handle {
|
||||
mNavMeshToolProgress.mCellsCount,
|
||||
mNavMeshToolProgress.mExpectedMaxProgress,
|
||||
ui.navMeshProgressBar->maximum(),
|
||||
ui.navMeshProgressBar->value(),
|
||||
};
|
||||
while (true)
|
||||
{
|
||||
NavMeshTool::Message message;
|
||||
const std::byte* const nextPosition = NavMeshTool::deserialize(position, end, message);
|
||||
if (nextPosition == position)
|
||||
break;
|
||||
position = nextPosition;
|
||||
handle = std::visit(handle, NavMeshTool::decode(message));
|
||||
}
|
||||
if (position != begin)
|
||||
mNavMeshToolProgress.mMessagesData = mNavMeshToolProgress.mMessagesData.mid(position - begin);
|
||||
mNavMeshToolProgress.mCellsCount = handle.mCellsCount;
|
||||
mNavMeshToolProgress.mExpectedMaxProgress = handle.mExpectedMaxProgress;
|
||||
ui.navMeshProgressBar->setMaximum(handle.mMaxProgress);
|
||||
ui.navMeshProgressBar->setValue(handle.mProgress);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::readNavMeshToolStdout()
|
||||
{
|
||||
QProcess& process = *mNavMeshToolInvoker->getProcess();
|
||||
QByteArray& logData = mNavMeshToolProgress.mLogData;
|
||||
logData.append(process.readAllStandardOutput());
|
||||
const int lineEnd = logData.lastIndexOf('\n');
|
||||
if (lineEnd == -1)
|
||||
return;
|
||||
const int size = logData.size() >= lineEnd && logData[lineEnd - 1] == '\r' ? lineEnd - 1 : lineEnd;
|
||||
ui.navMeshLogPlainTextEdit->appendPlainText(QString::fromUtf8(logData.data(), size));
|
||||
logData = logData.mid(lineEnd + 1);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::navMeshToolFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
updateNavMeshProgress(0);
|
||||
ui.navMeshLogPlainTextEdit->appendPlainText(QString::fromUtf8(mNavMeshToolInvoker->getProcess()->readAllStandardOutput()));
|
||||
if (exitCode == 0 && exitStatus == QProcess::ExitStatus::NormalExit)
|
||||
ui.navMeshProgressBar->setValue(ui.navMeshProgressBar->maximum());
|
||||
ui.cancelNavMeshButton->setEnabled(false);
|
||||
ui.navMeshProgressBar->setEnabled(false);
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
#define DATAFILESPAGE_H
|
||||
|
||||
#include "ui_datafilespage.h"
|
||||
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
|
@ -20,6 +20,7 @@ namespace Config { class GameSettings;
|
|||
|
||||
namespace Launcher
|
||||
{
|
||||
class MainDialog;
|
||||
class TextInputDialog;
|
||||
class ProfilesComboBox;
|
||||
|
||||
|
@ -32,7 +33,7 @@ namespace Launcher
|
|||
|
||||
public:
|
||||
explicit DataFilesPage (Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
||||
Config::LauncherSettings &launcherSettings, QWidget *parent = 0);
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent = nullptr);
|
||||
|
||||
QAbstractItemModel* profilesModel() const;
|
||||
|
||||
|
@ -42,12 +43,6 @@ namespace Launcher
|
|||
void saveSettings(const QString &profile = "");
|
||||
bool loadSettings();
|
||||
|
||||
/**
|
||||
* Returns the file paths of all selected content files
|
||||
* @return the file paths of all selected content files
|
||||
*/
|
||||
QStringList selectedFilePaths();
|
||||
|
||||
signals:
|
||||
void signalProfileChanged (int index);
|
||||
void signalLoadedCellsChanged(QStringList selectedFiles);
|
||||
|
@ -65,17 +60,37 @@ namespace Launcher
|
|||
|
||||
void updateNewProfileOkButton(const QString &text);
|
||||
void updateCloneProfileOkButton(const QString &text);
|
||||
void addSubdirectories(bool append);
|
||||
void sortDirectories();
|
||||
void removeDirectory();
|
||||
void moveArchive(int step);
|
||||
void moveDirectory(int step);
|
||||
|
||||
void on_newProfileAction_triggered();
|
||||
void on_cloneProfileAction_triggered();
|
||||
void on_deleteProfileAction_triggered();
|
||||
|
||||
void startNavMeshTool();
|
||||
void killNavMeshTool();
|
||||
void readNavMeshToolStdout();
|
||||
void readNavMeshToolStderr();
|
||||
void navMeshToolFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
public:
|
||||
/// Content List that is always present
|
||||
const static char *mDefaultContentListName;
|
||||
|
||||
private:
|
||||
struct NavMeshToolProgress
|
||||
{
|
||||
QByteArray mLogData;
|
||||
QByteArray mMessagesData;
|
||||
std::map<std::uint64_t, std::string> mWorldspaces;
|
||||
int mCellsCount = 0;
|
||||
int mExpectedMaxProgress = 0;
|
||||
};
|
||||
|
||||
MainDialog *mMainDialog;
|
||||
TextInputDialog *mNewProfileDialog;
|
||||
TextInputDialog *mCloneProfileDialog;
|
||||
|
||||
|
@ -87,12 +102,15 @@ namespace Launcher
|
|||
QString mPreviousProfile;
|
||||
QStringList previousSelectedFiles;
|
||||
QString mDataLocal;
|
||||
QStringList mKnownArchives;
|
||||
QStringList mNewDataDirs;
|
||||
|
||||
void setPluginsCheckstates(Qt::CheckState state);
|
||||
Process::ProcessInvoker* mNavMeshToolInvoker;
|
||||
NavMeshToolProgress mNavMeshToolProgress;
|
||||
|
||||
void addArchive(const QString& name, Qt::CheckState selected, int row = -1);
|
||||
void addArchivesFromDir(const QString& dir);
|
||||
void buildView();
|
||||
void setupConfig();
|
||||
void readConfig();
|
||||
void setProfile (int index, bool savePrevious);
|
||||
void setProfile (const QString &previous, const QString ¤t, bool savePrevious);
|
||||
void removeProfile (const QString &profile);
|
||||
|
@ -102,6 +120,16 @@ namespace Launcher
|
|||
void populateFileViews(const QString& contentModelName);
|
||||
void reloadCells(QStringList selectedFiles);
|
||||
void refreshDataFilesView ();
|
||||
void updateNavMeshProgress(int minDataSize);
|
||||
QString selectDirectory();
|
||||
|
||||
/**
|
||||
* Returns the file paths of all selected content files
|
||||
* @return the file paths of all selected content files
|
||||
*/
|
||||
QStringList selectedFilePaths() const;
|
||||
QStringList selectedArchivePaths(bool all=false) const;
|
||||
QStringList selectedDirectoriesPaths() const;
|
||||
|
||||
class PathIterator
|
||||
{
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include "graphicspage.hpp"
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
#include <QScreen>
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
@ -14,12 +12,14 @@
|
|||
#include <SDL_video.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <array>
|
||||
|
||||
QString getAspect(int x, int y)
|
||||
{
|
||||
int gcd = std::gcd (x, y);
|
||||
if (gcd == 0)
|
||||
return QString();
|
||||
|
||||
int xaspect = x / gcd;
|
||||
int yaspect = y / gcd;
|
||||
// special case: 8 : 5 is usually referred to as 16:10
|
||||
|
@ -29,10 +29,8 @@ QString getAspect(int x, int y)
|
|||
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
|
||||
}
|
||||
|
||||
Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent)
|
||||
Launcher::GraphicsPage::GraphicsPage(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, mCfgMgr(cfg)
|
||||
, mEngineSettings(engineSettings)
|
||||
{
|
||||
setObjectName ("GraphicsPage");
|
||||
setupUi(this);
|
||||
|
@ -42,12 +40,11 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings:
|
|||
customWidthSpinBox->setMaximum(res.width());
|
||||
customHeightSpinBox->setMaximum(res.height());
|
||||
|
||||
connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int)));
|
||||
connect(windowModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotFullScreenChanged(int)));
|
||||
connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));
|
||||
connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int)));
|
||||
connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool)));
|
||||
connect(shadowDistanceCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotShadowDistLimitToggled(bool)));
|
||||
|
||||
}
|
||||
|
||||
bool Launcher::GraphicsPage::setupSDL()
|
||||
|
@ -91,26 +88,29 @@ bool Launcher::GraphicsPage::loadSettings()
|
|||
if (!setupSDL())
|
||||
return false;
|
||||
|
||||
if (mEngineSettings.getBool("vsync", "Video"))
|
||||
// Visuals
|
||||
if (Settings::Manager::getBool("vsync", "Video"))
|
||||
vSyncCheckBox->setCheckState(Qt::Checked);
|
||||
|
||||
if (mEngineSettings.getBool("fullscreen", "Video"))
|
||||
fullScreenCheckBox->setCheckState(Qt::Checked);
|
||||
size_t windowMode = static_cast<size_t>(Settings::Manager::getInt("window mode", "Video"));
|
||||
if (windowMode > static_cast<size_t>(Settings::WindowMode::Windowed))
|
||||
windowMode = 0;
|
||||
windowModeComboBox->setCurrentIndex(windowMode);
|
||||
|
||||
if (mEngineSettings.getBool("window border", "Video"))
|
||||
if (Settings::Manager::getBool("window border", "Video"))
|
||||
windowBorderCheckBox->setCheckState(Qt::Checked);
|
||||
|
||||
// aaValue is the actual value (0, 1, 2, 4, 8, 16)
|
||||
int aaValue = mEngineSettings.getInt("antialiasing", "Video");
|
||||
int aaValue = Settings::Manager::getInt("antialiasing", "Video");
|
||||
// aaIndex is the index into the allowed values in the pull down.
|
||||
int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue));
|
||||
if (aaIndex != -1)
|
||||
antiAliasingComboBox->setCurrentIndex(aaIndex);
|
||||
|
||||
int width = mEngineSettings.getInt("resolution x", "Video");
|
||||
int height = mEngineSettings.getInt("resolution y", "Video");
|
||||
int width = Settings::Manager::getInt("resolution x", "Video");
|
||||
int height = Settings::Manager::getInt("resolution y", "Video");
|
||||
QString resolution = QString::number(width) + QString(" x ") + QString::number(height);
|
||||
screenComboBox->setCurrentIndex(mEngineSettings.getInt("screen", "Video"));
|
||||
screenComboBox->setCurrentIndex(Settings::Manager::getInt("screen", "Video"));
|
||||
|
||||
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
|
||||
|
||||
|
@ -123,40 +123,49 @@ bool Launcher::GraphicsPage::loadSettings()
|
|||
customHeightSpinBox->setValue(height);
|
||||
}
|
||||
|
||||
float fpsLimit = mEngineSettings.getFloat("framerate limit", "Video");
|
||||
float fpsLimit = Settings::Manager::getFloat("framerate limit", "Video");
|
||||
if (fpsLimit != 0)
|
||||
{
|
||||
framerateLimitCheckBox->setCheckState(Qt::Checked);
|
||||
framerateLimitSpinBox->setValue(fpsLimit);
|
||||
}
|
||||
|
||||
if (mEngineSettings.getBool("actor shadows", "Shadows"))
|
||||
// Lighting
|
||||
int lightingMethod = 1;
|
||||
if (Settings::Manager::getString("lighting method", "Shaders") == "legacy")
|
||||
lightingMethod = 0;
|
||||
else if (Settings::Manager::getString("lighting method", "Shaders") == "shaders")
|
||||
lightingMethod = 2;
|
||||
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
||||
|
||||
// Shadows
|
||||
if (Settings::Manager::getBool("actor shadows", "Shadows"))
|
||||
actorShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (mEngineSettings.getBool("player shadows", "Shadows"))
|
||||
if (Settings::Manager::getBool("player shadows", "Shadows"))
|
||||
playerShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (mEngineSettings.getBool("terrain shadows", "Shadows"))
|
||||
if (Settings::Manager::getBool("terrain shadows", "Shadows"))
|
||||
terrainShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (mEngineSettings.getBool("object shadows", "Shadows"))
|
||||
if (Settings::Manager::getBool("object shadows", "Shadows"))
|
||||
objectShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (mEngineSettings.getBool("enable indoor shadows", "Shadows"))
|
||||
if (Settings::Manager::getBool("enable indoor shadows", "Shadows"))
|
||||
indoorShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
|
||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(
|
||||
shadowComputeSceneBoundsComboBox->findText(
|
||||
QString(tr(mEngineSettings.getString("compute scene bounds", "Shadows").c_str()))));
|
||||
QString(tr(Settings::Manager::getString("compute scene bounds", "Shadows").c_str()))));
|
||||
|
||||
int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows");
|
||||
int shadowDistLimit = Settings::Manager::getInt("maximum shadow map distance", "Shadows");
|
||||
if (shadowDistLimit > 0)
|
||||
{
|
||||
shadowDistanceCheckBox->setCheckState(Qt::Checked);
|
||||
shadowDistanceSpinBox->setValue(shadowDistLimit);
|
||||
}
|
||||
|
||||
float shadowFadeStart = mEngineSettings.getFloat("shadow fade start", "Shadows");
|
||||
float shadowFadeStart = Settings::Manager::getFloat("shadow fade start", "Shadows");
|
||||
if (shadowFadeStart != 0)
|
||||
fadeStartSpinBox->setValue(shadowFadeStart);
|
||||
|
||||
int shadowRes = mEngineSettings.getInt("shadow map resolution", "Shadows");
|
||||
int shadowRes = Settings::Manager::getInt("shadow map resolution", "Shadows");
|
||||
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
|
||||
if (shadowResIndex != -1)
|
||||
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
|
||||
|
@ -166,23 +175,25 @@ bool Launcher::GraphicsPage::loadSettings()
|
|||
|
||||
void Launcher::GraphicsPage::saveSettings()
|
||||
{
|
||||
// Visuals
|
||||
|
||||
// Ensure we only set the new settings if they changed. This is to avoid cluttering the
|
||||
// user settings file (which by definition should only contain settings the user has touched)
|
||||
bool cVSync = vSyncCheckBox->checkState();
|
||||
if (cVSync != mEngineSettings.getBool("vsync", "Video"))
|
||||
mEngineSettings.setBool("vsync", "Video", cVSync);
|
||||
if (cVSync != Settings::Manager::getBool("vsync", "Video"))
|
||||
Settings::Manager::setBool("vsync", "Video", cVSync);
|
||||
|
||||
bool cFullScreen = fullScreenCheckBox->checkState();
|
||||
if (cFullScreen != mEngineSettings.getBool("fullscreen", "Video"))
|
||||
mEngineSettings.setBool("fullscreen", "Video", cFullScreen);
|
||||
int cWindowMode = windowModeComboBox->currentIndex();
|
||||
if (cWindowMode != Settings::Manager::getInt("window mode", "Video"))
|
||||
Settings::Manager::setInt("window mode", "Video", cWindowMode);
|
||||
|
||||
bool cWindowBorder = windowBorderCheckBox->checkState();
|
||||
if (cWindowBorder != mEngineSettings.getBool("window border", "Video"))
|
||||
mEngineSettings.setBool("window border", "Video", cWindowBorder);
|
||||
if (cWindowBorder != Settings::Manager::getBool("window border", "Video"))
|
||||
Settings::Manager::setBool("window border", "Video", cWindowBorder);
|
||||
|
||||
int cAAValue = antiAliasingComboBox->currentText().toInt();
|
||||
if (cAAValue != mEngineSettings.getInt("antialiasing", "Video"))
|
||||
mEngineSettings.setInt("antialiasing", "Video", cAAValue);
|
||||
if (cAAValue != Settings::Manager::getInt("antialiasing", "Video"))
|
||||
Settings::Manager::setInt("antialiasing", "Video", cAAValue);
|
||||
|
||||
int cWidth = 0;
|
||||
int cHeight = 0;
|
||||
|
@ -197,33 +208,40 @@ void Launcher::GraphicsPage::saveSettings()
|
|||
cHeight = customHeightSpinBox->value();
|
||||
}
|
||||
|
||||
if (cWidth != mEngineSettings.getInt("resolution x", "Video"))
|
||||
mEngineSettings.setInt("resolution x", "Video", cWidth);
|
||||
if (cWidth != Settings::Manager::getInt("resolution x", "Video"))
|
||||
Settings::Manager::setInt("resolution x", "Video", cWidth);
|
||||
|
||||
if (cHeight != mEngineSettings.getInt("resolution y", "Video"))
|
||||
mEngineSettings.setInt("resolution y", "Video", cHeight);
|
||||
if (cHeight != Settings::Manager::getInt("resolution y", "Video"))
|
||||
Settings::Manager::setInt("resolution y", "Video", cHeight);
|
||||
|
||||
int cScreen = screenComboBox->currentIndex();
|
||||
if (cScreen != mEngineSettings.getInt("screen", "Video"))
|
||||
mEngineSettings.setInt("screen", "Video", cScreen);
|
||||
if (cScreen != Settings::Manager::getInt("screen", "Video"))
|
||||
Settings::Manager::setInt("screen", "Video", cScreen);
|
||||
|
||||
if (framerateLimitCheckBox->checkState())
|
||||
if (framerateLimitCheckBox->checkState() != Qt::Unchecked)
|
||||
{
|
||||
float cFpsLimit = framerateLimitSpinBox->value();
|
||||
if (cFpsLimit != mEngineSettings.getFloat("framerate limit", "Video"))
|
||||
mEngineSettings.setFloat("framerate limit", "Video", cFpsLimit);
|
||||
if (cFpsLimit != Settings::Manager::getFloat("framerate limit", "Video"))
|
||||
Settings::Manager::setFloat("framerate limit", "Video", cFpsLimit);
|
||||
}
|
||||
else if (mEngineSettings.getFloat("framerate limit", "Video") != 0)
|
||||
else if (Settings::Manager::getFloat("framerate limit", "Video") != 0)
|
||||
{
|
||||
mEngineSettings.setFloat("framerate limit", "Video", 0);
|
||||
Settings::Manager::setFloat("framerate limit", "Video", 0);
|
||||
}
|
||||
|
||||
int cShadowDist = shadowDistanceCheckBox->checkState() ? shadowDistanceSpinBox->value() : 0;
|
||||
if (mEngineSettings.getInt("maximum shadow map distance", "Shadows") != cShadowDist)
|
||||
mEngineSettings.setInt("maximum shadow map distance", "Shadows", cShadowDist);
|
||||
// Lighting
|
||||
static std::array<std::string, 3> lightingMethodMap = {"legacy", "shaders compatibility", "shaders"};
|
||||
const std::string& cLightingMethod = lightingMethodMap[lightingMethodComboBox->currentIndex()];
|
||||
if (cLightingMethod != Settings::Manager::getString("lighting method", "Shaders"))
|
||||
Settings::Manager::setString("lighting method", "Shaders", cLightingMethod);
|
||||
|
||||
// Shadows
|
||||
int cShadowDist = shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;
|
||||
if (Settings::Manager::getInt("maximum shadow map distance", "Shadows") != cShadowDist)
|
||||
Settings::Manager::setInt("maximum shadow map distance", "Shadows", cShadowDist);
|
||||
float cFadeStart = fadeStartSpinBox->value();
|
||||
if (cShadowDist > 0 && mEngineSettings.getFloat("shadow fade start", "Shadows") != cFadeStart)
|
||||
mEngineSettings.setFloat("shadow fade start", "Shadows", cFadeStart);
|
||||
if (cShadowDist > 0 && Settings::Manager::getFloat("shadow fade start", "Shadows") != cFadeStart)
|
||||
Settings::Manager::setFloat("shadow fade start", "Shadows", cFadeStart);
|
||||
|
||||
bool cActorShadows = actorShadowsCheckBox->checkState();
|
||||
bool cObjectShadows = objectShadowsCheckBox->checkState();
|
||||
|
@ -231,42 +249,42 @@ void Launcher::GraphicsPage::saveSettings()
|
|||
bool cPlayerShadows = playerShadowsCheckBox->checkState();
|
||||
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
||||
{
|
||||
if (!mEngineSettings.getBool("enable shadows", "Shadows"))
|
||||
mEngineSettings.setBool("enable shadows", "Shadows", true);
|
||||
if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows)
|
||||
mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows);
|
||||
if (mEngineSettings.getBool("player shadows", "Shadows") != cPlayerShadows)
|
||||
mEngineSettings.setBool("player shadows", "Shadows", cPlayerShadows);
|
||||
if (mEngineSettings.getBool("object shadows", "Shadows") != cObjectShadows)
|
||||
mEngineSettings.setBool("object shadows", "Shadows", cObjectShadows);
|
||||
if (mEngineSettings.getBool("terrain shadows", "Shadows") != cTerrainShadows)
|
||||
mEngineSettings.setBool("terrain shadows", "Shadows", cTerrainShadows);
|
||||
if (!Settings::Manager::getBool("enable shadows", "Shadows"))
|
||||
Settings::Manager::setBool("enable shadows", "Shadows", true);
|
||||
if (Settings::Manager::getBool("actor shadows", "Shadows") != cActorShadows)
|
||||
Settings::Manager::setBool("actor shadows", "Shadows", cActorShadows);
|
||||
if (Settings::Manager::getBool("player shadows", "Shadows") != cPlayerShadows)
|
||||
Settings::Manager::setBool("player shadows", "Shadows", cPlayerShadows);
|
||||
if (Settings::Manager::getBool("object shadows", "Shadows") != cObjectShadows)
|
||||
Settings::Manager::setBool("object shadows", "Shadows", cObjectShadows);
|
||||
if (Settings::Manager::getBool("terrain shadows", "Shadows") != cTerrainShadows)
|
||||
Settings::Manager::setBool("terrain shadows", "Shadows", cTerrainShadows);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mEngineSettings.getBool("enable shadows", "Shadows"))
|
||||
mEngineSettings.setBool("enable shadows", "Shadows", false);
|
||||
if (mEngineSettings.getBool("actor shadows", "Shadows"))
|
||||
mEngineSettings.setBool("actor shadows", "Shadows", false);
|
||||
if (mEngineSettings.getBool("player shadows", "Shadows"))
|
||||
mEngineSettings.setBool("player shadows", "Shadows", false);
|
||||
if (mEngineSettings.getBool("object shadows", "Shadows"))
|
||||
mEngineSettings.setBool("object shadows", "Shadows", false);
|
||||
if (mEngineSettings.getBool("terrain shadows", "Shadows"))
|
||||
mEngineSettings.setBool("terrain shadows", "Shadows", false);
|
||||
if (Settings::Manager::getBool("enable shadows", "Shadows"))
|
||||
Settings::Manager::setBool("enable shadows", "Shadows", false);
|
||||
if (Settings::Manager::getBool("actor shadows", "Shadows"))
|
||||
Settings::Manager::setBool("actor shadows", "Shadows", false);
|
||||
if (Settings::Manager::getBool("player shadows", "Shadows"))
|
||||
Settings::Manager::setBool("player shadows", "Shadows", false);
|
||||
if (Settings::Manager::getBool("object shadows", "Shadows"))
|
||||
Settings::Manager::setBool("object shadows", "Shadows", false);
|
||||
if (Settings::Manager::getBool("terrain shadows", "Shadows"))
|
||||
Settings::Manager::setBool("terrain shadows", "Shadows", false);
|
||||
}
|
||||
|
||||
bool cIndoorShadows = indoorShadowsCheckBox->checkState();
|
||||
if (mEngineSettings.getBool("enable indoor shadows", "Shadows") != cIndoorShadows)
|
||||
mEngineSettings.setBool("enable indoor shadows", "Shadows", cIndoorShadows);
|
||||
if (Settings::Manager::getBool("enable indoor shadows", "Shadows") != cIndoorShadows)
|
||||
Settings::Manager::setBool("enable indoor shadows", "Shadows", cIndoorShadows);
|
||||
|
||||
int cShadowRes = shadowResolutionComboBox->currentText().toInt();
|
||||
if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows"))
|
||||
mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes);
|
||||
if (cShadowRes != Settings::Manager::getInt("shadow map resolution", "Shadows"))
|
||||
Settings::Manager::setInt("shadow map resolution", "Shadows", cShadowRes);
|
||||
|
||||
auto cComputeSceneBounds = shadowComputeSceneBoundsComboBox->currentText().toStdString();
|
||||
if (cComputeSceneBounds != mEngineSettings.getString("compute scene bounds", "Shadows"))
|
||||
mEngineSettings.setString("compute scene bounds", "Shadows", cComputeSceneBounds);
|
||||
if (cComputeSceneBounds != Settings::Manager::getString("compute scene bounds", "Shadows"))
|
||||
Settings::Manager::setString("compute scene bounds", "Shadows", cComputeSceneBounds);
|
||||
}
|
||||
|
||||
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
||||
|
@ -299,9 +317,9 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
|||
return result;
|
||||
}
|
||||
|
||||
QString aspect = getAspect(mode.w, mode.h);
|
||||
QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h);
|
||||
|
||||
QString aspect = getAspect(mode.w, mode.h);
|
||||
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
|
||||
resolution.append(tr("\t(Wide ") + aspect + ")");
|
||||
|
||||
|
@ -339,9 +357,9 @@ void Launcher::GraphicsPage::screenChanged(int screen)
|
|||
}
|
||||
}
|
||||
|
||||
void Launcher::GraphicsPage::slotFullScreenChanged(int state)
|
||||
void Launcher::GraphicsPage::slotFullScreenChanged(int mode)
|
||||
{
|
||||
if (state == Qt::Checked) {
|
||||
if (mode == static_cast<int>(Settings::WindowMode::Fullscreen) || mode == static_cast<int>(Settings::WindowMode::WindowedFullscreen)) {
|
||||
standardRadioButton->toggle();
|
||||
customRadioButton->setEnabled(false);
|
||||
customWidthSpinBox->setEnabled(false);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef GRAPHICSPAGE_H
|
||||
#define GRAPHICSPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_graphicspage.h"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
@ -20,7 +18,7 @@ namespace Launcher
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent = 0);
|
||||
explicit GraphicsPage(QWidget *parent = nullptr);
|
||||
|
||||
void saveSettings();
|
||||
bool loadSettings();
|
||||
|
@ -35,9 +33,6 @@ namespace Launcher
|
|||
void slotShadowDistLimitToggled(bool checked);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
Settings::Manager &mEngineSettings;
|
||||
|
||||
QVector<QStringList> mResolutionsPerScreen;
|
||||
|
||||
static QStringList getAvailableResolutions(int screen);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTranslator>
|
||||
#include <QTextCodec>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#include <components/debug/debugging.hpp>
|
||||
#include <components/platform/platform.hpp>
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
#undef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
@ -14,8 +14,10 @@
|
|||
|
||||
#include "maindialog.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int runLauncher(int argc, char *argv[])
|
||||
{
|
||||
Platform::init();
|
||||
|
||||
try
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
@ -51,3 +53,8 @@ int main(int argc, char *argv[])
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return wrapApplication(runLauncher, argc, argv, "Launcher");
|
||||
}
|
||||
|
|
|
@ -3,16 +3,15 @@
|
|||
#include <components/version/version.hpp>
|
||||
#include <components/misc/helpviewer.hpp>
|
||||
|
||||
#include <QDate>
|
||||
#include <QTime>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QFontDatabase>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QCloseEvent>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <QDebug>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include "playpage.hpp"
|
||||
#include "graphicspage.hpp"
|
||||
|
@ -55,8 +54,8 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
|
|||
iconWidget->setCurrentRow(0);
|
||||
iconWidget->setFlow(QListView::LeftToRight);
|
||||
|
||||
QPushButton *helpButton = new QPushButton(tr("Help"));
|
||||
QPushButton *playButton = new QPushButton(tr("Play"));
|
||||
auto *helpButton = new QPushButton(tr("Help"));
|
||||
auto *playButton = new QPushButton(tr("Play"));
|
||||
buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close"));
|
||||
buttonBox->addButton(helpButton, QDialogButtonBox::HelpRole);
|
||||
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
|
||||
|
@ -82,31 +81,31 @@ void Launcher::MainDialog::createIcons()
|
|||
if (!QIcon::hasThemeIcon("document-new"))
|
||||
QIcon::setThemeName("tango");
|
||||
|
||||
QListWidgetItem *playButton = new QListWidgetItem(iconWidget);
|
||||
auto *playButton = new QListWidgetItem(iconWidget);
|
||||
playButton->setIcon(QIcon(":/images/openmw.png"));
|
||||
playButton->setText(tr("Play"));
|
||||
playButton->setTextAlignment(Qt::AlignCenter);
|
||||
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
|
||||
auto *dataFilesButton = new QListWidgetItem(iconWidget);
|
||||
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||
dataFilesButton->setText(tr("Data Files"));
|
||||
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
||||
auto *graphicsButton = new QListWidgetItem(iconWidget);
|
||||
graphicsButton->setIcon(QIcon(":/images/preferences-video.png"));
|
||||
graphicsButton->setText(tr("Graphics"));
|
||||
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
||||
auto *settingsButton = new QListWidgetItem(iconWidget);
|
||||
settingsButton->setIcon(QIcon(":/images/preferences.png"));
|
||||
settingsButton->setText(tr("Settings"));
|
||||
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
|
||||
auto *advancedButton = new QListWidgetItem(iconWidget);
|
||||
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png"));
|
||||
advancedButton->setText(tr("Advanced"));
|
||||
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||
|
@ -126,9 +125,9 @@ void Launcher::MainDialog::createPages()
|
|||
|
||||
mPlayPage = new PlayPage(this);
|
||||
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
||||
mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this);
|
||||
mGraphicsPage = new GraphicsPage(this);
|
||||
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
||||
mAdvancedPage = new AdvancedPage(mCfgMgr, mGameSettings, mEngineSettings, this);
|
||||
mAdvancedPage = new AdvancedPage(mGameSettings, this);
|
||||
|
||||
// Set the combobox of the play page to imitate the combobox on the datafilespage
|
||||
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
|
||||
|
@ -150,7 +149,6 @@ void Launcher::MainDialog::createPages()
|
|||
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
|
||||
// Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread
|
||||
connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);
|
||||
|
||||
}
|
||||
|
||||
Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
|
||||
|
@ -158,6 +156,20 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
|
|||
if (!setupLauncherSettings())
|
||||
return FirstRunDialogResultFailure;
|
||||
|
||||
// Dialog wizard and setup will fail if the config directory does not already exist
|
||||
QDir userConfigDir = QDir(QString::fromStdString(mCfgMgr.getUserConfigPath().string()));
|
||||
if ( ! userConfigDir.exists() ) {
|
||||
if ( ! userConfigDir.mkpath(".") )
|
||||
{
|
||||
cfgError(tr("Error opening OpenMW configuration file"),
|
||||
tr("<br><b>Could not create directory %0</b><br><br> \
|
||||
Please make sure you have the right permissions \
|
||||
and try again.<br>").arg(userConfigDir.canonicalPath())
|
||||
);
|
||||
return FirstRunDialogResultFailure;
|
||||
}
|
||||
}
|
||||
|
||||
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
|
@ -322,54 +334,46 @@ bool Launcher::MainDialog::setupGameSettings()
|
|||
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
|
||||
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
|
||||
|
||||
// Load the user config file first, separately
|
||||
// So we can write it properly, uncontaminated
|
||||
QString path = userPath + QLatin1String("openmw.cfg");
|
||||
QFile file(path);
|
||||
QFile file;
|
||||
|
||||
qDebug() << "Loading config file:" << path.toUtf8().constData();
|
||||
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
cfgError(tr("Error opening OpenMW configuration file"),
|
||||
tr("<br><b>Could not open %0 for reading</b><br><br> \
|
||||
Please make sure you have the right permissions \
|
||||
and try again.<br>").arg(file.fileName()));
|
||||
return false;
|
||||
}
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGameSettings.readUserFile(stream);
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Now the rest - priority: user > local > global
|
||||
QStringList paths;
|
||||
paths.append(globalPath + QString("openmw.cfg"));
|
||||
paths.append(localPath + QString("openmw.cfg"));
|
||||
paths.append(userPath + QString("openmw.cfg"));
|
||||
|
||||
for (const QString &path2 : paths)
|
||||
auto loadFile = [&] (const QString& path, bool(Config::GameSettings::*reader)(QTextStream&, bool), bool ignoreContent = false) -> std::optional<bool>
|
||||
{
|
||||
qDebug() << "Loading config file:" << path2.toUtf8().constData();
|
||||
|
||||
file.setFileName(path2);
|
||||
qDebug() << "Loading config file:" << path.toUtf8().constData();
|
||||
file.setFileName(path);
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
cfgError(tr("Error opening OpenMW configuration file"),
|
||||
tr("<br><b>Could not open %0 for reading</b><br><br> \
|
||||
Please make sure you have the right permissions \
|
||||
and try again.<br>").arg(file.fileName()));
|
||||
return false;
|
||||
tr("<br><b>Could not open %0 for reading</b><br><br> \
|
||||
Please make sure you have the right permissions \
|
||||
and try again.<br>").arg(file.fileName()));
|
||||
return {};
|
||||
}
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGameSettings.readFile(stream);
|
||||
(mGameSettings.*reader)(stream, ignoreContent);
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Load the user config file first, separately
|
||||
// So we can write it properly, uncontaminated
|
||||
if(!loadFile(userPath + QLatin1String("openmw.cfg"), &Config::GameSettings::readUserFile))
|
||||
return false;
|
||||
|
||||
// Now the rest - priority: user > local > global
|
||||
if(auto result = loadFile(localPath + QString("openmw.cfg"), &Config::GameSettings::readFile, true))
|
||||
{
|
||||
// Load global if local wasn't found
|
||||
if(!*result && !loadFile(globalPath + QString("openmw.cfg"), &Config::GameSettings::readFile, true))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
if(!loadFile(userPath + QString("openmw.cfg"), &Config::GameSettings::readFile))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -419,57 +423,23 @@ bool Launcher::MainDialog::setupGameData()
|
|||
|
||||
bool Launcher::MainDialog::setupGraphicsSettings()
|
||||
{
|
||||
// This method is almost a copy of OMW::Engine::loadSettings(). They should definitely
|
||||
// remain consistent, and possibly be merged into a shared component. At the very least
|
||||
// the filenames should be in the CfgMgr component.
|
||||
|
||||
// Ensure to clear previous settings in case we had already loaded settings.
|
||||
mEngineSettings.clear();
|
||||
|
||||
// Create the settings manager and load default settings file
|
||||
const std::string localDefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
|
||||
const std::string globalDefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
|
||||
std::string defaultPath;
|
||||
|
||||
// Prefer the settings-default.cfg in the current directory.
|
||||
if (boost::filesystem::exists(localDefault))
|
||||
defaultPath = localDefault;
|
||||
else if (boost::filesystem::exists(globalDefault))
|
||||
defaultPath = globalDefault;
|
||||
// Something's very wrong if we can't find the file at all.
|
||||
else {
|
||||
cfgError(tr("Error reading OpenMW configuration file"),
|
||||
tr("<br><b>Could not find settings-default.cfg</b><br><br> \
|
||||
The problem may be due to an incomplete installation of OpenMW.<br> \
|
||||
Reinstalling OpenMW may resolve the problem."));
|
||||
Settings::Manager::clear(); // Ensure to clear previous settings in case we had already loaded settings.
|
||||
try
|
||||
{
|
||||
boost::program_options::variables_map variables;
|
||||
boost::program_options::options_description desc;
|
||||
mCfgMgr.addCommonOptions(desc);
|
||||
mCfgMgr.readConfiguration(variables, desc, true);
|
||||
Settings::Manager::load(mCfgMgr);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
cfgError(tr("Error reading OpenMW configuration files"),
|
||||
tr("<br>The problem may be due to an incomplete installation of OpenMW.<br> \
|
||||
Reinstalling OpenMW may resolve the problem.<br>") + e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the default settings, report any parsing errors.
|
||||
try {
|
||||
mEngineSettings.loadDefault(defaultPath);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::string msg = std::string("<br><b>Error reading settings-default.cfg</b><br><br>") + e.what();
|
||||
cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load user settings if they exist
|
||||
const std::string userPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
|
||||
// User settings are not required to exist, so if they don't we're done.
|
||||
if (!boost::filesystem::exists(userPath)) return true;
|
||||
|
||||
try {
|
||||
mEngineSettings.loadUser(userPath);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::string msg = std::string("<br><b>Error reading settings.cfg</b><br><br>") + e.what();
|
||||
cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Launcher::MainDialog::loadSettings()
|
||||
|
@ -543,7 +513,7 @@ bool Launcher::MainDialog::writeSettings()
|
|||
// Graphics settings
|
||||
const std::string settingsPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
|
||||
try {
|
||||
mEngineSettings.saveUser(settingsPath);
|
||||
Settings::Manager::saveUser(settingsPath);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::string msg = "<br><b>Error writing settings.cfg</b><br><br>" +
|
||||
|
@ -609,7 +579,7 @@ void Launcher::MainDialog::play()
|
|||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>You do not have a game file selected.</b><br><br> \
|
||||
OpenMW will not start without a game file selected.<br>"));
|
||||
msgBox.exec();
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
#ifndef MAINDIALOG_H
|
||||
#define MAINDIALOG_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QProcess>
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/config/launchersettings.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#endif
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
|
@ -48,8 +44,8 @@ namespace Launcher
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainDialog(QWidget *parent = 0);
|
||||
~MainDialog();
|
||||
explicit MainDialog(QWidget *parent = nullptr);
|
||||
~MainDialog() override;
|
||||
|
||||
FirstRunDialogResult showFirstRunDialog();
|
||||
|
||||
|
@ -98,7 +94,6 @@ namespace Launcher
|
|||
Files::ConfigurationManager mCfgMgr;
|
||||
|
||||
Config::GameSettings mGameSettings;
|
||||
Settings::Manager mEngineSettings;
|
||||
Config::LauncherSettings mLauncherSettings;
|
||||
|
||||
};
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef PLAYPAGE_H
|
||||
#define PLAYPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_playpage.h"
|
||||
|
||||
class QComboBox;
|
||||
|
@ -16,7 +14,7 @@ namespace Launcher
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlayPage(QWidget *parent = 0);
|
||||
PlayPage(QWidget *parent = nullptr);
|
||||
void setProfilesModel(QAbstractItemModel *model);
|
||||
|
||||
signals:
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include <signal.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_video.h>
|
||||
|
||||
bool initSDL()
|
||||
{
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/config/launchersettings.hpp>
|
||||
|
||||
#include "utils/textinputdialog.hpp"
|
||||
#include "datafilespage.hpp"
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#ifndef SETTINGSPAGE_HPP
|
||||
#define SETTINGSPAGE_HPP
|
||||
|
||||
#include <QWidget>
|
||||
#include <QProcess>
|
||||
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
#include "ui_settingspage.h"
|
||||
|
@ -24,8 +21,8 @@ namespace Launcher
|
|||
|
||||
public:
|
||||
SettingsPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent = 0);
|
||||
~SettingsPage();
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent = nullptr);
|
||||
~SettingsPage() override;
|
||||
|
||||
void saveSettings();
|
||||
bool loadSettings();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "cellnameloader.hpp"
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/contentselector/view/contentselector.hpp>
|
||||
|
||||
QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
|
||||
|
@ -10,6 +10,8 @@ QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
|
|||
|
||||
// Loop through all content files
|
||||
for (auto &contentPath : contentPaths) {
|
||||
if (contentPath.endsWith(".omwscripts", Qt::CaseInsensitive))
|
||||
continue;
|
||||
esmReader.open(contentPath.toStdString());
|
||||
|
||||
// Loop through all records
|
||||
|
@ -35,7 +37,7 @@ QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
|
|||
|
||||
bool CellNameLoader::isCellRecord(ESM::NAME &recordName)
|
||||
{
|
||||
return recordName.intval == ESM::REC_CELL;
|
||||
return recordName.toInt() == ESM::REC_CELL;
|
||||
}
|
||||
|
||||
QString CellNameLoader::getCellName(ESM::ESMReader &esmReader)
|
||||
|
@ -46,3 +48,4 @@ QString CellNameLoader::getCellName(ESM::ESMReader &esmReader)
|
|||
|
||||
return QString::fromStdString(cell.mName);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
#ifndef OPENMW_CELLNAMELOADER_H
|
||||
#define OPENMW_CELLNAMELOADER_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
|
||||
namespace ESM {class ESMReader; struct Cell;}
|
||||
namespace ContentSelectorView {class ContentSelector;}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#define LINEEDIT_H
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QStyle>
|
||||
#include <QStylePainter>
|
||||
#include <QToolButton>
|
||||
|
||||
|
@ -24,7 +23,7 @@ class LineEdit : public QLineEdit
|
|||
QString mPlaceholderText;
|
||||
|
||||
public:
|
||||
LineEdit(QWidget *parent = 0);
|
||||
LineEdit(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *) override;
|
||||
|
|
60
apps/launcher/utils/openalutil.cpp
Normal file
60
apps/launcher/utils/openalutil.cpp
Normal file
|
@ -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;
|
||||
}
|
8
apps/launcher/utils/openalutil.hpp
Normal file
8
apps/launcher/utils/openalutil.hpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
std::vector<std::string> enumerateOpenALDevices();
|
||||
std::vector<std::string> enumerateOpenALDevicesHrtf();
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
#include <QRegExpValidator>
|
||||
#include <QLineEdit>
|
||||
#include <QString>
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "profilescombobox.hpp"
|
||||
|
||||
|
@ -30,10 +27,10 @@ void ProfilesComboBox::setEditEnabled(bool editable)
|
|||
setEditable(true);
|
||||
setValidator(mValidator);
|
||||
|
||||
ComboBoxLineEdit *edit = new ComboBoxLineEdit(this);
|
||||
auto *edit = new ComboBoxLineEdit(this);
|
||||
|
||||
setLineEdit(edit);
|
||||
setCompleter(0);
|
||||
setCompleter(nullptr);
|
||||
|
||||
connect(lineEdit(), SIGNAL(editingFinished()), this,
|
||||
SLOT(slotEditingFinished()));
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "components/contentselector/view/combobox.hpp"
|
||||
#include "lineedit.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
|
||||
class QString;
|
||||
|
||||
|
@ -16,12 +16,12 @@ public:
|
|||
class ComboBoxLineEdit : public LineEdit
|
||||
{
|
||||
public:
|
||||
explicit ComboBoxLineEdit (QWidget *parent = 0);
|
||||
explicit ComboBoxLineEdit (QWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit ProfilesComboBox(QWidget *parent = 0);
|
||||
explicit ProfilesComboBox(QWidget *parent = nullptr);
|
||||
void setEditEnabled(bool editable);
|
||||
void setCurrentProfile(int index)
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue