mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 16:15:31 +00:00
Merge remote-tracking branch 'upstream/master' into why_are_the_christmas_lights_still_up
This commit is contained in:
commit
582f7b52cf
111 changed files with 1756 additions and 1205 deletions
|
@ -30,6 +30,29 @@ stages:
|
||||||
paths:
|
paths:
|
||||||
- build/install/
|
- build/install/
|
||||||
|
|
||||||
|
Coverity:
|
||||||
|
extends: .Debian
|
||||||
|
only:
|
||||||
|
- schedules
|
||||||
|
before_script:
|
||||||
|
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
||||||
|
- curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
|
||||||
|
- tar xfz /tmp/cov-analysis-linux64.tgz
|
||||||
|
script:
|
||||||
|
- CI/before_script.linux.sh
|
||||||
|
- cd build
|
||||||
|
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build . -- -j $(nproc)
|
||||||
|
after_script:
|
||||||
|
- tar cfz cov-int.tar.gz cov-int
|
||||||
|
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME \
|
||||||
|
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL \
|
||||||
|
--form file=@cov-int.tar.gz --form version="`git describe --tags`" \
|
||||||
|
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID
|
||||||
|
variables:
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
timeout: 2h
|
||||||
|
|
||||||
Debian_GCC:
|
Debian_GCC:
|
||||||
extends: .Debian
|
extends: .Debian
|
||||||
cache:
|
cache:
|
||||||
|
@ -94,6 +117,7 @@ Debian_Clang_tests:
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
||||||
MacOS:
|
MacOS:
|
||||||
|
image: macos-11-xcode-12
|
||||||
tags:
|
tags:
|
||||||
- macos
|
- macos
|
||||||
stage: build
|
stage: build
|
||||||
|
@ -104,13 +128,17 @@ MacOS:
|
||||||
- rm -fr build/* # remove anything in the build directory
|
- rm -fr build/* # remove anything in the build directory
|
||||||
- CI/before_install.osx.sh
|
- CI/before_install.osx.sh
|
||||||
- CI/before_script.osx.sh
|
- CI/before_script.osx.sh
|
||||||
- cd build; make -j2 package
|
- cd build; make -j $(sysctl -n hw.logicalcpu) package
|
||||||
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done
|
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/OpenMW-*.dmg
|
- build/OpenMW-*.dmg
|
||||||
- "build/**/*.log"
|
- "build/**/*.log"
|
||||||
|
|
||||||
|
macOS10.15_Xcode11:
|
||||||
|
extends: MacOS
|
||||||
|
image: macos-10.15-xcode-11
|
||||||
|
|
||||||
variables: &engine-targets
|
variables: &engine-targets
|
||||||
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
|
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
|
||||||
package: "Engine"
|
package: "Engine"
|
||||||
|
|
|
@ -10,7 +10,7 @@ addons:
|
||||||
- sourceline: 'ppa:openmw/openmw'
|
- sourceline: 'ppa:openmw/openmw'
|
||||||
packages: [
|
packages: [
|
||||||
# Dev
|
# Dev
|
||||||
build-essential, cmake, clang-tools, ccache,
|
build-essential, cmake, clang-tools-9, ccache,
|
||||||
# Boost
|
# Boost
|
||||||
libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,
|
libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
|
@ -37,8 +37,8 @@ matrix:
|
||||||
os: linux
|
os: linux
|
||||||
dist: focal
|
dist: focal
|
||||||
env:
|
env:
|
||||||
- MATRIX_EVAL="CC=clang && CXX=clang++"
|
- MATRIX_EVAL="CC=clang-9 && CXX=clang++-9"
|
||||||
- ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++"
|
- ANALYZE="scan-build-9 --force-analyze-debug-code --use-cc clang-9 --use-c++ clang++-9"
|
||||||
compiler: clang
|
compiler: clang
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
|
@ -66,11 +66,13 @@ deploy:
|
||||||
repo: OpenMW/openmw
|
repo: OpenMW/openmw
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
|
if: repository_slug = OpenMW/openmw AND branch = master
|
||||||
recipients:
|
recipients:
|
||||||
- corrmage+travis-ci@gmail.com
|
- corrmage+travis-ci@gmail.com
|
||||||
on_success: change
|
on_success: change
|
||||||
on_failure: always
|
on_failure: always
|
||||||
irc:
|
irc:
|
||||||
|
if: repository_slug = OpenMW/openmw AND branch = master
|
||||||
channels:
|
channels:
|
||||||
- "chat.freenode.net#openmw"
|
- "chat.freenode.net#openmw"
|
||||||
on_success: change
|
on_success: change
|
||||||
|
|
|
@ -185,6 +185,7 @@ Programmers
|
||||||
sergoz
|
sergoz
|
||||||
ShadowRadiance
|
ShadowRadiance
|
||||||
Siimacore
|
Siimacore
|
||||||
|
Simon Meulenbeek (simonmb)
|
||||||
sir_herrbatka
|
sir_herrbatka
|
||||||
smbas
|
smbas
|
||||||
Sophie Kirschner (pineapplemachine)
|
Sophie Kirschner (pineapplemachine)
|
||||||
|
@ -198,6 +199,7 @@ Programmers
|
||||||
Sylvain Thesnieres (Garvek)
|
Sylvain Thesnieres (Garvek)
|
||||||
t6
|
t6
|
||||||
terrorfisch
|
terrorfisch
|
||||||
|
Tess (tescoShoppah)
|
||||||
thegriglat
|
thegriglat
|
||||||
Thomas Luppi (Digmaster)
|
Thomas Luppi (Digmaster)
|
||||||
tlmullis
|
tlmullis
|
||||||
|
|
|
@ -115,6 +115,8 @@
|
||||||
Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs
|
Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs
|
||||||
Bug #5912: ImprovedBound mod doesn't work
|
Bug #5912: ImprovedBound mod doesn't work
|
||||||
Bug #5914: BM: The Swimmer can't reach destination
|
Bug #5914: BM: The Swimmer can't reach destination
|
||||||
|
Bug #5923: Clicking on empty spaces between journal entries might show random topics
|
||||||
|
Bug #5934: AddItem command doesn't accept negative values
|
||||||
Feature #390: 3rd person look "over the shoulder"
|
Feature #390: 3rd person look "over the shoulder"
|
||||||
Feature #832: OpenMW-CS: Handle deleted references
|
Feature #832: OpenMW-CS: Handle deleted references
|
||||||
Feature #1536: Show more information about level on menu
|
Feature #1536: Show more information about level on menu
|
||||||
|
@ -122,6 +124,7 @@
|
||||||
Feature #2404: Levelled List can not be placed into a container
|
Feature #2404: Levelled List can not be placed into a container
|
||||||
Feature #2686: Timestamps in openmw.log
|
Feature #2686: Timestamps in openmw.log
|
||||||
Feature #3171: OpenMW-CS: Instance drag selection
|
Feature #3171: OpenMW-CS: Instance drag selection
|
||||||
|
Feature #3983: Wizard: Add link to buy Morrowind
|
||||||
Feature #4894: Consider actors as obstacles for pathfinding
|
Feature #4894: Consider actors as obstacles for pathfinding
|
||||||
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
|
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
|
||||||
Feature #4977: Use the "default icon.tga" when an item's icon is not found
|
Feature #4977: Use the "default icon.tga" when an item's icon is not found
|
||||||
|
@ -133,6 +136,7 @@
|
||||||
Feature #5456: Basic collada animation support
|
Feature #5456: Basic collada animation support
|
||||||
Feature #5457: Realistic diagonal movement
|
Feature #5457: Realistic diagonal movement
|
||||||
Feature #5486: Fixes trainers to choose their training skills based on their base skill points
|
Feature #5486: Fixes trainers to choose their training skills based on their base skill points
|
||||||
|
Feature #5511: Add in game option to toggle HRTF support in OpenMW
|
||||||
Feature #5519: Code Patch tab in launcher
|
Feature #5519: Code Patch tab in launcher
|
||||||
Feature #5524: Resume failed script execution after reload
|
Feature #5524: Resume failed script execution after reload
|
||||||
Feature #5545: Option to allow stealing from an unconscious NPC during combat
|
Feature #5545: Option to allow stealing from an unconscious NPC during combat
|
||||||
|
@ -147,6 +151,7 @@
|
||||||
Feature #5730: Add graphic herbalism option to the launcher and documents
|
Feature #5730: Add graphic herbalism option to the launcher and documents
|
||||||
Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.
|
Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.
|
||||||
Feature #5813: Instanced groundcover support
|
Feature #5813: Instanced groundcover support
|
||||||
|
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
|
||||||
Feature #5828: Support more than 8 lights
|
Feature #5828: Support more than 8 lights
|
||||||
Feature #5910: Fall back to delta time when physics can't keep up
|
Feature #5910: Fall back to delta time when physics can't keep up
|
||||||
Task #5480: Drop Qt4 support
|
Task #5480: Drop Qt4 support
|
||||||
|
|
|
@ -16,7 +16,7 @@ cmake \
|
||||||
-D CMAKE_CXX_FLAGS="-stdlib=libc++" \
|
-D CMAKE_CXX_FLAGS="-stdlib=libc++" \
|
||||||
-D CMAKE_C_FLAGS_RELEASE="-g -O0" \
|
-D CMAKE_C_FLAGS_RELEASE="-g -O0" \
|
||||||
-D CMAKE_CXX_FLAGS_RELEASE="-g -O0" \
|
-D CMAKE_CXX_FLAGS_RELEASE="-g -O0" \
|
||||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.12" \
|
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.14" \
|
||||||
-D CMAKE_BUILD_TYPE=RELEASE \
|
-D CMAKE_BUILD_TYPE=RELEASE \
|
||||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||||
-D BUILD_OPENMW=TRUE \
|
-D BUILD_OPENMW=TRUE \
|
||||||
|
|
|
@ -20,6 +20,7 @@ struct Arguments
|
||||||
std::string mode;
|
std::string mode;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
std::string extractfile;
|
std::string extractfile;
|
||||||
|
std::string addfile;
|
||||||
std::string outdir;
|
std::string outdir;
|
||||||
|
|
||||||
bool longformat;
|
bool longformat;
|
||||||
|
@ -36,6 +37,10 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
||||||
" Extract a file from the input archive.\n\n"
|
" Extract a file from the input archive.\n\n"
|
||||||
" bsatool extractall archivefile [output_directory]\n"
|
" bsatool extractall archivefile [output_directory]\n"
|
||||||
" Extract all files from the input archive.\n\n"
|
" Extract all files from the input archive.\n\n"
|
||||||
|
" bsatool add [-a] archivefile file_to_add\n"
|
||||||
|
" Add a file to the input archive.\n\n"
|
||||||
|
" bsatool create [-c] archivefile\n"
|
||||||
|
" Create an archive.\n\n"
|
||||||
"Allowed options");
|
"Allowed options");
|
||||||
|
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
|
@ -95,7 +100,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
||||||
}
|
}
|
||||||
|
|
||||||
info.mode = variables["mode"].as<std::string>();
|
info.mode = variables["mode"].as<std::string>();
|
||||||
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall"))
|
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall" || info.mode == "add" || info.mode == "create"))
|
||||||
{
|
{
|
||||||
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n"
|
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n"
|
||||||
<< desc << std::endl;
|
<< desc << std::endl;
|
||||||
|
@ -126,6 +131,17 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
||||||
if (variables["input-file"].as< std::vector<std::string> >().size() > 2)
|
if (variables["input-file"].as< std::vector<std::string> >().size() > 2)
|
||||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2];
|
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2];
|
||||||
}
|
}
|
||||||
|
else if (info.mode == "add")
|
||||||
|
{
|
||||||
|
if (variables["input-file"].as< std::vector<std::string> >().size() < 1)
|
||||||
|
{
|
||||||
|
std::cout << "\nERROR: file to add unspecified\n\n"
|
||||||
|
<< desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||||
|
info.addfile = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||||
|
}
|
||||||
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
|
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||||
|
|
||||||
|
@ -138,6 +154,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
||||||
int list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
int list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
||||||
int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
||||||
int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
||||||
|
int add(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
@ -157,6 +174,12 @@ int main(int argc, char** argv)
|
||||||
else
|
else
|
||||||
bsa = std::make_unique<Bsa::BSAFile>(Bsa::BSAFile());
|
bsa = std::make_unique<Bsa::BSAFile>(Bsa::BSAFile());
|
||||||
|
|
||||||
|
if (info.mode == "create")
|
||||||
|
{
|
||||||
|
bsa->open(info.filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bsa->open(info.filename);
|
bsa->open(info.filename);
|
||||||
|
|
||||||
if (info.mode == "list")
|
if (info.mode == "list")
|
||||||
|
@ -165,6 +188,8 @@ int main(int argc, char** argv)
|
||||||
return extract(bsa, info);
|
return extract(bsa, info);
|
||||||
else if (info.mode == "extractall")
|
else if (info.mode == "extractall")
|
||||||
return extractAll(bsa, info);
|
return extractAll(bsa, info);
|
||||||
|
else if (info.mode == "add")
|
||||||
|
return add(bsa, info);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
|
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
|
||||||
|
@ -188,13 +213,13 @@ int list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||||
{
|
{
|
||||||
// Long format
|
// Long format
|
||||||
std::ios::fmtflags f(std::cout.flags());
|
std::ios::fmtflags f(std::cout.flags());
|
||||||
std::cout << std::setw(50) << std::left << file.name;
|
std::cout << std::setw(50) << std::left << file.name();
|
||||||
std::cout << std::setw(8) << std::left << std::dec << file.fileSize;
|
std::cout << std::setw(8) << std::left << std::dec << file.fileSize;
|
||||||
std::cout << "@ 0x" << std::hex << file.offset << std::endl;
|
std::cout << "@ 0x" << std::hex << file.offset << std::endl;
|
||||||
std::cout.flags(f);
|
std::cout.flags(f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
std::cout << file.name << std::endl;
|
std::cout << file.name() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -253,7 +278,7 @@ int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||||
{
|
{
|
||||||
for (const auto &file : bsa->getList())
|
for (const auto &file : bsa->getList())
|
||||||
{
|
{
|
||||||
std::string extractPath(file.name);
|
std::string extractPath(file.name());
|
||||||
Misc::StringUtils::replaceAll(extractPath, "\\", "/");
|
Misc::StringUtils::replaceAll(extractPath, "\\", "/");
|
||||||
|
|
||||||
// Get the target path (the path the file will be extracted to)
|
// Get the target path (the path the file will be extracted to)
|
||||||
|
@ -272,7 +297,7 @@ int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||||
|
|
||||||
// Get a stream for the file to extract
|
// Get a stream for the file to extract
|
||||||
// (inefficient because getFile iter on the list again)
|
// (inefficient because getFile iter on the list again)
|
||||||
Files::IStreamPtr data = bsa->getFile(file.name);
|
Files::IStreamPtr data = bsa->getFile(file.name());
|
||||||
bfs::ofstream out(target, std::ios::binary);
|
bfs::ofstream out(target, std::ios::binary);
|
||||||
|
|
||||||
// Write the file to disk
|
// Write the file to disk
|
||||||
|
@ -283,3 +308,11 @@ int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int add(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
|
||||||
|
{
|
||||||
|
boost::filesystem::fstream stream(info.addfile, std::ios_base::binary | std::ios_base::out | std::ios_base::in);
|
||||||
|
bsa->addFile(info.addfile, stream);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ set(LAUNCHER
|
||||||
utils/profilescombobox.cpp
|
utils/profilescombobox.cpp
|
||||||
utils/textinputdialog.cpp
|
utils/textinputdialog.cpp
|
||||||
utils/lineedit.cpp
|
utils/lineedit.cpp
|
||||||
|
utils/openalutil.cpp
|
||||||
|
|
||||||
${CMAKE_SOURCE_DIR}/files/windows/launcher.rc
|
${CMAKE_SOURCE_DIR}/files/windows/launcher.rc
|
||||||
)
|
)
|
||||||
|
@ -31,6 +32,7 @@ set(LAUNCHER_HEADER
|
||||||
utils/profilescombobox.hpp
|
utils/profilescombobox.hpp
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
utils/lineedit.hpp
|
utils/lineedit.hpp
|
||||||
|
utils/openalutil.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Headers that must be pre-processed
|
# Headers that must be pre-processed
|
||||||
|
@ -47,6 +49,7 @@ set(LAUNCHER_HEADER_MOC
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
utils/profilescombobox.hpp
|
utils/profilescombobox.hpp
|
||||||
utils/lineedit.hpp
|
utils/lineedit.hpp
|
||||||
|
utils/openalutil.hpp
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -95,6 +98,7 @@ endif (WIN32)
|
||||||
|
|
||||||
target_link_libraries(openmw-launcher
|
target_link_libraries(openmw-launcher
|
||||||
${SDL2_LIBRARY_ONLY}
|
${SDL2_LIBRARY_ONLY}
|
||||||
|
${OPENAL_LIBRARY}
|
||||||
components
|
components
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,14 @@
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QCompleter>
|
#include <QCompleter>
|
||||||
#include <QProxyStyle>
|
#include <QProxyStyle>
|
||||||
|
#include <QString>
|
||||||
#include <components/contentselector/view/contentselector.hpp>
|
#include <components/contentselector/view/contentselector.hpp>
|
||||||
#include <components/contentselector/model/esmfile.hpp>
|
#include <components/contentselector/model/esmfile.hpp>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "utils/openalutil.hpp"
|
||||||
|
|
||||||
Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings,
|
Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings,
|
||||||
Settings::Manager &engineSettings, QWidget *parent)
|
Settings::Manager &engineSettings, QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
|
@ -21,7 +24,17 @@ Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings,
|
||||||
setObjectName ("AdvancedPage");
|
setObjectName ("AdvancedPage");
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
|
for(const char * name : Launcher::enumerateOpenALDevices())
|
||||||
|
{
|
||||||
|
audioDeviceSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));
|
||||||
|
}
|
||||||
|
for(const char * name : Launcher::enumerateOpenALDevicesHrtf())
|
||||||
|
{
|
||||||
|
hrtfProfileSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));
|
||||||
|
}
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
mCellNameCompleter.setModel(&mCellNameCompleterModel);
|
mCellNameCompleter.setModel(&mCellNameCompleterModel);
|
||||||
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
|
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
|
||||||
}
|
}
|
||||||
|
@ -135,6 +148,34 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
{
|
||||||
|
std::string selectedAudioDevice = mEngineSettings.getString("device", "Sound");
|
||||||
|
if (selectedAudioDevice.empty() == false)
|
||||||
|
{
|
||||||
|
int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice));
|
||||||
|
if (audioDeviceIndex != -1)
|
||||||
|
{
|
||||||
|
audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int hrtfEnabledIndex = mEngineSettings.getInt("hrtf enable", "Sound");
|
||||||
|
if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1)
|
||||||
|
{
|
||||||
|
enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1);
|
||||||
|
}
|
||||||
|
std::string selectedHRTFProfile = mEngineSettings.getString("hrtf", "Sound");
|
||||||
|
if (selectedHRTFProfile.empty() == false)
|
||||||
|
{
|
||||||
|
int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile));
|
||||||
|
if (hrtfProfileIndex != -1)
|
||||||
|
{
|
||||||
|
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
{
|
{
|
||||||
loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera");
|
loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera");
|
||||||
|
@ -259,6 +300,33 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
static std::array<std::string, 3> lightingMethodMap = {"legacy", "shaders compatibility", "shaders"};
|
static std::array<std::string, 3> lightingMethodMap = {"legacy", "shaders compatibility", "shaders"};
|
||||||
mEngineSettings.setString("lighting method", "Shaders", lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
mEngineSettings.setString("lighting method", "Shaders", lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
{
|
||||||
|
int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex();
|
||||||
|
if (audioDeviceIndex != 0)
|
||||||
|
{
|
||||||
|
mEngineSettings.setString("device", "Sound", audioDeviceSelectorComboBox->currentText().toUtf8().constData());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mEngineSettings.setString("device", "Sound", "");
|
||||||
|
}
|
||||||
|
int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1;
|
||||||
|
if (hrtfEnabledIndex != mEngineSettings.getInt("hrtf enable", "Sound"))
|
||||||
|
{
|
||||||
|
mEngineSettings.setInt("hrtf enable", "Sound", hrtfEnabledIndex);
|
||||||
|
}
|
||||||
|
int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex();
|
||||||
|
if (selectedHRTFProfileIndex != 0)
|
||||||
|
{
|
||||||
|
mEngineSettings.setString("hrtf", "Sound", hrtfProfileSelectorComboBox->currentText().toUtf8().constData());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mEngineSettings.setString("hrtf", "Sound", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
QString getAspect(int x, int y)
|
QString getAspect(int x, int y)
|
||||||
{
|
{
|
||||||
int gcd = std::gcd (x, y);
|
int gcd = std::gcd (x, y);
|
||||||
|
if (gcd == 0)
|
||||||
|
return QString();
|
||||||
|
|
||||||
int xaspect = x / gcd;
|
int xaspect = x / gcd;
|
||||||
int yaspect = y / gcd;
|
int yaspect = y / gcd;
|
||||||
// special case: 8 : 5 is usually referred to as 16:10
|
// special case: 8 : 5 is usually referred to as 16:10
|
||||||
|
@ -298,9 +301,9 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString aspect = getAspect(mode.w, mode.h);
|
|
||||||
QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h);
|
QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h);
|
||||||
|
|
||||||
|
QString aspect = getAspect(mode.w, mode.h);
|
||||||
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
|
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
|
||||||
resolution.append(tr("\t(Wide ") + aspect + ")");
|
resolution.append(tr("\t(Wide ") + aspect + ")");
|
||||||
|
|
||||||
|
|
55
apps/launcher/utils/openalutil.cpp
Normal file
55
apps/launcher/utils/openalutil.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <apps/openmw/mwsound/alext.h>
|
||||||
|
|
||||||
|
#include "openalutil.hpp"
|
||||||
|
|
||||||
|
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||||
|
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<const char *> Launcher::enumerateOpenALDevices()
|
||||||
|
{
|
||||||
|
std::vector<const char *> devlist;
|
||||||
|
const ALCchar *devnames;
|
||||||
|
|
||||||
|
if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"))
|
||||||
|
{
|
||||||
|
devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(devnames && *devnames)
|
||||||
|
{
|
||||||
|
devlist.emplace_back(devnames);
|
||||||
|
devnames += strlen(devnames)+1;
|
||||||
|
}
|
||||||
|
return devlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const char *> Launcher::enumerateOpenALDevicesHrtf()
|
||||||
|
{
|
||||||
|
std::vector<const char *> ret;
|
||||||
|
|
||||||
|
ALCdevice *device = alcOpenDevice(nullptr);
|
||||||
|
if(device && alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
|
||||||
|
{
|
||||||
|
LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
|
||||||
|
void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT");
|
||||||
|
memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));
|
||||||
|
ALCint num_hrtf;
|
||||||
|
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
|
||||||
|
ret.reserve(num_hrtf);
|
||||||
|
for(ALCint i = 0;i < num_hrtf && i < 20;++i)
|
||||||
|
{
|
||||||
|
const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
|
||||||
|
ret.emplace_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
7
apps/launcher/utils/openalutil.hpp
Normal file
7
apps/launcher/utils/openalutil.hpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Launcher
|
||||||
|
{
|
||||||
|
std::vector<const char *> enumerateOpenALDevices();
|
||||||
|
std::vector<const char *> enumerateOpenALDevicesHrtf();
|
||||||
|
}
|
|
@ -56,7 +56,9 @@ void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData
|
||||||
|
|
||||||
|
|
||||||
CSMWorld::IngredientColumns::IngredientColumns (const InventoryColumns& columns)
|
CSMWorld::IngredientColumns::IngredientColumns (const InventoryColumns& columns)
|
||||||
: InventoryColumns (columns) {}
|
: InventoryColumns (columns)
|
||||||
|
, mEffects(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
CSMWorld::IngredientRefIdAdapter::IngredientRefIdAdapter (const IngredientColumns& columns)
|
CSMWorld::IngredientRefIdAdapter::IngredientRefIdAdapter (const IngredientColumns& columns)
|
||||||
: InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, columns),
|
: InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, columns),
|
||||||
|
@ -585,7 +587,13 @@ void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns)
|
CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns)
|
||||||
: InventoryColumns (columns) {}
|
: InventoryColumns (columns)
|
||||||
|
, mTime(nullptr)
|
||||||
|
, mRadius(nullptr)
|
||||||
|
, mColor(nullptr)
|
||||||
|
, mSound(nullptr)
|
||||||
|
, mEmitterType(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns)
|
CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns)
|
||||||
: InventoryRefIdAdapter<ESM::Light> (UniversalId::Type_Light, columns), mColumns (columns)
|
: InventoryRefIdAdapter<ESM::Light> (UniversalId::Type_Light, columns), mColumns (columns)
|
||||||
|
@ -1454,7 +1462,15 @@ int CSMWorld::CreatureMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *co
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns)
|
CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns)
|
||||||
: EnchantableColumns (columns) {}
|
: EnchantableColumns (columns)
|
||||||
|
, mType(nullptr)
|
||||||
|
, mHealth(nullptr)
|
||||||
|
, mSpeed(nullptr)
|
||||||
|
, mReach(nullptr)
|
||||||
|
, mChop{nullptr}
|
||||||
|
, mSlash{nullptr}
|
||||||
|
, mThrust{nullptr}
|
||||||
|
{}
|
||||||
|
|
||||||
CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns)
|
CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns)
|
||||||
: EnchantableRefIdAdapter<ESM::Weapon> (UniversalId::Type_Weapon, columns), mColumns (columns)
|
: EnchantableRefIdAdapter<ESM::Weapon> (UniversalId::Type_Weapon, columns), mColumns (columns)
|
||||||
|
|
|
@ -178,7 +178,11 @@ namespace CSMWorld
|
||||||
const RefIdColumn *mName;
|
const RefIdColumn *mName;
|
||||||
const RefIdColumn *mScript;
|
const RefIdColumn *mScript;
|
||||||
|
|
||||||
NameColumns (const ModelColumns& base) : ModelColumns (base) {}
|
NameColumns (const ModelColumns& base)
|
||||||
|
: ModelColumns (base)
|
||||||
|
, mName(nullptr)
|
||||||
|
, mScript(nullptr)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Adapter for IDs with names (all but levelled lists and statics)
|
/// \brief Adapter for IDs with names (all but levelled lists and statics)
|
||||||
|
@ -247,7 +251,12 @@ namespace CSMWorld
|
||||||
const RefIdColumn *mWeight;
|
const RefIdColumn *mWeight;
|
||||||
const RefIdColumn *mValue;
|
const RefIdColumn *mValue;
|
||||||
|
|
||||||
InventoryColumns (const NameColumns& base) : NameColumns (base) {}
|
InventoryColumns (const NameColumns& base)
|
||||||
|
: NameColumns (base)
|
||||||
|
, mIcon(nullptr)
|
||||||
|
, mWeight(nullptr)
|
||||||
|
, mValue(nullptr)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Adapter for IDs that can go into an inventory
|
/// \brief Adapter for IDs that can go into an inventory
|
||||||
|
@ -405,7 +414,11 @@ namespace CSMWorld
|
||||||
const RefIdColumn *mEnchantment;
|
const RefIdColumn *mEnchantment;
|
||||||
const RefIdColumn *mEnchantmentPoints;
|
const RefIdColumn *mEnchantmentPoints;
|
||||||
|
|
||||||
EnchantableColumns (const InventoryColumns& base) : InventoryColumns (base) {}
|
EnchantableColumns (const InventoryColumns& base)
|
||||||
|
: InventoryColumns (base)
|
||||||
|
, mEnchantment(nullptr)
|
||||||
|
, mEnchantmentPoints(nullptr)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Adapter for enchantable IDs
|
/// \brief Adapter for enchantable IDs
|
||||||
|
@ -474,7 +487,11 @@ namespace CSMWorld
|
||||||
const RefIdColumn *mQuality;
|
const RefIdColumn *mQuality;
|
||||||
const RefIdColumn *mUses;
|
const RefIdColumn *mUses;
|
||||||
|
|
||||||
ToolColumns (const InventoryColumns& base) : InventoryColumns (base) {}
|
ToolColumns (const InventoryColumns& base)
|
||||||
|
: InventoryColumns (base)
|
||||||
|
, mQuality(nullptr)
|
||||||
|
, mUses(nullptr)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes)
|
/// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes)
|
||||||
|
@ -549,7 +566,17 @@ namespace CSMWorld
|
||||||
const RefIdColumn *mAiPackages;
|
const RefIdColumn *mAiPackages;
|
||||||
std::map<const RefIdColumn *, unsigned int> mServices;
|
std::map<const RefIdColumn *, unsigned int> mServices;
|
||||||
|
|
||||||
ActorColumns (const NameColumns& base) : NameColumns (base) {}
|
ActorColumns (const NameColumns& base)
|
||||||
|
: NameColumns (base)
|
||||||
|
, mHello(nullptr)
|
||||||
|
, mFlee(nullptr)
|
||||||
|
, mFight(nullptr)
|
||||||
|
, mAlarm(nullptr)
|
||||||
|
, mInventory(nullptr)
|
||||||
|
, mSpells(nullptr)
|
||||||
|
, mDestinations(nullptr)
|
||||||
|
, mAiPackages(nullptr)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Adapter for actor IDs (handles common AI functionality)
|
/// \brief Adapter for actor IDs (handles common AI functionality)
|
||||||
|
@ -2054,7 +2081,11 @@ namespace CSMWorld
|
||||||
const RefIdColumn *mLevList;
|
const RefIdColumn *mLevList;
|
||||||
const RefIdColumn *mNestedListLevList;
|
const RefIdColumn *mNestedListLevList;
|
||||||
|
|
||||||
LevListColumns (const BaseColumns& base) : BaseColumns (base) {}
|
LevListColumns (const BaseColumns& base)
|
||||||
|
: BaseColumns (base)
|
||||||
|
, mLevList(nullptr)
|
||||||
|
, mNestedListLevList(nullptr)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
|
|
|
@ -33,26 +33,6 @@ namespace Compiler
|
||||||
class Context;
|
class Context;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWScript
|
|
||||||
{
|
|
||||||
class ScriptManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWSound
|
|
||||||
{
|
|
||||||
class SoundManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWWorld
|
|
||||||
{
|
|
||||||
class World;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Files
|
namespace Files
|
||||||
{
|
{
|
||||||
struct ConfigurationManager;
|
struct ConfigurationManager;
|
||||||
|
|
|
@ -32,7 +32,6 @@ namespace MyGUI
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
struct Class;
|
|
||||||
class ESMReader;
|
class ESMReader;
|
||||||
class ESMWriter;
|
class ESMWriter;
|
||||||
struct CellId;
|
struct CellId;
|
||||||
|
|
|
@ -281,13 +281,13 @@ namespace MWBase
|
||||||
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
|
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
|
||||||
virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0;
|
virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0;
|
||||||
|
|
||||||
virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool moveToActive=false) = 0;
|
virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) = 0;
|
||||||
///< @return an updated Ptr in case the Ptr's cell changes
|
///< @return an updated Ptr in case the Ptr's cell changes
|
||||||
|
|
||||||
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;
|
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;
|
||||||
///< @return an updated Ptr
|
///< @return an updated Ptr
|
||||||
|
|
||||||
virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec) = 0;
|
virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec, bool moveToActive) = 0;
|
||||||
///< @return an updated Ptr
|
///< @return an updated Ptr
|
||||||
|
|
||||||
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
|
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
|
||||||
|
@ -607,7 +607,7 @@ namespace MWBase
|
||||||
|
|
||||||
/// Return a vector aiming the actor's weapon towards a target.
|
/// Return a vector aiming the actor's weapon towards a target.
|
||||||
/// @note The length of the vector is the distance between actor and target.
|
/// @note The length of the vector is the distance between actor and target.
|
||||||
virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;
|
virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) = 0;
|
||||||
|
|
||||||
/// Return the distance between actor's weapon and target's collision box.
|
/// Return the distance between actor's weapon and target's collision box.
|
||||||
virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;
|
virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;
|
||||||
|
|
|
@ -46,11 +46,6 @@ namespace MWClass
|
||||||
mStore.readState(inventory);
|
mStore.readState(inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::CustomData *ContainerCustomData::clone() const
|
|
||||||
{
|
|
||||||
return new ContainerCustomData (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
ContainerCustomData& ContainerCustomData::asContainerCustomData()
|
ContainerCustomData& ContainerCustomData::asContainerCustomData()
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -72,7 +67,7 @@ namespace MWClass
|
||||||
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
|
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
|
||||||
|
|
||||||
// store
|
// store
|
||||||
ptr.getRefData().setCustomData (std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()).release());
|
ptr.getRefData().setCustomData (std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()));
|
||||||
|
|
||||||
MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());
|
MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());
|
||||||
}
|
}
|
||||||
|
@ -317,7 +312,7 @@ namespace MWClass
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ESM::ContainerState& containerState = state.asContainerState();
|
const ESM::ContainerState& containerState = state.asContainerState();
|
||||||
ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory).release());
|
ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
|
void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
|
||||||
|
|
|
@ -13,15 +13,13 @@ namespace ESM
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
class ContainerCustomData : public MWWorld::CustomData
|
class ContainerCustomData : public MWWorld::TypedCustomData<ContainerCustomData>
|
||||||
{
|
{
|
||||||
MWWorld::ContainerStore mStore;
|
MWWorld::ContainerStore mStore;
|
||||||
public:
|
public:
|
||||||
ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell);
|
ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell);
|
||||||
ContainerCustomData(const ESM::InventoryState& inventory);
|
ContainerCustomData(const ESM::InventoryState& inventory);
|
||||||
|
|
||||||
MWWorld::CustomData *clone() const override;
|
|
||||||
|
|
||||||
ContainerCustomData& asContainerCustomData() override;
|
ContainerCustomData& asContainerCustomData() override;
|
||||||
const ContainerCustomData& asContainerCustomData() const override;
|
const ContainerCustomData& asContainerCustomData() const override;
|
||||||
|
|
||||||
|
|
|
@ -51,14 +51,16 @@ namespace
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
|
|
||||||
class CreatureCustomData : public MWWorld::CustomData
|
class CreatureCustomData : public MWWorld::TypedCustomData<CreatureCustomData>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MWMechanics::CreatureStats mCreatureStats;
|
MWMechanics::CreatureStats mCreatureStats;
|
||||||
MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures
|
std::unique_ptr<MWWorld::ContainerStore> mContainerStore; // may be InventoryStore for some creatures
|
||||||
MWMechanics::Movement mMovement;
|
MWMechanics::Movement mMovement;
|
||||||
|
|
||||||
MWWorld::CustomData *clone() const override;
|
CreatureCustomData() = default;
|
||||||
|
CreatureCustomData(const CreatureCustomData& other);
|
||||||
|
CreatureCustomData(CreatureCustomData&& other) noexcept = default;
|
||||||
|
|
||||||
CreatureCustomData& asCreatureCustomData() override
|
CreatureCustomData& asCreatureCustomData() override
|
||||||
{
|
{
|
||||||
|
@ -68,16 +70,13 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureCustomData() : mContainerStore(nullptr) {}
|
|
||||||
virtual ~CreatureCustomData() { delete mContainerStore; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MWWorld::CustomData *CreatureCustomData::clone() const
|
CreatureCustomData::CreatureCustomData(const CreatureCustomData& other)
|
||||||
|
: mCreatureStats(other.mCreatureStats),
|
||||||
|
mContainerStore(other.mContainerStore->clone()),
|
||||||
|
mMovement(other.mMovement)
|
||||||
{
|
{
|
||||||
CreatureCustomData* cloned = new CreatureCustomData (*this);
|
|
||||||
cloned->mContainerStore = mContainerStore->clone();
|
|
||||||
return cloned;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Creature::GMST& Creature::getGmst()
|
const Creature::GMST& Creature::getGmst()
|
||||||
|
@ -148,16 +147,16 @@ namespace MWClass
|
||||||
// inventory
|
// inventory
|
||||||
bool hasInventory = hasInventoryStore(ptr);
|
bool hasInventory = hasInventoryStore(ptr);
|
||||||
if (hasInventory)
|
if (hasInventory)
|
||||||
data->mContainerStore = new MWWorld::InventoryStore();
|
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
|
||||||
else
|
else
|
||||||
data->mContainerStore = new MWWorld::ContainerStore();
|
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
|
||||||
|
|
||||||
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
|
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
|
||||||
|
|
||||||
data->mCreatureStats.setNeedRecalcDynamicStats(false);
|
data->mCreatureStats.setNeedRecalcDynamicStats(false);
|
||||||
|
|
||||||
// store
|
// store
|
||||||
ptr.getRefData().setCustomData(data.release());
|
ptr.getRefData().setCustomData(std::move(data));
|
||||||
|
|
||||||
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
|
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
|
||||||
|
|
||||||
|
@ -758,11 +757,11 @@ namespace MWClass
|
||||||
std::unique_ptr<CreatureCustomData> data (new CreatureCustomData);
|
std::unique_ptr<CreatureCustomData> data (new CreatureCustomData);
|
||||||
|
|
||||||
if (hasInventoryStore(ptr))
|
if (hasInventoryStore(ptr))
|
||||||
data->mContainerStore = new MWWorld::InventoryStore();
|
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
|
||||||
else
|
else
|
||||||
data->mContainerStore = new MWWorld::ContainerStore();
|
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
|
||||||
|
|
||||||
ptr.getRefData().setCustomData (data.release());
|
ptr.getRefData().setCustomData (std::move(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -10,15 +10,13 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
class CreatureLevListCustomData : public MWWorld::CustomData
|
class CreatureLevListCustomData : public MWWorld::TypedCustomData<CreatureLevListCustomData>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// actorId of the creature we spawned
|
// actorId of the creature we spawned
|
||||||
int mSpawnActorId;
|
int mSpawnActorId;
|
||||||
bool mSpawn; // Should a new creature be spawned?
|
bool mSpawn; // Should a new creature be spawned?
|
||||||
|
|
||||||
MWWorld::CustomData *clone() const override;
|
|
||||||
|
|
||||||
CreatureLevListCustomData& asCreatureLevListCustomData() override
|
CreatureLevListCustomData& asCreatureLevListCustomData() override
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -29,11 +27,6 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MWWorld::CustomData *CreatureLevListCustomData::clone() const
|
|
||||||
{
|
|
||||||
return new CreatureLevListCustomData (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const
|
std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
|
@ -138,11 +131,11 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
if (!ptr.getRefData().getCustomData())
|
||||||
{
|
{
|
||||||
std::unique_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
|
std::unique_ptr<CreatureLevListCustomData> data = std::make_unique<CreatureLevListCustomData>();
|
||||||
data->mSpawnActorId = -1;
|
data->mSpawnActorId = -1;
|
||||||
data->mSpawn = true;
|
data->mSpawn = true;
|
||||||
|
|
||||||
ptr.getRefData().setCustomData(data.release());
|
ptr.getRefData().setCustomData(std::move(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,11 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
class DoorCustomData : public MWWorld::CustomData
|
class DoorCustomData : public MWWorld::TypedCustomData<DoorCustomData>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MWWorld::DoorState mDoorState = MWWorld::DoorState::Idle;
|
MWWorld::DoorState mDoorState = MWWorld::DoorState::Idle;
|
||||||
|
|
||||||
MWWorld::CustomData *clone() const override;
|
|
||||||
|
|
||||||
DoorCustomData& asDoorCustomData() override
|
DoorCustomData& asDoorCustomData() override
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -48,11 +46,6 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MWWorld::CustomData *DoorCustomData::clone() const
|
|
||||||
{
|
|
||||||
return new DoorCustomData (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
||||||
{
|
{
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
|
@ -327,8 +320,7 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
if (!ptr.getRefData().getCustomData())
|
||||||
{
|
{
|
||||||
std::unique_ptr<DoorCustomData> data(new DoorCustomData);
|
ptr.getRefData().setCustomData(std::make_unique<DoorCustomData>());
|
||||||
ptr.getRefData().setCustomData(data.release());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -246,15 +246,13 @@ namespace
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
|
|
||||||
class NpcCustomData : public MWWorld::CustomData
|
class NpcCustomData : public MWWorld::TypedCustomData<NpcCustomData>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MWMechanics::NpcStats mNpcStats;
|
MWMechanics::NpcStats mNpcStats;
|
||||||
MWMechanics::Movement mMovement;
|
MWMechanics::Movement mMovement;
|
||||||
MWWorld::InventoryStore mInventoryStore;
|
MWWorld::InventoryStore mInventoryStore;
|
||||||
|
|
||||||
MWWorld::CustomData *clone() const override;
|
|
||||||
|
|
||||||
NpcCustomData& asNpcCustomData() override
|
NpcCustomData& asNpcCustomData() override
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -265,11 +263,6 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MWWorld::CustomData *NpcCustomData::clone() const
|
|
||||||
{
|
|
||||||
return new NpcCustomData (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Npc::GMST& Npc::getGmst()
|
const Npc::GMST& Npc::getGmst()
|
||||||
{
|
{
|
||||||
static GMST gmst;
|
static GMST gmst;
|
||||||
|
@ -397,7 +390,7 @@ namespace MWClass
|
||||||
data->mNpcStats.setGoldPool(gold);
|
data->mNpcStats.setGoldPool(gold);
|
||||||
|
|
||||||
// store
|
// store
|
||||||
ptr.getRefData().setCustomData (data.release());
|
ptr.getRefData().setCustomData(std::move(data));
|
||||||
|
|
||||||
getInventoryStore(ptr).autoEquip(ptr);
|
getInventoryStore(ptr).autoEquip(ptr);
|
||||||
}
|
}
|
||||||
|
@ -1302,8 +1295,7 @@ namespace MWClass
|
||||||
if (!ptr.getRefData().getCustomData())
|
if (!ptr.getRefData().getCustomData())
|
||||||
{
|
{
|
||||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||||
std::unique_ptr<NpcCustomData> data (new NpcCustomData);
|
ptr.getRefData().setCustomData(std::make_unique<NpcCustomData>());
|
||||||
ptr.getRefData().setCustomData (data.release());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "bookpage.hpp"
|
#include "bookpage.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "MyGUI_RenderItem.h"
|
#include "MyGUI_RenderItem.h"
|
||||||
#include "MyGUI_RenderManager.h"
|
#include "MyGUI_RenderManager.h"
|
||||||
#include "MyGUI_TextureUtility.h"
|
#include "MyGUI_TextureUtility.h"
|
||||||
|
@ -894,6 +896,27 @@ protected:
|
||||||
return mIsPageReset || (mPage != page);
|
return mIsPageReset || (mPage != page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<MyGUI::IntPoint> getAdjustedPos(int left, int top, bool move = false)
|
||||||
|
{
|
||||||
|
if (!mBook)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (mPage >= mBook->mPages.size())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
MyGUI::IntPoint pos (left, top);
|
||||||
|
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||||
|
// work around inconsistency in MyGUI where the mouse press coordinates aren't
|
||||||
|
// transformed by the current Layer (even though mouse *move* events are).
|
||||||
|
if(!move)
|
||||||
|
pos = mNode->getLayer()->getPosition(left, top);
|
||||||
|
#endif
|
||||||
|
pos.left -= mCroppedParent->getAbsoluteLeft ();
|
||||||
|
pos.top -= mCroppedParent->getAbsoluteTop ();
|
||||||
|
pos.top += mViewTop;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef TypesetBookImpl::StyleImpl Style;
|
typedef TypesetBookImpl::StyleImpl Style;
|
||||||
|
@ -952,16 +975,10 @@ public:
|
||||||
|
|
||||||
void onMouseMove (int left, int top)
|
void onMouseMove (int left, int top)
|
||||||
{
|
{
|
||||||
if (!mBook)
|
Style * hit = nullptr;
|
||||||
return;
|
if(auto pos = getAdjustedPos(left, top, true))
|
||||||
|
if(pos->top <= mViewBottom)
|
||||||
if (mPage >= mBook->mPages.size())
|
hit = mBook->hitTestWithMargin (pos->left, pos->top);
|
||||||
return;
|
|
||||||
|
|
||||||
left -= mCroppedParent->getAbsoluteLeft ();
|
|
||||||
top -= mCroppedParent->getAbsoluteTop ();
|
|
||||||
|
|
||||||
Style * hit = mBook->hitTestWithMargin (left, mViewTop + top);
|
|
||||||
|
|
||||||
if (mLastDown == MyGUI::MouseButton::None)
|
if (mLastDown == MyGUI::MouseButton::None)
|
||||||
{
|
{
|
||||||
|
@ -991,24 +1008,11 @@ public:
|
||||||
|
|
||||||
void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id)
|
void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id)
|
||||||
{
|
{
|
||||||
if (!mBook)
|
auto pos = getAdjustedPos(left, top);
|
||||||
return;
|
|
||||||
|
|
||||||
if (mPage >= mBook->mPages.size())
|
if (pos && mLastDown == MyGUI::MouseButton::None)
|
||||||
return;
|
|
||||||
|
|
||||||
// work around inconsistency in MyGUI where the mouse press coordinates aren't
|
|
||||||
// transformed by the current Layer (even though mouse *move* events are).
|
|
||||||
MyGUI::IntPoint pos (left, top);
|
|
||||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
|
||||||
pos = mNode->getLayer()->getPosition(left, top);
|
|
||||||
#endif
|
|
||||||
pos.left -= mCroppedParent->getAbsoluteLeft ();
|
|
||||||
pos.top -= mCroppedParent->getAbsoluteTop ();
|
|
||||||
|
|
||||||
if (mLastDown == MyGUI::MouseButton::None)
|
|
||||||
{
|
{
|
||||||
mFocusItem = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top);
|
mFocusItem = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr;
|
||||||
mItemActive = true;
|
mItemActive = true;
|
||||||
|
|
||||||
dirtyFocusItem ();
|
dirtyFocusItem ();
|
||||||
|
@ -1019,25 +1023,11 @@ public:
|
||||||
|
|
||||||
void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id)
|
void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id)
|
||||||
{
|
{
|
||||||
if (!mBook)
|
auto pos = getAdjustedPos(left, top);
|
||||||
return;
|
|
||||||
|
|
||||||
if (mPage >= mBook->mPages.size())
|
if (pos && mLastDown == id)
|
||||||
return;
|
|
||||||
|
|
||||||
// work around inconsistency in MyGUI where the mouse release coordinates aren't
|
|
||||||
// transformed by the current Layer (even though mouse *move* events are).
|
|
||||||
MyGUI::IntPoint pos (left, top);
|
|
||||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
|
||||||
pos = mNode->getLayer()->getPosition(left, top);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pos.left -= mCroppedParent->getAbsoluteLeft ();
|
|
||||||
pos.top -= mCroppedParent->getAbsoluteTop ();
|
|
||||||
|
|
||||||
if (mLastDown == id)
|
|
||||||
{
|
{
|
||||||
Style * item = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top);
|
Style * item = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr;
|
||||||
|
|
||||||
bool clicked = mFocusItem == item;
|
bool clicked = mFocusItem == item;
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,6 @@
|
||||||
|
|
||||||
#include "itemmodel.hpp"
|
#include "itemmodel.hpp"
|
||||||
|
|
||||||
namespace MWWorld
|
|
||||||
{
|
|
||||||
class Environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MyGUI
|
namespace MyGUI
|
||||||
{
|
{
|
||||||
class Gui;
|
class Gui;
|
||||||
|
@ -19,7 +14,6 @@ namespace MyGUI
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class WindowManager;
|
|
||||||
class ContainerWindow;
|
class ContainerWindow;
|
||||||
class ItemView;
|
class ItemView;
|
||||||
class SortFilterItemModel;
|
class SortFilterItemModel;
|
||||||
|
|
|
@ -15,11 +15,6 @@ namespace Gui
|
||||||
class MWList;
|
class MWList;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class ResponseCallback;
|
class ResponseCallback;
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace MWGui
|
||||||
, mUpdateTimer(0.f)
|
, mUpdateTimer(0.f)
|
||||||
{
|
{
|
||||||
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||||
if (uiScale > 1.0)
|
if (uiScale > 0.f)
|
||||||
mScaleFactor = uiScale;
|
mScaleFactor = uiScale;
|
||||||
|
|
||||||
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
|
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
|
||||||
|
|
|
@ -7,11 +7,6 @@
|
||||||
#include <MyGUI_RenderManager.h>
|
#include <MyGUI_RenderManager.h>
|
||||||
|
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class RaceSelectionPreview;
|
class RaceSelectionPreview;
|
||||||
|
|
|
@ -11,11 +11,6 @@ namespace ESM
|
||||||
struct Spell;
|
struct Spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class ReviewDialog : public WindowModal
|
class ReviewDialog : public WindowModal
|
||||||
|
|
|
@ -65,6 +65,9 @@ namespace
|
||||||
std::string getAspect (int x, int y)
|
std::string getAspect (int x, int y)
|
||||||
{
|
{
|
||||||
int gcd = std::gcd (x, y);
|
int gcd = std::gcd (x, y);
|
||||||
|
if (gcd == 0)
|
||||||
|
return std::string();
|
||||||
|
|
||||||
int xaspect = x / gcd;
|
int xaspect = x / gcd;
|
||||||
int yaspect = y / gcd;
|
int yaspect = y / gcd;
|
||||||
// special case: 8 : 5 is usually referred to as 16:10
|
// special case: 8 : 5 is usually referred to as 16:10
|
||||||
|
@ -282,8 +285,10 @@ namespace MWGui
|
||||||
std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
|
std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
|
||||||
for (std::pair<int, int>& resolution : resolutions)
|
for (std::pair<int, int>& resolution : resolutions)
|
||||||
{
|
{
|
||||||
std::string str = MyGUI::utility::toString(resolution.first) + " x " + MyGUI::utility::toString(resolution.second)
|
std::string str = MyGUI::utility::toString(resolution.first) + " x " + MyGUI::utility::toString(resolution.second);
|
||||||
+ " (" + getAspect(resolution.first, resolution.second) + ")";
|
std::string aspect = getAspect(resolution.first, resolution.second);
|
||||||
|
if (!aspect.empty())
|
||||||
|
str = str + " (" + aspect + ")";
|
||||||
|
|
||||||
if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE)
|
if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE)
|
||||||
mResolutionList->addItem(str);
|
mResolutionList->addItem(str);
|
||||||
|
|
|
@ -3,11 +3,6 @@
|
||||||
|
|
||||||
#include "windowbase.hpp"
|
#include "windowbase.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class SettingsWindow : public WindowBase
|
class SettingsWindow : public WindowBase
|
||||||
|
|
|
@ -15,11 +15,6 @@ namespace MyGUI
|
||||||
class Widget;
|
class Widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class SpellBuyingWindow : public ReferenceInterface, public WindowBase
|
class SpellBuyingWindow : public ReferenceInterface, public WindowBase
|
||||||
|
|
|
@ -393,7 +393,8 @@ namespace MWGui
|
||||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||||
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
|
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
|
||||||
|
|
||||||
if (MyGUI::utility::parseInt(mPriceLabel->getCaption()) > playerGold)
|
int price = MyGUI::utility::parseInt(mPriceLabel->getCaption());
|
||||||
|
if (price > playerGold)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}");
|
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}");
|
||||||
return;
|
return;
|
||||||
|
@ -401,8 +402,6 @@ namespace MWGui
|
||||||
|
|
||||||
mSpell.mName = mNameEdit->getCaption();
|
mSpell.mName = mNameEdit->getCaption();
|
||||||
|
|
||||||
int price = MyGUI::utility::parseInt(mPriceLabel->getCaption());
|
|
||||||
|
|
||||||
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
|
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
|
||||||
|
|
||||||
// add gold to NPC trading gold pool
|
// add gold to NPC trading gold pool
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class WindowManager;
|
|
||||||
|
|
||||||
class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener
|
class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -3,11 +3,6 @@
|
||||||
|
|
||||||
#include "windowbase.hpp"
|
#include "windowbase.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class TextInputDialog : public WindowModal
|
class TextInputDialog : public WindowModal
|
||||||
|
|
|
@ -58,12 +58,12 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TimeAdvancer::getHours()
|
int TimeAdvancer::getHours() const
|
||||||
{
|
{
|
||||||
return mHours;
|
return mHours;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TimeAdvancer::isRunning()
|
bool TimeAdvancer::isRunning() const
|
||||||
{
|
{
|
||||||
return mRunning;
|
return mRunning;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ namespace MWGui
|
||||||
void stop();
|
void stop();
|
||||||
void onFrame(float dt);
|
void onFrame(float dt);
|
||||||
|
|
||||||
int getHours();
|
int getHours() const;
|
||||||
bool isRunning();
|
bool isRunning() const;
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
|
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
|
||||||
|
|
|
@ -11,12 +11,6 @@ namespace MyGUI
|
||||||
class Widget;
|
class Widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWGui
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class TravelWindow : public ReferenceInterface, public WindowBase
|
class TravelWindow : public ReferenceInterface, public WindowBase
|
||||||
|
|
|
@ -3,11 +3,6 @@
|
||||||
|
|
||||||
#include "layout.hpp"
|
#include "layout.hpp"
|
||||||
|
|
||||||
namespace MWBase
|
|
||||||
{
|
|
||||||
class WindowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class Ptr;
|
class Ptr;
|
||||||
|
@ -15,7 +10,6 @@ namespace MWWorld
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class WindowManager;
|
|
||||||
class DragAndDrop;
|
class DragAndDrop;
|
||||||
|
|
||||||
class WindowBase: public Layout
|
class WindowBase: public Layout
|
||||||
|
|
|
@ -114,7 +114,6 @@ namespace MWGui
|
||||||
class TrainingWindow;
|
class TrainingWindow;
|
||||||
class SpellIcons;
|
class SpellIcons;
|
||||||
class MerchantRepair;
|
class MerchantRepair;
|
||||||
class Repair;
|
|
||||||
class SoulgemDialog;
|
class SoulgemDialog;
|
||||||
class Recharge;
|
class Recharge;
|
||||||
class CompanionWindow;
|
class CompanionWindow;
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class WindowManager;
|
|
||||||
|
|
||||||
class WindowPinnableBase: public WindowBase
|
class WindowPinnableBase: public WindowBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -70,7 +70,7 @@ namespace MWInput
|
||||||
}
|
}
|
||||||
|
|
||||||
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||||
if (uiScale != 0.f)
|
if (uiScale > 0.f)
|
||||||
mInvUiScalingFactor = 1.f / uiScale;
|
mInvUiScalingFactor = 1.f / uiScale;
|
||||||
|
|
||||||
float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input");
|
float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input");
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace MWInput
|
||||||
, mGuiCursorEnabled(true)
|
, mGuiCursorEnabled(true)
|
||||||
{
|
{
|
||||||
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||||
if (uiScale != 0.f)
|
if (uiScale > 0.f)
|
||||||
mInvUiScalingFactor = 1.f / uiScale;
|
mInvUiScalingFactor = 1.f / uiScale;
|
||||||
|
|
||||||
int w,h;
|
int w,h;
|
||||||
|
|
|
@ -45,13 +45,13 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
|
||||||
return true; //Door is no longer opening
|
return true; //Door is no longer opening
|
||||||
|
|
||||||
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
|
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
|
||||||
float x = pos.pos[0] - tPos.pos[0];
|
float x = pos.pos[1] - tPos.pos[1];
|
||||||
float y = pos.pos[1] - tPos.pos[1];
|
float y = pos.pos[0] - tPos.pos[0];
|
||||||
|
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||||
|
|
||||||
// Turn away from the door and move when turn completed
|
// Turn away from the door and move when turn completed
|
||||||
if (zTurn(actor, std::atan2(x,y) + getAdjustedAngle(), osg::DegreesToRadians(5.f)))
|
if (zTurn(actor, std::atan2(y,x) + getAdjustedAngle(), osg::DegreesToRadians(5.f)))
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||||
else
|
else
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
|
|
|
@ -228,7 +228,6 @@ namespace MWMechanics
|
||||||
const osg::Vec3f vActorPos(pos.asVec3());
|
const osg::Vec3f vActorPos(pos.asVec3());
|
||||||
const osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3());
|
const osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3());
|
||||||
|
|
||||||
osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target);
|
|
||||||
float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);
|
float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);
|
||||||
|
|
||||||
storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS);
|
storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS);
|
||||||
|
@ -236,13 +235,14 @@ namespace MWMechanics
|
||||||
if (isRangedCombat)
|
if (isRangedCombat)
|
||||||
{
|
{
|
||||||
// rotate actor taking into account target movement direction and projectile speed
|
// rotate actor taking into account target movement direction and projectile speed
|
||||||
vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength);
|
osg::Vec3f vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength);
|
||||||
|
|
||||||
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
|
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
|
||||||
storage.mMovement.mRotation[2] = getZAngleToDir(vAimDir);
|
storage.mMovement.mRotation[2] = getZAngleToDir(vAimDir);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, false);
|
||||||
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
|
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
|
||||||
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
||||||
}
|
}
|
||||||
|
@ -698,7 +698,7 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t
|
||||||
// idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same
|
// idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same
|
||||||
|
|
||||||
osg::Vec3f vTargetPos = target.getRefData().getPosition().asVec3();
|
osg::Vec3f vTargetPos = target.getRefData().getPosition().asVec3();
|
||||||
osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target);
|
osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, true);
|
||||||
float distToTarget = vDirToTarget.length();
|
float distToTarget = vDirToTarget.length();
|
||||||
|
|
||||||
osg::Vec3f vTargetMoveDir = vTargetPos - vLastTargetPos;
|
osg::Vec3f vTargetMoveDir = vTargetPos - vLastTargetPos;
|
||||||
|
|
|
@ -2955,7 +2955,7 @@ void CharacterController::updateHeadTracking(float duration)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// no head node to look at, fall back to look at center of collision box
|
// no head node to look at, fall back to look at center of collision box
|
||||||
direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget);
|
direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget, false);
|
||||||
}
|
}
|
||||||
direction.normalize();
|
direction.normalize();
|
||||||
|
|
||||||
|
|
|
@ -681,7 +681,7 @@ namespace MWMechanics
|
||||||
// Deviating from Morrowind here: it doesn't increase disposition on marginal wins,
|
// Deviating from Morrowind here: it doesn't increase disposition on marginal wins,
|
||||||
// which seems to be a bug (MCP fixes it too).
|
// which seems to be a bug (MCP fixes it too).
|
||||||
// Original logic: x = 0, y = -iPerMinChange
|
// Original logic: x = 0, y = -iPerMinChange
|
||||||
x = -iPerMinChange;
|
x = iPerMinChange;
|
||||||
y = x; // This goes unused.
|
y = x; // This goes unused.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -179,6 +179,7 @@ bool Actor::setPosition(const osg::Vec3f& position)
|
||||||
if (mSkipSimulation)
|
if (mSkipSimulation)
|
||||||
return false;
|
return false;
|
||||||
bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged;
|
bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged;
|
||||||
|
updateWorldPosition();
|
||||||
applyOffsetChange();
|
applyOffsetChange();
|
||||||
mPreviousPosition = mPosition;
|
mPreviousPosition = mPosition;
|
||||||
mPosition = position;
|
mPosition = position;
|
||||||
|
|
|
@ -317,7 +317,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
// init
|
// init
|
||||||
for (auto& data : actorsData)
|
for (auto& data : actorsData)
|
||||||
data.updatePosition();
|
data.updatePosition(mCollisionWorld);
|
||||||
mPrevStepCount = numSteps;
|
mPrevStepCount = numSteps;
|
||||||
mRemainingSteps = numSteps;
|
mRemainingSteps = numSteps;
|
||||||
mTimeAccum = timeAccum;
|
mTimeAccum = timeAccum;
|
||||||
|
|
|
@ -60,6 +60,22 @@
|
||||||
#include "movementsolver.hpp"
|
#include "movementsolver.hpp"
|
||||||
#include "mtphysics.hpp"
|
#include "mtphysics.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool canMoveToWaterSurface(const MWPhysics::Actor* physicActor, const float waterlevel, btCollisionWorld* world)
|
||||||
|
{
|
||||||
|
if (!physicActor)
|
||||||
|
return false;
|
||||||
|
const float halfZ = physicActor->getHalfExtents().z();
|
||||||
|
const osg::Vec3f actorPosition = physicActor->getPosition();
|
||||||
|
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
|
||||||
|
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
|
||||||
|
MWPhysics::ActorTracer tracer;
|
||||||
|
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, world);
|
||||||
|
return (tracer.mFraction >= 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
|
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
|
||||||
|
@ -347,16 +363,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
|
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
|
||||||
{
|
{
|
||||||
const Actor* physicActor = getActor(actor);
|
return ::canMoveToWaterSurface(getActor(actor), waterlevel, mCollisionWorld.get());
|
||||||
if (!physicActor)
|
|
||||||
return false;
|
|
||||||
const float halfZ = physicActor->getHalfExtents().z();
|
|
||||||
const osg::Vec3f actorPosition = physicActor->getPosition();
|
|
||||||
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
|
|
||||||
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
|
|
||||||
ActorTracer tracer;
|
|
||||||
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld.get());
|
|
||||||
return (tracer.mFraction >= 1.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const
|
osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const
|
||||||
|
@ -772,16 +779,10 @@ namespace MWPhysics
|
||||||
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
|
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
|
||||||
|
|
||||||
bool waterCollision = false;
|
bool waterCollision = false;
|
||||||
bool moveToWaterSurface = false;
|
|
||||||
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
|
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
|
||||||
{
|
{
|
||||||
if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
|
if (physicActor->getCollisionMode() || !world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
|
||||||
waterCollision = true;
|
waterCollision = true;
|
||||||
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel))
|
|
||||||
{
|
|
||||||
moveToWaterSurface = true;
|
|
||||||
waterCollision = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
physicActor->setCanWaterWalk(waterCollision);
|
physicActor->setCanWaterWalk(waterCollision);
|
||||||
|
@ -794,7 +795,7 @@ namespace MWPhysics
|
||||||
if (!willSimulate)
|
if (!willSimulate)
|
||||||
standingOn = physicActor->getStandingOnPtr();
|
standingOn = physicActor->getStandingOnPtr();
|
||||||
|
|
||||||
actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel);
|
actorsFrameData.emplace_back(std::move(physicActor), standingOn, waterCollision, movement, slowFall, waterlevel);
|
||||||
}
|
}
|
||||||
mMovementQueue.clear();
|
mMovementQueue.clear();
|
||||||
return actorsFrameData;
|
return actorsFrameData;
|
||||||
|
@ -937,9 +938,9 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
|
|
||||||
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,
|
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,
|
||||||
bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel)
|
bool waterCollision, osg::Vec3f movement, float slowFall, float waterlevel)
|
||||||
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
|
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
|
||||||
mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface),
|
mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision),
|
||||||
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
|
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
|
||||||
{
|
{
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
|
@ -953,7 +954,7 @@ namespace MWPhysics
|
||||||
mWasOnGround = actor->getOnGround();
|
mWasOnGround = actor->getOnGround();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActorFrameData::updatePosition()
|
void ActorFrameData::updatePosition(btCollisionWorld* world)
|
||||||
{
|
{
|
||||||
mActorRaw->updateWorldPosition();
|
mActorRaw->updateWorldPosition();
|
||||||
// If physics runs "fast enough", position are interpolated without simulation
|
// If physics runs "fast enough", position are interpolated without simulation
|
||||||
|
@ -961,10 +962,10 @@ namespace MWPhysics
|
||||||
// regardless of simulation speed.
|
// regardless of simulation speed.
|
||||||
mActorRaw->applyOffsetChange();
|
mActorRaw->applyOffsetChange();
|
||||||
mPosition = mActorRaw->getPosition();
|
mPosition = mActorRaw->getPosition();
|
||||||
if (mMoveToWaterSurface)
|
if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world))
|
||||||
{
|
{
|
||||||
mPosition.z() = mWaterlevel;
|
mPosition.z() = mWaterlevel;
|
||||||
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z());
|
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z(), false);
|
||||||
}
|
}
|
||||||
mOldHeight = mPosition.z();
|
mOldHeight = mPosition.z();
|
||||||
mRefpos = mActorRaw->getPtr().getRefData().getPosition();
|
mRefpos = mActorRaw->getPtr().getRefData().getPosition();
|
||||||
|
|
|
@ -79,7 +79,7 @@ namespace MWPhysics
|
||||||
struct ActorFrameData
|
struct ActorFrameData
|
||||||
{
|
{
|
||||||
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
|
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
|
||||||
void updatePosition();
|
void updatePosition(btCollisionWorld* world);
|
||||||
std::weak_ptr<Actor> mActor;
|
std::weak_ptr<Actor> mActor;
|
||||||
Actor* mActorRaw;
|
Actor* mActorRaw;
|
||||||
MWWorld::Ptr mStandingOn;
|
MWWorld::Ptr mStandingOn;
|
||||||
|
@ -90,7 +90,7 @@ namespace MWPhysics
|
||||||
bool mDidJump;
|
bool mDidJump;
|
||||||
bool mFloatToSurface;
|
bool mFloatToSurface;
|
||||||
bool mNeedLand;
|
bool mNeedLand;
|
||||||
bool mMoveToWaterSurface;
|
bool mWaterCollision;
|
||||||
float mWaterlevel;
|
float mWaterlevel;
|
||||||
float mSlowFall;
|
float mSlowFall;
|
||||||
float mOldHeight;
|
float mOldHeight;
|
||||||
|
|
|
@ -93,7 +93,7 @@ LocalMap::LocalMap(osg::Group* root)
|
||||||
{
|
{
|
||||||
// Increase map resolution, if use UI scaling
|
// Increase map resolution, if use UI scaling
|
||||||
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||||
if (uiScale > 1.0)
|
if (uiScale > 0.f)
|
||||||
mMapResolution *= uiScale;
|
mMapResolution *= uiScale;
|
||||||
|
|
||||||
SceneUtil::FindByNameVisitor find("Scene Root");
|
SceneUtil::FindByNameVisitor find("Scene Root");
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
std::set<ESM::RefNum> mDisabled;
|
std::set<ESM::RefNum> mDisabled;
|
||||||
std::set<ESM::RefNum> mBlacklist;
|
std::set<ESM::RefNum> mBlacklist;
|
||||||
bool operator==(const RefTracker&other) { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; }
|
bool operator==(const RefTracker&other) const { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; }
|
||||||
};
|
};
|
||||||
RefTracker mRefTracker;
|
RefTracker mRefTracker;
|
||||||
RefTracker mRefTrackerNew;
|
RefTracker mRefTrackerNew;
|
||||||
|
|
|
@ -855,6 +855,7 @@ namespace MWRender
|
||||||
RenderingManager::RayResult result;
|
RenderingManager::RayResult result;
|
||||||
result.mHit = false;
|
result.mHit = false;
|
||||||
result.mHitRefnum.mContentFile = -1;
|
result.mHitRefnum.mContentFile = -1;
|
||||||
|
result.mHitRefnum.mIndex = -1;
|
||||||
result.mRatio = 0;
|
result.mRatio = 0;
|
||||||
if (intersector->containsIntersections())
|
if (intersector->containsIntersections())
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,6 +45,9 @@
|
||||||
#include <components/sceneutil/visitor.hpp>
|
#include <components/sceneutil/visitor.hpp>
|
||||||
#include <components/sceneutil/shadow.hpp>
|
#include <components/sceneutil/shadow.hpp>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include <components/nifosg/particle.hpp>
|
#include <components/nifosg/particle.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
@ -1164,7 +1167,7 @@ void SkyManager::create()
|
||||||
{
|
{
|
||||||
assert(!mCreated);
|
assert(!mCreated);
|
||||||
|
|
||||||
mAtmosphereDay = mSceneManager->getInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot);
|
mAtmosphereDay = mSceneManager->getInstance(Settings::Manager::getString("skyatmosphere", "Models"), mEarlyRenderBinRoot);
|
||||||
ModVertexAlphaVisitor modAtmosphere(0);
|
ModVertexAlphaVisitor modAtmosphere(0);
|
||||||
mAtmosphereDay->accept(modAtmosphere);
|
mAtmosphereDay->accept(modAtmosphere);
|
||||||
|
|
||||||
|
@ -1176,10 +1179,10 @@ void SkyManager::create()
|
||||||
mEarlyRenderBinRoot->addChild(mAtmosphereNightNode);
|
mEarlyRenderBinRoot->addChild(mAtmosphereNightNode);
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> atmosphereNight;
|
osg::ref_ptr<osg::Node> atmosphereNight;
|
||||||
if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif"))
|
if (mSceneManager->getVFS()->exists(Settings::Manager::getString("skynight02", "Models")))
|
||||||
atmosphereNight = mSceneManager->getInstance("meshes/sky_night_02.nif", mAtmosphereNightNode);
|
atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString("skynight02", "Models"), mAtmosphereNightNode);
|
||||||
else
|
else
|
||||||
atmosphereNight = mSceneManager->getInstance("meshes/sky_night_01.nif", mAtmosphereNightNode);
|
atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString("skynight01", "Models"), mAtmosphereNightNode);
|
||||||
atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||||
ModVertexAlphaVisitor modStars(2);
|
ModVertexAlphaVisitor modStars(2);
|
||||||
atmosphereNight->accept(modStars);
|
atmosphereNight->accept(modStars);
|
||||||
|
@ -1193,14 +1196,14 @@ void SkyManager::create()
|
||||||
|
|
||||||
mCloudNode = new osg::PositionAttitudeTransform;
|
mCloudNode = new osg::PositionAttitudeTransform;
|
||||||
mEarlyRenderBinRoot->addChild(mCloudNode);
|
mEarlyRenderBinRoot->addChild(mCloudNode);
|
||||||
mCloudMesh = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode);
|
mCloudMesh = mSceneManager->getInstance(Settings::Manager::getString("skyclouds", "Models"), mCloudNode);
|
||||||
ModVertexAlphaVisitor modClouds(1);
|
ModVertexAlphaVisitor modClouds(1);
|
||||||
mCloudMesh->accept(modClouds);
|
mCloudMesh->accept(modClouds);
|
||||||
mCloudUpdater = new CloudUpdater;
|
mCloudUpdater = new CloudUpdater;
|
||||||
mCloudUpdater->setOpacity(1.f);
|
mCloudUpdater->setOpacity(1.f);
|
||||||
mCloudMesh->addUpdateCallback(mCloudUpdater);
|
mCloudMesh->addUpdateCallback(mCloudUpdater);
|
||||||
|
|
||||||
mCloudMesh2 = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode);
|
mCloudMesh2 = mSceneManager->getInstance(Settings::Manager::getString("skyclouds", "Models"), mCloudNode);
|
||||||
mCloudMesh2->accept(modClouds);
|
mCloudMesh2->accept(modClouds);
|
||||||
mCloudUpdater2 = new CloudUpdater;
|
mCloudUpdater2 = new CloudUpdater;
|
||||||
mCloudUpdater2->setOpacity(0.f);
|
mCloudUpdater2->setOpacity(0.f);
|
||||||
|
@ -1597,7 +1600,7 @@ void SkyManager::update(float duration)
|
||||||
if (mParticleNode)
|
if (mParticleNode)
|
||||||
{
|
{
|
||||||
// Morrowind deliberately rotates the blizzard mesh, so so should we.
|
// Morrowind deliberately rotates the blizzard mesh, so so should we.
|
||||||
if (mCurrentParticleEffect == "meshes\\blizzard.nif")
|
if (mCurrentParticleEffect == Settings::Manager::getString("weatherblizzard", "Models"))
|
||||||
quat.makeRotate(osg::Vec3f(-1,0,0), mStormDirection);
|
quat.makeRotate(osg::Vec3f(-1,0,0), mStormDirection);
|
||||||
mParticleNode->setAttitude(quat);
|
mParticleNode->setAttitude(quat);
|
||||||
}
|
}
|
||||||
|
@ -1897,16 +1900,16 @@ void SkyManager::setWaterHeight(float height)
|
||||||
|
|
||||||
void SkyManager::listAssetsToPreload(std::vector<std::string>& models, std::vector<std::string>& textures)
|
void SkyManager::listAssetsToPreload(std::vector<std::string>& models, std::vector<std::string>& textures)
|
||||||
{
|
{
|
||||||
models.emplace_back("meshes/sky_atmosphere.nif");
|
models.emplace_back(Settings::Manager::getString("skyatmosphere", "Models"));
|
||||||
if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif"))
|
if (mSceneManager->getVFS()->exists(Settings::Manager::getString("skynight02", "Models")))
|
||||||
models.emplace_back("meshes/sky_night_02.nif");
|
models.emplace_back(Settings::Manager::getString("skynight02", "Models"));
|
||||||
models.emplace_back("meshes/sky_night_01.nif");
|
models.emplace_back(Settings::Manager::getString("skynight01", "Models"));
|
||||||
models.emplace_back("meshes/sky_clouds_01.nif");
|
models.emplace_back(Settings::Manager::getString("skyclouds", "Models"));
|
||||||
|
|
||||||
models.emplace_back("meshes\\ashcloud.nif");
|
models.emplace_back(Settings::Manager::getString("weatherashcloud", "Models"));
|
||||||
models.emplace_back("meshes\\blightcloud.nif");
|
models.emplace_back(Settings::Manager::getString("weatherblightcloud", "Models"));
|
||||||
models.emplace_back("meshes\\snow.nif");
|
models.emplace_back(Settings::Manager::getString("weathersnow", "Models"));
|
||||||
models.emplace_back("meshes\\blizzard.nif");
|
models.emplace_back(Settings::Manager::getString("weatherblizzard", "Models"));
|
||||||
|
|
||||||
textures.emplace_back("textures/tx_mooncircle_full_s.dds");
|
textures.emplace_back("textures/tx_mooncircle_full_s.dds");
|
||||||
textures.emplace_back("textures/tx_mooncircle_full_m.dds");
|
textures.emplace_back("textures/tx_mooncircle_full_m.dds");
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace MWScript
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
if (count<0)
|
if (count<0)
|
||||||
throw std::runtime_error ("second argument for AddItem must be non-negative");
|
count = static_cast<uint16_t>(count);
|
||||||
|
|
||||||
// no-op
|
// no-op
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
|
|
|
@ -235,13 +235,20 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GlobalScripts::readRecord (ESM::ESMReader& reader, uint32_t type)
|
bool GlobalScripts::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
||||||
{
|
{
|
||||||
if (type==ESM::REC_GSCR)
|
if (type==ESM::REC_GSCR)
|
||||||
{
|
{
|
||||||
ESM::GlobalScript script;
|
ESM::GlobalScript script;
|
||||||
script.load (reader);
|
script.load (reader);
|
||||||
|
|
||||||
|
if (script.mTargetRef.hasContentFile())
|
||||||
|
{
|
||||||
|
auto iter = contentFileMap.find(script.mTargetRef.mContentFile);
|
||||||
|
if (iter != contentFileMap.end())
|
||||||
|
script.mTargetRef.mContentFile = iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
auto iter = mScripts.find (script.mId);
|
auto iter = mScripts.find (script.mId);
|
||||||
|
|
||||||
if (iter==mScripts.end())
|
if (iter==mScripts.end())
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace MWScript
|
||||||
|
|
||||||
void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||||
|
|
||||||
bool readRecord (ESM::ESMReader& reader, uint32_t type);
|
bool readRecord (ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap);
|
||||||
///< Records for variables that do not exist are dropped silently.
|
///< Records for variables that do not exist are dropped silently.
|
||||||
///
|
///
|
||||||
/// \return Known type?
|
/// \return Known type?
|
||||||
|
|
|
@ -10,16 +10,6 @@
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
namespace MWSound
|
|
||||||
{
|
|
||||||
class SoundManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWInput
|
|
||||||
{
|
|
||||||
struct MWInputManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
{
|
{
|
||||||
class Locals;
|
class Locals;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace MWScript
|
||||||
std::vector<MWWorld::Ptr> actors;
|
std::vector<MWWorld::Ptr> actors;
|
||||||
MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);
|
MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);
|
||||||
for (auto& actor : actors)
|
for (auto& actor : actors)
|
||||||
MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff);
|
MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class R>
|
template<class R>
|
||||||
|
@ -290,11 +290,11 @@ namespace MWScript
|
||||||
float terrainHeight = -std::numeric_limits<float>::max();
|
float terrainHeight = -std::numeric_limits<float>::max();
|
||||||
if (ptr.getCell()->isExterior())
|
if (ptr.getCell()->isExterior())
|
||||||
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos);
|
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos);
|
||||||
|
|
||||||
if (pos < terrainHeight)
|
if (pos < terrainHeight)
|
||||||
pos = terrainHeight;
|
pos = terrainHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
newPos[2] = pos;
|
newPos[2] = pos;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -303,7 +303,7 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||||
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos));
|
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true);
|
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true, true);
|
||||||
}
|
}
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
||||||
|
|
||||||
|
@ -726,7 +726,7 @@ namespace MWScript
|
||||||
// This approach can be used to create elevators.
|
// This approach can be used to create elevators.
|
||||||
moveStandingActors(ptr, diff);
|
moveStandingActors(ptr, diff);
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||||
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff));
|
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -762,7 +762,7 @@ namespace MWScript
|
||||||
// This approach can be used to create elevators.
|
// This approach can be used to create elevators.
|
||||||
moveStandingActors(ptr, diff);
|
moveStandingActors(ptr, diff);
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||||
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff));
|
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ namespace MWSound
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoundManager(const VFS::Manager* vfs, bool useSound);
|
SoundManager(const VFS::Manager* vfs, bool useSound);
|
||||||
virtual ~SoundManager();
|
~SoundManager() override;
|
||||||
|
|
||||||
void processChangedSettings(const Settings::CategorySettingVector& settings) override;
|
void processChangedSettings(const Settings::CategorySettingVector& settings) override;
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,14 @@ void MWState::QuickSaveManager::visitSave(const Slot *saveSlot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MWState::QuickSaveManager::isOldestSave(const Slot *compare)
|
bool MWState::QuickSaveManager::isOldestSave(const Slot *compare) const
|
||||||
{
|
{
|
||||||
if(mOldestSlotVisited == nullptr)
|
if(mOldestSlotVisited == nullptr)
|
||||||
return true;
|
return true;
|
||||||
return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp);
|
return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MWState::QuickSaveManager::shouldCreateNewSlot()
|
bool MWState::QuickSaveManager::shouldCreateNewSlot() const
|
||||||
{
|
{
|
||||||
return (mSlotsVisited < mMaxSaves);
|
return (mSlotsVisited < mMaxSaves);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ namespace MWState{
|
||||||
unsigned int mSlotsVisited;
|
unsigned int mSlotsVisited;
|
||||||
const Slot *mOldestSlotVisited;
|
const Slot *mOldestSlotVisited;
|
||||||
private:
|
private:
|
||||||
bool shouldCreateNewSlot();
|
bool shouldCreateNewSlot() const;
|
||||||
bool isOldestSave(const Slot *compare);
|
bool isOldestSave(const Slot *compare) const;
|
||||||
public:
|
public:
|
||||||
QuickSaveManager(std::string &saveName, unsigned int maxSaves);
|
QuickSaveManager(std::string &saveName, unsigned int maxSaves);
|
||||||
///< A utility class to manage multiple quicksave slots
|
///< A utility class to manage multiple quicksave slots
|
||||||
|
|
|
@ -461,7 +461,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
||||||
|
|
||||||
case ESM::REC_GSCR:
|
case ESM::REC_GSCR:
|
||||||
|
|
||||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.intval);
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.intval, contentFileMap);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESM::REC_GMAP:
|
case ESM::REC_GMAP:
|
||||||
|
|
|
@ -132,9 +132,11 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const
|
||||||
|
|
||||||
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
|
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
|
||||||
: mStore (store), mReader (reader),
|
: mStore (store), mReader (reader),
|
||||||
mIdCache (Settings::Manager::getInt("pointers cache size", "Cells"), std::pair<std::string, CellStore *> ("", (CellStore*)nullptr)),
|
|
||||||
mIdCacheIndex (0)
|
mIdCacheIndex (0)
|
||||||
{}
|
{
|
||||||
|
int cacheSize = std::max(Settings::Manager::getInt("pointers cache size", "Cells"), 0);
|
||||||
|
mIdCache = IdCache(cacheSize, std::pair<std::string, CellStore *> ("", (CellStore*)nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y)
|
MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y)
|
||||||
{
|
{
|
||||||
|
@ -259,8 +261,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell,
|
||||||
MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
|
MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
|
||||||
{
|
{
|
||||||
// First check the cache
|
// First check the cache
|
||||||
for (std::vector<std::pair<std::string, CellStore *> >::iterator iter (mIdCache.begin());
|
for (IdCache::iterator iter (mIdCache.begin()); iter!=mIdCache.end(); ++iter)
|
||||||
iter!=mIdCache.end(); ++iter)
|
|
||||||
if (iter->first==name && iter->second)
|
if (iter->first==name && iter->second)
|
||||||
{
|
{
|
||||||
Ptr ptr = getPtr (name, *iter->second);
|
Ptr ptr = getPtr (name, *iter->second);
|
||||||
|
|
|
@ -28,11 +28,12 @@ namespace MWWorld
|
||||||
/// \brief Cell container
|
/// \brief Cell container
|
||||||
class Cells
|
class Cells
|
||||||
{
|
{
|
||||||
|
typedef std::vector<std::pair<std::string, CellStore *> > IdCache;
|
||||||
const MWWorld::ESMStore& mStore;
|
const MWWorld::ESMStore& mStore;
|
||||||
std::vector<ESM::ESMReader>& mReader;
|
std::vector<ESM::ESMReader>& mReader;
|
||||||
mutable std::map<std::string, CellStore> mInteriors;
|
mutable std::map<std::string, CellStore> mInteriors;
|
||||||
mutable std::map<std::pair<int, int>, CellStore> mExteriors;
|
mutable std::map<std::pair<int, int>, CellStore> mExteriors;
|
||||||
std::vector<std::pair<std::string, CellStore *> > mIdCache;
|
IdCache mIdCache;
|
||||||
std::size_t mIdCacheIndex;
|
std::size_t mIdCacheIndex;
|
||||||
|
|
||||||
Cells (const Cells&);
|
Cells (const Cells&);
|
||||||
|
|
|
@ -77,10 +77,24 @@ MWWorld::ResolutionListener::~ResolutionListener()
|
||||||
{
|
{
|
||||||
if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
|
if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
|
||||||
{
|
{
|
||||||
for(const auto&& ptr : mStore)
|
try
|
||||||
ptr.getRefData().setCount(0);
|
{
|
||||||
|
for(const auto&& ptr : mStore)
|
||||||
|
ptr.getRefData().setCount(0);
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Failed to clear temporary container contents of " << mStore.mPtr.get<ESM::Container>()->mBase->mId << ": " << e.what();
|
||||||
|
}
|
||||||
mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
|
mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
|
||||||
addScripts(mStore, mStore.mPtr.mCell);
|
try
|
||||||
|
{
|
||||||
|
addScripts(mStore, mStore.mPtr.mCell);
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Failed to restart item scripts inside " << mStore.mPtr.get<ESM::Container>()->mBase->mId << ": " << e.what();
|
||||||
|
}
|
||||||
mStore.mResolved = false;
|
mStore.mResolved = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual ~ContainerStore();
|
virtual ~ContainerStore();
|
||||||
|
|
||||||
virtual ContainerStore* clone() { return new ContainerStore(*this); }
|
virtual std::unique_ptr<ContainerStore> clone() { return std::make_unique<ContainerStore>(*this); }
|
||||||
|
|
||||||
ConstContainerStoreIterator cbegin (int mask = Type_All) const;
|
ConstContainerStoreIterator cbegin (int mask = Type_All) const;
|
||||||
ConstContainerStoreIterator cend() const;
|
ConstContainerStoreIterator cend() const;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef GAME_MWWORLD_CUSTOMDATA_H
|
#ifndef GAME_MWWORLD_CUSTOMDATA_H
|
||||||
#define GAME_MWWORLD_CUSTOMDATA_H
|
#define GAME_MWWORLD_CUSTOMDATA_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
class CreatureCustomData;
|
class CreatureCustomData;
|
||||||
|
@ -19,7 +21,7 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual ~CustomData() {}
|
virtual ~CustomData() {}
|
||||||
|
|
||||||
virtual CustomData *clone() const = 0;
|
virtual std::unique_ptr<CustomData> clone() const = 0;
|
||||||
|
|
||||||
// Fast version of dynamic_cast<X&>. Needs to be overridden in the respective class.
|
// Fast version of dynamic_cast<X&>. Needs to be overridden in the respective class.
|
||||||
|
|
||||||
|
@ -38,6 +40,15 @@ namespace MWWorld
|
||||||
virtual MWClass::CreatureLevListCustomData& asCreatureLevListCustomData();
|
virtual MWClass::CreatureLevListCustomData& asCreatureLevListCustomData();
|
||||||
virtual const MWClass::CreatureLevListCustomData& asCreatureLevListCustomData() const;
|
virtual const MWClass::CreatureLevListCustomData& asCreatureLevListCustomData() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct TypedCustomData : CustomData
|
||||||
|
{
|
||||||
|
std::unique_ptr<CustomData> clone() const final
|
||||||
|
{
|
||||||
|
return std::make_unique<T>(*static_cast<const T*>(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -123,7 +123,7 @@ namespace MWWorld
|
||||||
|
|
||||||
InventoryStore& operator= (const InventoryStore& store);
|
InventoryStore& operator= (const InventoryStore& store);
|
||||||
|
|
||||||
InventoryStore* clone() override { return new InventoryStore(*this); }
|
std::unique_ptr<ContainerStore> clone() override { return std::make_unique<InventoryStore>(*this); }
|
||||||
|
|
||||||
ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true) override;
|
ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true) override;
|
||||||
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
|
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
|
||||||
|
|
|
@ -40,8 +40,6 @@ namespace MWWorld
|
||||||
void RefData::cleanup()
|
void RefData::cleanup()
|
||||||
{
|
{
|
||||||
mBaseNode = nullptr;
|
mBaseNode = nullptr;
|
||||||
|
|
||||||
delete mCustomData;
|
|
||||||
mCustomData = nullptr;
|
mCustomData = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,21 +221,20 @@ namespace MWWorld
|
||||||
return mPosition;
|
return mPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefData::setCustomData (CustomData *data)
|
void RefData::setCustomData(std::unique_ptr<CustomData>&& value) noexcept
|
||||||
{
|
{
|
||||||
mChanged = true; // We do not currently track CustomData, so assume anything with a CustomData is changed
|
mChanged = true; // We do not currently track CustomData, so assume anything with a CustomData is changed
|
||||||
delete mCustomData;
|
mCustomData = std::move(value);
|
||||||
mCustomData = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomData *RefData::getCustomData()
|
CustomData *RefData::getCustomData()
|
||||||
{
|
{
|
||||||
return mCustomData;
|
return mCustomData.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomData *RefData::getCustomData() const
|
const CustomData *RefData::getCustomData() const
|
||||||
{
|
{
|
||||||
return mCustomData;
|
return mCustomData.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RefData::hasChanged() const
|
bool RefData::hasChanged() const
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
#include <components/esm/animationstate.hpp>
|
#include <components/esm/animationstate.hpp>
|
||||||
|
|
||||||
#include "../mwscript/locals.hpp"
|
#include "../mwscript/locals.hpp"
|
||||||
|
#include "../mwworld/customdata.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace SceneUtil
|
namespace SceneUtil
|
||||||
{
|
{
|
||||||
|
@ -44,7 +46,7 @@ namespace MWWorld
|
||||||
|
|
||||||
ESM::AnimationState mAnimationState;
|
ESM::AnimationState mAnimationState;
|
||||||
|
|
||||||
CustomData *mCustomData;
|
std::unique_ptr<CustomData> mCustomData;
|
||||||
|
|
||||||
void copy (const RefData& refData);
|
void copy (const RefData& refData);
|
||||||
|
|
||||||
|
@ -68,6 +70,7 @@ namespace MWWorld
|
||||||
/// perform these operations).
|
/// perform these operations).
|
||||||
|
|
||||||
RefData (const RefData& refData);
|
RefData (const RefData& refData);
|
||||||
|
RefData (RefData&& other) noexcept = default;
|
||||||
|
|
||||||
~RefData();
|
~RefData();
|
||||||
|
|
||||||
|
@ -76,6 +79,7 @@ namespace MWWorld
|
||||||
/// perform this operations).
|
/// perform this operations).
|
||||||
|
|
||||||
RefData& operator= (const RefData& refData);
|
RefData& operator= (const RefData& refData);
|
||||||
|
RefData& operator= (RefData&& other) noexcept = default;
|
||||||
|
|
||||||
/// Return base node (can be a null pointer).
|
/// Return base node (can be a null pointer).
|
||||||
SceneUtil::PositionAttitudeTransform* getBaseNode();
|
SceneUtil::PositionAttitudeTransform* getBaseNode();
|
||||||
|
@ -117,7 +121,7 @@ namespace MWWorld
|
||||||
void setPosition (const ESM::Position& pos);
|
void setPosition (const ESM::Position& pos);
|
||||||
const ESM::Position& getPosition() const;
|
const ESM::Position& getPosition() const;
|
||||||
|
|
||||||
void setCustomData (CustomData *data);
|
void setCustomData(std::unique_ptr<CustomData>&& value) noexcept;
|
||||||
///< Set custom data (potentially replacing old custom data). The ownership of \a data is
|
///< Set custom data (potentially replacing old custom data). The ownership of \a data is
|
||||||
/// transferred to this.
|
/// transferred to this.
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ namespace Loading
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
struct Navigator;
|
struct Navigator;
|
||||||
class Water;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
|
|
|
@ -862,19 +862,6 @@ namespace MWWorld
|
||||||
if (reference == getPlayerPtr())
|
if (reference == getPlayerPtr())
|
||||||
throw std::runtime_error("can not disable player object");
|
throw std::runtime_error("can not disable player object");
|
||||||
|
|
||||||
// A common pattern to teleport NPC in scripts is a sequence of SetPos/Disable/Enable
|
|
||||||
// Disable/Enable create a new physics actor, and so the SetPos call is lost
|
|
||||||
// Call moveObject so that the newly created physics actor will have up-to-date position
|
|
||||||
if (reference.getClass().isActor())
|
|
||||||
{
|
|
||||||
auto* physactor = mPhysics->getActor(reference);
|
|
||||||
if (physactor)
|
|
||||||
{
|
|
||||||
physactor->applyOffsetChange();
|
|
||||||
const auto position = physactor->getSimulationPosition();
|
|
||||||
moveObject(reference, position.x(), position.y(), position.z(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reference.getRefData().disable();
|
reference.getRefData().disable();
|
||||||
|
|
||||||
if (reference.getCellRef().getRefNum().hasContentFile())
|
if (reference.getCellRef().getRefNum().hasContentFile())
|
||||||
|
@ -1251,7 +1238,7 @@ namespace MWWorld
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive)
|
MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive)
|
||||||
{
|
{
|
||||||
int cellX, cellY;
|
int cellX, cellY;
|
||||||
positionToIndex(x, y, cellX, cellY);
|
positionToIndex(x, y, cellX, cellY);
|
||||||
|
@ -1266,21 +1253,14 @@ namespace MWWorld
|
||||||
return moveObject(ptr, cell, x, y, z, movePhysics);
|
return moveObject(ptr, cell, x, y, z, movePhysics);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive)
|
MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive)
|
||||||
{
|
|
||||||
return moveObjectImp(ptr, x, y, z, true, moveToActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec)
|
|
||||||
{
|
{
|
||||||
auto* actor = mPhysics->getActor(ptr);
|
auto* actor = mPhysics->getActor(ptr);
|
||||||
if (actor)
|
if (actor)
|
||||||
{
|
|
||||||
actor->adjustPosition(vec);
|
actor->adjustPosition(vec);
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
|
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
|
||||||
return moveObject(ptr, newpos.x(), newpos.y(), newpos.z());
|
return moveObject(ptr, newpos.x(), newpos.y(), newpos.z(), false, moveToActive && ptr != getPlayerPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::scaleObject (const Ptr& ptr, float scale)
|
void World::scaleObject (const Ptr& ptr, float scale)
|
||||||
|
@ -1546,7 +1526,7 @@ namespace MWWorld
|
||||||
auto* physactor = mPhysics->getActor(actor);
|
auto* physactor = mPhysics->getActor(actor);
|
||||||
assert(physactor);
|
assert(physactor);
|
||||||
const auto position = physactor->getSimulationPosition();
|
const auto position = physactor->getSimulationPosition();
|
||||||
moveObjectImp(actor, position.x(), position.y(), position.z(), false);
|
moveObject(actor, position.x(), position.y(), position.z(), false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,7 +1536,7 @@ namespace MWWorld
|
||||||
auto* physactor = mPhysics->getActor(*player);
|
auto* physactor = mPhysics->getActor(*player);
|
||||||
assert(physactor);
|
assert(physactor);
|
||||||
const auto position = physactor->getSimulationPosition();
|
const auto position = physactor->getSimulationPosition();
|
||||||
moveObjectImp(*player, position.x(), position.y(), position.z(), false);
|
moveObject(*player, position.x(), position.y(), position.z(), false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3885,14 +3865,12 @@ namespace MWWorld
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f World::aimToTarget(const ConstPtr &actor, const ConstPtr &target)
|
osg::Vec3f World::aimToTarget(const ConstPtr &actor, const ConstPtr &target, bool isRangedCombat)
|
||||||
{
|
{
|
||||||
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
|
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
|
||||||
osg::Vec3f weaponHalfExtents = mPhysics->getHalfExtents(actor);
|
float heightRatio = isRangedCombat ? 2.f * Constants::TorsoHeight : 1.f;
|
||||||
osg::Vec3f targetPos = target.getRefData().getPosition().asVec3();
|
weaponPos.z() += mPhysics->getHalfExtents(actor).z() * heightRatio;
|
||||||
osg::Vec3f targetHalfExtents = mPhysics->getHalfExtents(target);
|
osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target);
|
||||||
weaponPos.z() += weaponHalfExtents.z() * 2 * Constants::TorsoHeight;
|
|
||||||
targetPos.z() += targetHalfExtents.z();
|
|
||||||
return (targetPos - weaponPos);
|
return (targetPos - weaponPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,6 @@ namespace ToUTF8
|
||||||
class Utf8Encoder;
|
class Utf8Encoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentLoader;
|
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
class Object;
|
class Object;
|
||||||
|
@ -138,9 +136,6 @@ namespace MWWorld
|
||||||
|
|
||||||
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags);
|
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags);
|
||||||
|
|
||||||
Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false);
|
|
||||||
///< @return an updated Ptr in case the Ptr's cell changes
|
|
||||||
|
|
||||||
Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);
|
Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);
|
||||||
|
|
||||||
void updateSoundListener();
|
void updateSoundListener();
|
||||||
|
@ -376,13 +371,13 @@ namespace MWWorld
|
||||||
|
|
||||||
void undeleteObject (const Ptr& ptr) override;
|
void undeleteObject (const Ptr& ptr) override;
|
||||||
|
|
||||||
MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive=false) override;
|
MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) override;
|
||||||
///< @return an updated Ptr in case the Ptr's cell changes
|
///< @return an updated Ptr in case the Ptr's cell changes
|
||||||
|
|
||||||
MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;
|
MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;
|
||||||
///< @return an updated Ptr
|
///< @return an updated Ptr
|
||||||
|
|
||||||
MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec) override;
|
MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive) override;
|
||||||
///< @return an updated Ptr
|
///< @return an updated Ptr
|
||||||
|
|
||||||
void scaleObject (const Ptr& ptr, float scale) override;
|
void scaleObject (const Ptr& ptr, float scale) override;
|
||||||
|
@ -705,7 +700,7 @@ namespace MWWorld
|
||||||
|
|
||||||
/// Return a vector aiming the actor's weapon towards a target.
|
/// Return a vector aiming the actor's weapon towards a target.
|
||||||
/// @note The length of the vector is the distance between actor and target.
|
/// @note The length of the vector is the distance between actor and target.
|
||||||
osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) override;
|
osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) override;
|
||||||
|
|
||||||
/// Return the distance between actor's weapon and target's collision box.
|
/// Return the distance between actor's weapon and target's collision box.
|
||||||
float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) override;
|
float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) override;
|
||||||
|
|
|
@ -13,6 +13,7 @@ if (GTEST_FOUND AND GMOCK_FOUND)
|
||||||
mwdialogue/test_keywordsearch.cpp
|
mwdialogue/test_keywordsearch.cpp
|
||||||
|
|
||||||
esm/test_fixed_string.cpp
|
esm/test_fixed_string.cpp
|
||||||
|
esm/variant.cpp
|
||||||
|
|
||||||
misc/test_stringops.cpp
|
misc/test_stringops.cpp
|
||||||
misc/test_endianness.cpp
|
misc/test_endianness.cpp
|
||||||
|
|
513
apps/openmw_test_suite/esm/variant.cpp
Normal file
513
apps/openmw_test_suite/esm/variant.cpp
Normal file
|
@ -0,0 +1,513 @@
|
||||||
|
#include <components/esm/variant.hpp>
|
||||||
|
#include <components/esm/esmwriter.hpp>
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
#include <components/esm/loadglob.hpp>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace testing;
|
||||||
|
using namespace ESM;
|
||||||
|
|
||||||
|
Variant makeVariant(VarType type)
|
||||||
|
{
|
||||||
|
Variant v;
|
||||||
|
v.setType(type);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant makeVariant(VarType type, int value)
|
||||||
|
{
|
||||||
|
Variant v;
|
||||||
|
v.setType(type);
|
||||||
|
v.setInteger(value);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantTest, move_constructed_should_have_data)
|
||||||
|
{
|
||||||
|
Variant a(int{42});
|
||||||
|
const Variant b(std::move(a));
|
||||||
|
ASSERT_EQ(b.getInteger(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantTest, copy_constructed_is_equal_to_source)
|
||||||
|
{
|
||||||
|
const Variant a(int{42});
|
||||||
|
const Variant b(a);
|
||||||
|
ASSERT_EQ(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantTest, copy_constructed_does_not_share_data_with_source)
|
||||||
|
{
|
||||||
|
const Variant a(int{42});
|
||||||
|
Variant b(a);
|
||||||
|
b.setInteger(13);
|
||||||
|
ASSERT_EQ(a.getInteger(), 42);
|
||||||
|
ASSERT_EQ(b.getInteger(), 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantTest, move_assigned_should_have_data)
|
||||||
|
{
|
||||||
|
Variant b;
|
||||||
|
{
|
||||||
|
Variant a(int{42});
|
||||||
|
b = std::move(a);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(b.getInteger(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantTest, copy_assigned_is_equal_to_source)
|
||||||
|
{
|
||||||
|
const Variant a(int{42});
|
||||||
|
Variant b;
|
||||||
|
b = a;
|
||||||
|
ASSERT_EQ(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantTest, not_equal_is_negation_of_equal)
|
||||||
|
{
|
||||||
|
const Variant a(int{42});
|
||||||
|
Variant b;
|
||||||
|
b = a;
|
||||||
|
ASSERT_TRUE(!(a != b));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantTest, different_types_are_not_equal)
|
||||||
|
{
|
||||||
|
ASSERT_NE(Variant(int{42}), Variant(float{2.7f}));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ESMVariantWriteToOStreamTest : TestWithParam<std::tuple<Variant, std::string>> {};
|
||||||
|
|
||||||
|
TEST_P(ESMVariantWriteToOStreamTest, should_write)
|
||||||
|
{
|
||||||
|
const auto [variant, result] = GetParam();
|
||||||
|
std::ostringstream s;
|
||||||
|
s << variant;
|
||||||
|
ASSERT_EQ(s.str(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(VariantAsString, ESMVariantWriteToOStreamTest, Values(
|
||||||
|
std::make_tuple(Variant(), "variant none"),
|
||||||
|
std::make_tuple(Variant(int{42}), "variant long: 42"),
|
||||||
|
std::make_tuple(Variant(float{2.7f}), "variant float: 2.7"),
|
||||||
|
std::make_tuple(Variant(std::string("foo")), "variant string: \"foo\""),
|
||||||
|
std::make_tuple(makeVariant(VT_Unknown), "variant unknown"),
|
||||||
|
std::make_tuple(makeVariant(VT_Short, 42), "variant short: 42"),
|
||||||
|
std::make_tuple(makeVariant(VT_Int, 42), "variant int: 42")
|
||||||
|
));
|
||||||
|
|
||||||
|
struct ESMVariantGetTypeTest : Test {};
|
||||||
|
|
||||||
|
TEST(ESMVariantGetTypeTest, default_constructed_should_return_none)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(Variant().getType(), VT_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetTypeTest, for_constructed_from_int_should_return_long)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(Variant(int{}).getType(), VT_Long);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetTypeTest, for_constructed_from_float_should_return_float)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(Variant(float{}).getType(), VT_Float);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetTypeTest, for_constructed_from_lvalue_string_should_return_string)
|
||||||
|
{
|
||||||
|
const std::string string;
|
||||||
|
ASSERT_EQ(Variant(string).getType(), VT_String);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetTypeTest, for_constructed_from_rvalue_string_should_return_string)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(Variant(std::string{}).getType(), VT_String);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ESMVariantGetIntegerTest : Test {};
|
||||||
|
|
||||||
|
TEST(ESMVariantGetIntegerTest, for_default_constructed_should_throw_exception)
|
||||||
|
{
|
||||||
|
ASSERT_THROW(Variant().getInteger(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetIntegerTest, for_constructed_from_int_should_return_same_value)
|
||||||
|
{
|
||||||
|
const Variant variant(int{42});
|
||||||
|
ASSERT_EQ(variant.getInteger(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetIntegerTest, for_constructed_from_float_should_return_casted_to_int)
|
||||||
|
{
|
||||||
|
const Variant variant(float{2.7});
|
||||||
|
ASSERT_EQ(variant.getInteger(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetIntegerTest, for_constructed_from_string_should_throw_exception)
|
||||||
|
{
|
||||||
|
const Variant variant(std::string("foo"));
|
||||||
|
ASSERT_THROW(variant.getInteger(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetFloatTest, for_default_constructed_should_throw_exception)
|
||||||
|
{
|
||||||
|
ASSERT_THROW(Variant().getFloat(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetFloatTest, for_constructed_from_int_should_return_casted_to_float)
|
||||||
|
{
|
||||||
|
const Variant variant(int{42});
|
||||||
|
ASSERT_EQ(variant.getFloat(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetFloatTest, for_constructed_from_float_should_return_same_value)
|
||||||
|
{
|
||||||
|
const Variant variant(float{2.7f});
|
||||||
|
ASSERT_EQ(variant.getFloat(), 2.7f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetFloatTest, for_constructed_from_string_should_throw_exception)
|
||||||
|
{
|
||||||
|
const Variant variant(std::string("foo"));
|
||||||
|
ASSERT_THROW(variant.getFloat(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetStringTest, for_default_constructed_should_throw_exception)
|
||||||
|
{
|
||||||
|
ASSERT_THROW(Variant().getString(), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetStringTest, for_constructed_from_int_should_throw_exception)
|
||||||
|
{
|
||||||
|
const Variant variant(int{42});
|
||||||
|
ASSERT_THROW(variant.getString(), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetStringTest, for_constructed_from_float_should_throw_exception)
|
||||||
|
{
|
||||||
|
const Variant variant(float{2.7});
|
||||||
|
ASSERT_THROW(variant.getString(), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantGetStringTest, for_constructed_from_string_should_return_same_value)
|
||||||
|
{
|
||||||
|
const Variant variant(std::string("foo"));
|
||||||
|
ASSERT_EQ(variant.getString(), "foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_unknown_should_reset_data)
|
||||||
|
{
|
||||||
|
Variant variant(int{42});
|
||||||
|
variant.setType(VT_Unknown);
|
||||||
|
ASSERT_THROW(variant.getInteger(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_none_should_reset_data)
|
||||||
|
{
|
||||||
|
Variant variant(int{42});
|
||||||
|
variant.setType(VT_None);
|
||||||
|
ASSERT_THROW(variant.getInteger(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_same_type_should_not_change_value)
|
||||||
|
{
|
||||||
|
Variant variant(int{42});
|
||||||
|
variant.setType(VT_Long);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_float_replaced_by_int_should_cast_float_to_int)
|
||||||
|
{
|
||||||
|
Variant variant(float{2.7f});
|
||||||
|
variant.setType(VT_Int);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_string_replaced_by_int_should_set_default_initialized_data)
|
||||||
|
{
|
||||||
|
Variant variant(std::string("foo"));
|
||||||
|
variant.setType(VT_Int);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_default_constructed_replaced_by_float_should_set_default_initialized_value)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Float);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_float_replaced_by_short_should_cast_data_to_int)
|
||||||
|
{
|
||||||
|
Variant variant(float{2.7f});
|
||||||
|
variant.setType(VT_Short);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_float_replaced_by_long_should_cast_data_to_int)
|
||||||
|
{
|
||||||
|
Variant variant(float{2.7f});
|
||||||
|
variant.setType(VT_Long);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_int_replaced_by_float_should_cast_data_to_float)
|
||||||
|
{
|
||||||
|
Variant variant(int{42});
|
||||||
|
variant.setType(VT_Float);
|
||||||
|
ASSERT_EQ(variant.getFloat(), 42.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetTypeTest, for_int_replaced_by_string_should_set_default_initialized_data)
|
||||||
|
{
|
||||||
|
Variant variant(int{42});
|
||||||
|
variant.setType(VT_String);
|
||||||
|
ASSERT_EQ(variant.getString(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetIntegerTest, for_default_constructed_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
ASSERT_THROW(variant.setInteger(42), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetIntegerTest, for_unknown_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Unknown);
|
||||||
|
ASSERT_THROW(variant.setInteger(42), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetIntegerTest, for_default_int_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant(int{13});
|
||||||
|
variant.setInteger(42);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetIntegerTest, for_int_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Int);
|
||||||
|
variant.setInteger(42);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetIntegerTest, for_short_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Short);
|
||||||
|
variant.setInteger(42);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetIntegerTest, for_float_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant(float{2.7f});
|
||||||
|
variant.setInteger(42);
|
||||||
|
ASSERT_EQ(variant.getFloat(), 42.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetIntegerTest, for_string_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant(std::string{});
|
||||||
|
ASSERT_THROW(variant.setInteger(42), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetFloatTest, for_default_constructed_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetFloatTest, for_unknown_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Unknown);
|
||||||
|
ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetFloatTest, for_default_int_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant(int{13});
|
||||||
|
variant.setFloat(2.7f);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetFloatTest, for_int_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Int);
|
||||||
|
variant.setFloat(2.7f);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetFloatTest, for_short_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Short);
|
||||||
|
variant.setFloat(2.7f);
|
||||||
|
ASSERT_EQ(variant.getInteger(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetFloatTest, for_float_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant(float{2.7f});
|
||||||
|
variant.setFloat(3.14f);
|
||||||
|
ASSERT_EQ(variant.getFloat(), 3.14f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetFloatTest, for_string_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant(std::string{});
|
||||||
|
ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetStringTest, for_default_constructed_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
ASSERT_THROW(variant.setString("foo"), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetStringTest, for_unknown_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Unknown);
|
||||||
|
ASSERT_THROW(variant.setString("foo"), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetStringTest, for_default_int_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant(int{13});
|
||||||
|
ASSERT_THROW(variant.setString("foo"), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetStringTest, for_int_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Int);
|
||||||
|
ASSERT_THROW(variant.setString("foo"), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetStringTest, for_short_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant;
|
||||||
|
variant.setType(VT_Short);
|
||||||
|
ASSERT_THROW(variant.setString("foo"), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetStringTest, for_float_should_throw_exception)
|
||||||
|
{
|
||||||
|
Variant variant(float{2.7f});
|
||||||
|
ASSERT_THROW(variant.setString("foo"), std::bad_variant_access);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ESMVariantSetStringTest, for_string_should_change_value)
|
||||||
|
{
|
||||||
|
Variant variant(std::string("foo"));
|
||||||
|
variant.setString("bar");
|
||||||
|
ASSERT_EQ(variant.getString(), "bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WriteToESMTestCase
|
||||||
|
{
|
||||||
|
Variant mVariant;
|
||||||
|
Variant::Format mFormat;
|
||||||
|
std::size_t mDataSize {};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string write(const Variant& variant, const Variant::Format format)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
ESM::ESMWriter writer;
|
||||||
|
writer.save(out);
|
||||||
|
variant.write(writer, format);
|
||||||
|
writer.close();
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant read(const Variant::Format format, const std::string& data)
|
||||||
|
{
|
||||||
|
Variant result;
|
||||||
|
ESM::ESMReader reader;
|
||||||
|
reader.open(std::make_shared<std::istringstream>(data), "");
|
||||||
|
result.read(reader, format);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant writeAndRead(const Variant& variant, const Variant::Format format, std::size_t dataSize)
|
||||||
|
{
|
||||||
|
const std::string data = write(variant, format);
|
||||||
|
EXPECT_EQ(data.size(), dataSize);
|
||||||
|
return read(format, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ESMVariantToESMTest : TestWithParam<WriteToESMTestCase> {};
|
||||||
|
|
||||||
|
TEST_P(ESMVariantToESMTest, deserialized_is_equal_to_serialized)
|
||||||
|
{
|
||||||
|
const auto param = GetParam();
|
||||||
|
const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize);
|
||||||
|
ASSERT_EQ(param.mVariant, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMTest, Values(
|
||||||
|
WriteToESMTestCase {Variant(), Variant::Format_Gmst, 324},
|
||||||
|
WriteToESMTestCase {Variant(int{42}), Variant::Format_Global, 345},
|
||||||
|
WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Global, 345},
|
||||||
|
WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Info, 336},
|
||||||
|
WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Local, 336},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Global, 345},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Local, 334},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Info, 336},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Local, 336}
|
||||||
|
));
|
||||||
|
|
||||||
|
struct ESMVariantToESMNoneTest : TestWithParam<WriteToESMTestCase> {};
|
||||||
|
|
||||||
|
TEST_P(ESMVariantToESMNoneTest, deserialized_is_none)
|
||||||
|
{
|
||||||
|
const auto param = GetParam();
|
||||||
|
const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize);
|
||||||
|
ASSERT_EQ(Variant(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMNoneTest, Values(
|
||||||
|
WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Gmst, 336},
|
||||||
|
WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Gmst, 335},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Gmst, 336}
|
||||||
|
));
|
||||||
|
|
||||||
|
struct ESMVariantWriteToESMFailTest : TestWithParam<WriteToESMTestCase> {};
|
||||||
|
|
||||||
|
TEST_P(ESMVariantWriteToESMFailTest, write_is_not_supported)
|
||||||
|
{
|
||||||
|
const auto param = GetParam();
|
||||||
|
std::ostringstream out;
|
||||||
|
ESM::ESMWriter writer;
|
||||||
|
writer.save(out);
|
||||||
|
ASSERT_THROW(param.mVariant.write(writer, param.mFormat), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(VariantAndFormat, ESMVariantWriteToESMFailTest, Values(
|
||||||
|
WriteToESMTestCase {Variant(), Variant::Format_Global},
|
||||||
|
WriteToESMTestCase {Variant(), Variant::Format_Info},
|
||||||
|
WriteToESMTestCase {Variant(), Variant::Format_Local},
|
||||||
|
WriteToESMTestCase {Variant(int{42}), Variant::Format_Gmst},
|
||||||
|
WriteToESMTestCase {Variant(int{42}), Variant::Format_Info},
|
||||||
|
WriteToESMTestCase {Variant(int{42}), Variant::Format_Local},
|
||||||
|
WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Global},
|
||||||
|
WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Info},
|
||||||
|
WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Local},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Unknown), Variant::Format_Global},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Global},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Gmst},
|
||||||
|
WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Info}
|
||||||
|
));
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
#include "methodselectionpage.hpp"
|
#include "methodselectionpage.hpp"
|
||||||
#include "mainwizard.hpp"
|
#include "mainwizard.hpp"
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
Wizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) :
|
Wizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) :
|
||||||
QWizardPage(parent)
|
QWizardPage(parent)
|
||||||
{
|
{
|
||||||
|
@ -11,9 +14,12 @@ Wizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) :
|
||||||
#ifndef OPENMW_USE_UNSHIELD
|
#ifndef OPENMW_USE_UNSHIELD
|
||||||
retailDiscRadioButton->setEnabled(false);
|
retailDiscRadioButton->setEnabled(false);
|
||||||
existingLocationRadioButton->setChecked(true);
|
existingLocationRadioButton->setChecked(true);
|
||||||
|
buyLinkButton->released();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
registerField(QLatin1String("installation.retailDisc"), retailDiscRadioButton);
|
registerField(QLatin1String("installation.retailDisc"), retailDiscRadioButton);
|
||||||
|
|
||||||
|
connect(buyLinkButton, SIGNAL(released()), this, SLOT(handleBuyButton()));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Wizard::MethodSelectionPage::nextId() const
|
int Wizard::MethodSelectionPage::nextId() const
|
||||||
|
@ -24,3 +30,8 @@ int Wizard::MethodSelectionPage::nextId() const
|
||||||
return MainWizard::Page_ExistingInstallation;
|
return MainWizard::Page_ExistingInstallation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Wizard::MethodSelectionPage::handleBuyButton()
|
||||||
|
{
|
||||||
|
QDesktopServices::openUrl(QUrl("https://openmw.org/faq/#do_i_need_morrowind"));
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ namespace Wizard
|
||||||
|
|
||||||
int nextId() const override;
|
int nextId() const override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleBuyButton();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MainWizard *mWizard;
|
MainWizard *mWizard;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# Get the Google C++ Mocking Framework.
|
# Get the Google C++ Mocking Framework.
|
||||||
# (This file is almost an copy of the original FindGTest.cmake file,
|
# (This file is almost an copy of the original FindGTest.cmake file for GMock,
|
||||||
# altered to download and compile GMock and GTest if not found
|
|
||||||
# in GMOCK_ROOT or GTEST_ROOT respectively,
|
|
||||||
# feel free to use it as it is or modify it for your own needs.)
|
# feel free to use it as it is or modify it for your own needs.)
|
||||||
#
|
#
|
||||||
# Defines the following variables:
|
# Defines the following variables:
|
||||||
#
|
#
|
||||||
# GMOCK_FOUND - Found or got the Google Mocking framework
|
# GMOCK_FOUND - Found or got the Google Mocking framework
|
||||||
# GTEST_FOUND - Found or got the Google Testing framework
|
|
||||||
# GMOCK_INCLUDE_DIRS - GMock include directory
|
# GMOCK_INCLUDE_DIRS - GMock include directory
|
||||||
# GTEST_INCLUDE_DIRS - GTest include direcotry
|
|
||||||
#
|
#
|
||||||
# Also defines the library variables below as normal variables
|
# Also defines the library variables below as normal variables
|
||||||
#
|
#
|
||||||
|
@ -17,14 +13,8 @@
|
||||||
# GMOCK_LIBRARIES - libgmock
|
# GMOCK_LIBRARIES - libgmock
|
||||||
# GMOCK_MAIN_LIBRARIES - libgmock-main
|
# GMOCK_MAIN_LIBRARIES - libgmock-main
|
||||||
#
|
#
|
||||||
# GTEST_BOTH_LIBRARIES - Both libgtest & libgtest_main
|
|
||||||
# GTEST_LIBRARIES - libgtest
|
|
||||||
# GTEST_MAIN_LIBRARIES - libgtest_main
|
|
||||||
#
|
|
||||||
# Accepts the following variables as input:
|
# Accepts the following variables as input:
|
||||||
#
|
#
|
||||||
# GMOCK_ROOT - The root directory of the gmock install prefix
|
|
||||||
# GTEST_ROOT - The root directory of the gtest install prefix
|
|
||||||
# GMOCK_SRC_DIR -The directory of the gmock sources
|
# GMOCK_SRC_DIR -The directory of the gmock sources
|
||||||
# GMOCK_VER - The version of the gmock sources to be downloaded
|
# GMOCK_VER - The version of the gmock sources to be downloaded
|
||||||
#
|
#
|
||||||
|
@ -101,48 +91,6 @@
|
||||||
#
|
#
|
||||||
# * Kitware, Inc.
|
# * Kitware, Inc.
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Thanks to Daniel Blezek <blezek@gmail.com> for the GTEST_ADD_TESTS code
|
|
||||||
|
|
||||||
function(gtest_add_tests executable extra_args)
|
|
||||||
if(NOT ARGN)
|
|
||||||
message(FATAL_ERROR "Missing ARGN: Read the documentation for GTEST_ADD_TESTS")
|
|
||||||
endif()
|
|
||||||
if(ARGN STREQUAL "AUTO")
|
|
||||||
# obtain sources used for building that executable
|
|
||||||
get_property(ARGN TARGET ${executable} PROPERTY SOURCES)
|
|
||||||
endif()
|
|
||||||
set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*")
|
|
||||||
set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)")
|
|
||||||
foreach(source ${ARGN})
|
|
||||||
file(READ "${source}" contents)
|
|
||||||
string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents})
|
|
||||||
foreach(hit ${found_tests})
|
|
||||||
string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit})
|
|
||||||
|
|
||||||
# Parameterized tests have a different signature for the filter
|
|
||||||
if("x${test_type}" STREQUAL "xTEST_P")
|
|
||||||
string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1.\\2/*" test_name ${hit})
|
|
||||||
elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST")
|
|
||||||
string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" test_name ${hit})
|
|
||||||
elseif("x${test_type}" STREQUAL "xTYPED_TEST")
|
|
||||||
string(REGEX REPLACE ${gtest_case_name_regex} "\\1/*.\\2" test_name ${hit})
|
|
||||||
else()
|
|
||||||
message(WARNING "Could not parse GTest ${hit} for adding to CTest.")
|
|
||||||
continue()
|
|
||||||
endif()
|
|
||||||
add_test(NAME ${test_name} COMMAND ${executable} --gtest_filter=${test_name} ${extra_args})
|
|
||||||
endforeach()
|
|
||||||
endforeach()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
function(_append_debugs _endvar _library)
|
|
||||||
if(${_library} AND ${_library}_DEBUG)
|
|
||||||
set(_output optimized ${${_library}} debug ${${_library}_DEBUG})
|
|
||||||
else()
|
|
||||||
set(_output ${${_library}})
|
|
||||||
endif()
|
|
||||||
set(${_endvar} ${_output} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
function(_gmock_find_library _name)
|
function(_gmock_find_library _name)
|
||||||
find_library(${_name}
|
find_library(${_name}
|
||||||
|
@ -155,38 +103,20 @@ function(_gmock_find_library _name)
|
||||||
mark_as_advanced(${_name})
|
mark_as_advanced(${_name})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(_gtest_find_library _name)
|
|
||||||
find_library(${_name}
|
|
||||||
NAMES ${ARGN}
|
|
||||||
HINTS
|
|
||||||
ENV GTEST_ROOT
|
|
||||||
${GTEST_ROOT}
|
|
||||||
PATH_SUFFIXES ${_gtest_libpath_suffixes}
|
|
||||||
)
|
|
||||||
mark_as_advanced(${_name})
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
if(NOT DEFINED GMOCK_MSVC_SEARCH)
|
if(NOT DEFINED GMOCK_MSVC_SEARCH)
|
||||||
set(GMOCK_MSVC_SEARCH MD)
|
set(GMOCK_MSVC_SEARCH MD)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(_gmock_libpath_suffixes lib)
|
set(_gmock_libpath_suffixes lib)
|
||||||
set(_gtest_libpath_suffixes lib)
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
if(GMOCK_MSVC_SEARCH STREQUAL "MD")
|
if(GMOCK_MSVC_SEARCH STREQUAL "MD")
|
||||||
list(APPEND _gmock_libpath_suffixes
|
list(APPEND _gmock_libpath_suffixes
|
||||||
msvc/gmock-md/Debug
|
msvc/gmock-md/Debug
|
||||||
msvc/gmock-md/Release)
|
msvc/gmock-md/Release)
|
||||||
list(APPEND _gtest_libpath_suffixes
|
|
||||||
msvc/gtest-md/Debug
|
|
||||||
msvc/gtest-md/Release)
|
|
||||||
elseif(GMOCK_MSVC_SEARCH STREQUAL "MT")
|
elseif(GMOCK_MSVC_SEARCH STREQUAL "MT")
|
||||||
list(APPEND _gmock_libpath_suffixes
|
list(APPEND _gmock_libpath_suffixes
|
||||||
msvc/gmock/Debug
|
msvc/gmock/Debug
|
||||||
msvc/gmock/Release)
|
msvc/gmock/Release)
|
||||||
list(APPEND _gtest_libpath_suffixes
|
|
||||||
msvc/gtest/Debug
|
|
||||||
msvc/gtest/Release)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -197,13 +127,6 @@ find_path(GMOCK_INCLUDE_DIR gmock/gmock.h
|
||||||
)
|
)
|
||||||
mark_as_advanced(GMOCK_INCLUDE_DIR)
|
mark_as_advanced(GMOCK_INCLUDE_DIR)
|
||||||
|
|
||||||
find_path(GTEST_INCLUDE_DIR gtest/gtest.h
|
|
||||||
HINTS
|
|
||||||
$ENV{GTEST_ROOT}/include
|
|
||||||
${GTEST_ROOT}/include
|
|
||||||
)
|
|
||||||
mark_as_advanced(GTEST_INCLUDE_DIR)
|
|
||||||
|
|
||||||
if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD")
|
if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD")
|
||||||
# The provided /MD project files for Google Mock add -md suffixes to the
|
# The provided /MD project files for Google Mock add -md suffixes to the
|
||||||
# library names.
|
# library names.
|
||||||
|
@ -211,28 +134,12 @@ if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD")
|
||||||
_gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd)
|
_gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd)
|
||||||
_gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main)
|
_gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main)
|
||||||
_gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind)
|
_gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind)
|
||||||
|
|
||||||
_gtest_find_library(GTEST_LIBRARY gtest-md gtest)
|
|
||||||
_gtest_find_library(GTEST_LIBRARY_DEBUG gtest-mdd gtestd)
|
|
||||||
_gtest_find_library(GTEST_MAIN_LIBRARY gtest_main-md gtest_main)
|
|
||||||
_gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_main-mdd gtest_maind)
|
|
||||||
else()
|
else()
|
||||||
_gmock_find_library(GMOCK_LIBRARY gmock)
|
_gmock_find_library(GMOCK_LIBRARY gmock)
|
||||||
_gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd)
|
_gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd)
|
||||||
_gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main)
|
_gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main)
|
||||||
_gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind)
|
_gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind)
|
||||||
|
|
||||||
_gtest_find_library(GTEST_LIBRARY gtest)
|
|
||||||
_gtest_find_library(GTEST_LIBRARY_DEBUG gtestd)
|
|
||||||
_gtest_find_library(GTEST_MAIN_LIBRARY gtest_main)
|
|
||||||
_gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_maind)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT TARGET GTest::GTest)
|
|
||||||
add_library(GTest::GTest UNKNOWN IMPORTED)
|
|
||||||
endif()
|
|
||||||
if(NOT TARGET GTest::Main)
|
|
||||||
add_library(GTest::Main UNKNOWN IMPORTED)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT TARGET GMock::GMock)
|
if(NOT TARGET GMock::GMock)
|
||||||
|
@ -244,224 +151,17 @@ if(NOT TARGET GMock::Main)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(GMOCK_LIBRARY_EXISTS OFF)
|
set(GMOCK_LIBRARY_EXISTS OFF)
|
||||||
set(GTEST_LIBRARY_EXISTS OFF)
|
|
||||||
|
|
||||||
if(EXISTS "${GMOCK_LIBRARY}" OR EXISTS "${GMOCK_LIBRARY_DEBUG}" AND GMOCK_INCLUDE_DIR)
|
if(EXISTS "${GMOCK_LIBRARY}" OR EXISTS "${GMOCK_LIBRARY_DEBUG}" AND GMOCK_INCLUDE_DIR)
|
||||||
set(GMOCK_LIBRARY_EXISTS ON)
|
set(GMOCK_LIBRARY_EXISTS ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(EXISTS "${GTEST_LIBRARY}" OR EXISTS "${GTEST_LIBRARY_DEBUG}" AND GTEST_INCLUDE_DIR)
|
|
||||||
set(GTEST_LIBRARY_EXISTS ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT (${GMOCK_LIBRARY_EXISTS} AND ${GTEST_LIBRARY_EXISTS}))
|
|
||||||
|
|
||||||
include(ExternalProject)
|
|
||||||
|
|
||||||
if(GTEST_USE_STATIC_LIBS)
|
|
||||||
set(GTEST_CMAKE_ARGS -Dgtest_force_shared_crt:BOOL=ON -DBUILD_SHARED_LIBS=OFF)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
list(APPEND GTEST_CMAKE_ARGS
|
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
|
||||||
-Dgtest_hide_internal_symbols=ON
|
|
||||||
-DCMAKE_CXX_VISIBILITY_PRESET=hidden
|
|
||||||
-DCMAKE_VISIBILITY_INLINES_HIDDEN=ON
|
|
||||||
-DCMAKE_POLICY_DEFAULT_CMP0063=NEW
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
set(GTEST_LIBRARY_PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX})
|
|
||||||
else()
|
|
||||||
set(GTEST_CMAKE_ARGS -DBUILD_SHARED_LIBS=ON)
|
|
||||||
set(GTEST_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
|
|
||||||
endif()
|
|
||||||
if(WIN32)
|
|
||||||
list(APPEND GTEST_CMAKE_ARGS -Dgtest_disable_pthreads=ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${GMOCK_SRC_DIR}" STREQUAL "")
|
|
||||||
message(STATUS "Downloading GMock / GTest version ${GMOCK_VER} from git")
|
|
||||||
if("${GMOCK_VER}" STREQUAL "1.6.0" OR "${GMOCK_VER}" STREQUAL "1.7.0")
|
|
||||||
set(GTEST_BIN_DIR "${GMOCK_ROOT}/src/gtest-build")
|
|
||||||
set(GTEST_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GTEST_MAIN_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
mark_as_advanced(GTEST_LIBRARY)
|
|
||||||
mark_as_advanced(GTEST_MAIN_LIBRARY)
|
|
||||||
|
|
||||||
externalproject_add(
|
|
||||||
gtest
|
|
||||||
GIT_REPOSITORY "https://github.com/google/googletest.git"
|
|
||||||
GIT_TAG "release-${GMOCK_VER}"
|
|
||||||
PREFIX ${GMOCK_ROOT}
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
LOG_DOWNLOAD ON
|
|
||||||
LOG_CONFIGURE ON
|
|
||||||
LOG_BUILD ON
|
|
||||||
CMAKE_ARGS
|
|
||||||
${GTEST_CMAKE_ARGS}
|
|
||||||
BINARY_DIR ${GTEST_BIN_DIR}
|
|
||||||
BUILD_BYPRODUCTS
|
|
||||||
"${GTEST_LIBRARY}"
|
|
||||||
"${GTEST_MAIN_LIBRARY}"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build")
|
|
||||||
set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
mark_as_advanced(GMOCK_LIBRARY)
|
|
||||||
mark_as_advanced(GMOCK_MAIN_LIBRARY)
|
|
||||||
|
|
||||||
externalproject_add(
|
|
||||||
gmock
|
|
||||||
GIT_REPOSITORY "https://github.com/google/googlemock.git"
|
|
||||||
GIT_TAG "release-${GMOCK_VER}"
|
|
||||||
PREFIX ${GMOCK_ROOT}
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
LOG_DOWNLOAD ON
|
|
||||||
LOG_CONFIGURE ON
|
|
||||||
LOG_BUILD ON
|
|
||||||
CMAKE_ARGS
|
|
||||||
${GTEST_CMAKE_ARGS}
|
|
||||||
BINARY_DIR ${GMOCK_BIN_DIR}
|
|
||||||
BUILD_BYPRODUCTS
|
|
||||||
"${GMOCK_LIBRARY}"
|
|
||||||
"${GMOCK_MAIN_LIBRARY}"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_dependencies(gmock gtest)
|
|
||||||
|
|
||||||
add_dependencies(GTest::GTest gtest)
|
|
||||||
add_dependencies(GTest::Main gtest)
|
|
||||||
add_dependencies(GMock::GMock gmock)
|
|
||||||
add_dependencies(GMock::Main gmock)
|
|
||||||
|
|
||||||
externalproject_get_property(gtest source_dir)
|
|
||||||
set(GTEST_INCLUDE_DIR "${source_dir}/include")
|
|
||||||
mark_as_advanced(GTEST_INCLUDE_DIR)
|
|
||||||
externalproject_get_property(gmock source_dir)
|
|
||||||
set(GMOCK_INCLUDE_DIR "${source_dir}/include")
|
|
||||||
mark_as_advanced(GMOCK_INCLUDE_DIR)
|
|
||||||
else() #1.8.0
|
|
||||||
set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build")
|
|
||||||
set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
mark_as_advanced(GTEST_LIBRARY)
|
|
||||||
mark_as_advanced(GTEST_MAIN_LIBRARY)
|
|
||||||
mark_as_advanced(GMOCK_LIBRARY)
|
|
||||||
mark_as_advanced(GMOCK_MAIN_LIBRARY)
|
|
||||||
|
|
||||||
externalproject_add(
|
|
||||||
gmock
|
|
||||||
GIT_REPOSITORY "https://github.com/google/googletest.git"
|
|
||||||
GIT_TAG "release-${GMOCK_VER}"
|
|
||||||
PREFIX ${GMOCK_ROOT}
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
LOG_DOWNLOAD ON
|
|
||||||
LOG_CONFIGURE ON
|
|
||||||
LOG_BUILD ON
|
|
||||||
CMAKE_ARGS
|
|
||||||
${GTEST_CMAKE_ARGS}
|
|
||||||
BINARY_DIR "${GMOCK_BIN_DIR}"
|
|
||||||
BUILD_BYPRODUCTS
|
|
||||||
"${GTEST_LIBRARY}"
|
|
||||||
"${GTEST_MAIN_LIBRARY}"
|
|
||||||
"${GMOCK_LIBRARY}"
|
|
||||||
"${GMOCK_MAIN_LIBRARY}"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_dependencies(GTest::GTest gmock)
|
|
||||||
add_dependencies(GTest::Main gmock)
|
|
||||||
add_dependencies(GMock::GMock gmock)
|
|
||||||
add_dependencies(GMock::Main gmock)
|
|
||||||
|
|
||||||
externalproject_get_property(gmock source_dir)
|
|
||||||
set(GTEST_INCLUDE_DIR "${source_dir}/googletest/include")
|
|
||||||
set(GMOCK_INCLUDE_DIR "${source_dir}/googlemock/include")
|
|
||||||
mark_as_advanced(GMOCK_INCLUDE_DIR)
|
|
||||||
mark_as_advanced(GTEST_INCLUDE_DIR)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Prevent CMake from complaining about these directories missing when the libgtest/libgmock targets get used as dependencies
|
|
||||||
file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR})
|
|
||||||
else()
|
|
||||||
message(STATUS "Building Gmock / Gtest from dir ${GMOCK_SRC_DIR}")
|
|
||||||
|
|
||||||
set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build")
|
|
||||||
set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
mark_as_advanced(GTEST_LIBRARY)
|
|
||||||
mark_as_advanced(GTEST_MAIN_LIBRARY)
|
|
||||||
mark_as_advanced(GMOCK_LIBRARY)
|
|
||||||
mark_as_advanced(GMOCK_MAIN_LIBRARY)
|
|
||||||
|
|
||||||
if(EXISTS "${GMOCK_SRC_DIR}/gtest/include/gtest/gtest.h")
|
|
||||||
set(GTEST_INCLUDE_DIR "${GMOCK_SRC_DIR}/gtest/include")
|
|
||||||
mark_as_advanced(GTEST_INCLUDE_DIR)
|
|
||||||
endif()
|
|
||||||
if(EXISTS "${GMOCK_SRC_DIR}/include/gmock/gmock.h")
|
|
||||||
set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/include")
|
|
||||||
mark_as_advanced(GMOCK_INCLUDE_DIR)
|
|
||||||
elseif(EXISTS "${GMOCK_SRC_DIR}/../../include/gmock/gmock.h")
|
|
||||||
set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/../../include")
|
|
||||||
if(IS_ABSOLUTE "${GMOCK_INCLUDE_DIR}")
|
|
||||||
get_filename_component(GMOCK_INCLUDE_DIR "${GMOCK_INCLUDE_DIR}" ABSOLUTE)
|
|
||||||
endif()
|
|
||||||
mark_as_advanced(GMOCK_INCLUDE_DIR)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
externalproject_add(
|
|
||||||
gmock
|
|
||||||
SOURCE_DIR ${GMOCK_SRC_DIR}
|
|
||||||
PREFIX ${GMOCK_ROOT}
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
LOG_DOWNLOAD ON
|
|
||||||
LOG_CONFIGURE ON
|
|
||||||
LOG_BUILD ON
|
|
||||||
CMAKE_ARGS
|
|
||||||
${GTEST_CMAKE_ARGS}
|
|
||||||
BINARY_DIR "${GMOCK_BIN_DIR}"
|
|
||||||
BUILD_BYPRODUCTS
|
|
||||||
"${GTEST_LIBRARY}"
|
|
||||||
"${GTEST_MAIN_LIBRARY}"
|
|
||||||
"${GMOCK_LIBRARY}"
|
|
||||||
"${GMOCK_MAIN_LIBRARY}"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_dependencies(GTest::GTest gmock)
|
|
||||||
add_dependencies(GTest::Main gmock)
|
|
||||||
add_dependencies(GMock::GMock gmock)
|
|
||||||
add_dependencies(GMock::Main gmock)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
include(FindPackageHandleStandardArgs)
|
||||||
find_package_handle_standard_args(GTest DEFAULT_MSG GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY)
|
|
||||||
find_package_handle_standard_args(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY)
|
find_package_handle_standard_args(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY)
|
||||||
|
|
||||||
include(CMakeFindDependencyMacro)
|
include(CMakeFindDependencyMacro)
|
||||||
find_dependency(Threads)
|
find_dependency(Threads)
|
||||||
|
|
||||||
set_target_properties(GTest::GTest PROPERTIES
|
|
||||||
INTERFACE_LINK_LIBRARIES "Threads::Threads"
|
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
|
||||||
IMPORTED_LOCATION "${GTEST_LIBRARY}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(GTEST_INCLUDE_DIR)
|
|
||||||
set_target_properties(GTest::GTest PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}"
|
|
||||||
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_target_properties(GTest::Main PROPERTIES
|
|
||||||
INTERFACE_LINK_LIBRARIES "GTest::GTest"
|
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
|
||||||
IMPORTED_LOCATION "${GTEST_MAIN_LIBRARY}")
|
|
||||||
|
|
||||||
set_target_properties(GMock::GMock PROPERTIES
|
set_target_properties(GMock::GMock PROPERTIES
|
||||||
INTERFACE_LINK_LIBRARIES "Threads::Threads"
|
INTERFACE_LINK_LIBRARIES "Threads::Threads"
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||||
|
@ -477,13 +177,6 @@ if(GMOCK_INCLUDE_DIR)
|
||||||
# so just specify it on the link interface.
|
# so just specify it on the link interface.
|
||||||
set_property(TARGET GMock::GMock APPEND PROPERTY
|
set_property(TARGET GMock::GMock APPEND PROPERTY
|
||||||
INTERFACE_LINK_LIBRARIES GTest::GTest)
|
INTERFACE_LINK_LIBRARIES GTest::GTest)
|
||||||
elseif(GTEST_INCLUDE_DIR)
|
|
||||||
# GMock 1.7 and beyond doesn't have it as a link-time dependency anymore,
|
|
||||||
# so merge it's compile-time interface (include dirs) with ours.
|
|
||||||
set_property(TARGET GMock::GMock APPEND PROPERTY
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}")
|
|
||||||
set_property(TARGET GMock::GMock APPEND PROPERTY
|
|
||||||
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}")
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -492,17 +185,6 @@ set_target_properties(GMock::Main PROPERTIES
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||||
IMPORTED_LOCATION "${GMOCK_MAIN_LIBRARY}")
|
IMPORTED_LOCATION "${GMOCK_MAIN_LIBRARY}")
|
||||||
|
|
||||||
if(GTEST_FOUND)
|
|
||||||
set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIR})
|
|
||||||
set(GTEST_LIBRARIES GTest::GTest)
|
|
||||||
set(GTEST_MAIN_LIBRARIES GTest::Main)
|
|
||||||
set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
|
|
||||||
if(VERBOSE)
|
|
||||||
message(STATUS "GTest includes: ${GTEST_INCLUDE_DIRS}")
|
|
||||||
message(STATUS "GTest libs: ${GTEST_BOTH_LIBRARIES}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(GMOCK_FOUND)
|
if(GMOCK_FOUND)
|
||||||
set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR})
|
set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR})
|
||||||
set(GMOCK_LIBRARIES GMock::GMock)
|
set(GMOCK_LIBRARIES GMock::GMock)
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
|
||||||
using namespace Bsa;
|
using namespace Bsa;
|
||||||
|
|
||||||
|
@ -37,6 +38,31 @@ void BSAFile::fail(const std::string &msg)
|
||||||
throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + mFilename);
|
throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + mFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//the getHash code is from bsapack from ghostwheel
|
||||||
|
//the code is also the same as in https://github.com/arviceblot/bsatool_rs/commit/67cb59ec3aaeedc0849222ea387f031c33e48c81
|
||||||
|
BSAFile::Hash getHash(const std::string& name)
|
||||||
|
{
|
||||||
|
BSAFile::Hash hash;
|
||||||
|
unsigned l = (name.size() >> 1);
|
||||||
|
unsigned sum, off, temp, i, n;
|
||||||
|
|
||||||
|
for (sum = off = i = 0; i < l; i++) {
|
||||||
|
sum ^= (((unsigned)(name[i])) << (off & 0x1F));
|
||||||
|
off += 8;
|
||||||
|
}
|
||||||
|
hash.low = sum;
|
||||||
|
|
||||||
|
for (sum = off = 0; i < name.size(); i++) {
|
||||||
|
temp = (((unsigned)(name[i])) << (off & 0x1F));
|
||||||
|
sum ^= temp;
|
||||||
|
n = temp & 0x1F;
|
||||||
|
sum = (sum << (32 - n)) | (sum >> n); // binary "rotate right"
|
||||||
|
off += 8;
|
||||||
|
}
|
||||||
|
hash.high = sum;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
/// Read header information from the input source
|
/// Read header information from the input source
|
||||||
void BSAFile::readHeader()
|
void BSAFile::readHeader()
|
||||||
{
|
{
|
||||||
|
@ -113,14 +139,17 @@ void BSAFile::readHeader()
|
||||||
|
|
||||||
// Read the offset info into a temporary buffer
|
// Read the offset info into a temporary buffer
|
||||||
std::vector<uint32_t> offsets(3*filenum);
|
std::vector<uint32_t> offsets(3*filenum);
|
||||||
input.read(reinterpret_cast<char*>(&offsets[0]), 12*filenum);
|
input.read(reinterpret_cast<char*>(offsets.data()), 12*filenum);
|
||||||
|
|
||||||
// Read the string table
|
// Read the string table
|
||||||
mStringBuf.resize(dirsize-12*filenum);
|
mStringBuf.resize(dirsize-12*filenum);
|
||||||
input.read(&mStringBuf[0], mStringBuf.size());
|
input.read(mStringBuf.data(), mStringBuf.size());
|
||||||
|
|
||||||
// Check our position
|
// Check our position
|
||||||
assert(input.tellg() == std::streampos(12+dirsize));
|
assert(input.tellg() == std::streampos(12+dirsize));
|
||||||
|
std::vector<Hash> hashes(filenum);
|
||||||
|
static_assert(sizeof(Hash) == 8);
|
||||||
|
input.read(reinterpret_cast<char*>(hashes.data()), 8*filenum);
|
||||||
|
|
||||||
// Calculate the offset of the data buffer. All file offsets are
|
// Calculate the offset of the data buffer. All file offsets are
|
||||||
// relative to this. 12 header bytes + directory + hash table
|
// relative to this. 12 header bytes + directory + hash table
|
||||||
|
@ -129,23 +158,72 @@ void BSAFile::readHeader()
|
||||||
|
|
||||||
// Set up the the FileStruct table
|
// Set up the the FileStruct table
|
||||||
mFiles.resize(filenum);
|
mFiles.resize(filenum);
|
||||||
|
size_t endOfNameBuffer = 0;
|
||||||
for(size_t i=0;i<filenum;i++)
|
for(size_t i=0;i<filenum;i++)
|
||||||
{
|
{
|
||||||
FileStruct &fs = mFiles[i];
|
FileStruct &fs = mFiles[i];
|
||||||
fs.fileSize = offsets[i*2];
|
fs.fileSize = offsets[i*2];
|
||||||
fs.offset = offsets[i*2+1] + fileDataOffset;
|
fs.offset = offsets[i*2+1] + fileDataOffset;
|
||||||
fs.name = &mStringBuf[offsets[2*filenum+i]];
|
auto namesOffset = offsets[2*filenum+i];
|
||||||
|
fs.setNameInfos(namesOffset, &mStringBuf);
|
||||||
|
fs.hash = hashes[i];
|
||||||
|
|
||||||
|
endOfNameBuffer = std::max(endOfNameBuffer, namesOffset + std::strlen(fs.name())+1);
|
||||||
|
assert(endOfNameBuffer <= mStringBuf.size());
|
||||||
|
|
||||||
if(fs.offset + fs.fileSize > fsize)
|
if(fs.offset + fs.fileSize > fsize)
|
||||||
fail("Archive contains offsets outside itself");
|
fail("Archive contains offsets outside itself");
|
||||||
|
|
||||||
// Add the file name to the lookup
|
// Add the file name to the lookup
|
||||||
mLookup[fs.name] = i;
|
mLookup[fs.name()] = i;
|
||||||
}
|
}
|
||||||
|
mStringBuf.resize(endOfNameBuffer);
|
||||||
|
|
||||||
|
std::sort(mFiles.begin(), mFiles.end(), [](const FileStruct& left, const FileStruct& right) {
|
||||||
|
return left.offset < right.offset;
|
||||||
|
});
|
||||||
|
|
||||||
mIsLoaded = true;
|
mIsLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write header information to the output sink
|
||||||
|
void Bsa::BSAFile::writeHeader()
|
||||||
|
{
|
||||||
|
namespace bfs = boost::filesystem;
|
||||||
|
bfs::fstream output(mFilename, std::ios::binary | std::ios::in | std::ios::out);
|
||||||
|
|
||||||
|
uint32_t head[3];
|
||||||
|
head[0] = 0x100;
|
||||||
|
auto fileDataOffset = mFiles.empty() ? 12 : mFiles.front().offset;
|
||||||
|
head[1] = fileDataOffset - 12 - 8*mFiles.size();
|
||||||
|
|
||||||
|
output.seekp(0, std::ios_base::end);
|
||||||
|
|
||||||
|
head[2] = mFiles.size();
|
||||||
|
output.seekp(0);
|
||||||
|
output.write(reinterpret_cast<char*>(head), 12);
|
||||||
|
|
||||||
|
std::sort(mFiles.begin(), mFiles.end(), [](const FileStruct& left, const FileStruct& right) {
|
||||||
|
return std::make_pair(left.hash.low, left.hash.high) < std::make_pair(right.hash.low, right.hash.high);
|
||||||
|
});
|
||||||
|
|
||||||
|
size_t filenum = mFiles.size();
|
||||||
|
std::vector<uint32_t> offsets(3* filenum);
|
||||||
|
std::vector<Hash> hashes(filenum);
|
||||||
|
for(size_t i=0;i<filenum;i++)
|
||||||
|
{
|
||||||
|
auto& f = mFiles[i];
|
||||||
|
offsets[i*2] = f.fileSize;
|
||||||
|
offsets[i*2+1] = f.offset - fileDataOffset;
|
||||||
|
offsets[2*filenum+i] = f.namesOffset;
|
||||||
|
hashes[i] = f.hash;
|
||||||
|
}
|
||||||
|
output.write(reinterpret_cast<char*>(offsets.data()), sizeof(uint32_t)*offsets.size());
|
||||||
|
output.write(reinterpret_cast<char*>(mStringBuf.data()), mStringBuf.size());
|
||||||
|
output.seekp(fileDataOffset - 8*mFiles.size(), std::ios_base::beg);
|
||||||
|
output.write(reinterpret_cast<char*>(hashes.data()), sizeof(Hash)*hashes.size());
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the index of a given file name, or -1 if not found
|
/// Get the index of a given file name, or -1 if not found
|
||||||
int BSAFile::getIndex(const char *str) const
|
int BSAFile::getIndex(const char *str) const
|
||||||
{
|
{
|
||||||
|
@ -162,7 +240,22 @@ int BSAFile::getIndex(const char *str) const
|
||||||
void BSAFile::open(const std::string &file)
|
void BSAFile::open(const std::string &file)
|
||||||
{
|
{
|
||||||
mFilename = file;
|
mFilename = file;
|
||||||
readHeader();
|
if(boost::filesystem::exists(file))
|
||||||
|
readHeader();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
{ boost::filesystem::fstream(mFilename, std::ios::binary | std::ios::out); }
|
||||||
|
writeHeader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the archive, write the updated headers to the file
|
||||||
|
void Bsa::BSAFile::close()
|
||||||
|
{
|
||||||
|
if (!mHasChanged)
|
||||||
|
return;
|
||||||
|
|
||||||
|
writeHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
Files::IStreamPtr BSAFile::getFile(const char *file)
|
Files::IStreamPtr BSAFile::getFile(const char *file)
|
||||||
|
@ -181,3 +274,56 @@ Files::IStreamPtr BSAFile::getFile(const FileStruct *file)
|
||||||
{
|
{
|
||||||
return Files::openConstrainedFileStream (mFilename.c_str (), file->offset, file->fileSize);
|
return Files::openConstrainedFileStream (mFilename.c_str (), file->offset, file->fileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bsa::BSAFile::addFile(const std::string& filename, std::istream& file)
|
||||||
|
{
|
||||||
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
|
auto newStartOfDataBuffer = 12 + (12 + 8) * (mFiles.size() + 1) + mStringBuf.size() + filename.size() + 1;
|
||||||
|
if (mFiles.empty())
|
||||||
|
bfs::resize_file(mFilename, newStartOfDataBuffer);
|
||||||
|
|
||||||
|
bfs::fstream stream(mFilename, std::ios::binary | std::ios::in | std::ios::out);
|
||||||
|
|
||||||
|
FileStruct newFile;
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
newFile.fileSize = file.tellg();
|
||||||
|
newFile.setNameInfos(mStringBuf.size(), &mStringBuf);
|
||||||
|
newFile.hash = getHash(filename);
|
||||||
|
|
||||||
|
if(mFiles.empty())
|
||||||
|
newFile.offset = newStartOfDataBuffer;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<char> buffer;
|
||||||
|
while (mFiles.front().offset < newStartOfDataBuffer) {
|
||||||
|
FileStruct& firstFile = mFiles.front();
|
||||||
|
buffer.resize(firstFile.fileSize);
|
||||||
|
|
||||||
|
stream.seekg(firstFile.offset, std::ios::beg);
|
||||||
|
stream.read(buffer.data(), firstFile.fileSize);
|
||||||
|
|
||||||
|
stream.seekp(0, std::ios::end);
|
||||||
|
firstFile.offset = stream.tellp();
|
||||||
|
|
||||||
|
stream.write(buffer.data(), firstFile.fileSize);
|
||||||
|
|
||||||
|
//ensure sort order is preserved
|
||||||
|
std::rotate(mFiles.begin(), mFiles.begin() + 1, mFiles.end());
|
||||||
|
}
|
||||||
|
stream.seekp(0, std::ios::end);
|
||||||
|
newFile.offset = stream.tellp();
|
||||||
|
}
|
||||||
|
|
||||||
|
mStringBuf.insert(mStringBuf.end(), filename.begin(), filename.end());
|
||||||
|
mStringBuf.push_back('\0');
|
||||||
|
mFiles.push_back(newFile);
|
||||||
|
|
||||||
|
mHasChanged = true;
|
||||||
|
|
||||||
|
mLookup[filename.c_str()] = mFiles.size() - 1;
|
||||||
|
|
||||||
|
stream.seekp(0, std::ios::end);
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
stream << file.rdbuf();
|
||||||
|
}
|
||||||
|
|
|
@ -43,20 +43,42 @@ namespace Bsa
|
||||||
class BSAFile
|
class BSAFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
#pragma pack(push)
|
||||||
|
#pragma pack(1)
|
||||||
|
struct Hash
|
||||||
|
{
|
||||||
|
uint32_t low, high;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
/// Represents one file entry in the archive
|
/// Represents one file entry in the archive
|
||||||
struct FileStruct
|
struct FileStruct
|
||||||
{
|
{
|
||||||
|
void setNameInfos(size_t index,
|
||||||
|
std::vector<char>* stringBuf
|
||||||
|
) {
|
||||||
|
namesOffset = index;
|
||||||
|
namesBuffer = stringBuf;
|
||||||
|
}
|
||||||
|
|
||||||
// File size and offset in file. We store the offset from the
|
// File size and offset in file. We store the offset from the
|
||||||
// beginning of the file, not the offset into the data buffer
|
// beginning of the file, not the offset into the data buffer
|
||||||
// (which is what is stored in the archive.)
|
// (which is what is stored in the archive.)
|
||||||
uint32_t fileSize, offset;
|
uint32_t fileSize, offset;
|
||||||
|
Hash hash;
|
||||||
|
|
||||||
// Zero-terminated file name
|
// Zero-terminated file name
|
||||||
const char *name;
|
const char* name() const { return &(*namesBuffer)[namesOffset]; };
|
||||||
|
|
||||||
|
uint32_t namesOffset = 0;
|
||||||
|
std::vector<char>* namesBuffer = nullptr;
|
||||||
};
|
};
|
||||||
typedef std::vector<FileStruct> FileList;
|
typedef std::vector<FileStruct> FileList;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool mHasChanged = false;
|
||||||
|
|
||||||
/// Table of files in this archive
|
/// Table of files in this archive
|
||||||
FileList mFiles;
|
FileList mFiles;
|
||||||
|
|
||||||
|
@ -72,7 +94,7 @@ protected:
|
||||||
/// Case insensitive string comparison
|
/// Case insensitive string comparison
|
||||||
struct iltstr
|
struct iltstr
|
||||||
{
|
{
|
||||||
bool operator()(const char *s1, const char *s2) const
|
bool operator()(const std::string& s1, const std::string& s2) const
|
||||||
{ return Misc::StringUtils::ciLess(s1, s2); }
|
{ return Misc::StringUtils::ciLess(s1, s2); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,7 +102,7 @@ protected:
|
||||||
the files[] vector above. The iltstr ensures that file name
|
the files[] vector above. The iltstr ensures that file name
|
||||||
checks are case insensitive.
|
checks are case insensitive.
|
||||||
*/
|
*/
|
||||||
typedef std::map<const char*, int, iltstr> Lookup;
|
typedef std::map<std::string, int, iltstr> Lookup;
|
||||||
Lookup mLookup;
|
Lookup mLookup;
|
||||||
|
|
||||||
/// Error handling
|
/// Error handling
|
||||||
|
@ -88,9 +110,7 @@ protected:
|
||||||
|
|
||||||
/// Read header information from the input source
|
/// Read header information from the input source
|
||||||
virtual void readHeader();
|
virtual void readHeader();
|
||||||
|
virtual void writeHeader();
|
||||||
/// Read header information from the input source
|
|
||||||
|
|
||||||
|
|
||||||
/// Get the index of a given file name, or -1 if not found
|
/// Get the index of a given file name, or -1 if not found
|
||||||
/// @note Thread safe.
|
/// @note Thread safe.
|
||||||
|
@ -106,11 +126,16 @@ public:
|
||||||
: mIsLoaded(false)
|
: mIsLoaded(false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual ~BSAFile() = default;
|
virtual ~BSAFile()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
/// Open an archive file.
|
/// Open an archive file.
|
||||||
void open(const std::string &file);
|
void open(const std::string &file);
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
/* -----------------------------------
|
/* -----------------------------------
|
||||||
* Archive file routines
|
* Archive file routines
|
||||||
* -----------------------------------
|
* -----------------------------------
|
||||||
|
@ -131,6 +156,8 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual Files::IStreamPtr getFile(const FileStruct* file);
|
virtual Files::IStreamPtr getFile(const FileStruct* file);
|
||||||
|
|
||||||
|
virtual void addFile(const std::string& filename, std::istream& file);
|
||||||
|
|
||||||
/// Get a list of all files
|
/// Get a list of all files
|
||||||
/// @note Thread safe.
|
/// @note Thread safe.
|
||||||
const FileList &getList() const
|
const FileList &getList() const
|
||||||
|
|
|
@ -226,7 +226,6 @@ void CompressedBSAFile::readHeader()
|
||||||
FileStruct fileStruct{};
|
FileStruct fileStruct{};
|
||||||
fileStruct.fileSize = file.getSizeWithoutCompressionFlag();
|
fileStruct.fileSize = file.getSizeWithoutCompressionFlag();
|
||||||
fileStruct.offset = file.offset;
|
fileStruct.offset = file.offset;
|
||||||
fileStruct.name = nullptr;
|
|
||||||
mFiles.push_back(fileStruct);
|
mFiles.push_back(fileStruct);
|
||||||
|
|
||||||
fullPaths.push_back(folder);
|
fullPaths.push_back(folder);
|
||||||
|
@ -249,7 +248,7 @@ void CompressedBSAFile::readHeader()
|
||||||
}
|
}
|
||||||
|
|
||||||
//The vector guarantees that its elements occupy contiguous memory
|
//The vector guarantees that its elements occupy contiguous memory
|
||||||
mFiles[fileIndex].name = reinterpret_cast<char*>(mStringBuf.data() + mStringBuffOffset);
|
mFiles[fileIndex].setNameInfos(mStringBuffOffset, &mStringBuf);
|
||||||
|
|
||||||
fullPaths.at(fileIndex) += "\\" + std::string(mStringBuf.data() + mStringBuffOffset);
|
fullPaths.at(fileIndex) += "\\" + std::string(mStringBuf.data() + mStringBuffOffset);
|
||||||
|
|
||||||
|
@ -276,7 +275,7 @@ void CompressedBSAFile::readHeader()
|
||||||
fullPaths.at(fileIndex).c_str() + stringLength + 1u,
|
fullPaths.at(fileIndex).c_str() + stringLength + 1u,
|
||||||
mStringBuf.data() + mStringBuffOffset);
|
mStringBuf.data() + mStringBuffOffset);
|
||||||
|
|
||||||
mFiles[fileIndex].name = reinterpret_cast<char*>(mStringBuf.data() + mStringBuffOffset);
|
mFiles[fileIndex].setNameInfos(mStringBuffOffset, &mStringBuf);
|
||||||
|
|
||||||
mLookup[reinterpret_cast<char*>(mStringBuf.data() + mStringBuffOffset)] = fileIndex;
|
mLookup[reinterpret_cast<char*>(mStringBuf.data() + mStringBuffOffset)] = fileIndex;
|
||||||
mStringBuffOffset += stringLength + 1u;
|
mStringBuffOffset += stringLength + 1u;
|
||||||
|
@ -320,13 +319,19 @@ CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string
|
||||||
|
|
||||||
Files::IStreamPtr CompressedBSAFile::getFile(const FileStruct* file)
|
Files::IStreamPtr CompressedBSAFile::getFile(const FileStruct* file)
|
||||||
{
|
{
|
||||||
FileRecord fileRec = getFileRecord(file->name);
|
FileRecord fileRec = getFileRecord(file->name());
|
||||||
if (!fileRec.isValid()) {
|
if (!fileRec.isValid()) {
|
||||||
fail("File not found: " + std::string(file->name));
|
fail("File not found: " + std::string(file->name()));
|
||||||
}
|
}
|
||||||
return getFile(fileRec);
|
return getFile(fileRec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompressedBSAFile::addFile(const std::string& filename, std::istream& file)
|
||||||
|
{
|
||||||
|
assert(false); //not implemented yet
|
||||||
|
fail("Add file is not implemented for compressed BSA: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
Files::IStreamPtr CompressedBSAFile::getFile(const char* file)
|
Files::IStreamPtr CompressedBSAFile::getFile(const char* file)
|
||||||
{
|
{
|
||||||
FileRecord fileRec = getFileRecord(file);
|
FileRecord fileRec = getFileRecord(file);
|
||||||
|
@ -376,8 +381,10 @@ Files::IStreamPtr CompressedBSAFile::getFile(const FileRecord& fileRecord)
|
||||||
LZ4F_decompressionContext_t context = nullptr;
|
LZ4F_decompressionContext_t context = nullptr;
|
||||||
LZ4F_createDecompressionContext(&context, LZ4F_VERSION);
|
LZ4F_createDecompressionContext(&context, LZ4F_VERSION);
|
||||||
LZ4F_decompressOptions_t options = {};
|
LZ4F_decompressOptions_t options = {};
|
||||||
LZ4F_decompress(context, memoryStreamPtr->getRawData(), &uncompressedSize, buffer.get(), &size, &options);
|
LZ4F_errorCode_t errorCode = LZ4F_decompress(context, memoryStreamPtr->getRawData(), &uncompressedSize, buffer.get(), &size, &options);
|
||||||
LZ4F_errorCode_t errorCode = LZ4F_freeDecompressionContext(context);
|
if (LZ4F_isError(errorCode))
|
||||||
|
fail("LZ4 decompression error (file " + mFilename + "): " + LZ4F_getErrorName(errorCode));
|
||||||
|
errorCode = LZ4F_freeDecompressionContext(context);
|
||||||
if (LZ4F_isError(errorCode))
|
if (LZ4F_isError(errorCode))
|
||||||
fail("LZ4 decompression error (file " + mFilename + "): " + LZ4F_getErrorName(errorCode));
|
fail("LZ4 decompression error (file " + mFilename + "): " + LZ4F_getErrorName(errorCode));
|
||||||
}
|
}
|
||||||
|
@ -430,10 +437,10 @@ void CompressedBSAFile::convertCompressedSizesToUncompressed()
|
||||||
{
|
{
|
||||||
for (auto & mFile : mFiles)
|
for (auto & mFile : mFiles)
|
||||||
{
|
{
|
||||||
const FileRecord& fileRecord = getFileRecord(mFile.name);
|
const FileRecord& fileRecord = getFileRecord(mFile.name());
|
||||||
if (!fileRecord.isValid())
|
if (!fileRecord.isValid())
|
||||||
{
|
{
|
||||||
fail("Could not find file " + std::string(mFile.name) + " in BSA");
|
fail("Could not find file " + std::string(mFile.name()) + " in BSA");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileRecord.isCompressed(mCompressedByDefault))
|
if (!fileRecord.isCompressed(mCompressedByDefault))
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace Bsa
|
||||||
|
|
||||||
Files::IStreamPtr getFile(const char* filePath) override;
|
Files::IStreamPtr getFile(const char* filePath) override;
|
||||||
Files::IStreamPtr getFile(const FileStruct* fileStruct) override;
|
Files::IStreamPtr getFile(const FileStruct* fileStruct) override;
|
||||||
|
void addFile(const std::string& filename, std::istream& file) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,11 +146,10 @@ static void gdb_info(pid_t pid)
|
||||||
/*
|
/*
|
||||||
* Create a temp file to put gdb commands into.
|
* Create a temp file to put gdb commands into.
|
||||||
* Note: POSIX.1-2008 declares that the file should be already created with mode 0600 by default.
|
* Note: POSIX.1-2008 declares that the file should be already created with mode 0600 by default.
|
||||||
* Modern systems implement it and and suggest to do not touch masks in multithreaded applications.
|
* Modern systems implement it and suggest to do not touch masks in multithreaded applications.
|
||||||
* So CoverityScan warning is valid only for ancient versions of stdlib.
|
* So CoverityScan warning is valid only for ancient versions of stdlib.
|
||||||
*/
|
*/
|
||||||
strcpy(respfile, "/tmp/gdb-respfile-XXXXXX");
|
strcpy(respfile, "/tmp/gdb-respfile-XXXXXX");
|
||||||
// coverity[secure_temp]
|
|
||||||
if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr)
|
if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr)
|
||||||
{
|
{
|
||||||
fprintf(f, "attach %d\n"
|
fprintf(f, "attach %d\n"
|
||||||
|
|
|
@ -42,6 +42,7 @@ void ESM::Header::load (ESMReader &esm)
|
||||||
MasterData m;
|
MasterData m;
|
||||||
m.name = esm.getHString();
|
m.name = esm.getHString();
|
||||||
m.size = esm.getHNLong ("DATA");
|
m.size = esm.getHNLong ("DATA");
|
||||||
|
m.index = -1;
|
||||||
mMaster.push_back (m);
|
mMaster.push_back (m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,106 +14,53 @@ namespace
|
||||||
const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value;
|
const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value;
|
||||||
const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value;
|
const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value;
|
||||||
const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value;
|
const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value;
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Variant::Variant() : mType (VT_None), mData (nullptr) {}
|
template <typename T, bool orDefault = false>
|
||||||
|
struct GetValue
|
||||||
ESM::Variant::Variant(const std::string &value)
|
|
||||||
{
|
|
||||||
mData = nullptr;
|
|
||||||
mType = VT_None;
|
|
||||||
setType(VT_String);
|
|
||||||
setString(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Variant::Variant(int value)
|
|
||||||
{
|
|
||||||
mData = nullptr;
|
|
||||||
mType = VT_None;
|
|
||||||
setType(VT_Long);
|
|
||||||
setInteger(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Variant::Variant(float value)
|
|
||||||
{
|
|
||||||
mData = nullptr;
|
|
||||||
mType = VT_None;
|
|
||||||
setType(VT_Float);
|
|
||||||
setFloat(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Variant::~Variant()
|
|
||||||
{
|
|
||||||
delete mData;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Variant& ESM::Variant::operator= (const Variant& variant)
|
|
||||||
{
|
|
||||||
if (&variant!=this)
|
|
||||||
{
|
{
|
||||||
VariantDataBase *newData = variant.mData ? variant.mData->clone() : nullptr;
|
T operator()(int value) const { return static_cast<T>(value); }
|
||||||
|
|
||||||
delete mData;
|
T operator()(float value) const { return static_cast<T>(value); }
|
||||||
|
|
||||||
mType = variant.mType;
|
template <typename V>
|
||||||
mData = newData;
|
T operator()(const V&) const
|
||||||
}
|
{
|
||||||
|
if constexpr (orDefault)
|
||||||
|
return T {};
|
||||||
|
else
|
||||||
|
throw std::runtime_error("cannot convert variant");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return *this;
|
template <typename T>
|
||||||
}
|
struct SetValue
|
||||||
|
|
||||||
ESM::Variant& ESM::Variant::operator= (Variant&& variant)
|
|
||||||
{
|
|
||||||
if (&variant!=this)
|
|
||||||
{
|
{
|
||||||
delete mData;
|
T mValue;
|
||||||
|
|
||||||
mType = variant.mType;
|
explicit SetValue(T value) : mValue(value) {}
|
||||||
mData = variant.mData;
|
|
||||||
|
|
||||||
variant.mData = nullptr;
|
void operator()(int& value) const { value = static_cast<int>(mValue); }
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
void operator()(float& value) const { value = static_cast<float>(mValue); }
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Variant::Variant (const Variant& variant)
|
template <typename V>
|
||||||
: mType (variant.mType), mData (variant.mData ? variant.mData->clone() : nullptr)
|
void operator()(V&) const { throw std::runtime_error("cannot convert variant"); }
|
||||||
{}
|
};
|
||||||
|
|
||||||
ESM::Variant::Variant(Variant&& variant)
|
|
||||||
: mType (variant.mType), mData (variant.mData)
|
|
||||||
{
|
|
||||||
variant.mData = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::VarType ESM::Variant::getType() const
|
|
||||||
{
|
|
||||||
return mType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ESM::Variant::getString() const
|
std::string ESM::Variant::getString() const
|
||||||
{
|
{
|
||||||
if (!mData)
|
return std::get<std::string>(mData);
|
||||||
throw std::runtime_error ("can not convert empty variant to string");
|
|
||||||
|
|
||||||
return mData->getString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ESM::Variant::getInteger() const
|
int ESM::Variant::getInteger() const
|
||||||
{
|
{
|
||||||
if (!mData)
|
return std::visit(GetValue<int>{}, mData);
|
||||||
throw std::runtime_error ("can not convert empty variant to integer");
|
|
||||||
|
|
||||||
return mData->getInteger();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float ESM::Variant::getFloat() const
|
float ESM::Variant::getFloat() const
|
||||||
{
|
{
|
||||||
if (!mData)
|
return std::visit(GetValue<float>{}, mData);
|
||||||
throw std::runtime_error ("can not convert empty variant to float");
|
|
||||||
|
|
||||||
return mData->getFloat();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::Variant::read (ESMReader& esm, Format format)
|
void ESM::Variant::read (ESMReader& esm, Format format)
|
||||||
|
@ -202,9 +149,7 @@ void ESM::Variant::read (ESMReader& esm, Format format)
|
||||||
|
|
||||||
setType (type);
|
setType (type);
|
||||||
|
|
||||||
// data
|
std::visit(ReadESMVariantValue {esm, format, mType}, mData);
|
||||||
if (mData)
|
|
||||||
mData->read (esm, format, mType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::Variant::write (ESMWriter& esm, Format format) const
|
void ESM::Variant::write (ESMWriter& esm, Format format) const
|
||||||
|
@ -227,7 +172,7 @@ void ESM::Variant::write (ESMWriter& esm, Format format) const
|
||||||
// nothing to do here for GMST format
|
// nothing to do here for GMST format
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mData->write (esm, format, mType);
|
std::visit(WriteESMVariantValue {esm, format, mType}, mData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::Variant::write (std::ostream& stream) const
|
void ESM::Variant::write (std::ostream& stream) const
|
||||||
|
@ -246,27 +191,27 @@ void ESM::Variant::write (std::ostream& stream) const
|
||||||
|
|
||||||
case VT_Short:
|
case VT_Short:
|
||||||
|
|
||||||
stream << "variant short: " << mData->getInteger();
|
stream << "variant short: " << std::get<int>(mData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_Int:
|
case VT_Int:
|
||||||
|
|
||||||
stream << "variant int: " << mData->getInteger();
|
stream << "variant int: " << std::get<int>(mData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_Long:
|
case VT_Long:
|
||||||
|
|
||||||
stream << "variant long: " << mData->getInteger();
|
stream << "variant long: " << std::get<int>(mData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_Float:
|
case VT_Float:
|
||||||
|
|
||||||
stream << "variant float: " << mData->getFloat();
|
stream << "variant float: " << std::get<float>(mData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_String:
|
case VT_String:
|
||||||
|
|
||||||
stream << "variant string: \"" << mData->getString() << "\"";
|
stream << "variant string: \"" << std::get<std::string>(mData) << "\"";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,74 +220,50 @@ void ESM::Variant::setType (VarType type)
|
||||||
{
|
{
|
||||||
if (type!=mType)
|
if (type!=mType)
|
||||||
{
|
{
|
||||||
VariantDataBase *newData = nullptr;
|
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case VT_Unknown:
|
case VT_Unknown:
|
||||||
case VT_None:
|
case VT_None:
|
||||||
|
mData = std::monostate {};
|
||||||
break; // no data
|
break;
|
||||||
|
|
||||||
case VT_Short:
|
case VT_Short:
|
||||||
case VT_Int:
|
case VT_Int:
|
||||||
case VT_Long:
|
case VT_Long:
|
||||||
|
mData = std::visit(GetValue<int, true>{}, mData);
|
||||||
newData = new VariantIntegerData (mData);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_Float:
|
case VT_Float:
|
||||||
|
mData = std::visit(GetValue<float, true>{}, mData);
|
||||||
newData = new VariantFloatData (mData);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_String:
|
case VT_String:
|
||||||
|
mData = std::string {};
|
||||||
newData = new VariantStringData (mData);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete mData;
|
|
||||||
mData = newData;
|
|
||||||
mType = type;
|
mType = type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::Variant::setString (const std::string& value)
|
void ESM::Variant::setString (const std::string& value)
|
||||||
{
|
{
|
||||||
if (!mData)
|
std::get<std::string>(mData) = value;
|
||||||
throw std::runtime_error ("can not assign string to empty variant");
|
}
|
||||||
|
|
||||||
mData->setString (value);
|
void ESM::Variant::setString (std::string&& value)
|
||||||
|
{
|
||||||
|
std::get<std::string>(mData) = std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::Variant::setInteger (int value)
|
void ESM::Variant::setInteger (int value)
|
||||||
{
|
{
|
||||||
if (!mData)
|
std::visit(SetValue(value), mData);
|
||||||
throw std::runtime_error ("can not assign integer to empty variant");
|
|
||||||
|
|
||||||
mData->setInteger (value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::Variant::setFloat (float value)
|
void ESM::Variant::setFloat (float value)
|
||||||
{
|
{
|
||||||
if (!mData)
|
std::visit(SetValue(value), mData);
|
||||||
throw std::runtime_error ("can not assign float to empty variant");
|
|
||||||
|
|
||||||
mData->setFloat (value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ESM::Variant::isEqual (const Variant& value) const
|
|
||||||
{
|
|
||||||
if (mType!=value.mType)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!mData)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
assert (value.mData);
|
|
||||||
|
|
||||||
return mData->isEqual (*value.mData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& ESM::operator<< (std::ostream& stream, const Variant& value)
|
std::ostream& ESM::operator<< (std::ostream& stream, const Variant& value)
|
||||||
|
@ -350,13 +271,3 @@ std::ostream& ESM::operator<< (std::ostream& stream, const Variant& value)
|
||||||
value.write (stream);
|
value.write (stream);
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ESM::operator== (const Variant& left, const Variant& right)
|
|
||||||
{
|
|
||||||
return left.isEqual (right);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ESM::operator!= (const Variant& left, const Variant& right)
|
|
||||||
{
|
|
||||||
return !(left==right);
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <variant>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
@ -20,12 +22,10 @@ namespace ESM
|
||||||
VT_String
|
VT_String
|
||||||
};
|
};
|
||||||
|
|
||||||
class VariantDataBase;
|
|
||||||
|
|
||||||
class Variant
|
class Variant
|
||||||
{
|
{
|
||||||
VarType mType;
|
VarType mType;
|
||||||
VariantDataBase *mData;
|
std::variant<std::monostate, int, float, std::string> mData;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -37,21 +37,17 @@ namespace ESM
|
||||||
Format_Local // local script variables in save game files
|
Format_Local // local script variables in save game files
|
||||||
};
|
};
|
||||||
|
|
||||||
Variant();
|
Variant() : mType (VT_None), mData (std::monostate{}) {}
|
||||||
|
|
||||||
Variant (const std::string& value);
|
explicit Variant(const std::string& value) : mType(VT_String), mData(value) {}
|
||||||
Variant (int value);
|
|
||||||
Variant (float value);
|
|
||||||
|
|
||||||
~Variant();
|
explicit Variant(std::string&& value) : mType(VT_String), mData(std::move(value)) {}
|
||||||
|
|
||||||
Variant& operator= (const Variant& variant);
|
explicit Variant(int value) : mType(VT_Long), mData(value) {}
|
||||||
Variant& operator= (Variant && variant);
|
|
||||||
|
|
||||||
Variant (const Variant& variant);
|
explicit Variant(float value) : mType(VT_Float), mData(value) {}
|
||||||
Variant (Variant&& variant);
|
|
||||||
|
|
||||||
VarType getType() const;
|
VarType getType() const { return mType; }
|
||||||
|
|
||||||
std::string getString() const;
|
std::string getString() const;
|
||||||
///< Will throw an exception, if value can not be represented as a string.
|
///< Will throw an exception, if value can not be represented as a string.
|
||||||
|
@ -75,19 +71,27 @@ namespace ESM
|
||||||
void setString (const std::string& value);
|
void setString (const std::string& value);
|
||||||
///< Will throw an exception, if type is not compatible with string.
|
///< Will throw an exception, if type is not compatible with string.
|
||||||
|
|
||||||
|
void setString (std::string&& value);
|
||||||
|
///< Will throw an exception, if type is not compatible with string.
|
||||||
|
|
||||||
void setInteger (int value);
|
void setInteger (int value);
|
||||||
///< Will throw an exception, if type is not compatible with integer.
|
///< Will throw an exception, if type is not compatible with integer.
|
||||||
|
|
||||||
void setFloat (float value);
|
void setFloat (float value);
|
||||||
///< Will throw an exception, if type is not compatible with float.
|
///< Will throw an exception, if type is not compatible with float.
|
||||||
|
|
||||||
bool isEqual (const Variant& value) const;
|
friend bool operator==(const Variant& left, const Variant& right)
|
||||||
|
{
|
||||||
|
return std::tie(left.mType, left.mData) == std::tie(right.mType, right.mData);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const Variant& left, const Variant& right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const Variant& value);
|
std::ostream& operator<<(std::ostream& stream, const Variant& value);
|
||||||
|
|
||||||
bool operator== (const Variant& left, const Variant& right);
|
|
||||||
bool operator!= (const Variant& left, const Variant& right);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,71 +5,7 @@
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
ESM::VariantDataBase::~VariantDataBase() {}
|
void ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, std::string& out)
|
||||||
|
|
||||||
std::string ESM::VariantDataBase::getString (bool default_) const
|
|
||||||
{
|
|
||||||
if (default_)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
throw std::runtime_error ("can not convert variant to string");
|
|
||||||
}
|
|
||||||
|
|
||||||
int ESM::VariantDataBase::getInteger (bool default_) const
|
|
||||||
{
|
|
||||||
if (default_)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
throw std::runtime_error ("can not convert variant to integer");
|
|
||||||
}
|
|
||||||
|
|
||||||
float ESM::VariantDataBase::getFloat (bool default_) const
|
|
||||||
{
|
|
||||||
if (default_)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
throw std::runtime_error ("can not convert variant to float");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantDataBase::setString (const std::string& value)
|
|
||||||
{
|
|
||||||
throw std::runtime_error ("conversion of string to variant not possible");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantDataBase::setInteger (int value)
|
|
||||||
{
|
|
||||||
throw std::runtime_error ("conversion of integer to variant not possible");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantDataBase::setFloat (float value)
|
|
||||||
{
|
|
||||||
throw std::runtime_error ("conversion of float to variant not possible");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ESM::VariantStringData::VariantStringData (const VariantDataBase *data)
|
|
||||||
{
|
|
||||||
if (data)
|
|
||||||
mValue = data->getString (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::VariantDataBase *ESM::VariantStringData::clone() const
|
|
||||||
{
|
|
||||||
return new VariantStringData (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ESM::VariantStringData::getString (bool default_) const
|
|
||||||
{
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantStringData::setString (const std::string& value)
|
|
||||||
{
|
|
||||||
mValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarType type)
|
|
||||||
{
|
{
|
||||||
if (type!=VT_String)
|
if (type!=VT_String)
|
||||||
throw std::logic_error ("not a string type");
|
throw std::logic_error ("not a string type");
|
||||||
|
@ -84,10 +20,10 @@ void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarTy
|
||||||
esm.fail ("local variables of type string not supported");
|
esm.fail ("local variables of type string not supported");
|
||||||
|
|
||||||
// GMST
|
// GMST
|
||||||
mValue = esm.getHString();
|
out = esm.getHString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::VariantStringData::write (ESMWriter& esm, Variant::Format format, VarType type) const
|
void ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, const std::string& in)
|
||||||
{
|
{
|
||||||
if (type!=VT_String)
|
if (type!=VT_String)
|
||||||
throw std::logic_error ("not a string type");
|
throw std::logic_error ("not a string type");
|
||||||
|
@ -98,49 +34,14 @@ void ESM::VariantStringData::write (ESMWriter& esm, Variant::Format format, VarT
|
||||||
if (format==Variant::Format_Info)
|
if (format==Variant::Format_Info)
|
||||||
throw std::runtime_error ("info variables of type string not supported");
|
throw std::runtime_error ("info variables of type string not supported");
|
||||||
|
|
||||||
|
if (format==Variant::Format_Local)
|
||||||
|
throw std::runtime_error ("local variables of type string not supported");
|
||||||
|
|
||||||
// GMST
|
// GMST
|
||||||
esm.writeHNString ("STRV", mValue);
|
esm.writeHNString("STRV", in);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ESM::VariantStringData::isEqual (const VariantDataBase& value) const
|
void ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, int& out)
|
||||||
{
|
|
||||||
return dynamic_cast<const VariantStringData&> (value).mValue==mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ESM::VariantIntegerData::VariantIntegerData (const VariantDataBase *data) : mValue (0)
|
|
||||||
{
|
|
||||||
if (data)
|
|
||||||
mValue = data->getInteger (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::VariantDataBase *ESM::VariantIntegerData::clone() const
|
|
||||||
{
|
|
||||||
return new VariantIntegerData (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ESM::VariantIntegerData::getInteger (bool default_) const
|
|
||||||
{
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ESM::VariantIntegerData::getFloat (bool default_) const
|
|
||||||
{
|
|
||||||
return static_cast<float>(mValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantIntegerData::setInteger (int value)
|
|
||||||
{
|
|
||||||
mValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantIntegerData::setFloat (float value)
|
|
||||||
{
|
|
||||||
mValue = static_cast<int> (value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarType type)
|
|
||||||
{
|
{
|
||||||
if (type!=VT_Short && type!=VT_Long && type!=VT_Int)
|
if (type!=VT_Short && type!=VT_Long && type!=VT_Int)
|
||||||
throw std::logic_error ("not an integer type");
|
throw std::logic_error ("not an integer type");
|
||||||
|
@ -153,12 +54,12 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT
|
||||||
if (type==VT_Short)
|
if (type==VT_Short)
|
||||||
{
|
{
|
||||||
if (value!=value)
|
if (value!=value)
|
||||||
mValue = 0; // nan
|
out = 0; // nan
|
||||||
else
|
else
|
||||||
mValue = static_cast<short> (value);
|
out = static_cast<short> (value);
|
||||||
}
|
}
|
||||||
else if (type==VT_Long)
|
else if (type==VT_Long)
|
||||||
mValue = static_cast<int> (value);
|
out = static_cast<int> (value);
|
||||||
else
|
else
|
||||||
esm.fail ("unsupported global variable integer type");
|
esm.fail ("unsupported global variable integer type");
|
||||||
}
|
}
|
||||||
|
@ -173,7 +74,7 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT
|
||||||
esm.fail (stream.str());
|
esm.fail (stream.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.getHT (mValue);
|
esm.getHT(out);
|
||||||
}
|
}
|
||||||
else if (format==Variant::Format_Local)
|
else if (format==Variant::Format_Local)
|
||||||
{
|
{
|
||||||
|
@ -181,18 +82,18 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT
|
||||||
{
|
{
|
||||||
short value;
|
short value;
|
||||||
esm.getHT(value);
|
esm.getHT(value);
|
||||||
mValue = value;
|
out = value;
|
||||||
}
|
}
|
||||||
else if (type==VT_Int)
|
else if (type==VT_Int)
|
||||||
{
|
{
|
||||||
esm.getHT(mValue);
|
esm.getHT(out);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esm.fail("unsupported local variable integer type");
|
esm.fail("unsupported local variable integer type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, VarType type) const
|
void ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, int in)
|
||||||
{
|
{
|
||||||
if (type!=VT_Short && type!=VT_Long && type!=VT_Int)
|
if (type!=VT_Short && type!=VT_Long && type!=VT_Int)
|
||||||
throw std::logic_error ("not an integer type");
|
throw std::logic_error ("not an integer type");
|
||||||
|
@ -201,7 +102,7 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var
|
||||||
{
|
{
|
||||||
if (type==VT_Short || type==VT_Long)
|
if (type==VT_Short || type==VT_Long)
|
||||||
{
|
{
|
||||||
float value = static_cast<float>(mValue);
|
float value = static_cast<float>(in);
|
||||||
esm.writeHNString ("FNAM", type==VT_Short ? "s" : "l");
|
esm.writeHNString ("FNAM", type==VT_Short ? "s" : "l");
|
||||||
esm.writeHNT ("FLTV", value);
|
esm.writeHNT ("FLTV", value);
|
||||||
}
|
}
|
||||||
|
@ -219,72 +120,35 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var
|
||||||
throw std::runtime_error (stream.str());
|
throw std::runtime_error (stream.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.writeHNT ("INTV", mValue);
|
esm.writeHNT("INTV", in);
|
||||||
}
|
}
|
||||||
else if (format==Variant::Format_Local)
|
else if (format==Variant::Format_Local)
|
||||||
{
|
{
|
||||||
if (type==VT_Short)
|
if (type==VT_Short)
|
||||||
esm.writeHNT ("STTV", (short)mValue);
|
esm.writeHNT("STTV", static_cast<short>(in));
|
||||||
else if (type == VT_Int)
|
else if (type == VT_Int)
|
||||||
esm.writeHNT ("INTV", mValue);
|
esm.writeHNT("INTV", in);
|
||||||
else
|
else
|
||||||
throw std::runtime_error("unsupported local variable integer type");
|
throw std::runtime_error("unsupported local variable integer type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ESM::VariantIntegerData::isEqual (const VariantDataBase& value) const
|
void ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, float& out)
|
||||||
{
|
|
||||||
return dynamic_cast<const VariantIntegerData&> (value).mValue==mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ESM::VariantFloatData::VariantFloatData (const VariantDataBase *data) : mValue (0)
|
|
||||||
{
|
|
||||||
if (data)
|
|
||||||
mValue = data->getFloat (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::VariantDataBase *ESM::VariantFloatData::clone() const
|
|
||||||
{
|
|
||||||
return new VariantFloatData (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ESM::VariantFloatData::getInteger (bool default_) const
|
|
||||||
{
|
|
||||||
return static_cast<int> (mValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ESM::VariantFloatData::getFloat (bool default_) const
|
|
||||||
{
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantFloatData::setInteger (int value)
|
|
||||||
{
|
|
||||||
mValue = static_cast<float>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantFloatData::setFloat (float value)
|
|
||||||
{
|
|
||||||
mValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::VariantFloatData::read (ESMReader& esm, Variant::Format format, VarType type)
|
|
||||||
{
|
{
|
||||||
if (type!=VT_Float)
|
if (type!=VT_Float)
|
||||||
throw std::logic_error ("not a float type");
|
throw std::logic_error ("not a float type");
|
||||||
|
|
||||||
if (format==Variant::Format_Global)
|
if (format==Variant::Format_Global)
|
||||||
{
|
{
|
||||||
esm.getHNT (mValue, "FLTV");
|
esm.getHNT(out, "FLTV");
|
||||||
}
|
}
|
||||||
else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local)
|
else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local)
|
||||||
{
|
{
|
||||||
esm.getHT (mValue);
|
esm.getHT(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarType type) const
|
void ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, float in)
|
||||||
{
|
{
|
||||||
if (type!=VT_Float)
|
if (type!=VT_Float)
|
||||||
throw std::logic_error ("not a float type");
|
throw std::logic_error ("not a float type");
|
||||||
|
@ -292,15 +156,10 @@ void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarTy
|
||||||
if (format==Variant::Format_Global)
|
if (format==Variant::Format_Global)
|
||||||
{
|
{
|
||||||
esm.writeHNString ("FNAM", "f");
|
esm.writeHNString ("FNAM", "f");
|
||||||
esm.writeHNT ("FLTV", mValue);
|
esm.writeHNT("FLTV", in);
|
||||||
}
|
}
|
||||||
else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local)
|
else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local)
|
||||||
{
|
{
|
||||||
esm.writeHNT ("FLTV", mValue);
|
esm.writeHNT("FLTV", in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ESM::VariantFloatData::isEqual (const VariantDataBase& value) const
|
|
||||||
{
|
|
||||||
return dynamic_cast<const VariantFloatData&> (value).mValue==mValue;
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,177 +2,58 @@
|
||||||
#define OPENMW_ESM_VARIANTIMP_H
|
#define OPENMW_ESM_VARIANTIMP_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "variant.hpp"
|
#include "variant.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
class VariantDataBase
|
void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, std::string& value);
|
||||||
|
|
||||||
|
void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, float& value);
|
||||||
|
|
||||||
|
void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, int& value);
|
||||||
|
|
||||||
|
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, const std::string& value);
|
||||||
|
|
||||||
|
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, float value);
|
||||||
|
|
||||||
|
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, int value);
|
||||||
|
|
||||||
|
struct ReadESMVariantValue
|
||||||
{
|
{
|
||||||
public:
|
std::reference_wrapper<ESMReader> mReader;
|
||||||
|
Variant::Format mFormat;
|
||||||
|
VarType mType;
|
||||||
|
|
||||||
virtual ~VariantDataBase();
|
ReadESMVariantValue(ESMReader& reader, Variant::Format format, VarType type)
|
||||||
|
: mReader(reader), mFormat(format), mType(type) {}
|
||||||
|
|
||||||
virtual VariantDataBase *clone() const = 0;
|
void operator()(std::monostate) const {}
|
||||||
|
|
||||||
virtual std::string getString (bool default_ = false) const;
|
|
||||||
///< Will throw an exception, if value can not be represented as a string.
|
|
||||||
///
|
|
||||||
/// \note Numeric values are not converted to strings.
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
///
|
|
||||||
/// Default-implementation: throw an exception.
|
|
||||||
|
|
||||||
virtual int getInteger (bool default_ = false) const;
|
|
||||||
///< Will throw an exception, if value can not be represented as an integer (implicit
|
|
||||||
/// casting of float values is permitted).
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
///
|
|
||||||
/// Default-implementation: throw an exception.
|
|
||||||
|
|
||||||
virtual float getFloat (bool default_ = false) const;
|
|
||||||
///< Will throw an exception, if value can not be represented as a float value.
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
///
|
|
||||||
/// Default-implementation: throw an exception.
|
|
||||||
|
|
||||||
virtual void setString (const std::string& value);
|
|
||||||
///< Will throw an exception, if type is not compatible with string.
|
|
||||||
///
|
|
||||||
/// Default-implementation: throw an exception.
|
|
||||||
|
|
||||||
virtual void setInteger (int value);
|
|
||||||
///< Will throw an exception, if type is not compatible with integer.
|
|
||||||
///
|
|
||||||
/// Default-implementation: throw an exception.
|
|
||||||
|
|
||||||
virtual void setFloat (float value);
|
|
||||||
///< Will throw an exception, if type is not compatible with float.
|
|
||||||
///
|
|
||||||
/// Default-implementation: throw an exception.
|
|
||||||
|
|
||||||
virtual void read (ESMReader& esm, Variant::Format format, VarType type) = 0;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail
|
|
||||||
|
|
||||||
virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const = 0;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown.
|
|
||||||
|
|
||||||
virtual bool isEqual (const VariantDataBase& value) const = 0;
|
|
||||||
///< If the (C++) type of \a value does not match the type of *this, an exception is thrown.
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(T& value) const
|
||||||
|
{
|
||||||
|
readESMVariantValue(mReader.get(), mFormat, mType, value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class VariantStringData : public VariantDataBase
|
struct WriteESMVariantValue
|
||||||
{
|
{
|
||||||
std::string mValue;
|
std::reference_wrapper<ESMWriter> mWriter;
|
||||||
|
Variant::Format mFormat;
|
||||||
|
VarType mType;
|
||||||
|
|
||||||
public:
|
WriteESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type)
|
||||||
|
: mWriter(writer), mFormat(format), mType(type) {}
|
||||||
|
|
||||||
VariantStringData (const VariantDataBase *data = nullptr);
|
void operator()(std::monostate) const {}
|
||||||
///< Calling the constructor with an incompatible data type will result in a silent
|
|
||||||
/// default initialisation.
|
|
||||||
|
|
||||||
VariantDataBase *clone() const override;
|
template <typename T>
|
||||||
|
void operator()(const T& value) const
|
||||||
std::string getString (bool default_ = false) const override;
|
{
|
||||||
///< Will throw an exception, if value can not be represented as a string.
|
writeESMVariantValue(mWriter.get(), mFormat, mType, value);
|
||||||
///
|
}
|
||||||
/// \note Numeric values are not converted to strings.
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
|
|
||||||
void setString (const std::string& value) override;
|
|
||||||
///< Will throw an exception, if type is not compatible with string.
|
|
||||||
|
|
||||||
void read (ESMReader& esm, Variant::Format format, VarType type) override;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail
|
|
||||||
|
|
||||||
void write (ESMWriter& esm, Variant::Format format, VarType type) const override;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown.
|
|
||||||
|
|
||||||
bool isEqual (const VariantDataBase& value) const override;
|
|
||||||
///< If the (C++) type of \a value does not match the type of *this, an exception is thrown.
|
|
||||||
};
|
|
||||||
|
|
||||||
class VariantIntegerData : public VariantDataBase
|
|
||||||
{
|
|
||||||
int mValue;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
VariantIntegerData (const VariantDataBase *data = nullptr);
|
|
||||||
///< Calling the constructor with an incompatible data type will result in a silent
|
|
||||||
/// default initialisation.
|
|
||||||
|
|
||||||
VariantDataBase *clone() const override;
|
|
||||||
|
|
||||||
int getInteger (bool default_ = false) const override;
|
|
||||||
///< Will throw an exception, if value can not be represented as an integer (implicit
|
|
||||||
/// casting of float values is permitted).
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
|
|
||||||
float getFloat (bool default_ = false) const override;
|
|
||||||
///< Will throw an exception, if value can not be represented as a float value.
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
|
|
||||||
void setInteger (int value) override;
|
|
||||||
///< Will throw an exception, if type is not compatible with integer.
|
|
||||||
|
|
||||||
void setFloat (float value) override;
|
|
||||||
///< Will throw an exception, if type is not compatible with float.
|
|
||||||
|
|
||||||
void read (ESMReader& esm, Variant::Format format, VarType type) override;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail
|
|
||||||
|
|
||||||
void write (ESMWriter& esm, Variant::Format format, VarType type) const override;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown.
|
|
||||||
|
|
||||||
bool isEqual (const VariantDataBase& value) const override;
|
|
||||||
///< If the (C++) type of \a value does not match the type of *this, an exception is thrown.
|
|
||||||
};
|
|
||||||
|
|
||||||
class VariantFloatData : public VariantDataBase
|
|
||||||
{
|
|
||||||
float mValue;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
VariantFloatData (const VariantDataBase *data = nullptr);
|
|
||||||
///< Calling the constructor with an incompatible data type will result in a silent
|
|
||||||
/// default initialisation.
|
|
||||||
|
|
||||||
VariantDataBase *clone() const override;
|
|
||||||
|
|
||||||
int getInteger (bool default_ = false) const override;
|
|
||||||
///< Will throw an exception, if value can not be represented as an integer (implicit
|
|
||||||
/// casting of float values is permitted).
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
|
|
||||||
float getFloat (bool default_ = false) const override;
|
|
||||||
///< Will throw an exception, if value can not be represented as a float value.
|
|
||||||
///
|
|
||||||
/// \param default_ Return a default value instead of throwing an exception.
|
|
||||||
|
|
||||||
void setInteger (int value) override;
|
|
||||||
///< Will throw an exception, if type is not compatible with integer.
|
|
||||||
|
|
||||||
void setFloat (float value) override;
|
|
||||||
///< Will throw an exception, if type is not compatible with float.
|
|
||||||
|
|
||||||
void read (ESMReader& esm, Variant::Format format, VarType type) override;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail
|
|
||||||
|
|
||||||
void write (ESMWriter& esm, Variant::Format format, VarType type) const override;
|
|
||||||
///< If \a type is not supported by \a format, an exception is thrown.
|
|
||||||
|
|
||||||
bool isEqual (const VariantDataBase& value) const override;
|
|
||||||
///< If the (C++) type of \a value does not match the type of *this, an exception is thrown.
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -569,7 +569,8 @@ namespace Gui
|
||||||
resolution = std::min(960, std::max(48, resolution));
|
resolution = std::min(960, std::max(48, resolution));
|
||||||
|
|
||||||
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||||
resolution *= uiScale;
|
if (uiScale > 0.f)
|
||||||
|
resolution *= uiScale;
|
||||||
|
|
||||||
MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property");
|
MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property");
|
||||||
resolutionNode->addAttribute("key", "Resolution");
|
resolutionNode->addAttribute("key", "Resolution");
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace Misc
|
||||||
explicit FrameRateLimiter(std::chrono::duration<Rep, Ratio> maxFrameDuration,
|
explicit FrameRateLimiter(std::chrono::duration<Rep, Ratio> maxFrameDuration,
|
||||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())
|
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())
|
||||||
: mMaxFrameDuration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(maxFrameDuration))
|
: mMaxFrameDuration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(maxFrameDuration))
|
||||||
|
, mLastFrameDuration(0)
|
||||||
, mLastMeasurement(now)
|
, mLastMeasurement(now)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,6 @@ namespace osg
|
||||||
class Material;
|
class Material;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace osgParticle
|
|
||||||
{
|
|
||||||
class Emitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace NifOsg
|
namespace NifOsg
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -227,6 +227,7 @@ namespace Resource
|
||||||
, mAutoUseSpecularMaps(false)
|
, mAutoUseSpecularMaps(false)
|
||||||
, mApplyLightingToEnvMaps(false)
|
, mApplyLightingToEnvMaps(false)
|
||||||
, mLightingMethod(SceneUtil::LightingMethod::FFP)
|
, mLightingMethod(SceneUtil::LightingMethod::FFP)
|
||||||
|
, mConvertAlphaTestToAlphaToCoverage(false)
|
||||||
, mInstanceCache(new MultiObjectCache)
|
, mInstanceCache(new MultiObjectCache)
|
||||||
, mSharedStateManager(new SharedStateManager)
|
, mSharedStateManager(new SharedStateManager)
|
||||||
, mImageManager(imageManager)
|
, mImageManager(imageManager)
|
||||||
|
|
|
@ -375,6 +375,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
|
||||||
pack_evt.y = mMouseY = evt.motion.y;
|
pack_evt.y = mMouseY = evt.motion.y;
|
||||||
pack_evt.xrel = evt.motion.xrel;
|
pack_evt.xrel = evt.motion.xrel;
|
||||||
pack_evt.yrel = evt.motion.yrel;
|
pack_evt.yrel = evt.motion.yrel;
|
||||||
|
pack_evt.type = SDL_MOUSEMOTION;
|
||||||
if (mFirstMouseMove)
|
if (mFirstMouseMove)
|
||||||
{
|
{
|
||||||
// first event should be treated as non-relative, since there's no point of reference
|
// first event should be treated as non-relative, since there's no point of reference
|
||||||
|
@ -387,6 +388,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
|
||||||
{
|
{
|
||||||
mMouseZ += pack_evt.zrel = (evt.wheel.y * 120);
|
mMouseZ += pack_evt.zrel = (evt.wheel.y * 120);
|
||||||
pack_evt.z = mMouseZ;
|
pack_evt.z = mMouseZ;
|
||||||
|
pack_evt.type = SDL_MOUSEWHEEL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,6 +50,7 @@ namespace Shader
|
||||||
, mAutoUseNormalMaps(false)
|
, mAutoUseNormalMaps(false)
|
||||||
, mAutoUseSpecularMaps(false)
|
, mAutoUseSpecularMaps(false)
|
||||||
, mApplyLightingToEnvMaps(false)
|
, mApplyLightingToEnvMaps(false)
|
||||||
|
, mConvertAlphaTestToAlphaToCoverage(false)
|
||||||
, mTranslucentFramebuffer(false)
|
, mTranslucentFramebuffer(false)
|
||||||
, mShaderManager(shaderManager)
|
, mShaderManager(shaderManager)
|
||||||
, mImageManager(imageManager)
|
, mImageManager(imageManager)
|
||||||
|
|
|
@ -32,7 +32,7 @@ void BsaArchive::listResources(std::map<std::string, File *> &out, char (*normal
|
||||||
{
|
{
|
||||||
for (std::vector<BsaArchiveFile>::iterator it = mResources.begin(); it != mResources.end(); ++it)
|
for (std::vector<BsaArchiveFile>::iterator it = mResources.begin(); it != mResources.end(); ++it)
|
||||||
{
|
{
|
||||||
std::string ent = it->mInfo->name;
|
std::string ent = it->mInfo->name();
|
||||||
std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function);
|
std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function);
|
||||||
|
|
||||||
out[ent] = &*it;
|
out[ent] = &*it;
|
||||||
|
@ -43,7 +43,7 @@ bool BsaArchive::contains(const std::string& file, char (*normalize_function)(ch
|
||||||
{
|
{
|
||||||
for (const auto& it : mResources)
|
for (const auto& it : mResources)
|
||||||
{
|
{
|
||||||
std::string ent = it.mInfo->name;
|
std::string ent = it.mInfo->name();
|
||||||
std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function);
|
std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function);
|
||||||
if(file == ent)
|
if(file == ent)
|
||||||
return true;
|
return true;
|
||||||
|
|
19
docs/source/reference/modding/custom-models/index.rst
Normal file
19
docs/source/reference/modding/custom-models/index.rst
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#############
|
||||||
|
Custom Models
|
||||||
|
#############
|
||||||
|
|
||||||
|
Custom models can be imported into OpenMW using a variety of formats. Below is a quick overview of supported formats, followed by separate articles with further look at the pipelines.
|
||||||
|
|
||||||
|
* **COLLADA** has no license restrictions and is suitable for modding as well as standalone games based on the OpenMW engine. It supports static and animated models. While it doesn't yet work in all parts of the engine, work is being done to resolve the remaining limitations.
|
||||||
|
|
||||||
|
* **OSG native** has no license restrictions, but currently supports only static, non-animated models.
|
||||||
|
|
||||||
|
* **NIF** is the proprietary format used in the original Morrowind game. It supports static and animated models and everything else the format included in the original game.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:caption: Table of Contents
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
pipeline-blender-collada
|
||||||
|
pipeline-blender-osgnative
|
||||||
|
pipeline-blender-nif
|
|
@ -0,0 +1,84 @@
|
||||||
|
##############################
|
||||||
|
Blender to OpenMW with Collada
|
||||||
|
##############################
|
||||||
|
|
||||||
|
First, let's take a look at the pipeline requirements and how the fundamental properties of a scene translate from Blender to OpenMW.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
* `OpenMW 0.47 <https://openmw.org/downloads/>`_ or later
|
||||||
|
* `Blender 2.81 <https://www.blender.org/download/>`_ or later. Latest confirmed, working version is Blender 2.91
|
||||||
|
* `Better COLLADA Exporter <https://github.com/unelsson/collada-exporter>`_ tuned for OpenMW
|
||||||
|
* A model you would like to export
|
||||||
|
|
||||||
|
In addition, OpenMW needs to be configured to read COLLADA (dae) files instead of the default format (nif). In settings.cfg under [Models] section... TODO
|
||||||
|
|
||||||
|
Location
|
||||||
|
--------
|
||||||
|
|
||||||
|
Objects keep their visual location and origin they had in the original scene.
|
||||||
|
|
||||||
|
|
||||||
|
Rotation
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Blender’s +Z axis is up axis in OpenMW
|
||||||
|
* Blender’s +Y axis is front axis in OpenMW
|
||||||
|
* Blender’s X axis is left-right axis in OpenMW
|
||||||
|
|
||||||
|
|
||||||
|
Scale
|
||||||
|
-----
|
||||||
|
|
||||||
|
Scale ratio between Blender and OpenMW is 70 to 1. This means 70 blender units translate to 1 m in OpenMW.
|
||||||
|
|
||||||
|
However, a scale factor like this is impractical to work with. A better approach is to work with a scale of 1 Blender unit = 1m and apply the 70 scale factor in the Better COLLADA Exporter. The exporter will automatically scale all object, mesh, armature and animation data.
|
||||||
|
|
||||||
|
|
||||||
|
Exporter settings - static models
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Better COLLADA Exporter offers various options which are rather straightforward for static models. The important one is last in the list, to apply a scaling factor of 70 to the whole scene, so 1 blender unit equals 1 m in OpenMW. The following settings should be good for general use.
|
||||||
|
It's also very important to have "export selected" box checked, as otherwise the exporter may just fail with an error message. It's also important to have the correct window open, and the models selected before exporting.
|
||||||
|
|
||||||
|
|
||||||
|
Animated models to OpenMW
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Animated models are those where a hierarchy of bones, known as armature, deforms the mesh and makes things move. Besides the topics covered above, the following requirements apply.
|
||||||
|
|
||||||
|
Armature
|
||||||
|
--------
|
||||||
|
|
||||||
|
* For animated models, a single armature per COLLADA file is advised to avoid any potential problems.
|
||||||
|
* There needs to be a single top-most bone in the armature’s hierarchy, where both the deformation and control bones fall under it.
|
||||||
|
* Not all bones need to be exported. By disabing the bone’s “Deform” property and using the corresponding option in the exporter, it is possible to export only the bones needed for animation.
|
||||||
|
|
||||||
|
|
||||||
|
Animations
|
||||||
|
----------
|
||||||
|
|
||||||
|
Every action in Blender is exported as its own animation clip in COLLADA. Actions you don't wish to export need to have "-noexp" added to their name, with the corresponding option enabled in the exporter.
|
||||||
|
|
||||||
|
Due to current limitations of the format / exporter, the keyframes of any action must not overlap the keyframes of any other action. Thus in practice, the keyframes for each action need to be manually offset to their unique range on the timeline.
|
||||||
|
|
||||||
|
An animated .dae file needs a corresponding animation definition file, or textkeys, for OpenMW to understand. Textkeys are set in .txt file with the same name as the model. E.g. OpenMWDude.dae -> OpenMWDude.txt , each line having a textkey and a double number for timesignature. E.g. idle: start 0.03333333333333333.
|
||||||
|
|
||||||
|
Root Motion
|
||||||
|
-----------
|
||||||
|
|
||||||
|
OpenMW can read the movement of the root (top-most) bone and use it to move objects in the game world. For this to work, the root bone must be animated to move through space. The root bone must, in its default pose, be alligned with the world.
|
||||||
|
|
||||||
|
|
||||||
|
Exporter Settings
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
For animated models, use the following exporter settings. Before export, select all objects you wish to include in the exported file. TODO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
##########################
|
||||||
|
Blender to OpenMW with NIF
|
||||||
|
##########################
|
||||||
|
|
||||||
|
There is a lot of information available around the Internet on how to work with NIF files. We recommend you refer to https://www.niftools.org/ for more information.
|
||||||
|
|
||||||
|
For Blender specifically, you will need the following requirements.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
* `OpenMW <https://openmw.org/downloads/>`_
|
||||||
|
* `Blender 2.8+ <https://www.blender.org/download/>`_
|
||||||
|
* Either `Niftools addon <https://github.com/niftools/blender_niftools_addonr>`_
|
||||||
|
* Or `Morrowind Blender Plugin <https://blender-morrowind.readthedocs.io/en/latest/index.html>`_
|
||||||
|
* A model you would like to export
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
##################
|
#################################
|
||||||
Native Mesh Format
|
Blender to OpenMW with OSG native
|
||||||
##################
|
#################################
|
||||||
|
|
||||||
This article explains how to export a model from Blender to OpenMW using the OSG model format.
|
This article explains how to export a model from Blender to OpenMW using the OSG model format. It supports only basic, static models.
|
||||||
Starting with OpenMW version 0.38 we can utilize the OSG native model format.
|
For more details on the format, refer to `this forum post <https://forum.openmw.org/viewtopic.php?f=20&t=2949&p=35514#p35514>`_.
|
||||||
The OSG model format doesn't yet support all the features that NIF's support,
|
|
||||||
but works for basic models. For more details on the format, refer to
|
|
||||||
`this forum post <https://forum.openmw.org/viewtopic.php?f=20&t=2949&p=35514#p35514>`_.
|
|
||||||
|
|
||||||
Previously, NIF files were the only way to get models into the game.
|
|
||||||
Unfortunately, the NIF format is proprietary, bloated,
|
|
||||||
and the available exporters are not in great shape.
|
|
||||||
For example, the Blender NIF exporter currently only works with the very old Blender 2.49.
|
|
||||||
|
|
||||||
Prerequisites
|
Prerequisites
|
||||||
#############
|
#############
|
||||||
|
@ -103,12 +95,5 @@ Using shaders/normal maps
|
||||||
#########################
|
#########################
|
||||||
|
|
||||||
See :ref:`OSG Native Files`
|
See :ref:`OSG Native Files`
|
||||||
|
|
||||||
|
|
||||||
Conclusion
|
|
||||||
##########
|
|
||||||
|
|
||||||
These are the basics of getting a textured, static model from Blender into the game.
|
|
||||||
In the future, we will want a way to add texture animations,
|
|
||||||
skeletal animations, separate collision shapes,
|
|
||||||
and some other features that are currently only available via NIF files.
|
|
||||||
We will likely add these features to the native OSG format after OpenMW 1.0.
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue