1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-08-16 12:14:12 +00:00

Compare commits

..

No commits in common. "master" and "openmw-49-rc4" have entirely different histories.

1113 changed files with 17593 additions and 61817 deletions

View file

@ -1,22 +1,18 @@
Checks: > Checks: >
-*, -*,
boost-*,
portability-*, portability-*,
-portability-template-virtual-member-function,
clang-analyzer-*, clang-analyzer-*,
-clang-analyzer-optin.*, -clang-analyzer-optin*,
-clang-analyzer-cplusplus.NewDeleteLeaks, -clang-analyzer-cplusplus.NewDeleteLeaks,
-clang-analyzer-cplusplus.NewDelete,
-clang-analyzer-core.CallAndMessage, -clang-analyzer-core.CallAndMessage,
modernize-avoid-bind, -modernize-avoid-bind
readability-identifier-naming WarningsAsErrors: >
WarningsAsErrors: '*' -*,
HeaderFilterRegex: '(apps|components)/' boost-*,
CheckOptions: portability-*,
- key: readability-identifier-naming.ConceptCase clang-analyzer-*,
value: CamelCase -clang-analyzer-optin*,
- key: readability-identifier-naming.NamespaceCase -clang-analyzer-cplusplus.NewDeleteLeaks,
value: CamelCase -clang-analyzer-core.CallAndMessage
- key: readability-identifier-naming.NamespaceIgnoredRegexp HeaderFilterRegex: '^(apps|components)'
value: 'bpo|osg(DB|FX|Particle|Shadow|Viewer|Util)?'
- key: readability-identifier-naming.LocalVariableCase
value: camelBack

View file

@ -73,7 +73,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Building Dependencies - name: Install Building Dependencies
run: CI/before_install.macos.sh run: CI/before_install.osx.sh
- name: Prime ccache - name: Prime ccache
uses: hendrikmuhs/ccache-action@v1 uses: hendrikmuhs/ccache-action@v1
@ -82,9 +82,11 @@ jobs:
max-size: 1000M max-size: 1000M
- name: Configure - name: Configure
run: CI/before_script.macos.sh run: CI/before_script.osx.sh
- name: Build - name: Build
run: CI/macos/build.sh run: |
cd build
make -j $(sysctl -n hw.logicalcpu) package
Output-Envs: Output-Envs:
name: Read .env file and expose it as output name: Read .env file and expose it as output
@ -104,6 +106,7 @@ jobs:
fail-fast: true fail-fast: true
matrix: matrix:
image: image:
- "2019"
- "2022" - "2022"
uses: ./.github/workflows/windows.yml uses: ./.github/workflows/windows.yml

View file

@ -4,13 +4,9 @@ on:
workflow_call: workflow_call:
inputs: inputs:
image: image:
description: Window Server image description: MSVC image (2019/2022)
required: true required: true
type: string type: string
msvc:
description: MSVC version (2019/2022)
default: "2022"
type: string
vcpkg-deps-tag: vcpkg-deps-tag:
description: Git tag of our deps description: Git tag of our deps
required: true required: true
@ -49,7 +45,7 @@ jobs:
- name: Download prebuilt vcpkg packages - name: Download prebuilt vcpkg packages
working-directory: ${{ github.workspace }}/deps working-directory: ${{ github.workspace }}/deps
run: | run: |
$MANIFEST = "vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}.txt" $MANIFEST = "vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}.txt"
curl --fail --retry 3 -L -o "$MANIFEST" "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/$MANIFEST" curl --fail --retry 3 -L -o "$MANIFEST" "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/$MANIFEST"
$lines = Get-Content "$MANIFEST" $lines = Get-Content "$MANIFEST"
$URL = $lines[0] $URL = $lines[0]
@ -65,7 +61,7 @@ jobs:
- name: Extract archived prebuilt vcpkg packages - name: Extract archived prebuilt vcpkg packages
working-directory: ${{ github.workspace }}/deps working-directory: ${{ github.workspace }}/deps
run: 7z x -y -ovcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }} $env:archive run: 7z x -y -ovcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }} $env:archive
- name: Cache Qt - name: Cache Qt
id: qt-cache id: qt-cache
@ -98,12 +94,12 @@ jobs:
-B ${{ github.workspace }}/build -B ${{ github.workspace }}/build
-G Ninja -G Ninja
-D CMAKE_BUILD_TYPE=${{ inputs.build-type }} -D CMAKE_BUILD_TYPE=${{ inputs.build-type }}
-D CMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/scripts/buildsystems/vcpkg.cmake' -D CMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/scripts/buildsystems/vcpkg.cmake'
-D CMAKE_PREFIX_PATH='${{ github.workspace }}/deps/Qt/6.6.3/msvc2019_64' -D CMAKE_PREFIX_PATH='${{ github.workspace }}/deps/Qt/6.6.3/msvc2019_64'
${{ inputs.package && '-D CMAKE_CXX_FLAGS_RELEASE="/O2 /Ob2 /DNDEBUG /Zi"' || '' }} ${{ inputs.package && '-D CMAKE_CXX_FLAGS_RELEASE="/O2 /Ob2 /DNDEBUG /Zi"' || '' }}
${{ inputs.package && '-D "CMAKE_EXE_LINKER_FLAGS_RELEASE=/DEBUG /INCREMENTAL:NO"' || '' }} ${{ inputs.package && '-D "CMAKE_EXE_LINKER_FLAGS_RELEASE=/DEBUG /INCREMENTAL:NO"' || '' }}
-D LuaJit_INCLUDE_DIR='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/include/luajit' -D LuaJit_INCLUDE_DIR='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/include/luajit'
-D LuaJit_LIBRARY='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/lib/lua51.lib' -D LuaJit_LIBRARY='${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/lib/lua51.lib'
-D BUILD_BENCHMARKS=${{ ! inputs.package }} -D BUILD_BENCHMARKS=${{ ! inputs.package }}
-D BUILD_COMPONENTS_TESTS=${{ ! inputs.package }} -D BUILD_COMPONENTS_TESTS=${{ ! inputs.package }}
-D BUILD_OPENMW_TESTS=${{ ! inputs.package }} -D BUILD_OPENMW_TESTS=${{ ! inputs.package }}
@ -118,9 +114,9 @@ jobs:
- name: Copy missing DLLs - name: Copy missing DLLs
run: | run: |
cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/Release/MyGUIEngine.dll ${{ github.workspace }}/build cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/Release/MyGUIEngine.dll ${{ github.workspace }}/build
cp -Filter *.dll -Recurse ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/osgPlugins-3.6.5 ${{ github.workspace }}/build cp -Filter *.dll -Recurse ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/osgPlugins-3.6.5 ${{ github.workspace }}/build
cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.msvc }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/*.dll ${{ github.workspace }}/build cp ${{ github.workspace }}/deps/vcpkg-x64-${{ inputs.image }}-${{ inputs.vcpkg-deps-tag }}/installed/x64-windows/bin/*.dll ${{ github.workspace }}/build
- name: Copy Qt DLLs - name: Copy Qt DLLs
working-directory: ${{ github.workspace }}/deps/Qt/6.6.3/msvc2019_64 working-directory: ${{ github.workspace }}/deps/Qt/6.6.3/msvc2019_64
@ -176,7 +172,7 @@ jobs:
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
run: | run: |
job_url=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json jobs --jq '.jobs[] | select(.name == "windows-${{ inputs.msvc }}") | .url') job_url=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json jobs --jq '.jobs[] | select(.name == "windows-${{ inputs.image }}") | .url')
printf "Ref ${{ github.ref }}\nJob ${job_url}\nCommit ${{ github.sha }}\n" > install/CI-ID.txt printf "Ref ${{ github.ref }}\nJob ${job_url}\nCommit ${{ github.sha }}\n" > install/CI-ID.txt
cp install/CI-ID.txt pdb/CI-ID.txt cp install/CI-ID.txt pdb/CI-ID.txt
cp install/CI-ID.txt SymStore/CI-ID.txt cp install/CI-ID.txt SymStore/CI-ID.txt
@ -184,19 +180,19 @@ jobs:
- name: Store OpenMW archived pdb files - name: Store OpenMW archived pdb files
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: openmw-windows-${{ inputs.msvc }}-pdb-${{ github.sha }} name: openmw-windows-${{ inputs.image }}-pdb-${{ github.sha }}
path: ${{ github.workspace }}/pdb/* path: ${{ github.workspace }}/pdb/*
- name: Store OpenMW build artifacts - name: Store OpenMW build artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: openmw-windows-${{ inputs.msvc }}-${{ github.sha }} name: openmw-windows-${{ inputs.image }}-${{ github.sha }}
path: ${{ github.workspace }}/install/* path: ${{ github.workspace }}/install/*
- name: Store symbol server artifacts - name: Store symbol server artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: openmw-windows-${{ inputs.msvc }}-sym-store-${{ github.sha }} name: openmw-windows-${{ inputs.image }}-sym-store-${{ github.sha }}
path: ${{ github.workspace }}/SymStore/* path: ${{ github.workspace }}/SymStore/*
- name: Upload to symbol server - name: Upload to symbol server

View file

@ -9,8 +9,6 @@ stages:
- test - test
workflow: workflow:
auto_cancel:
on_new_commit: interruptible
rules: rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
@ -29,14 +27,14 @@ variables:
.Ubuntu_Image: .Ubuntu_Image:
tags: tags:
- saas-linux-medium-amd64 - saas-linux-medium-amd64
image: ubuntu:24.04 image: ubuntu:22.04
rules: rules:
- if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"
Ubuntu_GCC_preprocess: Ubuntu_GCC_preprocess:
extends: .Ubuntu_Image extends: .Ubuntu_Image
cache: cache:
key: Ubuntu_GCC_preprocess.ubuntu_24.04.v1 key: Ubuntu_GCC_preprocess.ubuntu_22.04.v1
paths: paths:
- apt-cache/ - apt-cache/
- .cache/pip/ - .cache/pip/
@ -45,12 +43,9 @@ Ubuntu_GCC_preprocess:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
before_script: before_script:
- CI/install_debian_deps.sh openmw-deps openmw-deps-dynamic gcc_preprocess - CI/install_debian_deps.sh openmw-deps openmw-deps-dynamic gcc_preprocess
- pip3 install --user --break-system-packages click termtables - pip3 install --user click termtables
script: script:
- CI/ubuntu_gcc_preprocess.sh - CI/ubuntu_gcc_preprocess.sh
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
.Ubuntu: .Ubuntu:
extends: .Ubuntu_Image extends: .Ubuntu_Image
@ -61,7 +56,6 @@ Ubuntu_GCC_preprocess:
stage: build stage: build
variables: variables:
CMAKE_EXE_LINKER_FLAGS: -fuse-ld=mold CMAKE_EXE_LINKER_FLAGS: -fuse-ld=mold
OPENMW_CXX_FLAGS: "-Werror -Werror=implicit-fallthrough"
script: script:
- df -h - df -h
- export CCACHE_BASEDIR="`pwd`" - export CCACHE_BASEDIR="`pwd`"
@ -82,9 +76,9 @@ Ubuntu_GCC_preprocess:
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi - if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_esm_refid_benchmark; fi - if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_esm_refid_benchmark; fi
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_settings_access_benchmark; fi - if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_settings_access_benchmark; fi
- ccache -svv - ccache -s
- df -h - df -h
- if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then ~/.local/bin/gcovr --xml-pretty --exclude-unreachable-branches --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --print-summary --root "${CI_PROJECT_DIR}" -j $(nproc) -o ../coverage.xml; fi - 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 '^components-tests.xml$' -e '^openmw-tests.xml$' -e '^openmw-cs-tests.xml$' | xargs -I '{}' rm -rf './{}' - ls | grep -v -e '^extern$' -e '^install$' -e '^components-tests.xml$' -e '^openmw-tests.xml$' -e '^openmw-cs-tests.xml$' | xargs -I '{}' rm -rf './{}'
- cd .. - cd ..
- df -h - df -h
@ -99,20 +93,19 @@ Ubuntu_GCC_preprocess:
Coverity: Coverity:
tags: tags:
- saas-linux-medium-amd64 - saas-linux-medium-amd64
image: ubuntu:24.04 image: ubuntu:22.04
stage: build stage: build
interruptible: false
rules: rules:
- if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_PIPELINE_SOURCE == "schedule"
cache: cache:
key: Coverity.ubuntu_24.04.v1 key: Coverity.ubuntu_22.04.v1
paths: paths:
- apt-cache/ - apt-cache/
- ccache/ - ccache/
variables: variables:
CCACHE_SIZE: 2G CCACHE_SIZE: 2G
CC: clang CC: clang-12
CXX: clang++ CXX: clang++-12
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
CMAKE_CXX_FLAGS_DEBUG: -O0 CMAKE_CXX_FLAGS_DEBUG: -O0
before_script: before_script:
@ -128,40 +121,23 @@ Coverity:
- ccache -z -M "${CCACHE_SIZE}" - ccache -z -M "${CCACHE_SIZE}"
- CI/before_script.linux.sh - CI/before_script.linux.sh
- cov-analysis-linux64-*/bin/cov-configure --template --comptype prefix --compiler ccache - cov-analysis-linux64-*/bin/cov-configure --template --comptype prefix --compiler ccache
# 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) - cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc)
- ccache -svv - ccache -s
after_script:
- tar cfz cov-int.tar.gz cov-int - tar cfz cov-int.tar.gz cov-int
- echo "OPENMW_JOB_ID=$CI_JOB_ID" >> build.env
artifacts:
expire_in: 1 day
paths:
- /builds/OpenMW/openmw/cov-int/build-log.txt
- /builds/OpenMW/openmw/cov-int.tar.gz
reports:
dotenv: build.env
Coverity_Upload:
image: ubuntu:24.04
stage: build
interruptible: false
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
before_script:
- CI/install_debian_deps.sh coverity_upload
script:
- echo "$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/$OPENMW_JOB_ID/artifacts/cov-int.tar.gz"
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form version="$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA" --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" --form description="CI_COMMIT_SHORT_SHA / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
--form url="$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/$OPENMW_JOB_ID/artifacts/cov-int.tar.gz" artifacts:
needs: paths:
- Coverity - /builds/OpenMW/openmw/cov-int/build-log.txt
Ubuntu_GCC: Ubuntu_GCC:
extends: .Ubuntu extends: .Ubuntu
cache: cache:
key: Ubuntu_GCC.ubuntu_24.04.v1 key: Ubuntu_GCC.ubuntu_22.04.v1
before_script: before_script:
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
variables: variables:
@ -174,20 +150,18 @@ Ubuntu_GCC:
Ubuntu_GCC_asan: Ubuntu_GCC_asan:
extends: Ubuntu_GCC extends: Ubuntu_GCC
cache: cache:
key: Ubuntu_GCC_asan.ubuntu_24.04.v1 key: Ubuntu_GCC_asan.ubuntu_22.04.v1
variables: variables:
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
CMAKE_CXX_FLAGS_DEBUG: -g -O1 -fno-omit-frame-pointer -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak CMAKE_CXX_FLAGS_DEBUG: -g -O1 -fno-omit-frame-pointer -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak
CMAKE_EXE_LINKER_FLAGS: -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak -fuse-ld=mold CMAKE_EXE_LINKER_FLAGS: -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak -fuse-ld=mold
BUILD_OPENMW_ONLY: 1 BUILD_OPENMW_ONLY: 1
# Disable -Werror due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562
OPENMW_CXX_FLAGS: ""
Clang_Format: Clang_Format:
extends: .Ubuntu_Image extends: .Ubuntu_Image
stage: checks stage: checks
cache: cache:
key: Ubuntu_Clang_Format.ubuntu_24.04.v1 key: Ubuntu_Clang_Format.ubuntu_22.04.v1
paths: paths:
- apt-cache/ - apt-cache/
variables: variables:
@ -203,11 +177,11 @@ Lupdate:
extends: .Ubuntu_Image extends: .Ubuntu_Image
stage: checks stage: checks
cache: cache:
key: Ubuntu_lupdate.ubuntu_24.04.v1 key: Ubuntu_lupdate.ubuntu_22.04.v1
paths: paths:
- apt-cache/ - apt-cache/
variables: variables:
LUPDATE: /usr/lib/qt6/bin/lupdate LUPDATE: lupdate
before_script: before_script:
- CI/install_debian_deps.sh openmw-qt-translations - CI/install_debian_deps.sh openmw-qt-translations
script: script:
@ -229,7 +203,7 @@ Teal:
Ubuntu_GCC_Debug: Ubuntu_GCC_Debug:
extends: .Ubuntu extends: .Ubuntu
cache: cache:
key: Ubuntu_GCC_Debug.ubuntu_24.04.v2 key: Ubuntu_GCC_Debug.ubuntu_22.04.v2
before_script: before_script:
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
variables: variables:
@ -245,7 +219,7 @@ Ubuntu_GCC_Debug:
Ubuntu_GCC_tests: Ubuntu_GCC_tests:
extends: Ubuntu_GCC extends: Ubuntu_GCC
cache: cache:
key: Ubuntu_GCC_tests.ubuntu_24.04.v1 key: Ubuntu_GCC_tests.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -254,12 +228,12 @@ Ubuntu_GCC_tests:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
.Ubuntu_GCC_tests_Debug: .Ubuntu_GCC_tests_Debug:
extends: Ubuntu_GCC extends: Ubuntu_GCC
cache: cache:
key: Ubuntu_GCC_tests_Debug.ubuntu_24.04.v1 key: Ubuntu_GCC_tests_Debug.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -270,12 +244,12 @@ Ubuntu_GCC_tests:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
Ubuntu_GCC_tests_asan: Ubuntu_GCC_tests_asan:
extends: Ubuntu_GCC extends: Ubuntu_GCC
cache: cache:
key: Ubuntu_GCC_tests_asan.ubuntu_24.04.v1 key: Ubuntu_GCC_tests_asan.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -283,22 +257,17 @@ Ubuntu_GCC_tests_asan:
CMAKE_CXX_FLAGS_DEBUG: -g -O1 -fno-omit-frame-pointer -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak CMAKE_CXX_FLAGS_DEBUG: -g -O1 -fno-omit-frame-pointer -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak
CMAKE_EXE_LINKER_FLAGS: -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak -fuse-ld=mold CMAKE_EXE_LINKER_FLAGS: -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak -fuse-ld=mold
ASAN_OPTIONS: halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 ASAN_OPTIONS: halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
# Disable -Werror due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562
OPENMW_CXX_FLAGS: ""
artifacts: artifacts:
paths: [] paths: []
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
Ubuntu_GCC_tests_ubsan: Ubuntu_GCC_tests_ubsan:
extends: Ubuntu_GCC extends: Ubuntu_GCC
cache: cache:
key: Ubuntu_GCC_tests_ubsan.ubuntu_24.04.v1 key: Ubuntu_GCC_tests_ubsan.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -310,15 +279,12 @@ Ubuntu_GCC_tests_ubsan:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
.Ubuntu_GCC_tests_tsan: .Ubuntu_GCC_tests_tsan:
extends: Ubuntu_GCC extends: Ubuntu_GCC
cache: cache:
key: Ubuntu_GCC_tests_tsan.ubuntu_24.04.v1 key: Ubuntu_GCC_tests_tsan.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -331,20 +297,16 @@ Ubuntu_GCC_tests_ubsan:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
Ubuntu_GCC_tests_coverage: Ubuntu_GCC_tests_coverage:
extends: .Ubuntu_GCC_tests_Debug extends: .Ubuntu_GCC_tests_Debug
cache: cache:
key: Ubuntu_GCC_tests_coverage.ubuntu_24.04.v1 key: Ubuntu_GCC_tests_coverage.ubuntu_22.04.v1
paths:
- .cache/pip
variables: variables:
BUILD_WITH_CODE_COVERAGE: 1 BUILD_WITH_CODE_COVERAGE: 1
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
before_script: before_script:
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic openmw-coverage - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic openmw-coverage
- pipx install gcovr
coverage: /^\s*lines:\s*\d+.\d+\%/ coverage: /^\s*lines:\s*\d+.\d+\%/
artifacts: artifacts:
paths: [] paths: []
@ -354,10 +316,7 @@ Ubuntu_GCC_tests_coverage:
coverage_report: coverage_report:
coverage_format: cobertura coverage_format: cobertura
path: coverage.xml path: coverage.xml
junit: build/*-tests.xml junit: build/*_tests.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
.Ubuntu_Static_Deps: .Ubuntu_Static_Deps:
extends: Ubuntu_Clang extends: Ubuntu_Clang
@ -369,7 +328,7 @@ Ubuntu_GCC_tests_coverage:
- "CI/**/*" - "CI/**/*"
- ".gitlab-ci.yml" - ".gitlab-ci.yml"
cache: cache:
key: Ubuntu_Static_Deps.ubuntu_24.04.v1 key: Ubuntu_Static_Deps.ubuntu_22.04.v1
paths: paths:
- apt-cache/ - apt-cache/
- ccache/ - ccache/
@ -386,7 +345,7 @@ Ubuntu_GCC_tests_coverage:
.Ubuntu_Static_Deps_tests: .Ubuntu_Static_Deps_tests:
extends: .Ubuntu_Static_Deps extends: .Ubuntu_Static_Deps
cache: cache:
key: Ubuntu_Static_Deps_tests.ubuntu_24.04.v1 key: Ubuntu_Static_Deps_tests.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -398,14 +357,14 @@ Ubuntu_GCC_tests_coverage:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
Ubuntu_Clang: Ubuntu_Clang:
extends: .Ubuntu extends: .Ubuntu
before_script: before_script:
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic - CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic
cache: cache:
key: Ubuntu_Clang.ubuntu_24.04.v2 key: Ubuntu_Clang.ubuntu_22.04.v2
variables: variables:
CC: clang CC: clang
CXX: clang++ CXX: clang++
@ -418,7 +377,7 @@ Ubuntu_Clang:
before_script: before_script:
- CI/install_debian_deps.sh clang clang-tidy openmw-deps openmw-deps-dynamic - CI/install_debian_deps.sh clang clang-tidy openmw-deps openmw-deps-dynamic
cache: cache:
key: Ubuntu_Clang_Tidy.ubuntu_24.04.v1 key: Ubuntu_Clang_Tidy.ubuntu_22.04.v1
variables: variables:
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
CMAKE_CXX_FLAGS_DEBUG: -O0 CMAKE_CXX_FLAGS_DEBUG: -O0
@ -429,19 +388,15 @@ Ubuntu_Clang:
- mkdir -pv "${CCACHE_DIR}" - mkdir -pv "${CCACHE_DIR}"
- ccache -z -M "${CCACHE_SIZE}" - ccache -z -M "${CCACHE_SIZE}"
- CI/before_script.linux.sh - CI/before_script.linux.sh
- cp extern/.clang-tidy build/.clang-tidy
- cd build - cd build
- find . -name *.o -exec touch {} \; - find . -name *.o -exec touch {} \;
- cmake --build . -- -j $(nproc) ${BUILD_TARGETS} - cmake --build . -- -j $(nproc) ${BUILD_TARGETS}
- ccache -svv - ccache -s
artifacts: artifacts:
paths: paths:
- build/ - build/
expire_in: 12h expire_in: 12h
timeout: 3h timeout: 3h
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
Ubuntu_Clang_Tidy_components: Ubuntu_Clang_Tidy_components:
extends: .Ubuntu_Clang_Tidy_Base extends: .Ubuntu_Clang_Tidy_Base
@ -454,7 +409,7 @@ Ubuntu_Clang_Tidy_openmw:
needs: needs:
- Ubuntu_Clang_Tidy_components - Ubuntu_Clang_Tidy_components
variables: variables:
BUILD_TARGETS: openmw openmw-tests BUILD_TARGETS: openmw
timeout: 3h timeout: 3h
Ubuntu_Clang_Tidy_openmw-cs: Ubuntu_Clang_Tidy_openmw-cs:
@ -470,13 +425,13 @@ Ubuntu_Clang_Tidy_other:
needs: needs:
- Ubuntu_Clang_Tidy_components - Ubuntu_Clang_Tidy_components
variables: variables:
BUILD_TARGETS: components-tests bsatool esmtool openmw-launcher openmw-iniimporter openmw-essimporter openmw-wizard niftest openmw-navmeshtool openmw-bulletobjecttool BUILD_TARGETS: bsatool esmtool openmw-launcher openmw-iniimporter openmw-essimporter openmw-wizard niftest components-tests openmw-tests openmw-cs-tests openmw-navmeshtool openmw-bulletobjecttool
timeout: 3h timeout: 3h
.Ubuntu_Clang_tests: .Ubuntu_Clang_tests:
extends: Ubuntu_Clang extends: Ubuntu_Clang
cache: cache:
key: Ubuntu_Clang_tests.ubuntu_24.04.v1 key: Ubuntu_Clang_tests.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -485,12 +440,12 @@ Ubuntu_Clang_Tidy_other:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
Ubuntu_Clang_tests_Debug: Ubuntu_Clang_tests_Debug:
extends: Ubuntu_Clang extends: Ubuntu_Clang
cache: cache:
key: Ubuntu_Clang_tests_Debug.ubuntu_24.04.v1 key: Ubuntu_Clang_tests_Debug.ubuntu_22.04.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
@ -500,21 +455,21 @@ Ubuntu_Clang_tests_Debug:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always when: always
reports: reports:
junit: build/*-tests.xml junit: build/*_tests.xml
.Ubuntu_integration_tests_base: .Ubuntu_integration_tests_base:
extends: .Ubuntu_Image extends: .Ubuntu_Image
stage: test stage: test
variables: variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
EXAMPLE_SUITE_REVISION: 3e01d10e073474a886a9efe3658088db14869627 EXAMPLE_SUITE_REVISION: f51b832e033429a7cdc520e0e48d7dfdb9141caa
cache: cache:
paths: paths:
- .cache/pip - .cache/pip
- apt-cache/ - apt-cache/
before_script: before_script:
- CI/install_debian_deps.sh $OPENMW_DEPS - CI/install_debian_deps.sh $OPENMW_DEPS
- pip3 install --user --break-system-packages numpy matplotlib termtables click - pip3 install --user numpy matplotlib termtables click
script: script:
- CI/run_integration_tests.sh - CI/run_integration_tests.sh
after_script: after_script:
@ -525,7 +480,7 @@ Ubuntu_Clang_integration_tests:
needs: needs:
- Ubuntu_Clang - Ubuntu_Clang
cache: cache:
key: Ubuntu_Clang_integration_tests.ubuntu_24.04.v2 key: Ubuntu_Clang_integration_tests.ubuntu_22.04.v2
variables: variables:
OPENMW_DEPS: openmw-integration-tests OPENMW_DEPS: openmw-integration-tests
@ -534,35 +489,27 @@ Ubuntu_GCC_integration_tests_asan:
needs: needs:
- Ubuntu_GCC_asan - Ubuntu_GCC_asan
cache: cache:
key: Ubuntu_GCC_integration_tests_asan.ubuntu_24.04.v1 key: Ubuntu_GCC_integration_tests_asan.ubuntu_22.04.v1
variables: variables:
OPENMW_DEPS: openmw-integration-tests libasan OPENMW_DEPS: openmw-integration-tests libasan6
ASAN_OPTIONS: halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_leaks=0 ASAN_OPTIONS: halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_leaks=0
.MacOS: .MacOS:
stage: build stage: build
rules: rules:
- if: $CI_PROJECT_ID != "7107382" - if: $CI_PROJECT_ID == "7107382"
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
- if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule"
image: macos-15-xcode-16
tags:
- saas-macos-medium-m1
cache: cache:
paths: paths:
- ccache/ - ccache/
script: script:
- CI/before_install.macos.sh - CI/before_install.osx.sh
- export CCACHE_BASEDIR="$(pwd)" - export CCACHE_BASEDIR="$(pwd)"
- export CCACHE_DIR="$(pwd)/ccache" - export CCACHE_DIR="$(pwd)/ccache"
- mkdir -pv "${CCACHE_DIR}" - mkdir -pv "${CCACHE_DIR}"
- CI/macos/ccache_prep.sh - ccache -z -M "${CCACHE_SIZE}"
- CI/before_script.macos.sh - CI/before_script.osx.sh
- CI/macos/build.sh - cd build; make -j $(sysctl -n hw.logicalcpu) package
- cd build - for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME##*/}.dmg"; done
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${DMG_IDENTIFIER}_${CI_COMMIT_REF_NAME##*/}.dmg"; done
- | - |
if [[ -n "${AWS_ACCESS_KEY_ID}" ]]; then if [[ -n "${AWS_ACCESS_KEY_ID}" ]]; then
echo "[default]" > ~/.s3cfg echo "[default]" > ~/.s3cfg
@ -577,33 +524,20 @@ Ubuntu_GCC_integration_tests_asan:
s3cmd put "${dmg}" s3://openmw-artifacts/${artifactDirectory} s3cmd put "${dmg}" s3://openmw-artifacts/${artifactDirectory}
done done
fi fi
- ../CI/macos/ccache_save.sh - ccache -s
artifacts: artifacts:
paths: paths:
- build/OpenMW-*.dmg - build/OpenMW-*.dmg
macOS15_Xcode16_amd64: macOS14_Xcode15_arm64:
extends: .MacOS extends: .MacOS
image: macos-14-xcode-15
tags:
- saas-macos-medium-m1
cache: cache:
key: macOS15_Xcode16_amd64.v1 key: macOS14_Xcode15_arm64.v1
variables: variables:
CCACHE_SIZE: 3G CCACHE_SIZE: 3G
DMG_IDENTIFIER: amd64
MACOS_AMD64: true
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_EMOJI: true
HOMEBREW_NO_INSTALL_CLEANUP: true
macOS15_Xcode16_arm64:
extends: .MacOS
cache:
key: macOS15_Xcode16_arm64.v1
variables:
DMG_IDENTIFIER: arm64
CCACHE_SIZE: 3G
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_EMOJI: true
HOMEBREW_NO_INSTALL_CLEANUP: true
.Compress_And_Upload_Symbols_Base: .Compress_And_Upload_Symbols_Base:
extends: .Ubuntu_Image extends: .Ubuntu_Image
@ -698,16 +632,12 @@ macOS15_Xcode16_arm64:
- | - |
if (Get-ChildItem -Recurse *.pdb) { if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
} }
Push-Location .. Push-Location ..
..\CI\Store-Symbols.ps1 -SkipCompress ..\CI\Store-Symbols.ps1 -SkipCompress
if(!$?) { Exit $LASTEXITCODE }
7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt 7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
Pop-Location Pop-Location
Get-ChildItem -Recurse *.pdb | Remove-Item Get-ChildItem -Recurse *.pdb | Remove-Item
} }
@ -715,20 +645,13 @@ macOS15_Xcode16_arm64:
- | - |
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
- |
if ($executables) {
foreach ($exe in $executables.Split(',')) {
& .\$exe
if(!$?) { Exit $LASTEXITCODE }
}
} }
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script: after_script:
- Get-Volume - Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache: cache:
key: ninja-2022-v13 key: ninja-2022-v11
paths: paths:
- ccache - ccache
- deps - deps
@ -856,16 +779,12 @@ macOS15_Xcode16_arm64:
- | - |
if (Get-ChildItem -Recurse *.pdb) { if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
} }
Push-Location .. Push-Location ..
..\CI\Store-Symbols.ps1 -SkipCompress ..\CI\Store-Symbols.ps1 -SkipCompress
if(!$?) { Exit $LASTEXITCODE }
7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt 7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
Pop-Location Pop-Location
Get-ChildItem -Recurse *.pdb | Remove-Item Get-ChildItem -Recurse *.pdb | Remove-Item
} }
@ -873,20 +792,13 @@ macOS15_Xcode16_arm64:
- | - |
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
- |
if ($executables) {
foreach ($exe in $executables.Split(',')) {
& .\$exe
if(!$?) { Exit $LASTEXITCODE }
}
} }
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script: after_script:
- Get-Volume - Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache: cache:
key: msbuild-2022-v13 key: msbuild-2022-v11
paths: paths:
- deps - deps
- MSVC2022_64/deps/Qt - MSVC2022_64/deps/Qt
@ -987,7 +899,7 @@ Windows_MSBuild_CacheInit:
- cd build - cd build
- cmake --build . -- -j $(nproc) - cmake --build . -- -j $(nproc)
# - cmake --install . # no one uses builds anyway, disable until 'no space left' is resolved # - cmake --install . # no one uses builds anyway, disable until 'no space left' is resolved
- ccache -svv - ccache -s
- df -h - df -h
- ls | grep -v -e '^extern$' -e '^install$' | xargs -I '{}' rm -rf './{}' - ls | grep -v -e '^extern$' -e '^install$' | xargs -I '{}' rm -rf './{}'
- cd .. - cd ..
@ -1015,7 +927,7 @@ Windows_MSBuild_CacheInit:
paths: paths:
- .cache/pip - .cache/pip
before_script: before_script:
- pip3 install --user --break-system-packages requests click discord_webhook - pip3 install --user requests click discord_webhook
script: script:
- scripts/find_missing_merge_requests.py --project_id=$CI_PROJECT_ID --ignored_mrs_path=$CI_PROJECT_DIR/.resubmitted_merge_requests.txt - scripts/find_missing_merge_requests.py --project_id=$CI_PROJECT_ID --ignored_mrs_path=$CI_PROJECT_DIR/.resubmitted_merge_requests.txt
@ -1029,7 +941,7 @@ Windows_MSBuild_CacheInit:
- flatpak build-bundle ./repo openmw.flatpak org.openmw.OpenMW.devel - flatpak build-bundle ./repo openmw.flatpak org.openmw.OpenMW.devel
cache: cache:
key: flatpak key: flatpak
paths: paths:
- ".flatpak-builder" - ".flatpak-builder"
artifacts: artifacts:
untracked: false untracked: false

View file

@ -10,4 +10,4 @@ python:
build: build:
os: ubuntu-22.04 os: ubuntu-22.04
tools: tools:
python: "3.9" python: "3.8"

View file

@ -10,8 +10,7 @@ If you feel your name is missing from this list, please add it to `AUTHORS.md`.
Programmers Programmers
----------- -----------
Alexey Dobrokhotov (Capo) - Project leader 2025-present Bret Curtis (psi29a) - Project leader 2019-present
Bret Curtis (psi29a) - Project leader 2019-2025
Marc Zinnschlag (Zini) - Project leader 2010-2018 Marc Zinnschlag (Zini) - Project leader 2010-2018
Nicolay Korslund - Project leader 2008-2010 Nicolay Korslund - Project leader 2008-2010
scrawl - Top contributor scrawl - Top contributor
@ -50,10 +49,10 @@ Programmers
Berulacks Berulacks
Bo Svensson Bo Svensson
Britt Mathis (galdor557) Britt Mathis (galdor557)
Capostrophic
Carl Maxwell Carl Maxwell
cc9cii cc9cii
Cédric Mocquillon Cédric Mocquillon
Charles Horn
Chris Boyce (slothlife) Chris Boyce (slothlife)
Chris Robinson (KittyCat) Chris Robinson (KittyCat)
Chris Vigil Chris Vigil
@ -197,7 +196,7 @@ Programmers
Qlonever Qlonever
Radu-Marius Popovici (rpopovici) Radu-Marius Popovici (rpopovici)
Rafael Moura (dhustkoder) Rafael Moura (dhustkoder)
Randy Davin (Kuyondo) Randy Davin (Kindi)
rdimesio rdimesio
rexelion rexelion
riothamus riothamus
@ -233,7 +232,6 @@ Programmers
Tess (tescoShoppah) Tess (tescoShoppah)
thegriglat thegriglat
Thomas Luppi (Digmaster) Thomas Luppi (Digmaster)
Tim Hagberg (hazardMan)
tlmullis tlmullis
trav trav
tri4ng1e tri4ng1e

View file

@ -1,97 +1,3 @@
0.50.0
------
Bug #2967: Inventory windows don't update when changing items by script
Bug #4437: Transformations for NiSkinInstance are ignored
Bug #4885: Disable in dialogue result script causes a crash
Bug #5331: Pathfinding works incorrectly when actor is moved from one interior cell to another
Bug #6029: Hostile NPCs are perfectly aware of a player character with a 75% Chameleon effect
Bug #6039: Next Spell keybind fails while selected enchanted item has multiple copies
Bug #6573: Editor: Selection behaves incorrectly on high-DPI displays
Bug #6792: Birth sign info box has no line breaks
Bug #7371: Equipping item from inventory does not play a Down sound when equipping fails
Bug #7622: Player's marksman weapons don't work on close actors underwater
Bug #7649: The sound and vfx of resisted enchanted items' magic still play
Bug #7693: I.ItemUsage should return an item to the selected stack if equipping/consumption is denied
Bug #7740: Magic items in the HUD aren't composited correctly
Bug #7799: Picking up ingredients while object paging active grid is on may cause a hiccup
Bug #7871: Kwama Queen doesn't start combat with player
Bug #7979: Paralyzed NPCs battlecry
Bug #8012: Startcombat and Stopcombat do not affect music in the menu mode
Bug #8245: The console command ShowVars does not list global mwscripts
Bug #8265: Topics are linked incorrectly
Bug #8303: On target spells cast by non-actors should fire underwater
Bug #8309: RemoveSpell should instantly remove the spell's effects
Bug #8318: Missing global variables are not handled gracefully in dialogue conditions
Bug #8333: Quest status subrecords should not actually cause parsing to skip remaining data
Bug #8340: Multi-effect enchantments are too expensive
Bug #8341: Repeat shader visitor passes discard parallax
Bug #8349: Travel to non-existent cell causes persistent black screen
Bug #8359: Some quick keys menu related issues
Bug #8371: Silence affects powers
Bug #8375: Moon phase cycle doesn't match Morrowind
Bug #8383: Casting bound helm or boots on beast races doesn't cleanup properly
Bug #8385: Russian encoding broken with locale parameters and calendar
Bug #8399: Jail skill increases don't count as progress towards the next level
Bug #8404: Prevent merchant equipping breaks on lights
Bug #8408: OpenMW doesn't report all the potential resting hindrances
Bug #8414: Waterwalking works when collision is disabled
Bug #8431: Behaviour of removed items from a container is buggy
Bug #8432: Changing to and from an interior cell doesn't update collision
Bug #8433: Wandering NPCs are not capable of avoiding easy obstacles
Bug #8436: Spell selection in a pinned spellbook window doesn't update
Bug #8437: Pinned inventory window's pin button doesn't look pressed
Bug #8446: Travel prices are strangely inconsistent
Bug #8447: Werewolf swimming animation breaks in third person perspective
Bug #8459: Changing magic effect base cost doesn't change spell price
Bug #8466: Showmap "" reveals nameless cells
Bug #8485: Witchwither disease and probably other common diseases don't work correctly
Bug #8490: Normals on Water disappear when Water Shader is Enabled but Refraction is Disabled
Bug #8500: OpenMW Alarm behaviour doesn't match morrowind.exe
Bug #8519: Multiple bounty is sometimes assigned to player when detected during a pickpocketing action
Bug #8540: Magic resistance is applied to effects without a magnitude
Bug #8557: Charm's disposition changes capped on 100, uncapped below 0
Bug #8582: addScript-attached local scripts start out inactive
Bug #8584: Spacing of service menu list entries is inconsistent
Bug #8585: Dialogue topic list doesn't have enough padding
Bug #8587: Minor INI importer problems
Bug #8593: Render targets do not generate mipmaps
Bug #8598: Post processing shaders don't interact with the vfs correctly
Bug #8599: Non-ASCII paths in BSA files don't work
Bug #8606: Floating point imprecision can mess with container capacity
Bug #8609: The crosshair is too large
Bug #8610: Terrain normal maps using NormalGL format instead of NormalDX
Bug #8612: Using aiactivate on an ingredient when graphical herbalism is enabled triggers non-stop pickup sounds
Bug #8614: Lua garbage collection fails to remove unused data
Bug #8615: Rest/wait time progress speed is different from vanilla
Bug #8620: Create/CloneCommand can reuse refNums, causing severe issues in-game
Bug #8650: Some plants turn invisible when being called types.Container.inventory(cont):isResolved()
Feature #2522: Support quick item transfer
Feature #3740: Gamepad GUI Mode
Feature #3769: Allow GetSpellEffects on enchantments
Feature #6000: Oblivion terrain rendering
Feature #6976: [Lua] Weather API
Feature #7813: Add audio doppler for 3d sounds
Feature #7966: Add Lua read access to TES3 player's journal records
Feature #8077: Save settings changes when clicking "ok"/closing the window
Feature #8112: Expose landscape record data to Lua
Feature #8113: Support extended selection in autodetected subdirectory dialog
Feature #8139: Editor: Redesign the selection markers
Feature #8285: Expose list of active shaders in postprocessing API
Feature #8290: Show player gold in Spellmaking
Feature #8313: Show the character name in the savegame details
Feature #8320: Add access mwscript source text to lua api
Feature #8334: Lua: AddTopic equivalent
Feature #8355: Lua: Window visibility checking in interfaces.UI
Feature #8509: FillJournal script instruction
Feature #8579: Bulk (un)indentation in mwscript editor
Feature #8580: Sort characters in the save loading menu
Feature #8597: Lua: Add more built-in event handlers
Feature #8629: Expose path grid data to Lua
Feature #8642: Partially dehardcode on-hit mechanics
Feature #8654: Allow lua world.createRecord to create NPC records
Task #8578: Drop support for Qt5
0.49.0 0.49.0
------ ------
@ -129,6 +35,7 @@
Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load
Bug #6025: Subrecords cannot overlap records Bug #6025: Subrecords cannot overlap records
Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex
Bug #6097: Level Progress Tooltip Sometimes Not Updated
Bug #6146: Lua command `actor:setEquipment` doesn't trigger mwscripts when equipping or unequipping a scripted item Bug #6146: Lua command `actor:setEquipment` doesn't trigger mwscripts when equipping or unequipping a scripted item
Bug #6156: 1ft Charm or Sound magic effect vfx doesn't work properly Bug #6156: 1ft Charm or Sound magic effect vfx doesn't work properly
Bug #6190: Unintuitive sun specularity time of day dependence Bug #6190: Unintuitive sun specularity time of day dependence
@ -321,17 +228,6 @@
Bug #8252: Plugin dependencies are not required to be loaded Bug #8252: Plugin dependencies are not required to be loaded
Bug #8295: Post-processing chain is case-sensitive Bug #8295: Post-processing chain is case-sensitive
Bug #8299: Crash while smoothing landscape Bug #8299: Crash while smoothing landscape
Bug #8364: Crash when clicking scrollbar without handle (divide by zero)
Bug #8378: Korean bitmap fonts are unusable
Bug #8439: Creatures without models can crash the game
Bug #8441: Freeze when using video main menu replacers
Bug #8445: Launcher crashes on exit when cell name loading thread is still running
Bug #8462: Crashes when resizing the window on macOS
Bug #8465: Blue screen w/ antialiasing and post-processing on macOS
Bug #8503: Camera does not handle NaN gracefully
Bug #8541: Lua: util.color:asHex produces wrong output for some colors
Bug #8567: Token replacement does not work via CLI and relative paths passed via the command line are not relative to the CWD
Bug #8576: Crash on exit when unresolving containers with scripted items
Feature #1415: Infinite fall failsafe Feature #1415: Infinite fall failsafe
Feature #2566: Handle NAM9 records for manual cell references Feature #2566: Handle NAM9 records for manual cell references
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
@ -496,7 +392,6 @@
Bug #6066: Addtopic "return" does not work from within script. No errors thrown Bug #6066: Addtopic "return" does not work from within script. No errors thrown
Bug #6067: ESP loader fails for certain subrecord orders 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 #6087: Bound items added directly to the inventory disappear if their corresponding spell effect ends
Bug #6097: Level Progress Tooltip Sometimes Not Updated
Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime 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 #6107: Fatigue is incorrectly recalculated when fortify effect is applied or removed
Bug #6109: Crash when playing a custom made menu_background file Bug #6109: Crash when playing a custom made menu_background file

View file

@ -1,7 +0,0 @@
#!/bin/sh -ex
if [[ "${MACOS_AMD64}" ]]; then
./CI/macos/before_install.amd64.sh
else
./CI/macos/before_install.arm64.sh
fi

30
CI/before_install.osx.sh Executable file
View file

@ -0,0 +1,30 @@
#!/bin/sh -ex
export HOMEBREW_NO_EMOJI=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
export HOMEBREW_AUTOREMOVE=1
brew tap --repair
brew update --quiet
brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd
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@5
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
# Install deps
brew install openal-soft icu4c yaml-cpp sqlite
ccache --version
cmake --version
qmake --version
if [[ "${MACOS_AMD64}" ]]; then
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240802.zip -o ~/openmw-deps.zip
unzip -o ~/openmw-deps.zip -d /tmp > /dev/null
else
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240818-arm64.tar.xz -o ~/openmw-deps.tar.xz
tar xf ~/openmw-deps.tar.xz -C /tmp > /dev/null
fi

View file

@ -17,7 +17,7 @@ declare -a CMAKE_CONF_OPTS=(
-DBUILD_SHARED_LIBS="${BUILD_SHARED_LIBS:-OFF}" -DBUILD_SHARED_LIBS="${BUILD_SHARED_LIBS:-OFF}"
-DUSE_SYSTEM_TINYXML=ON -DUSE_SYSTEM_TINYXML=ON
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON -DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON
-DOPENMW_CXX_FLAGS="${OPENMW_CXX_FLAGS}" # flags specific to OpenMW project -DOPENMW_CXX_FLAGS="-Werror -Werror=implicit-fallthrough" # flags specific to OpenMW project
) )
if [[ "${CMAKE_EXE_LINKER_FLAGS}" ]]; then if [[ "${CMAKE_EXE_LINKER_FLAGS}" ]]; then
@ -38,7 +38,7 @@ fi
if [[ $CI_CLANG_TIDY ]]; then if [[ $CI_CLANG_TIDY ]]; then
CMAKE_CONF_OPTS+=( CMAKE_CONF_OPTS+=(
-DCMAKE_CXX_CLANG_TIDY=clang-tidy -DCMAKE_CXX_CLANG_TIDY="clang-tidy;--warnings-as-errors=*"
-DBUILD_COMPONENTS_TESTS=ON -DBUILD_COMPONENTS_TESTS=ON
-DBUILD_OPENMW_TESTS=ON -DBUILD_OPENMW_TESTS=ON
-DBUILD_OPENCS_TESTS=ON -DBUILD_OPENCS_TESTS=ON

View file

@ -1,77 +0,0 @@
#!/bin/sh -e
# Silence a git warning
git config --global advice.detachedHead false
rm -fr build
mkdir build
cd build
DEPENDENCIES_ROOT="/tmp/openmw-deps"
if [[ "${MACOS_AMD64}" ]]; then
QT_PATH=$(arch -x86_64 /usr/local/bin/brew --prefix qt@6)
ICU_PATH=$(arch -x86_64 /usr/local/bin/brew --prefix icu4c)
OPENAL_PATH=$(arch -x86_64 /usr/local/bin/brew --prefix openal-soft)
CCACHE_EXECUTABLE=$(arch -x86_64 /usr/local/bin/brew --prefix ccache)/bin/ccache
else
QT_PATH=$(brew --prefix qt@6)
ICU_PATH=$(brew --prefix icu4c)
OPENAL_PATH=$(brew --prefix openal-soft)
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
fi
declare -a CMAKE_CONF_OPTS=(
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH;$OPENAL_PATH"
-D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
-D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
-D CMAKE_CXX_FLAGS="-stdlib=libc++"
-D CMAKE_C_COMPILER="clang"
-D CMAKE_CXX_COMPILER="clang++"
-D CMAKE_OSX_DEPLOYMENT_TARGET="13.6"
-D OPENMW_USE_SYSTEM_RECASTNAVIGATION=TRUE
-D Boost_INCLUDE_DIR="$DEPENDENCIES_ROOT/include"
-D OSGPlugins_LIB_DIR="$DEPENDENCIES_ROOT/lib/osgPlugins-3.6.5"
-D ICU_ROOT="$ICU_PATH"
-D OPENMW_OSX_DEPLOYMENT=TRUE
)
declare -a BUILD_OPTS=(
-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 BUILD_NAVMESHTOOL=TRUE
-D BUILD_BULLETOBJECTTOOL=TRUE
-G"Unix Makefiles"
)
if [[ "${MACOS_AMD64}" ]]; then
CMAKE_CONF_OPTS+=(
-D CMAKE_OSX_ARCHITECTURES="x86_64"
)
fi
if [[ "${CMAKE_BUILD_TYPE}" ]]; then
CMAKE_CONF_OPTS+=(
-D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
else
CMAKE_CONF_OPTS+=(
-D CMAKE_BUILD_TYPE=RelWithDebInfo
)
fi
if [[ "${MACOS_AMD64}" ]]; then
arch -x86_64 cmake \
"${CMAKE_CONF_OPTS[@]}" \
"${BUILD_OPTS[@]}" \
..
else
cmake \
"${CMAKE_CONF_OPTS[@]}" \
"${BUILD_OPTS[@]}" \
..
fi

View file

@ -377,8 +377,6 @@ case $VS_VERSION in
MSVC_DISPLAY_YEAR="2022" MSVC_DISPLAY_YEAR="2022"
QT_MSVC_YEAR="2019" QT_MSVC_YEAR="2019"
VCPKG_TRIPLET="x64-windows"
;; ;;
16|16.0|2019 ) 16|16.0|2019 )
@ -388,8 +386,6 @@ case $VS_VERSION in
MSVC_DISPLAY_YEAR="2019" MSVC_DISPLAY_YEAR="2019"
QT_MSVC_YEAR="2019" QT_MSVC_YEAR="2019"
VCPKG_TRIPLET="x64-windows-2019"
;; ;;
15|15.0|2017 ) 15|15.0|2017 )
@ -550,7 +546,7 @@ fi
QT_VER='6.6.3' QT_VER='6.6.3'
AQT_VERSION='v3.1.15' AQT_VERSION='v3.1.15'
VCPKG_TAG="2025-07-23" VCPKG_TAG="2024-11-10"
VCPKG_PATH="vcpkg-x64-${VS_VERSION:?}-${VCPKG_TAG:?}" VCPKG_PATH="vcpkg-x64-${VS_VERSION:?}-${VCPKG_TAG:?}"
VCPKG_PDB_PATH="vcpkg-x64-${VS_VERSION:?}-pdb-${VCPKG_TAG:?}" VCPKG_PDB_PATH="vcpkg-x64-${VS_VERSION:?}-pdb-${VCPKG_TAG:?}"
VCPKG_MANIFEST="${VCPKG_PATH:?}.txt" VCPKG_MANIFEST="${VCPKG_PATH:?}.txt"
@ -637,16 +633,16 @@ printf "vcpkg packages ${VCPKG_TAG:?}... "
fi fi
add_cmake_opts -DCMAKE_TOOLCHAIN_FILE="$(real_pwd)/${VCPKG_PATH:?}/scripts/buildsystems/vcpkg.cmake" add_cmake_opts -DCMAKE_TOOLCHAIN_FILE="$(real_pwd)/${VCPKG_PATH:?}/scripts/buildsystems/vcpkg.cmake"
add_cmake_opts -DLuaJit_INCLUDE_DIR="$(real_pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/include/luajit" add_cmake_opts -DLuaJit_INCLUDE_DIR="$(real_pwd)/${VCPKG_PATH:?}/installed/x64-windows/include/luajit"
add_cmake_opts -DLuaJit_LIBRARY="$(real_pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/lib/lua51.lib" add_cmake_opts -DLuaJit_LIBRARY="$(real_pwd)/${VCPKG_PATH:?}/installed/x64-windows/lib/lua51.lib"
for CONFIGURATION in ${CONFIGURATIONS[@]}; do for CONFIGURATION in ${CONFIGURATIONS[@]}; do
if [[ ${CONFIGURATION:?} == "Debug" ]]; then if [[ ${CONFIGURATION:?} == "Debug" ]]; then
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/debug/bin" VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/x64-windows/debug/bin"
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Debug/MyGUIEngine_d.dll" add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Debug/MyGUIEngine_d.dll"
else else
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/bin" VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/x64-windows/bin"
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Release/MyGUIEngine.dll" add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Release/MyGUIEngine.dll"
fi fi
@ -708,12 +704,17 @@ printf "Qt ${QT_VER}... "
DLLSUFFIX="" DLLSUFFIX=""
fi fi
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_MAJOR_VER}"{Core,Gui,Network,OpenGL,OpenGLWidgets,Widgets,Svg}${DLLSUFFIX}.dll if [ "${QT_MAJOR_VER}" -eq 6 ]; then
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_MAJOR_VER}"{Core,Gui,Network,OpenGL,OpenGLWidgets,Widgets,Svg}${DLLSUFFIX}.dll
# Since Qt 6.7.0 plugin is called "qmodernwindowsstyle" # Since Qt 6.7.0 plugin is called "qmodernwindowsstyle"
if [ "${QT_MINOR_VER}" -ge 7 ]; then if [ "${QT_MINOR_VER}" -ge 7 ]; then
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qmodernwindowsstyle${DLLSUFFIX}.dll" add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qmodernwindowsstyle${DLLSUFFIX}.dll"
else
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
fi
else else
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_MAJOR_VER}"{Core,Gui,Network,OpenGL,Widgets,Svg}${DLLSUFFIX}.dll
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll" add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
fi fi

53
CI/before_script.osx.sh Executable file
View file

@ -0,0 +1,53 @@
#!/bin/sh -e
# Silence a git warning
git config --global advice.detachedHead false
rm -fr build
mkdir build
cd build
DEPENDENCIES_ROOT="/tmp/openmw-deps"
QT_PATH=$(brew --prefix qt@5)
ICU_PATH=$(brew --prefix icu4c)
OPENAL_PATH=$(brew --prefix openal-soft)
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
declare -a CMAKE_CONF_OPTS=(
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH;$OPENAL_PATH"
-D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
-D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE"
-D CMAKE_CXX_FLAGS="-stdlib=libc++"
-D CMAKE_C_COMPILER="clang"
-D CMAKE_CXX_COMPILER="clang++"
-D CMAKE_OSX_DEPLOYMENT_TARGET="13.6"
-D OPENMW_USE_SYSTEM_RECASTNAVIGATION=TRUE
-D Boost_INCLUDE_DIR="$DEPENDENCIES_ROOT/include"
-D OSGPlugins_LIB_DIR="$DEPENDENCIES_ROOT/lib/osgPlugins-3.6.5"
-D ICU_ROOT="$ICU_PATH"
-D OPENMW_OSX_DEPLOYMENT=TRUE
)
if [[ "${CMAKE_BUILD_TYPE}" ]]; then
CMAKE_CONF_OPTS+=(
-D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
else
CMAKE_CONF_OPTS+=(
-D CMAKE_BUILD_TYPE=RelWithDebInfo
)
fi
cmake \
"${CMAKE_CONF_OPTS[@]}" \
-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 BUILD_NAVMESHTOOL=TRUE \
-D BUILD_BULLETOBJECTTOOL=TRUE \
-G"Unix Makefiles" \
..

View file

@ -1,6 +1,7 @@
#!/bin/bash -ex #!/bin/bash -ex
git ls-files -- ':(exclude)extern/' '*.cpp' '*.hpp' '*.h' | git ls-files -- ':(exclude)extern/' '*.cpp' '*.hpp' '*.h' |
grep -vP '/[a-z0-9]+\.(cpp|hpp|h)$' && grep -vP '/[a-z0-9]+\.(cpp|hpp|h)$' |
grep -vFf CI/file_name_exceptions.txt &&
( echo 'File names do not follow the naming convention, see https://wiki.openmw.org/index.php?title=Naming_Conventions#Files'; exit -1 ) ( echo 'File names do not follow the naming convention, see https://wiki.openmw.org/index.php?title=Naming_Conventions#Files'; exit -1 )
exit 0 exit 0

View file

@ -0,0 +1,48 @@
apps/openmw/android_main.cpp
apps/openmw/mwsound/efx-presets.h
apps/openmw/mwsound/ffmpeg_decoder.cpp
apps/openmw/mwsound/ffmpeg_decoder.hpp
apps/openmw/mwsound/openal_output.cpp
apps/openmw/mwsound/openal_output.hpp
apps/openmw/mwsound/sound_buffer.cpp
apps/openmw/mwsound/sound_buffer.hpp
apps/openmw/mwsound/sound_decoder.hpp
apps/openmw/mwsound/sound_output.hpp
apps/components_tests/esm/test_fixed_string.cpp
apps/components_tests/files/conversion_tests.cpp
apps/components_tests/lua/test_async.cpp
apps/components_tests/lua/test_configuration.cpp
apps/components_tests/lua/test_l10n.cpp
apps/components_tests/lua/test_lua.cpp
apps/components_tests/lua/test_scriptscontainer.cpp
apps/components_tests/lua/test_serialization.cpp
apps/components_tests/lua/test_storage.cpp
apps/components_tests/lua/test_ui_content.cpp
apps/components_tests/lua/test_utilpackage.cpp
apps/components_tests/lua/test_inputactions.cpp
apps/components_tests/lua/test_yaml.cpp
apps/components_tests/misc/test_endianness.cpp
apps/components_tests/misc/test_resourcehelpers.cpp
apps/components_tests/misc/test_stringops.cpp
apps/openmw_tests/mwdialogue/test_keywordsearch.cpp
apps/openmw_tests/mwscript/test_scripts.cpp
apps/openmw_tests/mwscript/test_utils.hpp
apps/openmw_tests/mwworld/test_store.cpp
components/bsa/bsa_file.cpp
components/bsa/bsa_file.hpp
components/crashcatcher/windows_crashcatcher.cpp
components/crashcatcher/windows_crashcatcher.hpp
components/crashcatcher/windows_crashmonitor.cpp
components/crashcatcher/windows_crashmonitor.hpp
components/crashcatcher/windows_crashshm.hpp
components/fx/lexer_types.hpp
components/fx/parse_constants.hpp
components/platform/file.posix.cpp
components/platform/file.stdio.cpp
components/platform/file.win32.cpp
components/sdlutil/gl4es_init.cpp
components/sdlutil/gl4es_init.h
components/to_utf8/gen_iconv.cpp
components/to_utf8/tables_gen.hpp
components/to_utf8/to_utf8.cpp
components/to_utf8/to_utf8.hpp

View file

@ -1 +1 @@
VCPKG_DEPS_TAG=2025-07-23 VCPKG_DEPS_TAG=2024-11-10

View file

@ -11,8 +11,7 @@ print_help() {
declare -rA GROUPED_DEPS=( declare -rA GROUPED_DEPS=(
[gcc]="binutils gcc build-essential cmake ccache curl unzip git pkg-config mold" [gcc]="binutils gcc build-essential cmake ccache curl unzip git pkg-config mold"
[clang]="binutils clang make cmake ccache curl unzip git pkg-config mold" [clang]="binutils clang make cmake ccache curl unzip git pkg-config mold"
[coverity]="binutils clang make cmake ccache curl unzip git pkg-config file" [coverity]="binutils clang-12 make cmake ccache curl unzip git pkg-config"
[coverity_upload]="curl"
[gcc_preprocess]=" [gcc_preprocess]="
binutils binutils
build-essential build-essential
@ -34,10 +33,10 @@ declare -rA GROUPED_DEPS=(
libboost-system-dev libboost-iostreams-dev libboost-system-dev libboost-iostreams-dev
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
libsdl2-dev libqt6opengl6-dev qt6-tools-dev qt6-tools-dev-tools libopenal-dev libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-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 libluajit-5.1-dev librecast-dev libsqlite3-dev ca-certificates libicu-dev
libyaml-cpp-dev libqt6svg6 libqt6svg6-dev libyaml-cpp-dev libqt5svg5 libqt5svg5-dev
" "
# These dependencies can alternatively be built and linked statically. # These dependencies can alternatively be built and linked statically.
@ -58,22 +57,22 @@ declare -rA GROUPED_DEPS=(
libsdl2-dev libboost-system-dev libboost-filesystem-dev libgl-dev libsdl2-dev libboost-system-dev libboost-filesystem-dev libgl-dev
" "
[openmw-coverage]="pipx" [openmw-coverage]="gcovr"
[openmw-integration-tests]=" [openmw-integration-tests]="
ca-certificates ca-certificates
gdb gdb
git git
git-lfs git-lfs
libavcodec60 libavcodec58
libavformat60 libavformat58
libavutil58 libavutil56
libboost-iostreams1.83.0 libboost-iostreams1.74.0
libboost-program-options1.83.0 libboost-program-options1.74.0
libboost-system1.83.0 libboost-system1.74.0
libbullet3.24 libbullet3.24
libcollada-dom2.5-dp0 libcollada-dom2.5-dp0
libicu74 libicu70
libjpeg8 libjpeg8
libluajit-5.1-2 libluajit-5.1-2
liblz4-1 liblz4-1
@ -81,30 +80,30 @@ declare -rA GROUPED_DEPS=(
libopenal1 libopenal1
libopenscenegraph161 libopenscenegraph161
libpng16-16 libpng16-16
libqt6opengl6 libqt5opengl5
librecast1 librecast1
libsdl2-2.0-0 libsdl2-2.0-0
libsqlite3-0 libsqlite3-0
libswresample4 libswresample3
libswscale7 libswscale5
libtinyxml2.6.2v5 libtinyxml2.6.2v5
libyaml-cpp0.8 libyaml-cpp0.8
python3-pip python3-pip
xvfb xvfb
" "
[libasan]="libasan8" [libasan6]="libasan6"
[android]="binutils build-essential cmake ccache curl unzip git pkg-config" [android]="binutils build-essential cmake ccache curl unzip git pkg-config"
[openmw-clang-format]=" [openmw-clang-format]="
clang-format-14 clang-format-14
git-core git-core
" "
[openmw-qt-translations]=" [openmw-qt-translations]="
qt6-tools-dev qttools5-dev
qt6-tools-dev-tools qttools5-dev-tools
git-core git-core
" "
) )
@ -127,24 +126,10 @@ export APT_CACHE_DIR="${PWD}/apt-cache"
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
set -x set -x
mkdir -pv "$APT_CACHE_DIR" mkdir -pv "$APT_CACHE_DIR"
apt-get update -yqq
while true; do
apt-get update -yqq && break
done
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common gnupg >/dev/null apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common gnupg >/dev/null
add-apt-repository -y ppa:openmw/openmw
while true; do add-apt-repository -y ppa:openmw/openmw-daily
add-apt-repository -y ppa:openmw/openmw && break add-apt-repository -y ppa:openmw/staging
done
while true; do
add-apt-repository -y ppa:openmw/openmw-daily && break
done
while true; do
add-apt-repository -y ppa:openmw/staging && break
done
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
apt list --installed apt list --installed

View file

@ -1,8 +0,0 @@
#!/bin/sh -ex
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
arch -x86_64 /usr/local/bin/brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd ccache cmake qt@6 openal-soft icu4c yaml-cpp sqlite
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240802.zip -o ~/openmw-deps.zip
unzip -o ~/openmw-deps.zip -d /tmp > /dev/null

View file

@ -1,9 +0,0 @@
#!/bin/sh -ex
brew tap --repair
brew update --quiet
brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd ccache cmake qt@6 openal-soft icu4c yaml-cpp sqlite
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240818-arm64.tar.xz -o ~/openmw-deps.tar.xz
tar xf ~/openmw-deps.tar.xz -C /tmp > /dev/null

View file

@ -1,9 +0,0 @@
#!/bin/sh -ex
cd build
if [[ "${MACOS_AMD64}" ]]; then
arch -x86_64 make -j $(sysctl -n hw.logicalcpu) package
else
make -j $(sysctl -n hw.logicalcpu) package
fi

View file

@ -1,7 +0,0 @@
#!/bin/sh -ex
if [[ "${MACOS_AMD64}" ]]; then
arch -x86_64 ccache -z -M "${CCACHE_SIZE}"
else
ccache -z -M "${CCACHE_SIZE}"
fi

View file

@ -1,7 +0,0 @@
#!/bin/sh -ex
if [[ "${MACOS_AMD64}" ]]; then
arch -x86_64 ccache -svv
else
ccache -svv
fi

View file

@ -9,7 +9,7 @@ git checkout FETCH_HEAD
cd .. cd ..
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \ xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \
scripts/integration_tests.py --verbose --omw build/install/bin/openmw --workdir integration_tests_output example-suite/ 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 ls integration_tests_output/*.osg_stats.log | while read v; do
scripts/osg_stats.py --stats '.*' --regexp_match < "${v}" scripts/osg_stats.py --stats '.*' --regexp_match < "${v}"

View file

@ -80,10 +80,10 @@ endif()
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_MINOR 49)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 92) set(OPENMW_LUA_API_REVISION 70)
set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_POSTPROCESSING_API_REVISION 2)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "") set(OPENMW_VERSION_TAGHASH "")
@ -249,8 +249,12 @@ endif()
find_package(LZ4 REQUIRED) find_package(LZ4 REQUIRED)
if (USE_QT) if (USE_QT)
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6) find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools Svg REQUIRED) if (QT_VERSION_MAJOR VERSION_EQUAL 5)
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL LinguistTools Svg REQUIRED)
else()
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools Svg REQUIRED)
endif()
message(STATUS "Using Qt${QT_VERSION}") message(STATUS "Using Qt${QT_VERSION}")
endif() endif()
@ -462,7 +466,7 @@ find_package(Boost 1.70.0 CONFIG REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONA
if(OPENMW_USE_SYSTEM_MYGUI) if(OPENMW_USE_SYSTEM_MYGUI)
find_package(MyGUI 3.4.3 REQUIRED) find_package(MyGUI 3.4.3 REQUIRED)
endif() endif()
find_package(SDL2 2.0.20 REQUIRED) find_package(SDL2 2.0.10 REQUIRED)
find_package(OpenAL REQUIRED) find_package(OpenAL REQUIRED)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
@ -586,10 +590,25 @@ if(OPENMW_LTO_BUILD)
endif() endif()
endif() endif()
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") set(OPENMW_CXX_FLAGS "-Wall -Wextra -Wundef -Wextra-semi -Wno-unused-parameter -pedantic -Wno-long-long -Wnon-virtual-dtor -Wunused ${OPENMW_CXX_FLAGS}")
endif()
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-potentially-evaluated-expression")
endif ()
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-unused-but-set-parameter -Wduplicated-branches -Wduplicated-cond -Wlogical-op")
endif()
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
# Extern # Extern
@ -600,42 +619,8 @@ if (BUILD_OPENCS OR BUILD_OPENCS_TESTS)
add_subdirectory (extern/osgQt) add_subdirectory (extern/osgQt)
endif() endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
add_compile_options("/W4")
set(WARNINGS_DISABLE
4100 # Unreferenced formal parameter (-Wunused-parameter)
4127 # Conditional expression is constant
4996 # Function was declared deprecated
5054 # Deprecated operations between enumerations of different types caused by Qt headers
)
foreach(d ${WARNINGS_DISABLE})
add_compile_options("/wd${d}")
endforeach(d)
if(OPENMW_MSVC_WERROR)
add_compile_options("/WX")
endif()
else ()
add_compile_options("-Wall" "-Wextra" "-Wundef" "-Wextra-semi" "-Wno-unused-parameter" "-pedantic" "-Wno-long-long" "-Wnon-virtual-dtor" "-Wunused")
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
add_compile_options("-Wno-potentially-evaluated-expression")
endif ()
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
add_compile_options("-Wno-unused-but-set-parameter" "-Wduplicated-branches" "-Wduplicated-cond" "-Wlogical-op")
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438
add_compile_options("-Wno-array-bounds")
endif()
endif ()
if (OPENMW_CXX_FLAGS) if (OPENMW_CXX_FLAGS)
separate_arguments(OPENMW_CXX_FLAGS NATIVE_COMMAND "${OPENMW_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMW_CXX_FLAGS}")
add_compile_options(${OPENMW_CXX_FLAGS})
endif() endif()
# Components # Components
@ -725,9 +710,87 @@ if (WIN32)
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif() endif()
# Play a bit with the warning levels
set(WARNINGS "/W4")
set(WARNINGS_DISABLE
4100 # Unreferenced formal parameter (-Wunused-parameter)
4127 # Conditional expression is constant
4996 # Function was declared deprecated
5054 # Deprecated operations between enumerations of different types caused by Qt headers
)
foreach(d ${WARNINGS_DISABLE})
list(APPEND WARNINGS "/wd${d}")
endforeach(d)
if(OPENMW_MSVC_WERROR)
list(APPEND WARNINGS "/WX")
endif()
target_compile_options(components PRIVATE ${WARNINGS})
target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS})
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920) if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE) target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
endif() endif()
if (BUILD_BSATOOL)
target_compile_options(bsatool PRIVATE ${WARNINGS})
endif()
if (BUILD_ESMTOOL)
target_compile_options(esmtool PRIVATE ${WARNINGS})
endif()
if (BUILD_ESSIMPORTER)
target_compile_options(openmw-essimporter PRIVATE ${WARNINGS})
endif()
if (BUILD_LAUNCHER)
target_compile_options(openmw-launcher PRIVATE ${WARNINGS})
endif()
if (BUILD_MWINIIMPORTER)
target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS})
endif()
if (BUILD_OPENCS)
target_compile_options(openmw-cs PRIVATE ${WARNINGS})
endif()
if (BUILD_OPENMW)
target_compile_options(openmw PRIVATE ${WARNINGS})
endif()
if (BUILD_WIZARD)
target_compile_options(openmw-wizard PRIVATE ${WARNINGS})
endif()
if (BUILD_COMPONENTS_TESTS)
target_compile_options(components-tests PRIVATE ${WARNINGS})
endif()
if (BUILD_BENCHMARKS)
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS})
endif()
if (BUILD_NAVMESHTOOL)
target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS})
endif()
if (BUILD_BULLETOBJECTTOOL)
target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD})
endif()
if (BUILD_OPENCS_TESTS)
target_compile_options(openmw-cs-tests PRIVATE ${WARNINGS})
endif()
if (BUILD_OPENMW_TESTS)
target_compile_options(openmw-tests PRIVATE ${WARNINGS})
endif()
endif(MSVC) endif(MSVC)
# TODO: At some point release builds should not use the console but rather write to a log file # TODO: At some point release builds should not use the console but rather write to a log file
@ -797,6 +860,7 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE)
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
include(BundleUtilities) include(BundleUtilities)
cmake_minimum_required(VERSION 3.1)
" COMPONENT Runtime) " COMPONENT Runtime)
set(ABSOLUTE_PLUGINS "") set(ABSOLUTE_PLUGINS "")

View file

@ -5,7 +5,7 @@ OpenMW is an open-source open-world RPG game engine that supports playing Morrow
OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set. OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set.
* Version: 0.50.0 * Version: 0.49.0
* License: GPLv3 (see [LICENSE](https://gitlab.com/OpenMW/openmw/-/raw/master/LICENSE) for more information) * License: GPLv3 (see [LICENSE](https://gitlab.com/OpenMW/openmw/-/raw/master/LICENSE) for more information)
* Website: https://www.openmw.org * Website: https://www.openmw.org
* IRC: #openmw on irc.libera.chat * IRC: #openmw on irc.libera.chat

View file

@ -179,7 +179,7 @@ namespace
generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random); generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);
std::size_t n = 0; std::size_t n = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
const auto& key = keys[n++ % keys.size()]; const auto& key = keys[n++ % keys.size()];
auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh); auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);

View file

@ -104,7 +104,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random); std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serialize()); benchmark::DoNotOptimize(refIds[i].serialize());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -118,7 +118,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serialize(); }); = generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serialize(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserialize(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserialize(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -131,7 +131,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random); std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -145,7 +145,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -158,7 +158,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateGeneratedRefIds(random); std::vector<ESM::RefId> refIds = generateGeneratedRefIds(random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -172,7 +172,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedGeneratedRefIds(random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedGeneratedRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -185,7 +185,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateIndexRefIds(random); std::vector<ESM::RefId> refIds = generateIndexRefIds(random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -199,7 +199,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedIndexRefIds(random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedIndexRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -212,7 +212,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateESM3ExteriorCellRefIds(random); std::vector<ESM::RefId> refIds = generateESM3ExteriorCellRefIds(random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -226,7 +226,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedESM3ExteriorCellRefIds(random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedESM3ExteriorCellRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())

View file

@ -9,7 +9,7 @@ namespace
{ {
void settingsManager(benchmark::State& state) void settingsManager(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::Manager::getFloat("sky blending start", "Fog")); benchmark::DoNotOptimize(Settings::Manager::getFloat("sky blending start", "Fog"));
} }
@ -17,7 +17,7 @@ namespace
void settingsManager2(benchmark::State& state) void settingsManager2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera")); benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera"));
benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing")); benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing"));
@ -26,7 +26,7 @@ namespace
void settingsManager3(benchmark::State& state) void settingsManager3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera")); benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera"));
benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing")); benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing"));
@ -36,7 +36,7 @@ namespace
void localStatic(benchmark::State& state) void localStatic(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
static float v = Settings::Manager::getFloat("sky blending start", "Fog"); static float v = Settings::Manager::getFloat("sky blending start", "Fog");
benchmark::DoNotOptimize(v); benchmark::DoNotOptimize(v);
@ -45,7 +45,7 @@ namespace
void localStatic2(benchmark::State& state) void localStatic2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
static float v1 = Settings::Manager::getFloat("near clip", "Camera"); static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing"); static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
@ -56,7 +56,7 @@ namespace
void localStatic3(benchmark::State& state) void localStatic3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
static float v1 = Settings::Manager::getFloat("near clip", "Camera"); static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing"); static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
@ -69,7 +69,7 @@ namespace
void settingsStorage(benchmark::State& state) void settingsStorage(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
float v = Settings::fog().mSkyBlendingStart.get(); float v = Settings::fog().mSkyBlendingStart.get();
benchmark::DoNotOptimize(v); benchmark::DoNotOptimize(v);
@ -78,7 +78,7 @@ namespace
void settingsStorage2(benchmark::State& state) void settingsStorage2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
bool v1 = Settings::postProcessing().mTransparentPostpass.get(); bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get(); float v2 = Settings::camera().mNearClip.get();
@ -89,7 +89,7 @@ namespace
void settingsStorage3(benchmark::State& state) void settingsStorage3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
bool v1 = Settings::postProcessing().mTransparentPostpass.get(); bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get(); float v2 = Settings::camera().mNearClip.get();
@ -102,7 +102,7 @@ namespace
void settingsStorageGet(benchmark::State& state) void settingsStorageGet(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::get<float>("Fog", "sky blending start")); benchmark::DoNotOptimize(Settings::get<float>("Fog", "sky blending start"));
} }
@ -110,7 +110,7 @@ namespace
void settingsStorageGet2(benchmark::State& state) void settingsStorageGet2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass")); benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass"));
benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip")); benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip"));
@ -119,7 +119,7 @@ namespace
void settingsStorageGet3(benchmark::State& state) void settingsStorageGet3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass")); benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass"));
benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip")); benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip"));

View file

@ -75,8 +75,8 @@ Allowed options)");
bpo::variables_map variables; bpo::variables_map variables;
try try
{ {
bpo::parsed_options validOpts = bpo::command_line_parser(argc, argv).options(all).positional(p).run(); bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(all).positional(p).run();
bpo::store(validOpts, variables); bpo::store(valid_opts, variables);
} }
catch (std::exception& e) catch (std::exception& e)
{ {

View file

@ -20,7 +20,7 @@
#include <components/resource/niffilemanager.hpp> #include <components/resource/niffilemanager.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <components/version/version.hpp> #include <components/version/version.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp> #include <components/vfs/registerarchives.hpp>
@ -53,6 +53,8 @@ namespace
bpo::options_description makeOptionsDescription() bpo::options_description makeOptionsDescription()
{ {
using Fallback::FallbackMap;
bpo::options_description result; bpo::options_description result;
auto addOption = result.add_options(); auto addOption = result.add_options();
addOption("help", "print help message"); addOption("help", "print help message");
@ -85,8 +87,7 @@ namespace
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other 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"); "\n\twin1252 - Western European (Latin) alphabet, used by default");
addOption("fallback", addOption("fallback", bpo::value<FallbackMap>()->default_value(FallbackMap(), "")->multitoken()->composing(),
bpo::value<Fallback::FallbackMap>()->default_value(Fallback::FallbackMap(), "")->multitoken()->composing(),
"fallback values"); "fallback values");
Files::ConfigurationManager::addCommonOptions(result); Files::ConfigurationManager::addCommonOptions(result);
@ -125,7 +126,6 @@ namespace
} }
Files::ConfigurationManager config; Files::ConfigurationManager config;
config.processPaths(variables, std::filesystem::current_path());
config.readConfiguration(variables, desc); config.readConfiguration(variables, desc);
Debug::setupLogging(config.getLogPath(), applicationName); Debug::setupLogging(config.getLogPath(), applicationName);
@ -155,7 +155,7 @@ namespace
VFS::Manager vfs; VFS::Manager vfs;
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder()); VFS::registerArchives(&vfs, fileCollections, archives, true);
Settings::Manager::load(config); Settings::Manager::load(config);

View file

@ -4,28 +4,29 @@ include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS})
file(GLOB UNITTEST_SRC_FILES file(GLOB UNITTEST_SRC_FILES
main.cpp main.cpp
esm/testfixedstring.cpp esm/test_fixed_string.cpp
esm/testrefid.cpp
esm/variant.cpp esm/variant.cpp
esm/testrefid.cpp
lua/testasync.cpp lua/test_lua.cpp
lua/testconfiguration.cpp lua/test_scriptscontainer.cpp
lua/testinputactions.cpp lua/test_utilpackage.cpp
lua/testl10n.cpp lua/test_serialization.cpp
lua/testlua.cpp lua/test_configuration.cpp
lua/testscriptscontainer.cpp lua/test_l10n.cpp
lua/testserialization.cpp lua/test_storage.cpp
lua/teststorage.cpp lua/test_async.cpp
lua/testuicontent.cpp lua/test_inputactions.cpp
lua/testutilpackage.cpp lua/test_yaml.cpp
lua/testyaml.cpp
lua/test_ui_content.cpp
misc/compression.cpp misc/compression.cpp
misc/progressreporter.cpp misc/progressreporter.cpp
misc/testendianness.cpp misc/test_endianness.cpp
misc/test_resourcehelpers.cpp
misc/test_stringops.cpp
misc/testmathutil.cpp misc/testmathutil.cpp
misc/testresourcehelpers.cpp
misc/teststringops.cpp
nifloader/testbulletnifloader.cpp nifloader/testbulletnifloader.cpp
@ -63,8 +64,8 @@ file(GLOB UNITTEST_SRC_FILES
esmloader/esmdata.cpp esmloader/esmdata.cpp
esmloader/record.cpp esmloader/record.cpp
files/conversiontests.cpp
files/hash.cpp files/hash.cpp
files/conversion_tests.cpp
toutf8/toutf8.cpp toutf8/toutf8.cpp

View file

@ -5,9 +5,7 @@
#include <components/detournavigator/makenavmesh.hpp> #include <components/detournavigator/makenavmesh.hpp>
#include <components/detournavigator/navmeshdbutils.hpp> #include <components/detournavigator/navmeshdbutils.hpp>
#include <components/detournavigator/serialization.hpp> #include <components/detournavigator/serialization.hpp>
#include <components/files/conversion.hpp>
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/testing/util.hpp>
#include <BulletCollision/CollisionShapes/btBoxShape.h> #include <BulletCollision/CollisionShapes/btBoxShape.h>
@ -374,106 +372,6 @@ namespace
} }
} }
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_recast_mesh)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "0.0.recastmesh.obj"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_recast_mesh_with_revision)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
mSettings.mEnableRecastMeshFileNameRevision = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "0.0.recastmesh.1.2.obj"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, writing_recast_mesh_to_absent_file_should_not_fail_tile_generation)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDir() / "absent";
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
EXPECT_FALSE(std::filesystem::exists(dir));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_navmesh)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "all_tiles_navmesh.bin"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_navmesh_with_revision)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
mSettings.mEnableNavMeshFileNameRevision = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "all_tiles_navmesh.1.1.bin"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, writing_navmesh_to_absent_file_should_not_fail_tile_generation)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDir() / "absent";
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
EXPECT_FALSE(std::filesystem::exists(dir));
}
struct DetourNavigatorSpatialJobQueueTest : Test struct DetourNavigatorSpatialJobQueueTest : Test
{ {
const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) }; const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) };

View file

@ -139,7 +139,7 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty)
{ {
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::NavMeshNotFound); Status::NavMeshNotFound);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>()); EXPECT_EQ(mPath, std::deque<osg::Vec3f>());
} }
@ -147,7 +147,7 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception)
{ {
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::StartPolygonNotFound); Status::StartPolygonNotFound);
} }
@ -156,7 +156,7 @@ namespace
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->removeAgent(mAgentBounds); mNavigator->removeAgent(mAgentBounds);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::StartPolygonNotFound); Status::StartPolygonNotFound);
} }
@ -172,7 +172,7 @@ namespace
updateGuard.reset(); updateGuard.reset();
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener); mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -194,7 +194,7 @@ namespace
updateGuard.reset(); updateGuard.reset();
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener); mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mStart, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mStart, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, ElementsAre(Vec3fEq(56.66666412353515625, 460, 1.99998295307159423828125))) << mPath; EXPECT_THAT(mPath, ElementsAre(Vec3fEq(56.66666412353515625, 460, 1.99998295307159423828125))) << mPath;
@ -218,7 +218,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -237,7 +237,7 @@ namespace
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -265,7 +265,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -285,7 +285,7 @@ namespace
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -318,7 +318,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -386,7 +386,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -421,7 +421,7 @@ namespace
mEnd.x() = 256; mEnd.x() = 256;
mEnd.z() = 300; mEnd.z() = 300;
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -453,8 +453,8 @@ namespace
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, EXPECT_EQ(
{}, mOut), findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -487,8 +487,8 @@ namespace
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, EXPECT_EQ(
{}, mOut), findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -520,7 +520,7 @@ namespace
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -549,7 +549,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -577,7 +577,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -658,7 +658,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -781,7 +781,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener); mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -806,7 +806,7 @@ namespace
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::PartialPath); Status::PartialPath);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -834,7 +834,7 @@ namespace
const float endTolerance = 1000.0f; const float endTolerance = 1000.0f;
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, endTolerance, {}, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, endTolerance, mOut),
Status::Success); Status::Success);
EXPECT_THAT(mPath, EXPECT_THAT(mPath,
@ -979,146 +979,6 @@ namespace
EXPECT_EQ(usedNavMeshTiles, 854); EXPECT_EQ(usedNavMeshTiles, 854);
} }
TEST_F(DetourNavigatorNavigatorTest, find_path_should_return_path_around_steep_mountains)
{
const std::array<float, 5 * 5> heightfieldData{ {
0, 0, 0, 0, 0, // row 0
0, 0, 0, 0, 0, // row 1
0, 0, 1000, 0, 0, // row 2
0, 0, 1000, 0, 0, // row 3
0, 0, 0, 0, 0, // row 4
} };
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const osg::Vec3f start(56, 56, 12);
const osg::Vec3f end(464, 464, 12);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
Status::Success);
EXPECT_THAT(mPath,
ElementsAre( //
Vec3fEq(56.66664886474609375, 56.66664886474609375, 11.33333301544189453125),
Vec3fEq(396.666656494140625, 79.33331298828125, 11.33333301544189453125),
Vec3fEq(430.666656494140625, 113.33331298828125, 11.33333301544189453125),
Vec3fEq(463.999969482421875, 463.999969482421875, 11.33333301544189453125)))
<< mPath;
}
TEST_F(DetourNavigatorNavigatorTest, find_path_should_return_path_around_steep_cliffs)
{
const std::array<float, 5 * 5> heightfieldData{ {
0, 0, 0, 0, 0, // row 0
0, 0, 0, 0, 0, // row 1
0, 0, -1000, 0, 0, // row 2
0, 0, -1000, 0, 0, // row 3
0, 0, 0, 0, 0, // row 4
} };
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const osg::Vec3f start(56, 56, 12);
const osg::Vec3f end(464, 464, 12);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
Status::Success);
EXPECT_THAT(mPath,
ElementsAre( //
Vec3fEq(56.66664886474609375, 56.66664886474609375, 8.66659259796142578125),
Vec3fEq(385.33331298828125, 79.33331298828125, 8.66659259796142578125),
Vec3fEq(430.666656494140625, 124.66664886474609375, 8.66659259796142578125),
Vec3fEq(463.999969482421875, 463.999969482421875, 8.66659259796142578125)))
<< mPath;
}
TEST_F(DetourNavigatorNavigatorTest, find_path_should_return_path_with_checkpoints)
{
const std::array<float, 5 * 5> heightfieldData{ {
0, 0, 0, 0, 0, // row 0
0, 0, 0, 0, 0, // row 1
0, 0, 1000, 0, 0, // row 2
0, 0, 1000, 0, 0, // row 3
0, 0, 0, 0, 0, // row 4
} };
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const std::vector<osg::Vec3f> checkpoints = {
osg::Vec3f(400, 70, 12),
};
const osg::Vec3f start(56, 56, 12);
const osg::Vec3f end(464, 464, 12);
EXPECT_EQ(
findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, checkpoints, mOut),
Status::Success);
EXPECT_THAT(mPath,
ElementsAre( //
Vec3fEq(56.66664886474609375, 56.66664886474609375, 11.33333301544189453125),
Vec3fEq(400, 70, 11.33333301544189453125),
Vec3fEq(430.666656494140625, 113.33331298828125, 11.33333301544189453125),
Vec3fEq(463.999969482421875, 463.999969482421875, 11.33333301544189453125)))
<< mPath;
}
TEST_F(DetourNavigatorNavigatorTest, find_path_should_skip_unreachable_checkpoints)
{
const std::array<float, 5 * 5> heightfieldData{ {
0, 0, 0, 0, 0, // row 0
0, 0, 0, 0, 0, // row 1
0, 0, 1000, 0, 0, // row 2
0, 0, 1000, 0, 0, // row 3
0, 0, 0, 0, 0, // row 4
} };
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const std::vector<osg::Vec3f> checkpoints = {
osg::Vec3f(400, 70, 10000),
osg::Vec3f(256, 256, 1000),
osg::Vec3f(-1000, -1000, 0),
};
const osg::Vec3f start(56, 56, 12);
const osg::Vec3f end(464, 464, 12);
EXPECT_EQ(
findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, checkpoints, mOut),
Status::Success);
EXPECT_THAT(mPath,
ElementsAre( //
Vec3fEq(56.66664886474609375, 56.66664886474609375, 11.33333301544189453125),
Vec3fEq(396.666656494140625, 79.33331298828125, 11.33333301544189453125),
Vec3fEq(430.666656494140625, 113.33331298828125, 11.33333301544189453125),
Vec3fEq(463.999969482421875, 463.999969482421875, 11.33333301544189453125)))
<< mPath;
}
struct DetourNavigatorNavigatorNotSupportedAgentBoundsTest : TestWithParam<AgentBounds> struct DetourNavigatorNavigatorNotSupportedAgentBoundsTest : TestWithParam<AgentBounds>
{ {
}; };

View file

@ -74,20 +74,19 @@ namespace
TEST(EsmFixedString, empty_strings) TEST(EsmFixedString, empty_strings)
{ {
constexpr std::string_view someStr = "some string";
{ {
SCOPED_TRACE("4 bytes"); SCOPED_TRACE("4 bytes");
ESM::NAME empty = ESM::NAME(); ESM::NAME empty = ESM::NAME();
EXPECT_TRUE(empty == ""); EXPECT_TRUE(empty == "");
EXPECT_TRUE(empty == static_cast<uint32_t>(0)); EXPECT_TRUE(empty == static_cast<uint32_t>(0));
EXPECT_TRUE(empty != someStr); EXPECT_TRUE(empty != "some string");
EXPECT_TRUE(empty != static_cast<uint32_t>(42)); EXPECT_TRUE(empty != static_cast<uint32_t>(42));
} }
{ {
SCOPED_TRACE("32 bytes"); SCOPED_TRACE("32 bytes");
ESM::NAME32 empty = ESM::NAME32(); ESM::NAME32 empty = ESM::NAME32();
EXPECT_TRUE(empty == ""); EXPECT_TRUE(empty == "");
EXPECT_TRUE(empty != someStr); EXPECT_TRUE(empty != "some string");
} }
} }

View file

@ -88,31 +88,4 @@ namespace
EXPECT_EQ(reader->getFileOffset(), sInitialOffset); EXPECT_EQ(reader->getFileOffset(), sInitialOffset);
} }
} }
TEST_F(ESM3ReadersCacheWithContentFile, CachedSizeAndName)
{
ESM::ReadersCache readers(2);
{
readers.get(0)->openRaw(std::make_unique<std::istringstream>("123"), "closed0.omwaddon");
readers.get(1)->openRaw(std::make_unique<std::istringstream>("12345"), "closed1.omwaddon");
readers.get(2)->openRaw(std::make_unique<std::istringstream>("1234567"), "free.omwaddon");
}
auto busy = readers.get(3);
busy->openRaw(std::make_unique<std::istringstream>("123456789"), "busy.omwaddon");
EXPECT_EQ(readers.getFileSize(0), 3);
EXPECT_EQ(readers.getName(0), "closed0.omwaddon");
EXPECT_EQ(readers.getFileSize(1), 5);
EXPECT_EQ(readers.getName(1), "closed1.omwaddon");
EXPECT_EQ(readers.getFileSize(2), 7);
EXPECT_EQ(readers.getName(2), "free.omwaddon");
EXPECT_EQ(readers.getFileSize(3), 9);
EXPECT_EQ(readers.getName(3), "busy.omwaddon");
// not-yet-seen indices give zero for their size
EXPECT_EQ(readers.getFileSize(4), 0);
}
} }

View file

@ -723,7 +723,7 @@ namespace ESM
TEST_P(Esm3SaveLoadRecordTest, landShouldNotChange) TEST_P(Esm3SaveLoadRecordTest, landShouldNotChange)
{ {
LandRecordData data; LandRecordData data;
std::iota(data.mHeights.begin(), data.mHeights.end(), 1.0f); std::iota(data.mHeights.begin(), data.mHeights.end(), 1);
std::for_each(data.mHeights.begin(), data.mHeights.end(), [](float& v) { v *= Land::sHeightScale; }); std::for_each(data.mHeights.begin(), data.mHeights.end(), [](float& v) { v *= Land::sHeightScale; });
data.mMinHeight = *std::min_element(data.mHeights.begin(), data.mHeights.end()); data.mMinHeight = *std::min_element(data.mHeights.begin(), data.mHeights.end());
data.mMaxHeight = *std::max_element(data.mHeights.begin(), data.mHeights.end()); data.mMaxHeight = *std::max_element(data.mHeights.begin(), data.mHeights.end());

View file

@ -10,7 +10,7 @@
#include <components/esmloader/load.hpp> #include <components/esmloader/load.hpp>
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include <components/files/multidircollection.hpp> #include <components/files/multidircollection.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>

View file

@ -30,7 +30,7 @@ namespace
TEST(FilesGetHash, shouldClearErrors) TEST(FilesGetHash, shouldClearErrors)
{ {
const auto fileName = outputFilePath("fileName"); const auto fileName = temporaryFilePath("fileName");
std::string content; std::string content;
std::fill_n(std::back_inserter(content), 1, 'a'); std::fill_n(std::back_inserter(content), 1, 'a');
std::istringstream stream(content); std::istringstream stream(content);
@ -41,7 +41,7 @@ namespace
TEST_P(FilesGetHash, shouldReturnHashForStringStream) TEST_P(FilesGetHash, shouldReturnHashForStringStream)
{ {
const auto fileName = outputFilePath("fileName"); const auto fileName = temporaryFilePath("fileName");
std::string content; std::string content;
std::fill_n(std::back_inserter(content), GetParam().mSize, 'a'); std::fill_n(std::back_inserter(content), GetParam().mSize, 'a');
std::istringstream stream(content); std::istringstream stream(content);

View file

@ -5,7 +5,7 @@
namespace namespace
{ {
using namespace testing; using namespace testing;
using namespace Fx::Lexer; using namespace fx::Lexer;
struct LexerTest : Test struct LexerTest : Test
{ {

View file

@ -91,7 +91,7 @@ namespace
)" }; )" };
using namespace testing; using namespace testing;
using namespace Fx; using namespace fx;
struct TechniqueTest : Test struct TechniqueTest : Test
{ {
@ -113,8 +113,7 @@ namespace
void compile(const std::string& name) void compile(const std::string& name)
{ {
mTechnique = std::make_unique<Technique>( mTechnique = std::make_unique<Technique>(*mVFS.get(), mImageManager, name, 1, 1, true, true);
*mVFS.get(), mImageManager, Technique::makeFileName(name), name, 1, 1, true, true);
mTechnique->compile(); mTechnique->compile();
} }
}; };

View file

@ -38,10 +38,10 @@ namespace
sol::state lua; sol::state lua;
LuaUtil::InputAction::Registry registry; LuaUtil::InputAction::Registry registry;
LuaUtil::InputAction::Info a({ "a", LuaUtil::InputAction::Type::Boolean, "test", "a_name", "a_description", LuaUtil::InputAction::Info a({ "a", LuaUtil::InputAction::Type::Boolean, "test", "a_name", "a_description",
sol::make_object(lua, false), false }); sol::make_object(lua, false) });
registry.insert(a); registry.insert(a);
LuaUtil::InputAction::Info b({ "b", LuaUtil::InputAction::Type::Boolean, "test", "b_name", "b_description", LuaUtil::InputAction::Info b({ "b", LuaUtil::InputAction::Type::Boolean, "test", "b_name", "b_description",
sol::make_object(lua, false), false }); sol::make_object(lua, false) });
registry.insert(b); registry.insert(b);
LuaUtil::Callback bindA({ lua.load("return function() return true end")(), sol::table(lua, sol::create) }); LuaUtil::Callback bindA({ lua.load("return function() return true end")(), sol::table(lua, sol::create) });
LuaUtil::Callback bindBToA( LuaUtil::Callback bindBToA(

View file

@ -24,8 +24,6 @@ namespace
constexpr VFS::Path::NormalizedView test2EnPath("l10n/test2/en.yaml"); constexpr VFS::Path::NormalizedView test2EnPath("l10n/test2/en.yaml");
constexpr VFS::Path::NormalizedView test3EnPath("l10n/test3/en.yaml"); constexpr VFS::Path::NormalizedView test3EnPath("l10n/test3/en.yaml");
constexpr VFS::Path::NormalizedView test3DePath("l10n/test3/de.yaml"); constexpr VFS::Path::NormalizedView test3DePath("l10n/test3/de.yaml");
constexpr VFS::Path::NormalizedView test4RuPath("l10n/test4/ru.yaml");
constexpr VFS::Path::NormalizedView test4EnPath("l10n/test4/en.yaml");
VFSTestFile invalidScript("not a script"); VFSTestFile invalidScript("not a script");
VFSTestFile incorrectScript( VFSTestFile incorrectScript(
@ -71,16 +69,6 @@ currency: "You have {money, number, currency}"
VFSTestFile test2En(R"X( VFSTestFile test2En(R"X(
good_morning: "Morning!" good_morning: "Morning!"
you_have_arrows: "Arrows count: {count}" you_have_arrows: "Arrows count: {count}"
)X");
VFSTestFile test4Ru(R"X(
skill_increase: "Ваш навык {навык} увеличился до {value}"
acrobatics: "Акробатика"
)X");
VFSTestFile test4En(R"X(
stat_increase: "Your {stat} has increased to {value}"
speed: "Speed"
)X"); )X");
struct LuaL10nTest : Test struct LuaL10nTest : Test
@ -92,8 +80,6 @@ speed: "Speed"
{ test2EnPath, &test2En }, { test2EnPath, &test2En },
{ test3EnPath, &test1En }, { test3EnPath, &test1En },
{ test3DePath, &test1De }, { test3DePath, &test1De },
{ test4RuPath, &test4Ru },
{ test4EnPath, &test4En },
}); });
LuaUtil::ScriptsConfiguration mCfg; LuaUtil::ScriptsConfiguration mCfg;
@ -105,7 +91,7 @@ speed: "Speed"
lua.protectedCall([&](LuaUtil::LuaView& view) { lua.protectedCall([&](LuaUtil::LuaView& view) {
sol::state_view& l = view.sol(); sol::state_view& l = view.sol();
internal::CaptureStdout(); internal::CaptureStdout();
L10n::Manager l10nManager(mVFS.get()); l10n::Manager l10nManager(mVFS.get());
l10nManager.setPreferredLocales({ "de", "en" }); l10nManager.setPreferredLocales({ "de", "en" });
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n"); EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n");
@ -183,18 +169,6 @@ speed: "Speed"
l.safe_script("t3 = l10n('Test3', 'de')"); l.safe_script("t3 = l10n('Test3', 'de')");
l10nManager.setPreferredLocales({ "en" }); l10nManager.setPreferredLocales({ "en" });
EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!"); EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!");
// Test that formatting arguments use a correct encoding
l.safe_script("t4 = l10n('Test4', 'ru')");
l10nManager.setPreferredLocales({ "ru", "en" });
EXPECT_EQ(get<std::string>(l, "t4('skill_increase', {навык='Акробатика', value=100})"),
"Ваш навык Акробатика увеличился до 100");
EXPECT_EQ(get<std::string>(l, "t4('skill_increase', {навык=t4('acrobatics'), value=100})"),
"Ваш навык Акробатика увеличился до 100");
EXPECT_EQ(get<std::string>(l, "t4('stat_increase', {stat='Speed', value=100})"),
"Your Speed has increased to 100");
EXPECT_EQ(get<std::string>(l, "t4('stat_increase', {stat=t4('speed'), value=100})"),
"Your Speed has increased to 100");
}); });
} }
} }

View file

@ -285,17 +285,17 @@ CUSTOM: customdata.lua
EXPECT_TRUE(scripts.addCustomScript(getId(test2Path))); EXPECT_TRUE(scripts.addCustomScript(getId(test2Path)));
sol::state_view sol = mLua.unsafeState(); sol::state_view sol = mLua.unsafeState();
std::string x0 = LuaUtil::serialize(sol.create_table_with("x", 0.5)); std::string X0 = LuaUtil::serialize(sol.create_table_with("x", 0.5));
std::string x1 = LuaUtil::serialize(sol.create_table_with("x", 1.5)); std::string X1 = LuaUtil::serialize(sol.create_table_with("x", 1.5));
{ {
testing::internal::CaptureStdout(); testing::internal::CaptureStdout();
scripts.receiveEvent("SomeEvent", x1); scripts.receiveEvent("SomeEvent", X1);
EXPECT_EQ(internal::GetCapturedStdout(), ""); EXPECT_EQ(internal::GetCapturedStdout(), "");
} }
{ {
testing::internal::CaptureStdout(); testing::internal::CaptureStdout();
scripts.receiveEvent("Event1", x1); scripts.receiveEvent("Event1", X1);
EXPECT_EQ(internal::GetCapturedStdout(), EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test2.lua]:\t event1 1.5\n" "Test[test2.lua]:\t event1 1.5\n"
"Test[stopevent.lua]:\t event1 1.5\n" "Test[stopevent.lua]:\t event1 1.5\n"
@ -303,21 +303,21 @@ CUSTOM: customdata.lua
} }
{ {
testing::internal::CaptureStdout(); testing::internal::CaptureStdout();
scripts.receiveEvent("Event2", x1); scripts.receiveEvent("Event2", X1);
EXPECT_EQ(internal::GetCapturedStdout(), EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test2.lua]:\t event2 1.5\n" "Test[test2.lua]:\t event2 1.5\n"
"Test[test1.lua]:\t event2 1.5\n"); "Test[test1.lua]:\t event2 1.5\n");
} }
{ {
testing::internal::CaptureStdout(); testing::internal::CaptureStdout();
scripts.receiveEvent("Event1", x0); scripts.receiveEvent("Event1", X0);
EXPECT_EQ(internal::GetCapturedStdout(), EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test2.lua]:\t event1 0.5\n" "Test[test2.lua]:\t event1 0.5\n"
"Test[stopevent.lua]:\t event1 0.5\n"); "Test[stopevent.lua]:\t event1 0.5\n");
} }
{ {
testing::internal::CaptureStdout(); testing::internal::CaptureStdout();
scripts.receiveEvent("Event2", x0); scripts.receiveEvent("Event2", X0);
EXPECT_EQ(internal::GetCapturedStdout(), EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test2.lua]:\t event2 0.5\n" "Test[test2.lua]:\t event2 0.5\n"
"Test[test1.lua]:\t event2 0.5\n"); "Test[test1.lua]:\t event2 0.5\n");
@ -333,12 +333,12 @@ CUSTOM: customdata.lua
EXPECT_TRUE(scripts.addCustomScript(getId(test2Path))); EXPECT_TRUE(scripts.addCustomScript(getId(test2Path)));
sol::state_view sol = mLua.unsafeState(); sol::state_view sol = mLua.unsafeState();
std::string x = LuaUtil::serialize(sol.create_table_with("x", 0.5)); std::string X = LuaUtil::serialize(sol.create_table_with("x", 0.5));
{ {
testing::internal::CaptureStdout(); testing::internal::CaptureStdout();
scripts.update(1.5f); scripts.update(1.5f);
scripts.receiveEvent("Event1", x); scripts.receiveEvent("Event1", X);
EXPECT_EQ(internal::GetCapturedStdout(), EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test1.lua]:\t update 1.5\n" "Test[test1.lua]:\t update 1.5\n"
"Test[test2.lua]:\t update 1.5\n" "Test[test2.lua]:\t update 1.5\n"
@ -352,7 +352,7 @@ CUSTOM: customdata.lua
scripts.removeScript(stopEventScriptId); scripts.removeScript(stopEventScriptId);
EXPECT_FALSE(scripts.hasScript(stopEventScriptId)); EXPECT_FALSE(scripts.hasScript(stopEventScriptId));
scripts.update(1.5f); scripts.update(1.5f);
scripts.receiveEvent("Event1", x); scripts.receiveEvent("Event1", X);
EXPECT_EQ(internal::GetCapturedStdout(), EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test1.lua]:\t update 1.5\n" "Test[test1.lua]:\t update 1.5\n"
"Test[test2.lua]:\t update 1.5\n" "Test[test2.lua]:\t update 1.5\n"
@ -363,7 +363,7 @@ CUSTOM: customdata.lua
testing::internal::CaptureStdout(); testing::internal::CaptureStdout();
scripts.removeScript(getId(test1Path)); scripts.removeScript(getId(test1Path));
scripts.update(1.5f); scripts.update(1.5f);
scripts.receiveEvent("Event1", x); scripts.receiveEvent("Event1", X);
EXPECT_EQ(internal::GetCapturedStdout(), EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test2.lua]:\t update 1.5\n" "Test[test2.lua]:\t update 1.5\n"
"Test[test2.lua]:\t event1 0.5\n"); "Test[test2.lua]:\t event1 0.5\n");
@ -638,9 +638,8 @@ CUSTOM: customdata.lua
sol::object deserialized = LuaUtil::deserialize(lua.sol(), data2.mScripts[0].mData, &serializer1); sol::object deserialized = LuaUtil::deserialize(lua.sol(), data2.mScripts[0].mData, &serializer1);
EXPECT_TRUE(deserialized.is<sol::table>()); EXPECT_TRUE(deserialized.is<sol::table>());
sol::table table = deserialized; sol::table table = deserialized;
if (!table.empty()) for (const auto& [key, value] : table)
{ {
const auto [key, value] = *table.cbegin();
EXPECT_TRUE(key.is<ESM::RefNum>()); EXPECT_TRUE(key.is<ESM::RefNum>());
EXPECT_TRUE(value.is<ESM::RefNum>()); EXPECT_TRUE(value.is<ESM::RefNum>());
EXPECT_EQ(key.as<ESM::RefNum>(), (ESM::RefNum{ 42, 34 })); EXPECT_EQ(key.as<ESM::RefNum>(), (ESM::RefNum{ 42, 34 }));

View file

@ -171,10 +171,10 @@ namespace
std::string serialized = LuaUtil::serialize(table); std::string serialized = LuaUtil::serialize(table);
EXPECT_EQ(serialized.size(), 139); EXPECT_EQ(serialized.size(), 139);
sol::table resTable = LuaUtil::deserialize(lua, serialized); sol::table res_table = LuaUtil::deserialize(lua, serialized);
sol::table resReadonlyTable = LuaUtil::deserialize(lua, serialized, nullptr, true); sol::table res_readonly_table = LuaUtil::deserialize(lua, serialized, nullptr, true);
for (auto t : { resTable, resReadonlyTable }) for (auto t : { res_table, res_readonly_table })
{ {
EXPECT_EQ(t.get<int>("aa"), 1); EXPECT_EQ(t.get<int>("aa"), 1);
EXPECT_EQ(t.get<bool>("ab"), true); EXPECT_EQ(t.get<bool>("ab"), true);
@ -185,8 +185,8 @@ namespace
EXPECT_EQ(t.get<osg::Vec2f>(2), osg::Vec2f(2, 1)); EXPECT_EQ(t.get<osg::Vec2f>(2), osg::Vec2f(2, 1));
} }
lua["t"] = resTable; lua["t"] = res_table;
lua["ro_t"] = resReadonlyTable; lua["ro_t"] = res_readonly_table;
EXPECT_NO_THROW(lua.safe_script("t.x = 5")); EXPECT_NO_THROW(lua.safe_script("t.x = 5"));
EXPECT_NO_THROW(lua.safe_script("t.nested.x = 5")); EXPECT_NO_THROW(lua.safe_script("t.nested.x = 5"));
EXPECT_ERROR(lua.safe_script("ro_t.x = 5"), "userdata value"); EXPECT_ERROR(lua.safe_script("ro_t.x = 5"), "userdata value");

View file

@ -9,33 +9,22 @@ namespace
{ {
using namespace testing; using namespace testing;
struct LuaUtilPackageTest : Test
{
LuaUtil::LuaState mLuaState{ nullptr, nullptr };
LuaUtilPackageTest()
{
mLuaState.addInternalLibSearchPath(
std::filesystem::path{ OPENMW_PROJECT_SOURCE_DIR } / "components" / "lua");
sol::state_view sol = mLuaState.unsafeState();
sol["util"] = LuaUtil::initUtilPackage(sol);
}
};
template <typename T> template <typename T>
T get(sol::state_view& lua, const std::string& luaCode) T get(sol::state& lua, const std::string& luaCode)
{ {
return lua.safe_script("return " + luaCode).get<T>(); return lua.safe_script("return " + luaCode).get<T>();
} }
std::string getAsString(sol::state_view& lua, std::string luaCode) std::string getAsString(sol::state& lua, std::string luaCode)
{ {
return LuaUtil::toString(lua.safe_script("return " + luaCode)); return LuaUtil::toString(lua.safe_script("return " + luaCode));
} }
TEST_F(LuaUtilPackageTest, Vector2) TEST(LuaUtilPackageTest, Vector2)
{ {
sol::state_view lua = mLuaState.unsafeState(); sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
lua["util"] = LuaUtil::initUtilPackage(lua);
lua.safe_script("v = util.vector2(3, 4)"); lua.safe_script("v = util.vector2(3, 4)");
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 3); EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 3);
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 4); EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 4);
@ -66,9 +55,11 @@ namespace
EXPECT_TRUE(get<bool>(lua, "swizzle['01'] == util.vector2(0, 1) and swizzle['0y'] == util.vector2(0, 2)")); EXPECT_TRUE(get<bool>(lua, "swizzle['01'] == util.vector2(0, 1) and swizzle['0y'] == util.vector2(0, 2)"));
} }
TEST_F(LuaUtilPackageTest, Vector3) TEST(LuaUtilPackageTest, Vector3)
{ {
sol::state_view lua = mLuaState.unsafeState(); sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
lua["util"] = LuaUtil::initUtilPackage(lua);
lua.safe_script("v = util.vector3(5, 12, 13)"); lua.safe_script("v = util.vector3(5, 12, 13)");
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5); EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12); EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
@ -103,9 +94,11 @@ namespace
get<bool>(lua, "swizzle['001'] == util.vector3(0, 0, 1) and swizzle['0yx'] == util.vector3(0, 2, 1)")); get<bool>(lua, "swizzle['001'] == util.vector3(0, 0, 1) and swizzle['0yx'] == util.vector3(0, 2, 1)"));
} }
TEST_F(LuaUtilPackageTest, Vector4) TEST(LuaUtilPackageTest, Vector4)
{ {
sol::state_view lua = mLuaState.unsafeState(); sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
lua["util"] = LuaUtil::initUtilPackage(lua);
lua.safe_script("v = util.vector4(5, 12, 13, 15)"); lua.safe_script("v = util.vector4(5, 12, 13, 15)");
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5); EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12); EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
@ -143,9 +136,11 @@ namespace
lua, "swizzle['0001'] == util.vector4(0, 0, 0, 1) and swizzle['0yx1'] == util.vector4(0, 2, 1, 1)")); lua, "swizzle['0001'] == util.vector4(0, 0, 0, 1) and swizzle['0yx1'] == util.vector4(0, 2, 1, 1)"));
} }
TEST_F(LuaUtilPackageTest, Color) TEST(LuaUtilPackageTest, Color)
{ {
sol::state_view lua = mLuaState.unsafeState(); sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
lua["util"] = LuaUtil::initUtilPackage(lua);
lua.safe_script("brown = util.color.rgba(0.75, 0.25, 0, 1)"); lua.safe_script("brown = util.color.rgba(0.75, 0.25, 0, 1)");
EXPECT_EQ(get<std::string>(lua, "tostring(brown)"), "(0.75, 0.25, 0, 1)"); EXPECT_EQ(get<std::string>(lua, "tostring(brown)"), "(0.75, 0.25, 0, 1)");
lua.safe_script("blue = util.color.rgb(0, 1, 0, 1)"); lua.safe_script("blue = util.color.rgb(0, 1, 0, 1)");
@ -160,9 +155,11 @@ namespace
EXPECT_TRUE(get<bool>(lua, "red:asRgb() == util.vector3(1, 0, 0)")); EXPECT_TRUE(get<bool>(lua, "red:asRgb() == util.vector3(1, 0, 0)"));
} }
TEST_F(LuaUtilPackageTest, Transform) TEST(LuaUtilPackageTest, Transform)
{ {
sol::state_view lua = mLuaState.unsafeState(); sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
lua["util"] = LuaUtil::initUtilPackage(lua);
lua["T"] = lua["util"]["transform"]; lua["T"] = lua["util"]["transform"];
lua["v"] = lua["util"]["vector3"]; lua["v"] = lua["util"]["vector3"];
EXPECT_ERROR(lua.safe_script("T.identity = nil"), "attempt to index"); EXPECT_ERROR(lua.safe_script("T.identity = nil"), "attempt to index");
@ -194,9 +191,11 @@ namespace
EXPECT_LT(get<float>(lua, "(rz_move_rx:inverse() * v(0, 1, 2) - v(1, 2, 3)):length()"), 1e-6); EXPECT_LT(get<float>(lua, "(rz_move_rx:inverse() * v(0, 1, 2) - v(1, 2, 3)):length()"), 1e-6);
} }
TEST_F(LuaUtilPackageTest, UtilityFunctions) TEST(LuaUtilPackageTest, UtilityFunctions)
{ {
sol::state_view lua = mLuaState.unsafeState(); sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
lua["util"] = LuaUtil::initUtilPackage(lua);
lua.safe_script("v = util.vector2(1, 0):rotate(math.rad(120))"); lua.safe_script("v = util.vector2(1, 0):rotate(math.rad(120))");
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), -0.5f); EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), -0.5f);
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 0.86602539f); EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 0.86602539f);
@ -204,10 +203,6 @@ namespace
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(0.1, 0, 1.5)"), 0.1f); EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(0.1, 0, 1.5)"), 0.1f);
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(-0.1, 0, 1.5)"), 0); EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(-0.1, 0, 1.5)"), 0);
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(2.1, 0, 1.5)"), 1.5f); EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(2.1, 0, 1.5)"), 1.5f);
EXPECT_FLOAT_EQ(get<float>(lua, "util.round(2.1)"), 2.0f);
EXPECT_FLOAT_EQ(get<float>(lua, "util.round(-2.1)"), -2.0f);
EXPECT_FLOAT_EQ(get<float>(lua, "util.remap(5, 0, 10, 0, 100)"), 50.0f);
EXPECT_FLOAT_EQ(get<float>(lua, "util.remap(-5, 0, 10, 0, 100)"), -50.0f);
lua.safe_script("t = util.makeReadOnly({x = 1})"); lua.safe_script("t = util.makeReadOnly({x = 1})");
EXPECT_FLOAT_EQ(get<float>(lua, "t.x"), 1); EXPECT_FLOAT_EQ(get<float>(lua, "t.x"), 1);
EXPECT_ERROR(lua.safe_script("t.y = 2"), "userdata value"); EXPECT_ERROR(lua.safe_script("t.y = 2"), "userdata value");

View file

@ -2,7 +2,6 @@
#include <components/misc/strings/conversion.hpp> #include <components/misc/strings/conversion.hpp>
#include <components/settings/parser.hpp> #include <components/settings/parser.hpp>
#include <components/settings/values.hpp> #include <components/settings/values.hpp>
#include <components/testing/util.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -25,9 +24,5 @@ int main(int argc, char** argv)
Settings::StaticValues::init(); Settings::StaticValues::init();
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
const int result = RUN_ALL_TESTS();
if (result == 0)
std::filesystem::remove_all(TestingOpenMW::outputDir());
return result;
} }

View file

@ -26,15 +26,6 @@ namespace
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({}); std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({});
constexpr VFS::Path::NormalizedView path("sound/foo.wav"); constexpr VFS::Path::NormalizedView path("sound/foo.wav");
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3"); EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3");
auto correctESM4SoundPath = [](auto path, auto* vfs) {
return Misc::ResourceHelpers::correctResourcePath({ { "sound" } }, path, vfs, ".mp3");
};
EXPECT_EQ(correctESM4SoundPath("foo.WAV", mVFS.get()), "sound\\foo.mp3");
EXPECT_EQ(correctESM4SoundPath("SOUND/foo.WAV", mVFS.get()), "sound\\foo.mp3");
EXPECT_EQ(correctESM4SoundPath("DATA\\SOUND\\foo.WAV", mVFS.get()), "sound\\foo.mp3");
EXPECT_EQ(correctESM4SoundPath("\\Data/Sound\\foo.WAV", mVFS.get()), "sound\\foo.mp3");
} }
namespace namespace

View file

@ -51,7 +51,7 @@ namespace Misc
const std::pair<osg::Quat, osg::Vec3f> eulerAnglesXZQuat[] = { const std::pair<osg::Quat, osg::Vec3f> eulerAnglesXZQuat[] = {
{ {
osg::Quat(1, 0, 0, 0), osg::Quat(1, 0, 0, 0),
osg::Vec3f(0, 0, osg::PIf), osg::Vec3f(0, 0, osg::PI),
}, },
{ {
osg::Quat(0, 1, 0, 0), osg::Quat(0, 1, 0, 0),
@ -59,7 +59,7 @@ namespace Misc
}, },
{ {
osg::Quat(0, 0, 1, 0), osg::Quat(0, 0, 1, 0),
osg::Vec3f(0, 0, osg::PIf), osg::Vec3f(0, 0, osg::PI),
}, },
{ {
osg::Quat(0, 0, 0, 1), osg::Quat(0, 0, 0, 1),
@ -128,15 +128,15 @@ namespace Misc
const std::pair<osg::Quat, osg::Vec3f> eulerAnglesZYXQuat[] = { const std::pair<osg::Quat, osg::Vec3f> eulerAnglesZYXQuat[] = {
{ {
osg::Quat(1, 0, 0, 0), osg::Quat(1, 0, 0, 0),
osg::Vec3f(osg::PIf, 0, 0), osg::Vec3f(osg::PI, 0, 0),
}, },
{ {
osg::Quat(0, 1, 0, 0), osg::Quat(0, 1, 0, 0),
osg::Vec3f(osg::PIf, 0, osg::PIf), osg::Vec3f(osg::PI, 0, osg::PI),
}, },
{ {
osg::Quat(0, 0, 1, 0), osg::Quat(0, 0, 1, 0),
osg::Vec3f(0, 0, osg::PIf), osg::Vec3f(0, 0, osg::PI),
}, },
{ {
osg::Quat(0, 0, 0, 1), osg::Quat(0, 0, 0, 1),

View file

@ -16,7 +16,7 @@ namespace
ShaderManager mManager; ShaderManager mManager;
ShaderManager::DefineMap mDefines; ShaderManager::DefineMap mDefines;
ShaderManagerTest() { mManager.setShaderPath(TestingOpenMW::outputDir()); } ShaderManagerTest() { mManager.setShaderPath("tests_output"); }
template <class F> template <class F>
void withShaderFile(const std::string& content, F&& f) void withShaderFile(const std::string& content, F&& f)

View file

@ -1,5 +1,5 @@
#include <components/misc/strings/conversion.hpp> #include <components/misc/strings/conversion.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>

View file

@ -101,9 +101,9 @@ Allowed options)");
try try
{ {
bpo::parsed_options validOpts = bpo::command_line_parser(argc, argv).options(all).positional(p).run(); bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(all).positional(p).run();
bpo::store(validOpts, variables); bpo::store(valid_opts, variables);
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -215,6 +215,8 @@ int main(int argc, char** argv)
std::cerr << "ERROR: " << e.what() << std::endl; std::cerr << "ERROR: " << e.what() << std::endl;
return 1; return 1;
} }
return 0;
} }
namespace namespace

View file

@ -1,7 +1,5 @@
#include "labels.hpp" #include "labels.hpp"
#include <format>
#include <components/esm3/dialoguecondition.hpp> #include <components/esm3/dialoguecondition.hpp>
#include <components/esm3/loadalch.hpp> #include <components/esm3/loadalch.hpp>
#include <components/esm3/loadbody.hpp> #include <components/esm3/loadbody.hpp>
@ -18,6 +16,8 @@
#include <components/esm3/loadspel.hpp> #include <components/esm3/loadspel.hpp>
#include <components/esm3/loadweap.hpp> #include <components/esm3/loadweap.hpp>
#include <components/misc/strings/format.hpp>
std::string_view bodyPartLabel(int idx) std::string_view bodyPartLabel(int idx)
{ {
if (idx >= 0 && idx <= 26) if (idx >= 0 && idx <= 26)
@ -675,7 +675,7 @@ std::string bodyPartFlags(int flags)
int unused = (0xFFFFFFFF ^ (ESM::BodyPart::BPF_Female | ESM::BodyPart::BPF_NotPlayable)); int unused = (0xFFFFFFFF ^ (ESM::BodyPart::BPF_Female | ESM::BodyPart::BPF_NotPlayable));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -699,7 +699,7 @@ std::string cellFlags(int flags)
^ (ESM::Cell::HasWater | ESM::Cell::Interior | ESM::Cell::NoSleep | ESM::Cell::QuasiEx | 0x00000040)); ^ (ESM::Cell::HasWater | ESM::Cell::Interior | ESM::Cell::NoSleep | ESM::Cell::QuasiEx | 0x00000040));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -717,7 +717,7 @@ std::string containerFlags(int flags)
int unused = (0xFFFFFFFF ^ (ESM::Container::Unknown | ESM::Container::Organic | ESM::Container::Respawn)); int unused = (0xFFFFFFFF ^ (ESM::Container::Unknown | ESM::Container::Organic | ESM::Container::Respawn));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -747,7 +747,7 @@ std::string creatureFlags(int flags)
| ESM::Creature::Bipedal | ESM::Creature::Respawn | ESM::Creature::Weapon | ESM::Creature::Essential)); | ESM::Creature::Bipedal | ESM::Creature::Respawn | ESM::Creature::Weapon | ESM::Creature::Essential));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:02X})", flags); properties += Misc::StringUtils::format("(0x%02X)", flags);
return properties; return properties;
} }
@ -760,7 +760,7 @@ std::string enchantmentFlags(int flags)
properties += "Autocalc "; properties += "Autocalc ";
if (flags & (0xFFFFFFFF ^ ESM::Enchantment::Autocalc)) if (flags & (0xFFFFFFFF ^ ESM::Enchantment::Autocalc))
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -778,7 +778,7 @@ std::string landFlags(std::uint32_t flags)
int unused = 0xFFFFFFFF ^ (ESM::Land::Flag_HeightsNormals | ESM::Land::Flag_Colors | ESM::Land::Flag_Textures); int unused = 0xFFFFFFFF ^ (ESM::Land::Flag_HeightsNormals | ESM::Land::Flag_Colors | ESM::Land::Flag_Textures);
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -794,7 +794,7 @@ std::string itemListFlags(int flags)
int unused = (0xFFFFFFFF ^ (ESM::ItemLevList::AllLevels | ESM::ItemLevList::Each)); int unused = (0xFFFFFFFF ^ (ESM::ItemLevList::AllLevels | ESM::ItemLevList::Each));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -808,7 +808,7 @@ std::string creatureListFlags(int flags)
int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels); int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels);
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -840,7 +840,7 @@ std::string lightFlags(int flags)
| ESM::Light::Pulse | ESM::Light::PulseSlow | ESM::Light::Negative | ESM::Light::OffDefault)); | ESM::Light::Pulse | ESM::Light::PulseSlow | ESM::Light::Negative | ESM::Light::OffDefault));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -888,7 +888,7 @@ std::string magicEffectFlags(int flags)
if (flags & 0xFFFC0000) if (flags & 0xFFFC0000)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -913,7 +913,7 @@ std::string npcFlags(int flags)
= (0xFF ^ (ESM::NPC::Base | ESM::NPC::Autocalc | ESM::NPC::Female | ESM::NPC::Respawn | ESM::NPC::Essential)); = (0xFF ^ (ESM::NPC::Base | ESM::NPC::Autocalc | ESM::NPC::Female | ESM::NPC::Respawn | ESM::NPC::Essential));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:02X})", flags); properties += Misc::StringUtils::format("(0x%02X)", flags);
return properties; return properties;
} }
@ -930,7 +930,7 @@ std::string raceFlags(int flags)
int unused = (0xFFFFFFFF ^ (ESM::Race::Playable | ESM::Race::Beast)); int unused = (0xFFFFFFFF ^ (ESM::Race::Playable | ESM::Race::Beast));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -948,7 +948,7 @@ std::string spellFlags(int flags)
int unused = (0xFFFFFFFF ^ (ESM::Spell::F_Autocalc | ESM::Spell::F_PCStart | ESM::Spell::F_Always)); int unused = (0xFFFFFFFF ^ (ESM::Spell::F_Autocalc | ESM::Spell::F_PCStart | ESM::Spell::F_Always));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -967,7 +967,7 @@ std::string weaponFlags(int flags)
int unused = (0xFFFFFFFF ^ (ESM::Weapon::Magical | ESM::Weapon::Silver)); int unused = (0xFFFFFFFF ^ (ESM::Weapon::Magical | ESM::Weapon::Silver));
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -987,7 +987,7 @@ std::string recordFlags(uint32_t flags)
int unused = ~(ESM::FLAG_Deleted | ESM::FLAG_Persistent | ESM::FLAG_Ignored | ESM::FLAG_Blocked); int unused = ~(ESM::FLAG_Deleted | ESM::FLAG_Persistent | ESM::FLAG_Ignored | ESM::FLAG_Blocked);
if (flags & unused) if (flags & unused)
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }
@ -1000,6 +1000,6 @@ std::string potionFlags(int flags)
properties += "Autocalc "; properties += "Autocalc ";
if (flags & (0xFFFFFFFF ^ ESM::Enchantment::Autocalc)) if (flags & (0xFFFFFFFF ^ ESM::Enchantment::Autocalc))
properties += "Invalid "; properties += "Invalid ";
properties += std::format("(0x{:08X})", flags); properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties; return properties;
} }

View file

@ -1,7 +1,6 @@
#include "record.hpp" #include "record.hpp"
#include "labels.hpp" #include "labels.hpp"
#include <format>
#include <iostream> #include <iostream>
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
@ -9,13 +8,15 @@
#include <components/esm3/cellstate.hpp> #include <components/esm3/cellstate.hpp>
#include <components/esm3/esmreader.hpp> #include <components/esm3/esmreader.hpp>
#include <components/misc/strings/conversion.hpp> #include <components/misc/strings/conversion.hpp>
#include <components/misc/strings/format.hpp>
namespace namespace
{ {
void printAIPackage(const ESM::AIPackage& p) void printAIPackage(const ESM::AIPackage& p)
{ {
std::cout << std::format(" AI Type: {} (0x{:08X})\n", aiTypeLabel(p.mType), std::uint32_t(p.mType)); std::cout << " AI Type: " << aiTypeLabel(p.mType) << " (" << Misc::StringUtils::format("0x%08X", p.mType)
<< ")" << std::endl;
if (p.mType == ESM::AI_Wander) if (p.mType == ESM::AI_Wander)
{ {
std::cout << " Distance: " << p.mWander.mDistance << std::endl; std::cout << " Distance: " << p.mWander.mDistance << std::endl;
@ -50,7 +51,7 @@ namespace
} }
else else
{ {
std::cout << std::format(" BadPackage: 0x{:08X}\n", std::uint32_t(p.mType)); std::cout << " BadPackage: " << Misc::StringUtils::format("0x%08X", p.mType) << std::endl;
} }
if (!p.mCellName.empty()) if (!p.mCellName.empty())
@ -59,81 +60,81 @@ namespace
std::string ruleString(const ESM::DialogueCondition& ss) std::string ruleString(const ESM::DialogueCondition& ss)
{ {
std::string_view typeStr = "INVALID"; std::string_view type_str = "INVALID";
std::string_view funcStr; std::string_view func_str;
switch (ss.mFunction) switch (ss.mFunction)
{ {
case ESM::DialogueCondition::Function_Global: case ESM::DialogueCondition::Function_Global:
typeStr = "Global"; type_str = "Global";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_Local: case ESM::DialogueCondition::Function_Local:
typeStr = "Local"; type_str = "Local";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_Journal: case ESM::DialogueCondition::Function_Journal:
typeStr = "Journal"; type_str = "Journal";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_Item: case ESM::DialogueCondition::Function_Item:
typeStr = "Item count"; type_str = "Item count";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_Dead: case ESM::DialogueCondition::Function_Dead:
typeStr = "Dead"; type_str = "Dead";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_NotId: case ESM::DialogueCondition::Function_NotId:
typeStr = "Not ID"; type_str = "Not ID";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_NotFaction: case ESM::DialogueCondition::Function_NotFaction:
typeStr = "Not Faction"; type_str = "Not Faction";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_NotClass: case ESM::DialogueCondition::Function_NotClass:
typeStr = "Not Class"; type_str = "Not Class";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_NotRace: case ESM::DialogueCondition::Function_NotRace:
typeStr = "Not Race"; type_str = "Not Race";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_NotCell: case ESM::DialogueCondition::Function_NotCell:
typeStr = "Not Cell"; type_str = "Not Cell";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
case ESM::DialogueCondition::Function_NotLocal: case ESM::DialogueCondition::Function_NotLocal:
typeStr = "Not Local"; type_str = "Not Local";
funcStr = ss.mVariable; func_str = ss.mVariable;
break; break;
default: default:
typeStr = "Function"; type_str = "Function";
funcStr = ruleFunction(ss.mFunction); func_str = ruleFunction(ss.mFunction);
break; break;
} }
std::string_view operStr = "??"; std::string_view oper_str = "??";
switch (ss.mComparison) switch (ss.mComparison)
{ {
case ESM::DialogueCondition::Comp_Eq: case ESM::DialogueCondition::Comp_Eq:
operStr = "=="; oper_str = "==";
break; break;
case ESM::DialogueCondition::Comp_Ne: case ESM::DialogueCondition::Comp_Ne:
operStr = "!="; oper_str = "!=";
break; break;
case ESM::DialogueCondition::Comp_Gt: case ESM::DialogueCondition::Comp_Gt:
operStr = "> "; oper_str = "> ";
break; break;
case ESM::DialogueCondition::Comp_Ge: case ESM::DialogueCondition::Comp_Ge:
operStr = ">="; oper_str = ">=";
break; break;
case ESM::DialogueCondition::Comp_Ls: case ESM::DialogueCondition::Comp_Ls:
operStr = "< "; oper_str = "< ";
break; break;
case ESM::DialogueCondition::Comp_Le: case ESM::DialogueCondition::Comp_Le:
operStr = "<="; oper_str = "<=";
break; break;
default: default:
break; break;
@ -142,7 +143,8 @@ namespace
std::ostringstream stream; std::ostringstream stream;
std::visit([&](auto value) { stream << value; }, ss.mValue); std::visit([&](auto value) { stream << value; }, ss.mValue);
std::string result = std::format("{:<12} {:<32} {:2} {}", typeStr, funcStr, operStr, stream.str()); std::string result
= Misc::StringUtils::format("%-12s %-32s %2s %s", type_str, func_str, oper_str, stream.str());
return result; return result;
} }
@ -174,10 +176,12 @@ namespace
{ {
for (const ESM::Transport::Dest& dest : transport) for (const ESM::Transport::Dest& dest : transport)
{ {
std::cout << std::format(" Destination Position: ({:12.3f},{:12.3f},{:12.3f})\n", dest.mPos.pos[0], std::cout << " Destination Position: " << Misc::StringUtils::format("%12.3f", dest.mPos.pos[0]) << ","
dest.mPos.pos[1], dest.mPos.pos[2]); << Misc::StringUtils::format("%12.3f", dest.mPos.pos[1]) << ","
std::cout << std::format(" Destination Rotation: ({:9.6f},{:9.6f},{:9.6f})\n", dest.mPos.rot[0], << Misc::StringUtils::format("%12.3f", dest.mPos.pos[2]) << ")" << std::endl;
dest.mPos.rot[1], dest.mPos.rot[2]); std::cout << " Destination Rotation: " << Misc::StringUtils::format("%9.6f", dest.mPos.rot[0]) << ","
<< Misc::StringUtils::format("%9.6f", dest.mPos.rot[1]) << ","
<< Misc::StringUtils::format("%9.6f", dest.mPos.rot[2]) << ")" << std::endl;
if (!dest.mCellName.empty()) if (!dest.mCellName.empty())
std::cout << " Destination Cell: " << dest.mCellName << std::endl; std::cout << " Destination Cell: " << dest.mCellName << std::endl;
} }
@ -579,7 +583,7 @@ namespace EsmTool
std::cout << " Water Level: " << mData.mWater << std::endl; std::cout << " Water Level: " << mData.mWater << std::endl;
} }
else else
std::cout << std::format(" Map Color: 0x{:08X}\n", mData.mMapColor); std::cout << " Map Color: " << Misc::StringUtils::format("0x%08X", mData.mMapColor) << std::endl;
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl; std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl;
} }
@ -590,7 +594,7 @@ namespace EsmTool
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Description: " << mData.mDescription << std::endl;
std::cout << " Playable: " << mData.mData.mIsPlayable << std::endl; std::cout << " Playable: " << mData.mData.mIsPlayable << std::endl;
std::cout << std::format(" AI Services: 0x{:08X}\n", mData.mData.mServices); std::cout << " AI Services: " << Misc::StringUtils::format("0x%08X", mData.mData.mServices) << std::endl;
for (size_t i = 0; i < mData.mData.mAttribute.size(); ++i) for (size_t i = 0; i < mData.mData.mAttribute.size(); ++i)
std::cout << " Attribute" << (i + 1) << ": " << attributeLabel(mData.mData.mAttribute[i]) << " (" std::cout << " Attribute" << (i + 1) << ": " << attributeLabel(mData.mData.mAttribute[i]) << " ("
<< mData.mData.mAttribute[i] << ")" << std::endl; << mData.mData.mAttribute[i] << ")" << std::endl;
@ -638,7 +642,8 @@ namespace EsmTool
std::cout << " Flags: " << containerFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << containerFlags(mData.mFlags) << std::endl;
std::cout << " Weight: " << mData.mWeight << std::endl; std::cout << " Weight: " << mData.mWeight << std::endl;
for (const ESM::ContItem& item : mData.mInventory.mList) for (const ESM::ContItem& item : mData.mInventory.mList)
std::cout << std::format(" Inventory: Count: {:4d} Item: ", item.mCount) << item.mItem << std::endl; std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount)
<< " Item: " << item.mItem << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl;
} }
@ -675,7 +680,8 @@ namespace EsmTool
std::cout << " Gold: " << mData.mData.mGold << std::endl; std::cout << " Gold: " << mData.mData.mGold << std::endl;
for (const ESM::ContItem& item : mData.mInventory.mList) for (const ESM::ContItem& item : mData.mInventory.mList)
std::cout << std::format(" Inventory: Count: {:4d} Item: ", item.mCount) << item.mItem << std::endl; std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount)
<< " Item: " << item.mItem << std::endl;
for (const auto& spell : mData.mSpells.mList) for (const auto& spell : mData.mSpells.mList)
std::cout << " Spell: " << spell << std::endl; std::cout << " Spell: " << spell << std::endl;
@ -687,7 +693,7 @@ namespace EsmTool
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl; std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl; std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl; std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
std::cout << std::format(" AI Services:0x{:08X}\n", mData.mAiData.mServices); std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
for (const ESM::AIPackage& package : mData.mAiPackage.mList) for (const ESM::AIPackage& package : mData.mAiPackage.mList)
printAIPackage(package); printAIPackage(package);
@ -1062,7 +1068,8 @@ namespace EsmTool
} }
for (const ESM::ContItem& item : mData.mInventory.mList) for (const ESM::ContItem& item : mData.mInventory.mList)
std::cout << std::format(" Inventory: Count: {:4d} Item: ", item.mCount) << item.mItem << std::endl; std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount)
<< " Item: " << item.mItem << std::endl;
for (const auto& spell : mData.mSpells.mList) for (const auto& spell : mData.mSpells.mList)
std::cout << " Spell: " << spell << std::endl; std::cout << " Spell: " << spell << std::endl;
@ -1074,7 +1081,7 @@ namespace EsmTool
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl; std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl; std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl; std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
std::cout << std::format(" AI Services:0x{:08X}\n", mData.mAiData.mServices); std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
for (const ESM::AIPackage& package : mData.mAiPackage.mList) for (const ESM::AIPackage& package : mData.mAiPackage.mList)
printAIPackage(package); printAIPackage(package);
@ -1185,8 +1192,8 @@ namespace EsmTool
std::cout << " Variable: " << variable << std::endl; std::cout << " Variable: " << variable << std::endl;
std::cout << " ByteCode: "; std::cout << " ByteCode: ";
for (unsigned char byte : mData.mScriptData) for (const unsigned char& byte : mData.mScriptData)
std::cout << std::format("{:02X}", byte); std::cout << Misc::StringUtils::format("%02X", (int)(byte));
std::cout << std::endl; std::cout << std::endl;
if (mPrintPlain) if (mPrintPlain)

View file

@ -15,7 +15,7 @@
#include <components/esm4/readerutils.hpp> #include <components/esm4/readerutils.hpp>
#include <components/esm4/records.hpp> #include <components/esm4/records.hpp>
#include <components/esm4/typetraits.hpp> #include <components/esm4/typetraits.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
namespace EsmTool namespace EsmTool
{ {

View file

@ -90,7 +90,7 @@ namespace ESSImport
scriptedAnim.mAbsolute = true; scriptedAnim.mAbsolute = true;
// Neither loop count nor queueing seems to be supported by the ess format. // Neither loop count nor queueing seems to be supported by the ess format.
scriptedAnim.mLoopCount = std::numeric_limits<size_t>::max(); scriptedAnim.mLoopCount = std::numeric_limits<size_t>::max();
state.mScriptedAnims.push_back(std::move(scriptedAnim)); state.mScriptedAnims.push_back(scriptedAnim);
} }
else else
// TODO: Handle 0xFF index, which seems to be used for finished animations. // TODO: Handle 0xFF index, which seems to be used for finished animations.

View file

@ -303,7 +303,7 @@ namespace ESSImport
marker.mWorldY = notepos[1]; marker.mWorldY = notepos[1];
marker.mNote = std::move(note); marker.mNote = std::move(note);
marker.mCell = cell.mId; marker.mCell = cell.mId;
mMarkers.push_back(std::move(marker)); mMarkers.push_back(marker);
} }
newcell.mRefs = std::move(cellrefs); newcell.mRefs = std::move(cellrefs);

View file

@ -181,14 +181,14 @@ namespace ESSImport
public: public:
void read(ESM::ESMReader& esm) override void read(ESM::ESMReader& esm) override
{ {
ESM::Class classRecord; ESM::Class class_;
bool isDeleted = false; bool isDeleted = false;
classRecord.load(esm, isDeleted); class_.load(esm, isDeleted);
if (classRecord.mId == "NEWCLASSID_CHARGEN") if (class_.mId == "NEWCLASSID_CHARGEN")
mContext->mCustomPlayerClassName = classRecord.mName; mContext->mCustomPlayerClassName = class_.mName;
mRecords[classRecord.mId] = classRecord; mRecords[class_.mId] = class_;
} }
}; };
@ -249,10 +249,10 @@ namespace ESSImport
{ {
ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory; ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory;
for (uint32_t i = 0; i < static_cast<uint32_t>(invState.mItems.size()); ++i) for (size_t i = 0; i < invState.mItems.size(); ++i)
{ {
// FIXME: in case of conflict (multiple items with this refID) use the already equipped one? // FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
if (invState.mItems[i].mRef.mRefID == refr.mActorData.mSelectedEnchantItem) if (invState.mItems[i].mRef.mRefID == ESM::RefId::stringRefId(refr.mActorData.mSelectedEnchantItem))
invState.mSelectedEnchantItem = i; invState.mSelectedEnchantItem = i;
} }
} }
@ -260,12 +260,12 @@ namespace ESSImport
void write(ESM::ESMWriter& esm) override void write(ESM::ESMWriter& esm) override
{ {
esm.startRecord(ESM::REC_ASPL); esm.startRecord(ESM::REC_ASPL);
esm.writeHNRefId("ID__", mSelectedSpell); esm.writeHNString("ID__", mSelectedSpell);
esm.endRecord(ESM::REC_ASPL); esm.endRecord(ESM::REC_ASPL);
} }
private: private:
ESM::RefId mSelectedSpell; std::string mSelectedSpell;
}; };
class ConvertPCDT : public Converter class ConvertPCDT : public Converter
@ -374,16 +374,16 @@ namespace ESSImport
void write(ESM::ESMWriter& esm) override void write(ESM::ESMWriter& esm) override
{ {
esm.startRecord(ESM::REC_DCOU); esm.startRecord(ESM::REC_DCOU);
for (const auto& [id, count] : mKillCounter) for (auto it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
{ {
esm.writeHNRefId("ID__", id); esm.writeHNString("ID__", it->first);
esm.writeHNT("COUN", count); esm.writeHNT("COUN", it->second);
} }
esm.endRecord(ESM::REC_DCOU); esm.endRecord(ESM::REC_DCOU);
} }
private: private:
std::map<ESM::RefId, int> mKillCounter; std::map<std::string, int> mKillCounter;
}; };
class ConvertFACT : public Converter class ConvertFACT : public Converter
@ -584,7 +584,7 @@ namespace ESSImport
script.load(esm); script.load(esm);
ESM::GlobalScript out; ESM::GlobalScript out;
convertSCPT(script, out); convertSCPT(script, out);
mScripts.push_back(std::move(out)); mScripts.push_back(out);
} }
void write(ESM::ESMWriter& esm) override void write(ESM::ESMWriter& esm) override
{ {

View file

@ -75,8 +75,8 @@ namespace ESSImport
// to change them ingame // to change them ingame
int mCombatStats[3][2]; int mCombatStats[3][2];
ESM::RefId mSelectedSpell; std::string mSelectedSpell;
ESM::RefId mSelectedEnchantItem; std::string mSelectedEnchantItem;
SCRI mSCRI; SCRI mSCRI;

View file

@ -107,12 +107,12 @@ namespace ESSImport
if (esm.isNextSub("WNAM")) if (esm.isNextSub("WNAM"))
{ {
ESM::RefId spellRefId = esm.getRefId(); std::string id = esm.getHString();
if (esm.isNextSub("XNAM")) if (esm.isNextSub("XNAM"))
mActorData.mSelectedEnchantItem = esm.getRefId(); mActorData.mSelectedEnchantItem = esm.getHString();
else else
mActorData.mSelectedSpell = std::move(spellRefId); mActorData.mSelectedSpell = std::move(id);
if (esm.isNextSub("YNAM")) if (esm.isNextSub("YNAM"))
esm.skipHSub(); // 4 byte, 0 esm.skipHSub(); // 4 byte, 0

View file

@ -25,7 +25,7 @@
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "importercontext.hpp" #include "importercontext.hpp"
@ -136,9 +136,9 @@ namespace ESSImport
sub.mName = esm.retSubName().toString(); sub.mName = esm.retSubName().toString();
sub.mData.resize(esm.getSubSize()); sub.mData.resize(esm.getSubSize());
esm.getExact(sub.mData.data(), sub.mData.size()); esm.getExact(sub.mData.data(), sub.mData.size());
rec.mSubrecords.push_back(std::move(sub)); rec.mSubrecords.push_back(sub);
} }
file.mRecords.push_back(std::move(rec)); file.mRecords.push_back(rec);
} }
} }

View file

@ -7,19 +7,7 @@ namespace ESSImport
void INFO::load(ESM::ESMReader& esm) void INFO::load(ESM::ESMReader& esm)
{ {
if (esm.peekNextSub("XNAM")) mInfo = esm.getHNString("INAM");
{
// TODO: Support older saves by turning XNAM into a RefId.
// XNAM is probably the number of the topic response within the topic record's linked list.
// Resolving this value will likely require loading Morrowind.esm.
esm.getSubName();
esm.skipHSub();
mInfo = ESM::RefId();
}
else
mInfo = esm.getHNRefId("INAM");
mActorRefId = esm.getHNString("ACDT"); mActorRefId = esm.getHNString("ACDT");
} }

View file

@ -3,8 +3,6 @@
#include <string> #include <string>
#include <components/esm/refid.hpp>
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -15,7 +13,7 @@ namespace ESSImport
struct INFO struct INFO
{ {
ESM::RefId mInfo; std::string mInfo;
std::string mActorRefId; std::string mActorRefId;
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);

View file

@ -49,7 +49,7 @@ namespace ESSImport
} }
if (!separateStacks) if (!separateStacks)
mItems.push_back(std::move(item)); mItems.push_back(item);
} }
// equipped items // equipped items

View file

@ -9,7 +9,7 @@ namespace ESSImport
{ {
while (esm.isNextSub("KNAM")) while (esm.isNextSub("KNAM"))
{ {
ESM::RefId refId = esm.getRefId(); std::string refId = esm.getHString();
int32_t count; int32_t count;
esm.getHNT(count, "CNAM"); esm.getHNT(count, "CNAM");
mKillCounter[refId] = count; mKillCounter[refId] = count;

View file

@ -3,8 +3,7 @@
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <string>
#include <components/esm/refid.hpp>
namespace ESM namespace ESM
{ {
@ -19,7 +18,8 @@ namespace ESSImport
{ {
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
std::map<ESM::RefId, int32_t> mKillCounter; /// RefId, kill count
std::map<std::string, int32_t> mKillCounter;
int32_t mWerewolfKills; int32_t mWerewolfKills;
}; };

View file

@ -38,7 +38,7 @@ namespace ESSImport
unsigned char xnam; // sentinel unsigned char xnam; // sentinel
esm.getHNT(xnam, "XNAM"); esm.getHNT(xnam, "XNAM");
mActiveSpells.push_back(std::move(spell)); mActiveSpells.push_back(spell);
} }
} }

View file

@ -15,7 +15,7 @@ int main(int argc, char** argv)
{ {
bpo::options_description desc(R"(Syntax: openmw-essimporter <options> infile.ess outfile.omwsave bpo::options_description desc(R"(Syntax: openmw-essimporter <options> infile.ess outfile.omwsave
Allowed options)"); Allowed options)");
bpo::positional_options_description positionalDesc; bpo::positional_options_description p_desc;
auto addOption = desc.add_options(); auto addOption = desc.add_options();
addOption("help,h", "produce help message"); addOption("help,h", "produce help message");
addOption("mwsave,m", bpo::value<Files::MaybeQuotedPath>(), "morrowind .ess save file"); addOption("mwsave,m", bpo::value<Files::MaybeQuotedPath>(), "morrowind .ess save file");
@ -23,13 +23,12 @@ Allowed options)");
addOption("compare,c", "compare two .ess files"); addOption("compare,c", "compare two .ess files");
addOption("encoding", boost::program_options::value<std::string>()->default_value("win1252"), addOption("encoding", boost::program_options::value<std::string>()->default_value("win1252"),
"encoding of the save file"); "encoding of the save file");
positionalDesc.add("mwsave", 1).add("output", 1); p_desc.add("mwsave", 1).add("output", 1);
Files::ConfigurationManager::addCommonOptions(desc); Files::ConfigurationManager::addCommonOptions(desc);
bpo::variables_map variables; bpo::variables_map variables;
bpo::parsed_options parsed bpo::parsed_options parsed = bpo::command_line_parser(argc, argv).options(desc).positional(p_desc).run();
= bpo::command_line_parser(argc, argv).options(desc).positional(positionalDesc).run();
bpo::store(parsed, variables); bpo::store(parsed, variables);
if (variables.count("help") || !variables.count("mwsave") || !variables.count("output")) if (variables.count("help") || !variables.count("mwsave") || !variables.count("output"))
@ -41,7 +40,6 @@ Allowed options)");
bpo::notify(variables); bpo::notify(variables);
Files::ConfigurationManager cfgManager(true); Files::ConfigurationManager cfgManager(true);
cfgManager.processPaths(variables, std::filesystem::current_path());
cfgManager.readConfiguration(variables, desc); cfgManager.readConfiguration(variables, desc);
const auto& essFile = variables["mwsave"].as<Files::MaybeQuotedPath>(); const auto& essFile = variables["mwsave"].as<Files::MaybeQuotedPath>();

View file

@ -8,9 +8,7 @@
#include <QList> #include <QList>
#include <QMessageBox> #include <QMessageBox>
#include <QPair> #include <QPair>
#include <QProgressDialog>
#include <QPushButton> #include <QPushButton>
#include <QTimer>
#include <algorithm> #include <algorithm>
#include <mutex> #include <mutex>
@ -39,6 +37,8 @@
#include "utils/profilescombobox.hpp" #include "utils/profilescombobox.hpp"
#include "utils/textinputdialog.hpp" #include "utils/textinputdialog.hpp"
#include "ui_directorypicker.h"
const char* Launcher::DataFilesPage::mDefaultContentListName = "Default"; const char* Launcher::DataFilesPage::mDefaultContentListName = "Default";
namespace namespace
@ -153,16 +153,13 @@ namespace Launcher
Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
Config::LauncherSettings& launcherSettings, MainDialog* parent) Config::LauncherSettings& launcherSettings, MainDialog* parent)
: QWidget(parent) : QWidget(parent)
, mDirectoryPickerDialog(new QDialog(this))
, mMainDialog(parent) , mMainDialog(parent)
, mCfgMgr(cfg) , mCfgMgr(cfg)
, mGameSettings(gameSettings) , mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings) , mLauncherSettings(launcherSettings)
, mNavMeshToolInvoker(new Process::ProcessInvoker(this)) , mNavMeshToolInvoker(new Process::ProcessInvoker(this))
, mReloadCellsThread(&DataFilesPage::reloadCells, this)
{ {
ui.setupUi(this); ui.setupUi(this);
mDirectoryPicker.setupUi(mDirectoryPickerDialog);
setObjectName("DataFilesPage"); setObjectName("DataFilesPage");
mSelector = new ContentSelectorView::ContentSelector(ui.contentSelectorWidget, /*showOMWScripts=*/true); mSelector = new ContentSelectorView::ContentSelector(ui.contentSelectorWidget, /*showOMWScripts=*/true);
const QString encoding = mGameSettings.value("encoding", { "win1252" }).value; const QString encoding = mGameSettings.value("encoding", { "win1252" }).value;
@ -203,24 +200,8 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C
// the addons and don't want to get signals of the system doing it during startup. // the addons and don't want to get signals of the system doing it during startup.
connect(mSelector, &ContentSelectorView::ContentSelector::signalAddonDataChanged, this, connect(mSelector, &ContentSelectorView::ContentSelector::signalAddonDataChanged, this,
&DataFilesPage::slotAddonDataChanged); &DataFilesPage::slotAddonDataChanged);
mReloadCellsTimer = new QTimer(this);
mReloadCellsTimer->setSingleShot(true);
mReloadCellsTimer->setInterval(200);
connect(mReloadCellsTimer, &QTimer::timeout, this, &DataFilesPage::onReloadCellsTimerTimeout);
// Call manually to indicate all changes to addon data during startup. // Call manually to indicate all changes to addon data during startup.
onReloadCellsTimerTimeout(); slotAddonDataChanged();
}
Launcher::DataFilesPage::~DataFilesPage()
{
{
const std::lock_guard lock(mReloadCellsMutex);
mAbortReloadCells = true;
mStartReloadCells.notify_one();
}
mReloadCellsThread.join();
} }
void Launcher::DataFilesPage::buildView() void Launcher::DataFilesPage::buildView()
@ -266,7 +247,6 @@ void Launcher::DataFilesPage::buildView()
buildArchiveContextMenu(); buildArchiveContextMenu();
buildDataFilesContextMenu(); buildDataFilesContextMenu();
buildDirectoryPickerContextMenu();
} }
void Launcher::DataFilesPage::slotCopySelectedItemsPaths() void Launcher::DataFilesPage::slotCopySelectedItemsPaths()
@ -299,10 +279,8 @@ void Launcher::DataFilesPage::buildArchiveContextMenu()
&DataFilesPage::slotShowArchiveContextMenu); &DataFilesPage::slotShowArchiveContextMenu);
mArchiveContextMenu = new QMenu(ui.archiveListWidget); mArchiveContextMenu = new QMenu(ui.archiveListWidget);
mArchiveContextMenu->addAction(tr("&Check Selected"), this, mArchiveContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems()));
[this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Checked); }); mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems()));
mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this,
[this]() { setCheckStateForMultiSelectedItems(ui.archiveListWidget, Qt::Unchecked); });
} }
void Launcher::DataFilesPage::buildDataFilesContextMenu() void Launcher::DataFilesPage::buildDataFilesContextMenu()
@ -317,18 +295,6 @@ void Launcher::DataFilesPage::buildDataFilesContextMenu()
tr("&Open Path in File Explorer"), this, &Launcher::DataFilesPage::slotOpenSelectedItemsPaths); tr("&Open Path in File Explorer"), this, &Launcher::DataFilesPage::slotOpenSelectedItemsPaths);
} }
void Launcher::DataFilesPage::buildDirectoryPickerContextMenu()
{
connect(mDirectoryPicker.dirListWidget, &QListWidget::customContextMenuRequested, this,
&DataFilesPage::slotShowDirectoryPickerContextMenu);
mDirectoryPickerMenu = new QMenu(mDirectoryPicker.dirListWidget);
mDirectoryPickerMenu->addAction(tr("&Check Selected"), this,
[this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Checked); });
mDirectoryPickerMenu->addAction(tr("&Uncheck Selected"), this,
[this]() { setCheckStateForMultiSelectedItems(mDirectoryPicker.dirListWidget, Qt::Unchecked); });
}
bool Launcher::DataFilesPage::loadSettings() bool Launcher::DataFilesPage::loadSettings()
{ {
ui.navMeshMaxSizeSpinBox->setValue(getMaxNavMeshDbFileSizeMiB()); ui.navMeshMaxSizeSpinBox->setValue(getMaxNavMeshDbFileSizeMiB());
@ -385,17 +351,9 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
if (!resourcesVfs.isEmpty()) if (!resourcesVfs.isEmpty())
directories.insert(0, { resourcesVfs }); directories.insert(0, { resourcesVfs });
QIcon containsDataIcon(":/images/openmw-plugin.png");
QProgressDialog progressBar("Adding data directories", {}, 0, static_cast<int>(directories.size()), this);
progressBar.setWindowModality(Qt::WindowModal);
std::unordered_set<QString> visitedDirectories; std::unordered_set<QString> visitedDirectories;
for (qsizetype i = 0; i < directories.size(); ++i) for (const Config::SettingValue& currentDir : directories)
{ {
progressBar.setValue(static_cast<int>(i));
const Config::SettingValue& currentDir = directories.at(i);
if (!visitedDirectories.insert(currentDir.value).second) if (!visitedDirectories.insert(currentDir.value).second)
continue; continue;
@ -444,7 +402,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
// Add a "data file" icon if the directory contains a content file // Add a "data file" icon if the directory contains a content file
if (mSelector->containsDataFiles(currentDir.value)) if (mSelector->containsDataFiles(currentDir.value))
{ {
item->setIcon(containsDataIcon); item->setIcon(QIcon(":/images/openmw-plugin.png"));
tooltip << tr("Contains content file(s)"); tooltip << tr("Contains content file(s)");
} }
@ -458,7 +416,6 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
} }
item->setToolTip(tooltip.join('\n')); item->setToolTip(tooltip.join('\n'));
} }
progressBar.setValue(progressBar.maximum());
mSelector->sortFiles(); mSelector->sortFiles();
QList<Config::SettingValue> selectedArchives = mGameSettings.getArchiveList(); QList<Config::SettingValue> selectedArchives = mGameSettings.getArchiveList();
@ -808,7 +765,7 @@ void Launcher::DataFilesPage::addSubdirectories(bool append)
return; return;
QString rootPath = QFileDialog::getExistingDirectory( QString rootPath = QFileDialog::getExistingDirectory(
this, tr("Select Directory"), {}, QFileDialog::ShowDirsOnly | QFileDialog::Option::ReadOnly); this, tr("Select Directory"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::Option::ReadOnly);
if (rootPath.isEmpty()) if (rootPath.isEmpty())
return; return;
@ -836,22 +793,28 @@ void Launcher::DataFilesPage::addSubdirectories(bool append)
return; return;
} }
mDirectoryPicker.dirListWidget->clear(); QDialog dialog;
Ui::SelectSubdirs select;
select.setupUi(&dialog);
for (const auto& dir : subdirs) for (const auto& dir : subdirs)
{ {
if (!ui.directoryListWidget->findItems(dir, Qt::MatchFixedString).isEmpty()) if (!ui.directoryListWidget->findItems(dir, Qt::MatchFixedString).isEmpty())
continue; continue;
QListWidgetItem* newDir = new QListWidgetItem(dir, mDirectoryPicker.dirListWidget); const auto lastRow = select.dirListWidget->count();
newDir->setCheckState(Qt::Unchecked); select.dirListWidget->addItem(dir);
select.dirListWidget->item(lastRow)->setCheckState(Qt::Unchecked);
} }
if (mDirectoryPickerDialog->exec() == QDialog::Rejected) dialog.show();
if (dialog.exec() == QDialog::Rejected)
return; return;
for (int i = 0; i < mDirectoryPicker.dirListWidget->count(); ++i) for (int i = 0; i < select.dirListWidget->count(); ++i)
{ {
const auto* dir = mDirectoryPicker.dirListWidget->item(i); const auto* dir = select.dirListWidget->item(i);
if (dir->checkState() == Qt::Checked) if (dir->checkState() == Qt::Checked)
{ {
ui.directoryListWidget->insertItem(selectedRow, dir->text()); ui.directoryListWidget->insertItem(selectedRow, dir->text());
@ -902,35 +865,38 @@ void Launcher::DataFilesPage::removeDirectory()
refreshDataFilesView(); refreshDataFilesView();
} }
void Launcher::DataFilesPage::showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos)
{
QPoint globalPos = list->viewport()->mapToGlobal(pos);
menu->exec(globalPos);
}
void Launcher::DataFilesPage::slotShowArchiveContextMenu(const QPoint& pos) void Launcher::DataFilesPage::slotShowArchiveContextMenu(const QPoint& pos)
{ {
showContextMenu(mArchiveContextMenu, ui.archiveListWidget, pos); QPoint globalPos = ui.archiveListWidget->viewport()->mapToGlobal(pos);
mArchiveContextMenu->exec(globalPos);
} }
void Launcher::DataFilesPage::slotShowDataFilesContextMenu(const QPoint& pos) void Launcher::DataFilesPage::slotShowDataFilesContextMenu(const QPoint& pos)
{ {
showContextMenu(mDataFilesContextMenu, ui.directoryListWidget, pos); QPoint globalPos = ui.directoryListWidget->viewport()->mapToGlobal(pos);
mDataFilesContextMenu->exec(globalPos);
} }
void Launcher::DataFilesPage::slotShowDirectoryPickerContextMenu(const QPoint& pos) void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(bool checked)
{ {
showContextMenu(mDirectoryPickerMenu, mDirectoryPicker.dirListWidget, pos); Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked;
}
void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState) for (QListWidgetItem* selectedItem : ui.archiveListWidget->selectedItems())
{
for (QListWidgetItem* selectedItem : list->selectedItems())
{ {
selectedItem->setCheckState(checkState); selectedItem->setCheckState(checkState);
} }
} }
void Launcher::DataFilesPage::slotUncheckMultiSelectedItems()
{
setCheckStateForMultiSelectedItems(false);
}
void Launcher::DataFilesPage::slotCheckMultiSelectedItems()
{
setCheckStateForMultiSelectedItems(true);
}
void Launcher::DataFilesPage::moveSources(QListWidget* sourceList, int step) void Launcher::DataFilesPage::moveSources(QListWidget* sourceList, int step)
{ {
const QList<QPair<int, QListWidgetItem*>> sortedItems = sortedSelectedItems(sourceList, step > 0); const QList<QPair<int, QListWidgetItem*>> sortedItems = sortedSelectedItems(sourceList, step > 0);
@ -1015,66 +981,32 @@ bool Launcher::DataFilesPage::showDeleteMessageBox(const QString& text)
void Launcher::DataFilesPage::slotAddonDataChanged() void Launcher::DataFilesPage::slotAddonDataChanged()
{ {
mReloadCellsTimer->start(); QStringList selectedFiles = selectedFilePaths();
} if (previousSelectedFiles != selectedFiles)
void Launcher::DataFilesPage::onReloadCellsTimerTimeout()
{
const ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
QStringList selectedFiles;
for (const ContentSelectorModel::EsmFile* item : items)
selectedFiles.append(item->filePath());
if (mSelectedFiles != selectedFiles)
{ {
const std::lock_guard lock(mReloadCellsMutex); previousSelectedFiles = selectedFiles;
mSelectedFiles = std::move(selectedFiles); // Loading cells for core Morrowind + Expansions takes about 0.2 seconds, which is enough to cause a
mReloadCells = true; // barely perceptible UI lag. Splitting into its own thread to alleviate that.
mStartReloadCells.notify_one(); std::thread loadCellsThread(&DataFilesPage::reloadCells, this, selectedFiles);
loadCellsThread.detach();
} }
} }
void Launcher::DataFilesPage::reloadCells() // Mutex lock to run reloadCells synchronously.
static std::mutex reloadCellsMutex;
void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
{ {
QStringList selectedFiles; // Use a mutex lock so that we can prevent two threads from executing the rest of this code at the same time
std::unique_lock lock(mReloadCellsMutex); // Based on https://stackoverflow.com/a/5429695/531762
std::unique_lock<std::mutex> lock(reloadCellsMutex);
while (true) // The following code will run only if there is not another thread currently running it
{ CellNameLoader cellNameLoader;
mStartReloadCells.wait(lock); QSet<QString> set = cellNameLoader.getCellNames(selectedFiles);
QStringList cellNamesList(set.begin(), set.end());
if (mAbortReloadCells) std::sort(cellNamesList.begin(), cellNamesList.end());
return; emit signalLoadedCellsChanged(cellNamesList);
if (!std::exchange(mReloadCells, false))
continue;
const QStringList newSelectedFiles = mSelectedFiles;
lock.unlock();
QStringList filteredFiles;
for (const QString& v : newSelectedFiles)
if (QFile::exists(v))
filteredFiles.append(v);
if (selectedFiles != filteredFiles)
{
selectedFiles = std::move(filteredFiles);
CellNameLoader cellNameLoader;
QSet<QString> set = cellNameLoader.getCellNames(selectedFiles);
QStringList cellNamesList(set.begin(), set.end());
std::sort(cellNamesList.begin(), cellNamesList.end());
emit signalLoadedCellsChanged(std::move(cellNamesList));
}
lock.lock();
if (mAbortReloadCells)
return;
}
} }
void Launcher::DataFilesPage::startNavMeshTool() void Launcher::DataFilesPage::startNavMeshTool()

View file

@ -2,7 +2,6 @@
#define DATAFILESPAGE_H #define DATAFILESPAGE_H
#include "ui_datafilespage.h" #include "ui_datafilespage.h"
#include "ui_directorypicker.h"
#include <components/process/processinvoker.hpp> #include <components/process/processinvoker.hpp>
@ -11,14 +10,9 @@
#include <QStringList> #include <QStringList>
#include <QWidget> #include <QWidget>
#include <condition_variable>
#include <mutex>
#include <thread>
class QSortFilterProxyModel; class QSortFilterProxyModel;
class QAbstractItemModel; class QAbstractItemModel;
class QMenu; class QMenu;
class QTimer;
namespace Files namespace Files
{ {
@ -47,16 +41,12 @@ namespace Launcher
ContentSelectorView::ContentSelector* mSelector; ContentSelectorView::ContentSelector* mSelector;
Ui::DataFilesPage ui; Ui::DataFilesPage ui;
QDialog* mDirectoryPickerDialog;
Ui::SelectSubdirs mDirectoryPicker;
QMenu* mArchiveContextMenu; QMenu* mArchiveContextMenu;
QMenu* mDataFilesContextMenu; QMenu* mDataFilesContextMenu;
QMenu* mDirectoryPickerMenu;
public: public:
explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
Config::LauncherSettings& launcherSettings, MainDialog* parent = nullptr); Config::LauncherSettings& launcherSettings, MainDialog* parent = nullptr);
~DataFilesPage();
QAbstractItemModel* profilesModel() const; QAbstractItemModel* profilesModel() const;
@ -91,7 +81,8 @@ namespace Launcher
void slotShowArchiveContextMenu(const QPoint& pos); void slotShowArchiveContextMenu(const QPoint& pos);
void slotShowDataFilesContextMenu(const QPoint& pos); void slotShowDataFilesContextMenu(const QPoint& pos);
void slotShowDirectoryPickerContextMenu(const QPoint& pos); void slotCheckMultiSelectedItems();
void slotUncheckMultiSelectedItems();
void on_newProfileAction_triggered(); void on_newProfileAction_triggered();
void on_cloneProfileAction_triggered(); void on_cloneProfileAction_triggered();
@ -128,7 +119,7 @@ namespace Launcher
Config::LauncherSettings& mLauncherSettings; Config::LauncherSettings& mLauncherSettings;
QString mPreviousProfile; QString mPreviousProfile;
QStringList mSelectedFiles; QStringList previousSelectedFiles;
QString mDataLocal; QString mDataLocal;
QStringList mKnownArchives; QStringList mKnownArchives;
QStringList mNewDataDirs; QStringList mNewDataDirs;
@ -136,21 +127,12 @@ namespace Launcher
Process::ProcessInvoker* mNavMeshToolInvoker; Process::ProcessInvoker* mNavMeshToolInvoker;
NavMeshToolProgress mNavMeshToolProgress; NavMeshToolProgress mNavMeshToolProgress;
bool mReloadCells = false;
bool mAbortReloadCells = false;
std::mutex mReloadCellsMutex;
std::condition_variable mStartReloadCells;
std::thread mReloadCellsThread;
QTimer* mReloadCellsTimer;
void addArchive(const QString& name, Qt::CheckState selected, int row = -1); void addArchive(const QString& name, Qt::CheckState selected, int row = -1);
void addArchivesFromDir(const QString& dir); void addArchivesFromDir(const QString& dir);
void buildView(); void buildView();
void buildArchiveContextMenu(); void buildArchiveContextMenu();
void buildDataFilesContextMenu(); void buildDataFilesContextMenu();
void buildDirectoryPickerContextMenu(); void setCheckStateForMultiSelectedItems(bool checked);
void showContextMenu(QMenu* menu, QListWidget* list, const QPoint& pos);
void setCheckStateForMultiSelectedItems(QListWidget* list, Qt::CheckState checkState);
void setProfile(int index, bool savePrevious); void setProfile(int index, bool savePrevious);
void setProfile(const QString& previous, const QString& current, bool savePrevious); void setProfile(const QString& previous, const QString& current, bool savePrevious);
void removeProfile(const QString& profile); void removeProfile(const QString& profile);
@ -158,8 +140,7 @@ namespace Launcher
void addProfile(const QString& profile, bool setAsCurrent); void addProfile(const QString& profile, bool setAsCurrent);
void checkForDefaultProfile(); void checkForDefaultProfile();
void populateFileViews(const QString& contentModelName); void populateFileViews(const QString& contentModelName);
void onReloadCellsTimerTimeout(); void reloadCells(QStringList selectedFiles);
void reloadCells();
void refreshDataFilesView(); void refreshDataFilesView();
void updateNavMeshProgress(int minDataSize); void updateNavMeshProgress(int minDataSize);
void slotCopySelectedItemsPaths(); void slotCopySelectedItemsPaths();

View file

@ -242,36 +242,11 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
{ {
if (mode == Settings::WindowMode::Fullscreen || mode == Settings::WindowMode::WindowedFullscreen) if (mode == Settings::WindowMode::Fullscreen || mode == Settings::WindowMode::WindowedFullscreen)
{ {
QString customSizeMessage = tr("Custom window size is available only in Windowed mode.");
QString windowBorderMessage = tr("Window border is available only in Windowed mode.");
standardRadioButton->toggle(); standardRadioButton->toggle();
customRadioButton->setEnabled(false); customRadioButton->setEnabled(false);
customWidthSpinBox->setEnabled(false); customWidthSpinBox->setEnabled(false);
customHeightSpinBox->setEnabled(false); customHeightSpinBox->setEnabled(false);
windowBorderCheckBox->setEnabled(false); windowBorderCheckBox->setEnabled(false);
windowBorderCheckBox->setToolTip(windowBorderMessage);
customWidthSpinBox->setToolTip(customSizeMessage);
customHeightSpinBox->setToolTip(customSizeMessage);
customRadioButton->setToolTip(customSizeMessage);
}
if (mode == Settings::WindowMode::Fullscreen)
{
resolutionComboBox->setEnabled(true);
resolutionComboBox->setToolTip("");
standardRadioButton->setToolTip("");
}
else if (mode == Settings::WindowMode::WindowedFullscreen)
{
QString fullScreenMessage = tr("Windowed Fullscreen mode always uses the native display resolution.");
resolutionComboBox->setEnabled(false);
resolutionComboBox->setToolTip(fullScreenMessage);
standardRadioButton->setToolTip(fullScreenMessage);
// Assume that a first item is a native screen resolution
resolutionComboBox->setCurrentIndex(0);
} }
else else
{ {
@ -279,13 +254,6 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
customWidthSpinBox->setEnabled(true); customWidthSpinBox->setEnabled(true);
customHeightSpinBox->setEnabled(true); customHeightSpinBox->setEnabled(true);
windowBorderCheckBox->setEnabled(true); windowBorderCheckBox->setEnabled(true);
resolutionComboBox->setEnabled(true);
resolutionComboBox->setToolTip("");
standardRadioButton->setToolTip("");
windowBorderCheckBox->setToolTip("");
customWidthSpinBox->setToolTip("");
customHeightSpinBox->setToolTip("");
customRadioButton->setToolTip("");
} }
} }

View file

@ -88,7 +88,11 @@ void Launcher::ImportPage::on_importerButton_clicked()
// Create the file if it doesn't already exist, else the importer will fail // Create the file if it doesn't already exist, else the importer will fail
auto path = mCfgMgr.getUserConfigPath(); auto path = mCfgMgr.getUserConfigPath();
path /= "openmw.cfg"; path /= "openmw.cfg";
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFile file(path); QFile file(path);
#else
QFile file(Files::pathToQString(path));
#endif
if (!file.exists()) if (!file.exists())
{ {

View file

@ -42,7 +42,7 @@ int runLauncher(int argc, char* argv[])
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string()); resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
} }
L10n::installQtTranslations(app, "launcher", resourcesPath); l10n::installQtTranslations(app, "launcher", resourcesPath);
Launcher::MainDialog mainWin(configurationManager); Launcher::MainDialog mainWin(configurationManager);

View file

@ -497,7 +497,11 @@ bool Launcher::MainDialog::writeSettings()
} }
// Game settings // Game settings
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFile file(userPath / Files::openmwCfgFile); QFile file(userPath / Files::openmwCfgFile);
#else
QFile file(Files::getUserConfigPathQString(mCfgMgr));
#endif
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) if (!file.open(QIODevice::ReadWrite | QIODevice::Text))
{ {

View file

@ -163,6 +163,8 @@ bool Launcher::SettingsPage::loadSettings()
loadSettingInt(Settings::physics().mAsyncNumThreads, *physicsThreadsSpinBox); loadSettingInt(Settings::physics().mAsyncNumThreads, *physicsThreadsSpinBox);
loadSettingBool( loadSettingBool(
Settings::game().mAllowActorsToFollowOverWaterSurface, *allowNPCToFollowOverWaterSurfaceCheckBox); Settings::game().mAllowActorsToFollowOverWaterSurface, *allowNPCToFollowOverWaterSurfaceCheckBox);
loadSettingBool(
Settings::game().mUnarmedCreatureAttacksDamageArmor, *unarmedCreatureAttacksDamageArmorCheckBox);
loadSettingInt(Settings::game().mActorCollisionShapeType, *actorCollisonShapeTypeComboBox); loadSettingInt(Settings::game().mActorCollisionShapeType, *actorCollisonShapeTypeComboBox);
} }
@ -291,7 +293,6 @@ bool Launcher::SettingsPage::loadSettings()
} }
} }
loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox); loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox);
dopplerSpinBox->setValue(Settings::sound().mDopplerFactor);
} }
// Interface Changes // Interface Changes
@ -303,9 +304,6 @@ bool Launcher::SettingsPage::loadSettings()
loadSettingBool(Settings::gui().mColorTopicEnable, *changeDialogTopicsCheckBox); loadSettingBool(Settings::gui().mColorTopicEnable, *changeDialogTopicsCheckBox);
showOwnedComboBox->setCurrentIndex(Settings::game().mShowOwned); showOwnedComboBox->setCurrentIndex(Settings::game().mShowOwned);
loadSettingBool(Settings::gui().mStretchMenuBackground, *stretchBackgroundCheckBox); loadSettingBool(Settings::gui().mStretchMenuBackground, *stretchBackgroundCheckBox);
connect(controllerMenusCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotControllerMenusToggled);
loadSettingBool(Settings::gui().mControllerMenus, *controllerMenusCheckBox);
loadSettingBool(Settings::gui().mControllerTooltips, *controllerMenuTooltipsCheckBox);
loadSettingBool(Settings::map().mAllowZooming, *useZoomOnMapCheckBox); loadSettingBool(Settings::map().mAllowZooming, *useZoomOnMapCheckBox);
loadSettingBool(Settings::game().mGraphicHerbalism, *graphicHerbalismCheckBox); loadSettingBool(Settings::game().mGraphicHerbalism, *graphicHerbalismCheckBox);
scalingSpinBox->setValue(Settings::gui().mScalingFactor); scalingSpinBox->setValue(Settings::gui().mScalingFactor);
@ -375,6 +373,8 @@ void Launcher::SettingsPage::saveSettings()
saveSettingInt(*physicsThreadsSpinBox, Settings::physics().mAsyncNumThreads); saveSettingInt(*physicsThreadsSpinBox, Settings::physics().mAsyncNumThreads);
saveSettingBool( saveSettingBool(
*allowNPCToFollowOverWaterSurfaceCheckBox, Settings::game().mAllowActorsToFollowOverWaterSurface); *allowNPCToFollowOverWaterSurfaceCheckBox, Settings::game().mAllowActorsToFollowOverWaterSurface);
saveSettingBool(
*unarmedCreatureAttacksDamageArmorCheckBox, Settings::game().mUnarmedCreatureAttacksDamageArmor);
saveSettingInt(*actorCollisonShapeTypeComboBox, Settings::game().mActorCollisionShapeType); saveSettingInt(*actorCollisonShapeTypeComboBox, Settings::game().mActorCollisionShapeType);
} }
@ -486,8 +486,6 @@ void Launcher::SettingsPage::saveSettings()
const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked; const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked;
Settings::sound().mCameraListener.set(cCameraListener); Settings::sound().mCameraListener.set(cCameraListener);
Settings::sound().mDopplerFactor.set(dopplerSpinBox->value());
} }
// Interface Changes // Interface Changes
@ -499,8 +497,6 @@ void Launcher::SettingsPage::saveSettings()
saveSettingBool(*changeDialogTopicsCheckBox, Settings::gui().mColorTopicEnable); saveSettingBool(*changeDialogTopicsCheckBox, Settings::gui().mColorTopicEnable);
saveSettingInt(*showOwnedComboBox, Settings::game().mShowOwned); saveSettingInt(*showOwnedComboBox, Settings::game().mShowOwned);
saveSettingBool(*stretchBackgroundCheckBox, Settings::gui().mStretchMenuBackground); saveSettingBool(*stretchBackgroundCheckBox, Settings::gui().mStretchMenuBackground);
saveSettingBool(*controllerMenusCheckBox, Settings::gui().mControllerMenus);
saveSettingBool(*controllerMenuTooltipsCheckBox, Settings::gui().mControllerTooltips);
saveSettingBool(*useZoomOnMapCheckBox, Settings::map().mAllowZooming); saveSettingBool(*useZoomOnMapCheckBox, Settings::map().mAllowZooming);
saveSettingBool(*graphicHerbalismCheckBox, Settings::game().mGraphicHerbalism); saveSettingBool(*graphicHerbalismCheckBox, Settings::game().mGraphicHerbalism);
Settings::gui().mScalingFactor.set(scalingSpinBox->value()); Settings::gui().mScalingFactor.set(scalingSpinBox->value());
@ -559,11 +555,6 @@ void Launcher::SettingsPage::slotAnimSourcesToggled(bool checked)
} }
} }
void Launcher::SettingsPage::slotControllerMenusToggled(bool checked)
{
controllerMenuTooltipsCheckBox->setEnabled(checked);
}
void Launcher::SettingsPage::slotPostProcessToggled(bool checked) void Launcher::SettingsPage::slotPostProcessToggled(bool checked)
{ {
postprocessTransparentPostpassCheckBox->setEnabled(checked); postprocessTransparentPostpassCheckBox->setEnabled(checked);

View file

@ -34,7 +34,6 @@ namespace Launcher
void slotSkyBlendingToggled(bool checked); void slotSkyBlendingToggled(bool checked);
void slotShadowDistLimitToggled(bool checked); void slotShadowDistLimitToggled(bool checked);
void slotDistantLandToggled(bool checked); void slotDistantLandToggled(bool checked);
void slotControllerMenusToggled(bool checked);
private: private:
Config::GameSettings& mGameSettings; Config::GameSettings& mGameSettings;

View file

@ -25,14 +25,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QListWidget" name="dirListWidget"> <widget class="QListWidget" name="dirListWidget"/>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>

View file

@ -53,7 +53,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="10" column="1">
<widget class="QCheckBox" name="normaliseRaceSpeedCheckBox"> <widget class="QCheckBox" name="normaliseRaceSpeedCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Don't use race weight in NPC movement speed calculations.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Don't use race weight in NPC movement speed calculations.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -63,7 +63,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="9" column="1">
<widget class="QCheckBox" name="classicCalmSpellsCheckBox"> <widget class="QCheckBox" name="classicCalmSpellsCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Stops combat with NPCs affected by Calm spells every frame -- like in Morrowind without the MCP.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Stops combat with NPCs affected by Calm spells every frame -- like in Morrowind without the MCP.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -73,7 +73,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="12" column="1">
<widget class="QCheckBox" name="avoidCollisionsCheckBox"> <widget class="QCheckBox" name="avoidCollisionsCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled NPCs apply evasion maneuver to avoid collisions with others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled NPCs apply evasion maneuver to avoid collisions with others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -123,7 +123,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QCheckBox" name="requireAppropriateAmmunitionCheckBox"> <widget class="QCheckBox" name="requireAppropriateAmmunitionCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, a magical ammunition is required to bypass normal weapon resistance or weakness. If disabled, a magical ranged weapon or a magical ammunition is required.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, a magical ammunition is required to bypass normal weapon resistance or weakness. If disabled, a magical ranged weapon or a magical ammunition is required.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -133,7 +133,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="13" column="1">
<widget class="QCheckBox" name="graphicHerbalismCheckBox"> <widget class="QCheckBox" name="graphicHerbalismCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -143,7 +143,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="11" column="1">
<widget class="QCheckBox" name="swimUpwardCorrectionCheckBox"> <widget class="QCheckBox" name="swimUpwardCorrectionCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Makes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Makes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -153,7 +153,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QCheckBox" name="enchantedWeaponsMagicalCheckBox"> <widget class="QCheckBox" name="enchantedWeaponsMagicalCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make enchanted weapons without Magical flag bypass normal weapons resistance, like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make enchanted weapons without Magical flag bypass normal weapons resistance, like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -183,7 +183,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="7" column="1">
<widget class="QCheckBox" name="canLootDuringDeathAnimationCheckBox"> <widget class="QCheckBox" name="canLootDuringDeathAnimationCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.&lt;/p&gt;&lt;p&gt;If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.&lt;/p&gt;&lt;p&gt;If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -203,7 +203,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="5" column="1">
<widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox"> <widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Effects of reflected Absorb spells are not mirrored - like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Effects of reflected Absorb spells are not mirrored - like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -213,6 +213,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1">
<widget class="QCheckBox" name="unarmedCreatureAttacksDamageArmorCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Makes unarmed creature attacks able to reduce armor condition, just as attacks from NPCs and armed creatures.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Unarmed Creature Attacks Damage Armor</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -420,14 +430,14 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QCheckBox" name="smoothAnimTransitionsCheckBox"> <widget class="QCheckBox" name="smoothAnimTransitionsCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled - makes transitions between different animations/poses much smoother. Also allows to load animation blending config YAML files that can be bundled with animations in order to customise blending styles.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled - makes transitions between different animations/poses much smoother. Also allows to load animation blending config YAML files that can be bundled with animations in order to customise blending styles.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Smooth Animation Transitions</string> <string>Smooth Animation Transitions</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
@ -1089,20 +1099,68 @@
<attribute name="title"> <attribute name="title">
<string>Audio</string> <string>Audio</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout">
<item> <item>
<layout class="QGridLayout" name="audioLayout"> <layout class="QHBoxLayout">
<item row="1" column="1"> <item>
<widget class="QComboBox" name="enableHRTFComboBox"> <widget class="QLabel" name="audioDeviceSelectorLabel">
<property name="toolTip">
<string>Select your preferred audio device.</string>
</property>
<property name="text">
<string>Audio Device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="audioDeviceSelectorComboBox">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>0</width> <width>283</width>
<height>0</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Default</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="enableHRTFLabel">
<property name="toolTip">
<string>This setting controls HRTF, which simulates 3D sound on stereo systems.</string>
</property>
<property name="text">
<string>HRTF</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="enableHRTFComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>283</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
@ -1126,74 +1184,11 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="1" column="0"> </layout>
<widget class="QLabel" name="enableHRTFLabel"> </item>
<property name="toolTip"> <item>
<string>This setting controls HRTF, which simulates 3D sound on stereo systems.</string> <layout class="QHBoxLayout">
</property> <item>
<property name="text">
<string>HRTF</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="cameraListenerCheckBox">
<property name="toolTip">
<string>In third-person view, use the camera as the sound listener instead of the player character.</string>
</property>
<property name="text">
<string>Use the Camera as the Sound Listener</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="dopplerLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls the strength of the Doppler effect. Zero means it is completely disabled.&lt;/p&gt;&lt;p&gt;The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Doppler Factor</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="dopplerSpinBox">
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.250000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="hrtfProfileSelectorComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Default</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="hrtfProfileSelectorLabel"> <widget class="QLabel" name="hrtfProfileSelectorLabel">
<property name="toolTip"> <property name="toolTip">
<string>Select your preferred HRTF profile.</string> <string>Select your preferred HRTF profile.</string>
@ -1203,27 +1198,17 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item>
<widget class="QLabel" name="audioDeviceSelectorLabel"> <widget class="QComboBox" name="hrtfProfileSelectorComboBox">
<property name="toolTip">
<string>Select your preferred audio device.</string>
</property>
<property name="text">
<string>Audio Device</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="audioDeviceSelectorComboBox">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>0</width> <width>283</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
@ -1239,6 +1224,16 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QCheckBox" name="cameraListenerCheckBox">
<property name="toolTip">
<string>In third-person view, use the camera as the sound listener instead of the player character.</string>
</property>
<property name="text">
<string>Use the Camera as the Sound Listener</string>
</property>
</widget>
</item>
<item> <item>
<spacer> <spacer>
<property name="orientation"> <property name="orientation">
@ -1246,8 +1241,8 @@
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>0</width>
<height>40</height> <height>0</height>
</size> </size>
</property> </property>
</spacer> </spacer>
@ -1403,29 +1398,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1">
<widget class="QCheckBox" name="controllerMenusCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make it easier to use game menus with a controller.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Controller Menus</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="controllerMenuTooltipsCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When using controller menus, make tooltips visible by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show Controller Tooltips By Default</string>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="showOwnedLabel"> <widget class="QLabel" name="showOwnedLabel">
<property name="text"> <property name="text">

View file

@ -43,10 +43,10 @@ std::vector<std::string> Launcher::enumerateOpenALDevicesHrtf()
LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr; LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT"); void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT");
memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr)); memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));
ALCint numHrtf; ALCint num_hrtf;
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &numHrtf); alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
ret.reserve(numHrtf); ret.reserve(num_hrtf);
for (ALCint i = 0; i < numHrtf; ++i) for (ALCint i = 0; i < num_hrtf; ++i)
{ {
const ALCchar* entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i); const ALCchar* entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
if (strcmp(entry, "") == 0) if (strcmp(entry, "") == 0)

View file

@ -9,22 +9,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
namespace namespace sfs = std::filesystem;
{
// from configfileparser.cpp
std::string trim_ws(const std::string& s)
{
std::string::size_type n, n2;
n = s.find_first_not_of(" \t\r\n");
if (n == std::string::npos)
return std::string();
else
{
n2 = s.find_last_not_of(" \t\r\n");
return s.substr(n, n2 - n + 1);
}
}
}
MwIniImporter::MwIniImporter() MwIniImporter::MwIniImporter()
: mVerbose(false) : mVerbose(false)
@ -326,10 +311,10 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::filesystem::pat
continue; continue;
} }
const std::string::size_type commentPos = utf8.find(';'); int comment_pos = static_cast<int>(utf8.find(';'));
if (commentPos != std::string::npos) if (comment_pos > 0)
{ {
utf8 = utf8.substr(0, commentPos); utf8 = utf8.substr(0, comment_pos);
} }
int pos = static_cast<int>(utf8.find('=')); int pos = static_cast<int>(utf8.find('='));
@ -351,7 +336,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::filesystem::pat
if (it == map.end()) if (it == map.end())
it = map.emplace_hint(it, std::move(key), std::vector<std::string>()); it = map.emplace_hint(it, std::move(key), std::vector<std::string>());
it->second.emplace_back(value); it->second.push_back(std::string(value));
} }
return map; return map;
@ -367,10 +352,12 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::filesystem::pat
std::string line; std::string line;
while (std::getline(file, line)) while (std::getline(file, line))
{ {
// ignore comments - keep in sync with configfileparser.cpp
if (line.find('#') == line.find_first_not_of(" \t\r\n")) // we cant say comment by only looking at first char anymore
int comment_pos = static_cast<int>(line.find('#'));
if (comment_pos > 0)
{ {
continue; line = line.substr(0, comment_pos);
} }
if (line.empty()) if (line.empty())
@ -386,14 +373,12 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::filesystem::pat
std::string key(line.substr(0, pos)); std::string key(line.substr(0, pos));
std::string value(line.substr(pos + 1)); std::string value(line.substr(pos + 1));
key = trim_ws(key);
value = trim_ws(value);
if (map.find(key) == map.end()) if (map.find(key) == map.end())
{ {
map.insert(std::make_pair(key, std::vector<std::string>())); map.insert(std::make_pair(key, std::vector<std::string>()));
} }
map[key].push_back(std::move(value)); map[key].push_back(value);
} }
return map; return map;

View file

@ -8,7 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
class MwIniImporter class MwIniImporter
{ {

View file

@ -10,6 +10,7 @@
#include <components/files/conversion.hpp> #include <components/files/conversion.hpp>
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
namespace sfs = std::filesystem;
#ifndef _WIN32 #ifndef _WIN32
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@ -62,7 +63,7 @@ int wmain(int argc, wchar_t* wargv[])
try try
{ {
bpo::options_description desc("Syntax: openmw-iniimporter <options> inifile configfile\nAllowed options"); bpo::options_description desc("Syntax: openmw-iniimporter <options> inifile configfile\nAllowed options");
bpo::positional_options_description positionalDesc; bpo::positional_options_description p_desc;
auto addOption = desc.add_options(); auto addOption = desc.add_options();
addOption("help,h", "produce help message"); addOption("help,h", "produce help message");
addOption("verbose,v", "verbose output"); addOption("verbose,v", "verbose output");
@ -79,12 +80,11 @@ int wmain(int argc, wchar_t* wargv[])
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other 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"); "\n\twin1252 - Western European (Latin) alphabet, used by default");
; ;
positionalDesc.add("ini", 1).add("cfg", 1); p_desc.add("ini", 1).add("cfg", 1);
bpo::variables_map vm; bpo::variables_map vm;
bpo::parsed_options parsed bpo::parsed_options parsed = bpo::command_line_parser(argc, argv).options(desc).positional(p_desc).run();
= bpo::command_line_parser(argc, argv).options(desc).positional(positionalDesc).run();
bpo::store(parsed, vm); bpo::store(parsed, vm);
if (vm.count("help") || !vm.count("ini") || !vm.count("cfg")) if (vm.count("help") || !vm.count("ini") || !vm.count("cfg"))
@ -126,20 +126,12 @@ int wmain(int argc, wchar_t* wargv[])
MwIniImporter importer; MwIniImporter importer;
importer.setVerbose(vm.count("verbose") != 0); importer.setVerbose(vm.count("verbose") != 0);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
// Font encoding settings // Font encoding settings
std::string encoding; std::string encoding(vm["encoding"].as<std::string>());
if (vm["encoding"].defaulted() && cfg.contains("encoding") && !cfg["encoding"].empty())
encoding = cfg["encoding"].back();
else
{
encoding = vm["encoding"].as<std::string>();
cfg["encoding"] = { encoding };
}
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding)); importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile); MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
if (!vm.count("fonts")) if (!vm.count("fonts"))
{ {

View file

@ -1,16 +1,20 @@
set(NAVMESHTOOL_LIB set(NAVMESHTOOL
worldspacedata.cpp worldspacedata.cpp
navmesh.cpp navmesh.cpp
main.cpp
)
source_group(apps\\navmeshtool FILES ${NAVMESHTOOL})
add_library(openmw-navmeshtool-lib STATIC
${NAVMESHTOOL}
) )
source_group(apps\\navmeshtool FILES ${NAVMESHTOOL_LIB} main.cpp)
add_library(openmw-navmeshtool-lib STATIC ${NAVMESHTOOL_LIB})
if (ANDROID) if (ANDROID)
add_library(openmw-navmeshtool SHARED main.cpp) add_library(openmw-navmeshtool SHARED
main.cpp
)
else() else()
openmw_add_executable(openmw-navmeshtool main.cpp) openmw_add_executable(openmw-navmeshtool ${NAVMESHTOOL})
endif() endif()
target_link_libraries(openmw-navmeshtool openmw-navmeshtool-lib) target_link_libraries(openmw-navmeshtool openmw-navmeshtool-lib)

View file

@ -25,7 +25,7 @@
#include <components/resource/niffilemanager.hpp> #include <components/resource/niffilemanager.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/settings/values.hpp> #include <components/settings/values.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include <components/version/version.hpp> #include <components/version/version.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp> #include <components/vfs/registerarchives.hpp>
@ -62,6 +62,8 @@ namespace NavMeshTool
bpo::options_description makeOptionsDescription() bpo::options_description makeOptionsDescription()
{ {
using Fallback::FallbackMap;
bpo::options_description result; bpo::options_description result;
auto addOption = result.add_options(); auto addOption = result.add_options();
addOption("help", "print help message"); addOption("help", "print help message");
@ -143,7 +145,6 @@ namespace NavMeshTool
} }
Files::ConfigurationManager config; Files::ConfigurationManager config;
config.processPaths(variables, std::filesystem::current_path());
config.readConfiguration(variables, desc); config.readConfiguration(variables, desc);
Debug::setupLogging(config.getLogPath(), applicationName); Debug::setupLogging(config.getLogPath(), applicationName);
@ -189,7 +190,7 @@ namespace NavMeshTool
VFS::Manager vfs; VFS::Manager vfs;
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder()); VFS::registerArchives(&vfs, fileCollections, archives, true);
Settings::Manager::load(config); Settings::Manager::load(config);
@ -224,8 +225,7 @@ namespace NavMeshTool
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay); Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay);
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay); Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
DetourNavigator::RecastGlobalAllocator::init(); DetourNavigator::RecastGlobalAllocator::init();
DetourNavigator::Settings navigatorSettings DetourNavigator::Settings navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
= DetourNavigator::makeSettingsFromSettingsManager(Debug::getRecastMaxLogLevel());
navigatorSettings.mRecast.mSwimHeightScale navigatorSettings.mRecast.mSwimHeightScale
= EsmLoader::getGameSetting(esmData.mGameSettings, "fSwimHeightScale").getFloat(); = EsmLoader::getGameSetting(esmData.mGameSettings, "fSwimHeightScale").getFloat();

View file

@ -201,8 +201,8 @@ namespace NavMeshTool
{ {
if (!land.has_value() || osg::Vec2i(land->mX, land->mY) != cellPosition if (!land.has_value() || osg::Vec2i(land->mX, land->mY) != cellPosition
|| (land->mDataTypes & ESM::Land::DATA_VHGT) == 0) || (land->mDataTypes & ESM::Land::DATA_VHGT) == 0)
return { HeightfieldPlane{ static_cast<float>(ESM::Land::DEFAULT_HEIGHT) }, return { HeightfieldPlane{ ESM::Land::DEFAULT_HEIGHT }, ESM::Land::DEFAULT_HEIGHT,
static_cast<float>(ESM::Land::DEFAULT_HEIGHT), static_cast<float>(ESM::Land::DEFAULT_HEIGHT) }; ESM::Land::DEFAULT_HEIGHT };
ESM::Land::LandData& landData = *landDatas.emplace_back(std::make_unique<ESM::Land::LandData>()); ESM::Land::LandData& landData = *landDatas.emplace_back(std::make_unique<ESM::Land::LandData>());
land->loadData(ESM::Land::DATA_VHGT, landData); land->loadData(ESM::Land::DATA_VHGT, landData);

View file

@ -113,7 +113,7 @@ bool isBSA(const std::filesystem::path& path)
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path) std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
{ {
if (isBSA(path)) if (isBSA(path))
return VFS::makeBsaArchive(path, nullptr); return VFS::makeBsaArchive(path);
if (std::filesystem::is_directory(path)) if (std::filesystem::is_directory(path))
return std::make_unique<VFS::FileSystemArchive>(path); return std::make_unique<VFS::FileSystemArchive>(path);
return nullptr; return nullptr;
@ -198,7 +198,7 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
{ {
try try
{ {
readVFS(VFS::makeBsaArchive(file.second, nullptr), file.second, quiet); readVFS(VFS::makeBsaArchive(file.second), file.second, quiet);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -235,8 +235,8 @@ Allowed options)");
bpo::variables_map variables; bpo::variables_map variables;
try try
{ {
bpo::parsed_options validOpts = bpo::command_line_parser(argc, argv).options(desc).positional(p).run(); bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).positional(p).run();
bpo::store(validOpts, variables); bpo::store(valid_opts, variables);
bpo::notify(variables); bpo::notify(variables);
if (variables.count("help")) if (variables.count("help"))
{ {

View file

@ -88,7 +88,7 @@ opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands objectmarker cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands
) )
opencs_units (view/render opencs_units (view/render
@ -240,7 +240,11 @@ target_link_libraries(openmw-cs-lib
components_qt components_qt
) )
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::OpenGLWidgets Qt::Svg) if (QT_VERSION_MAJOR VERSION_EQUAL 6)
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::OpenGLWidgets Qt::Svg)
else()
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::Svg)
endif()
if (WIN32) if (WIN32)
target_sources(openmw-cs PRIVATE ${CMAKE_SOURCE_DIR}/files/windows/openmw-cs.exe.manifest) target_sources(openmw-cs PRIVATE ${CMAKE_SOURCE_DIR}/files/windows/openmw-cs.exe.manifest)

View file

@ -34,10 +34,12 @@
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "view/doc/viewmanager.hpp" #include "view/doc/viewmanager.hpp"
using namespace Fallback;
CS::Editor::Editor(int argc, char** argv) CS::Editor::Editor(int argc, char** argv)
: mConfigVariables(readConfiguration()) : mConfigVariables(readConfiguration())
, mSettingsState(mCfgMgr) , mSettingsState(mCfgMgr)
@ -122,10 +124,7 @@ boost::program_options::variables_map CS::Editor::readConfiguration()
->default_value(std::vector<std::string>(), "fallback-archive") ->default_value(std::vector<std::string>(), "fallback-archive")
->multitoken()); ->multitoken());
addOption("fallback", addOption("fallback",
boost::program_options::value<Fallback::FallbackMap>() boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")->multitoken()->composing(),
->default_value(Fallback::FallbackMap(), "")
->multitoken()
->composing(),
"fallback values"); "fallback values");
Files::ConfigurationManager::addCommonOptions(desc); Files::ConfigurationManager::addCommonOptions(desc);
@ -142,7 +141,7 @@ std::pair<Files::PathContainer, std::vector<std::string>> CS::Editor::readConfig
{ {
boost::program_options::variables_map& variables = mConfigVariables; boost::program_options::variables_map& variables = mConfigVariables;
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap); Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
mEncodingName = variables["encoding"].as<std::string>(); mEncodingName = variables["encoding"].as<std::string>();
mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName)); mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName));
@ -166,7 +165,7 @@ std::pair<Files::PathContainer, std::vector<std::string>> CS::Editor::readConfig
if (!local.empty()) if (!local.empty())
{ {
std::filesystem::create_directories(local); std::filesystem::create_directories(local);
dataLocal.push_back(std::move(local)); dataLocal.push_back(local);
} }
mCfgMgr.filterOutNonExistingPaths(dataDirs); mCfgMgr.filterOutNonExistingPaths(dataDirs);
mCfgMgr.filterOutNonExistingPaths(dataLocal); mCfgMgr.filterOutNonExistingPaths(dataLocal);

View file

@ -12,7 +12,7 @@
#include <apps/opencs/model/world/universalid.hpp> #include <apps/opencs/model/world/universalid.hpp>
#include <components/files/multidircollection.hpp> #include <components/files/multidircollection.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "../world/data.hpp" #include "../world/data.hpp"
#include "../world/idcompletionmanager.hpp" #include "../world/idcompletionmanager.hpp"

View file

@ -9,7 +9,7 @@
#include <vector> #include <vector>
#include <components/files/multidircollection.hpp> #include <components/files/multidircollection.hpp>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "loader.hpp" #include "loader.hpp"

View file

@ -2,8 +2,11 @@
#include <utility> #include <utility>
#if defined(Q_OS_MAC)
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#endif
#include <QProcess> #include <QProcess>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -52,17 +55,16 @@ void CSMDoc::Runner::start(bool delayed)
QString path = "openmw"; QString path = "openmw";
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
path.append(QLatin1String(".exe")); path.append(QString(".exe"));
#endif #elif defined(Q_OS_MAC)
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
// the CS and engine are in separate .app directories
dir.cdUp(); dir.cdUp();
dir.cdUp(); dir.cdUp();
dir.cdUp(); dir.cdUp();
path.prepend("OpenMW.app/Contents/MacOS/"); path = dir.absoluteFilePath(path.prepend("OpenMW.app/Contents/MacOS/"));
#else
path.prepend(QString("./"));
#endif #endif
path = dir.absoluteFilePath(path);
mStartup = new QTemporaryFile(this); mStartup = new QTemporaryFile(this);
mStartup->open(); mStartup->open();

View file

@ -3,7 +3,7 @@
#include <QObject> #include <QObject>
#include <components/toutf8/toutf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "operation.hpp" #include "operation.hpp"
#include "savingstate.hpp" #include "savingstate.hpp"

Some files were not shown because too many files have changed in this diff Show more