forked from mirror/openmw-tes3mp
Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Marc Zinnschlag | c87b2aa072 | 10 years ago |
@ -1,16 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*.cpp]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
|
||||
[*.hpp]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
|
||||
[*.glsl]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = false
|
@ -1,69 +0,0 @@
|
||||
stages:
|
||||
- build
|
||||
|
||||
Debian:
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
image: gcc
|
||||
cache:
|
||||
key: apt-cache
|
||||
paths:
|
||||
- apt-cache/
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- apt-get update -yq
|
||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
|
||||
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
|
||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
|
||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
|
||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
|
||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
|
||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
|
||||
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
|
||||
stage: build
|
||||
script:
|
||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
||||
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../
|
||||
- make -j$cores_to_use
|
||||
- DESTDIR=artifacts make install
|
||||
artifacts:
|
||||
paths:
|
||||
- build/artifacts/
|
||||
MacOS:
|
||||
tags:
|
||||
- macos
|
||||
- xcode
|
||||
except:
|
||||
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
||||
stage: build
|
||||
allow_failure: true
|
||||
script:
|
||||
- rm -fr build/* # remove anything in the build directory
|
||||
- CI/before_install.osx.sh
|
||||
- CI/before_script.osx.sh
|
||||
- cd build; make -j2 package
|
||||
artifacts:
|
||||
paths:
|
||||
- build/OpenMW-*.dmg
|
||||
|
||||
Windows:
|
||||
tags:
|
||||
- win10
|
||||
- msvc2017
|
||||
except:
|
||||
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
||||
stage: build
|
||||
allow_failure: true
|
||||
script:
|
||||
# - env # turn on for debugging
|
||||
- sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
|
||||
- SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
|
||||
- call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
|
||||
- 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
|
||||
cache:
|
||||
paths:
|
||||
- deps
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.zip"
|
@ -1,3 +0,0 @@
|
||||
[submodule "extern/breakpad"]
|
||||
path = extern/breakpad
|
||||
url = https://chromium.googlesource.com/breakpad/breakpad
|
@ -1,92 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
# - osx
|
||||
osx_image: xcode9.4
|
||||
- osx
|
||||
language: cpp
|
||||
sudo: required
|
||||
dist: xenial
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- coverity_scan
|
||||
- /openmw-.*$/
|
||||
- /^[0-9]+\.[0-9]+\.[0-9]+.*$/
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: 1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ=
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:openmw/openmw'
|
||||
- sourceline: 'ppa:rakhimov/boost'
|
||||
- ubuntu-toolchain-r-test
|
||||
packages: [
|
||||
# Dev
|
||||
cmake, clang-6.0, libunshield-dev, libtinyxml-dev,
|
||||
g++-8,
|
||||
# Tests
|
||||
libgtest-dev, google-mock,
|
||||
# Boost
|
||||
libboost-filesystem1.61-dev, libboost-program-options1.61-dev, libboost-system1.61-dev,
|
||||
# FFmpeg
|
||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev,
|
||||
# Audio & Video
|
||||
libsdl2-dev, qtbase5-dev, libopenal-dev,
|
||||
# The other ones from OpenMW ppa
|
||||
libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev,
|
||||
# tes3mp stuff
|
||||
libboost1.61-dev, libqt5opengl5-dev, libluajit-5.1-dev
|
||||
]
|
||||
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "TES3MP/openmw-tes3mp"
|
||||
description: "<Your project description here>"
|
||||
notification_email: koncord@tes3mp.com
|
||||
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE"
|
||||
build_command: "make -j3"
|
||||
branch_pattern: coverity_scan
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env:
|
||||
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 "
|
||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
||||
compiler: clang
|
||||
- os: linux
|
||||
env:
|
||||
- MATRIX_CC="CC=gcc-8 && CXX=g++-8"
|
||||
- os: linux
|
||||
env:
|
||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
||||
allow_failures:
|
||||
- env:
|
||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
||||
- env:
|
||||
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 "
|
||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
||||
|
||||
before_install:
|
||||
- ./CI/before_install.${TRAVIS_OS_NAME}.sh
|
||||
before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi
|
||||
before_script:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_script.linux.sh; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
|
||||
script:
|
||||
- cd ./build
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
#notifications:
|
||||
# email:
|
||||
# recipients:
|
||||
# - corrmage+travis-ci@gmail.com
|
||||
# on_success: change
|
||||
# on_failure: always
|
||||
# irc:
|
||||
# channels:
|
||||
# - "chat.freenode.net#openmw"
|
||||
# on_success: change
|
||||
# on_failure: always
|
||||
# use_notice: true
|
||||
- make -j4
|
||||
after_script:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
||||
notifications:
|
||||
recipients:
|
||||
- lgromanowski+travis.ci@gmail.com
|
||||
- corrmage+travis-ci@gmail.com
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#openmw"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
use_notice: true
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
brew update
|
||||
|
||||
brew outdated cmake || brew upgrade cmake
|
||||
brew outdated pkgconfig || brew upgrade pkgconfig
|
||||
brew install qt
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip
|
||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
brew tap openmw/openmw
|
||||
brew update
|
||||
brew unlink boost
|
||||
brew install cmake openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg pkg-config qt unshield
|
||||
|
@ -1,39 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
free -m
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
# Set up compilers
|
||||
if [ ! -z "${MATRIX_CC}" ]; then
|
||||
eval "${MATRIX_CC}"
|
||||
fi
|
||||
|
||||
export RAKNET_ROOT=~/CrabNet
|
||||
|
||||
export CODE_COVERAGE=0
|
||||
if [ ! -z "${ANALYZE}" ]; then
|
||||
CODE_COVERAGE=1
|
||||
fi
|
||||
|
||||
${ANALYZE}cmake .. \
|
||||
-DDESIRED_QT_VERSION=5 \
|
||||
-DBUILD_OPENMW_MP=ON \
|
||||
-DBUILD_BROWSER=ON \
|
||||
-DBUILD_MASTER=ON \
|
||||
-DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \
|
||||
-DBUILD_BSATOOL=OFF \
|
||||
-DBUILD_ESMTOOL=OFF \
|
||||
-DBUILD_ESSIMPORTER=OFF \
|
||||
-DBUILD_LAUNCHER=OFF \
|
||||
-DBUILD_MWINIIMPORTER=OFF \
|
||||
-DBUILD_MYGUI_PLUGIN=OFF \
|
||||
-DBUILD_OPENCS=OFF \
|
||||
-DBUILD_WIZARD=OFF \
|
||||
-DBUILD_UNITTESTS=1 \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DBINDIR=/usr/games \
|
||||
-DCMAKE_BUILD_TYPE="None" \
|
||||
-DUSE_SYSTEM_TINYXML=TRUE \
|
||||
-DRakNet_LIBRARY_RELEASE=~/CrabNet/lib/libRakNetLibStatic.a \
|
||||
-DRakNet_LIBRARY_DEBUG=~/CrabNet/lib/libRakNetLibStatic.a
|
||||
cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
|
||||
|
@ -1,732 +0,0 @@
|
||||
#!/bin/bash
|
||||
# set -x # turn-on for debugging
|
||||
|
||||
MISSINGTOOLS=0
|
||||
|
||||
command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; }
|
||||
command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; }
|
||||
|
||||
if [ $MISSINGTOOLS -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORKINGDIR="$(pwd)"
|
||||
case "$WORKINGDIR" in
|
||||
*[[:space:]]*)
|
||||
echo "Error: Working directory contains spaces."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
APPVEYOR=${APPVEYOR:-}
|
||||
CI=${CI:-}
|
||||
STEP=${STEP:-}
|
||||
|
||||
VERBOSE=""
|
||||
STRIP=""
|
||||
SKIP_DOWNLOAD=""
|
||||
SKIP_EXTRACT=""
|
||||
KEEP=""
|
||||
UNITY_BUILD=""
|
||||
VS_VERSION=""
|
||||
PLATFORM=""
|
||||
CONFIGURATION=""
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
ARGSTR=$1
|
||||
shift
|
||||
|
||||
if [ ${ARGSTR:0:1} != "-" ]; then
|
||||
echo "Unknown argument $ARGSTR"
|
||||
echo "Try '$0 -h'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for (( i=1; i<${#ARGSTR}; i++ )); do
|
||||
ARG=${ARGSTR:$i:1}
|
||||
case $ARG in
|
||||
V )
|
||||
VERBOSE=true ;;
|
||||
|
||||
d )
|
||||
SKIP_DOWNLOAD=true ;;
|
||||
|
||||
e )
|
||||
SKIP_EXTRACT=true ;;
|
||||
|
||||
k )
|
||||
KEEP=true ;;
|
||||
|
||||
u )
|
||||
UNITY_BUILD=true ;;
|
||||
|
||||
v )
|
||||
VS_VERSION=$1
|
||||
shift ;;
|
||||
|
||||
p )
|
||||
PLATFORM=$1
|
||||
shift ;;
|
||||
|
||||
c )
|
||||
CONFIGURATION=$1
|
||||
shift ;;
|
||||
|
||||
h )
|
||||
cat <<EOF
|
||||
Usage: $0 [-cdehkpuvV]
|
||||
Options:
|
||||
-c <Release/Debug>
|
||||
Set the configuration, can also be set with environment variable CONFIGURATION.
|
||||
-d
|
||||
Skip checking the downloads.
|
||||
-e
|
||||
Skip extracting dependencies.
|
||||
-h
|
||||
Show this message.
|
||||
-k
|
||||
Keep the old build directory, default is to delete it.
|
||||
-p <Win32/Win64>
|
||||
Set the build platform, can also be set with environment variable PLATFORM.
|
||||
-u
|
||||
Configure for unity builds.
|
||||
-v <2013/2015/2017>
|
||||
Choose the Visual Studio version to use.
|
||||
-V
|
||||
Run verbosely
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown argument $ARG."
|
||||
echo "Try '$0 -h'"
|
||||
exit 1 ;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
STRIP="> /dev/null 2>&1"
|
||||
fi
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
echo "Running prebuild outside of Appveyor."
|
||||
|
||||
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
cd $(dirname "$DIR")/..
|
||||
else
|
||||
echo "Running prebuild in Appveyor."
|
||||
|
||||
cd "$APPVEYOR_BUILD_FOLDER"
|
||||
fi
|
||||
|
||||
run_cmd() {
|
||||
CMD="$1"
|
||||
shift
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
eval $CMD $@ > output.log 2>&1
|
||||
RET=$?
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
if [ -z $APPVEYOR ]; then
|
||||
echo "Command $CMD failed, output can be found in $(real_pwd)/output.log"
|
||||
else
|
||||
echo
|
||||
echo "Command $CMD failed;"
|
||||
cat output.log
|
||||
fi
|
||||
else
|
||||
rm output.log
|
||||
fi
|
||||
|
||||
return $RET
|
||||
else
|
||||
eval $CMD $@
|
||||
return $?
|
||||
fi
|
||||
}
|
||||
|
||||
download() {
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "Invalid parameters to download."
|
||||
return 1
|
||||
fi
|
||||
|
||||
NAME=$1
|
||||
shift
|
||||
|
||||
echo "$NAME..."
|
||||
|
||||
while [ $# -gt 1 ]; do
|
||||
URL=$1
|
||||
FILE=$2
|
||||
shift
|
||||
shift
|
||||
|
||||
if ! [ -f $FILE ]; then
|
||||
printf " Downloading $FILE... "
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
curl --silent --retry 10 -kLy 5 -o $FILE $URL
|
||||
RET=$?
|
||||
else
|
||||
curl --retry 10 -kLy 5 -o $FILE $URL
|
||||
RET=$?
|
||||
fi
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
echo "Failed!"
|
||||
else
|
||||
echo "Done."
|
||||
fi
|
||||
else
|
||||
echo " $FILE exists, skipping."
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $# -ne 0 ]; then
|
||||
echo "Missing parameter."
|
||||
fi
|
||||
}
|
||||
|
||||
real_pwd() {
|
||||
pwd | sed "s,/\(.\),\1:,"
|
||||
}
|
||||
|
||||
CMAKE_OPTS=""
|
||||
add_cmake_opts() {
|
||||
CMAKE_OPTS="$CMAKE_OPTS $@"
|
||||
}
|
||||
|
||||
RUNTIME_DLLS=""
|
||||
add_runtime_dlls() {
|
||||
RUNTIME_DLLS="$RUNTIME_DLLS $@"
|
||||
}
|
||||
|
||||
OSG_PLUGINS=""
|
||||
add_osg_dlls() {
|
||||
OSG_PLUGINS="$OSG_PLUGINS $@"
|
||||
}
|
||||
|
||||
QT_PLATFORMS=""
|
||||
add_qt_platform_dlls() {
|
||||
QT_PLATFORMS="$QT_PLATFORMS $@"
|
||||
}
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM="$(uname -m)"
|
||||
fi
|
||||
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
CONFIGURATION="Debug"
|
||||
fi
|
||||
|
||||
if [ -z $VS_VERSION ]; then
|
||||
VS_VERSION="2013"
|
||||
fi
|
||||
|
||||
case $VS_VERSION in
|
||||
15|15.0|2017 )
|
||||
GENERATOR="Visual Studio 15 2017"
|
||||
TOOLSET="vc141"
|
||||
MSVC_REAL_VER="15"
|
||||
MSVC_VER="14.1"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_DISPLAY_YEAR="2017"
|
||||
;;
|
||||
|
||||
14|14.0|2015 )
|
||||
GENERATOR="Visual Studio 14 2015"
|
||||
TOOLSET="vc140"
|
||||
MSVC_REAL_VER="14"
|
||||
MSVC_VER="14.0"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_DISPLAY_YEAR="2015"
|
||||
;;
|
||||
|
||||
12|12.0|2013 )
|
||||
GENERATOR="Visual Studio 12 2013"
|
||||
TOOLSET="vc120"
|
||||
MSVC_REAL_VER="12"
|
||||
MSVC_VER="12.0"
|
||||
MSVC_YEAR="2013"
|
||||
MSVC_DISPLAY_YEAR="2013"
|
||||
;;
|
||||
esac
|
||||
|
||||
case $PLATFORM in
|
||||
x64|x86_64|x86-64|win64|Win64 )
|
||||
ARCHNAME="x86-64"
|
||||
ARCHSUFFIX="64"
|
||||
BITS="64"
|
||||
|
||||
BASE_OPTS="-G\"$GENERATOR Win64\""
|
||||
add_cmake_opts "-G\"$GENERATOR Win64\""
|
||||
;;
|
||||
|
||||
x32|x86|i686|i386|win32|Win32 )
|
||||
ARCHNAME="x86"
|
||||
ARCHSUFFIX="86"
|
||||
BITS="32"
|
||||
|
||||
BASE_OPTS="-G\"$GENERATOR\""
|
||||
add_cmake_opts "-G\"$GENERATOR\""
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown platform $PLATFORM."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case $CONFIGURATION in
|
||||
debug|Debug|DEBUG )
|
||||
CONFIGURATION=Debug
|
||||
BUILD_CONFIG=Debug
|
||||
;;
|
||||
|
||||
release|Release|RELEASE )
|
||||
CONFIGURATION=Release
|
||||
BUILD_CONFIG=Release
|
||||
;;
|
||||
|
||||
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
|
||||
CONFIGURATION=Release
|
||||
BUILD_CONFIG=RelWithDebInfo
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! [ -z $UNITY_BUILD ]; then
|
||||
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "==================================="
|
||||
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
|
||||
echo "==================================="
|
||||
echo
|
||||
|
||||
# cd OpenMW/AppVeyor-test
|
||||
mkdir -p deps
|
||||
cd deps
|
||||
|
||||
DEPS="$(pwd)"
|
||||
|
||||
if [ -z $SKIP_DOWNLOAD ]; then
|
||||
echo "Downloading dependency packages."
|
||||
echo
|
||||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "Boost 1.67.0" \
|
||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \
|
||||
"boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
||||
fi
|
||||
|
||||
# Bullet
|
||||
download "Bullet 2.86" \
|
||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \
|
||||
"Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z"
|
||||
|
||||
# FFmpeg
|
||||
download "FFmpeg 3.2.4" \
|
||||
"https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \
|
||||
"ffmpeg-3.2.4-win${BITS}.zip" \
|
||||
"https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \
|
||||
"ffmpeg-3.2.4-dev-win${BITS}.zip"
|
||||
|
||||
# MyGUI
|
||||
download "MyGUI 3.2.2" \
|
||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \
|
||||
"MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z"
|
||||
|
||||
# OpenAL
|
||||
download "OpenAL-Soft 1.17.2" \
|
||||
"http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \
|
||||
"OpenAL-Soft-1.17.2.zip"
|
||||
|
||||
# OSG
|
||||
download "OpenSceneGraph 3.4.1-scrawl" \
|
||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
|
||||
"OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z"
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
if [ $BITS == "64" ]; then
|
||||
QT_SUFFIX="_64"
|
||||
else
|
||||
QT_SUFFIX=""
|
||||
fi
|
||||
|
||||
download "Qt 5.7.0" \
|
||||
"https://download.qt.io/archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
|
||||
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
|
||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
|
||||
"qt-5-install.qs"
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
download "SDL 2.0.7" \
|
||||
"https://www.libsdl.org/release/SDL2-devel-2.0.7-VC.zip" \
|
||||
"SDL2-2.0.7.zip"
|
||||
fi
|
||||
|
||||
cd .. #/..
|
||||
|
||||
# Set up dependencies
|
||||
BUILD_DIR="MSVC${MSVC_DISPLAY_YEAR}_${BITS}"
|
||||
if [ -z $KEEP ]; then
|
||||
echo
|
||||
echo "(Re)Creating build directory."
|
||||
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
|
||||
mkdir -p "${BUILD_DIR}/deps"
|
||||
cd "${BUILD_DIR}/deps"
|
||||
|
||||
DEPS_INSTALL="$(pwd)"
|
||||
cd $DEPS
|
||||
|
||||
echo
|
||||
echo "Extracting dependencies, this might take a while..."
|
||||
echo "---------------------------------------------------"
|
||||
echo
|
||||
|
||||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Boost 1.67.0... "
|
||||
else
|
||||
if [ $MSVC_VER -eq 12.0 ]; then
|
||||
printf "Boost 1.58.0 AppVeyor... "
|
||||
else
|
||||
printf "Boost 1.67.0 AppVeyor... "
|
||||
fi
|
||||
fi
|
||||
{
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
BOOST_SDK="$(real_pwd)/Boost"
|
||||
|
||||
# Boost's installer is still based on ms-dos API that doesn't support larger than 260 char path names
|
||||
# We work around this by installing to root of the current working drive and then move it to our deps
|
||||
# get the current working drive's root, we'll install to that temporarily
|
||||
CWD_DRIVE_ROOT="$(powershell -command '(get-location).Drive.Root')Boost_temp"
|
||||
CWD_DRIVE_ROOT_BASH=$(echo "$CWD_DRIVE_ROOT" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
if [ -d CWD_DRIVE_ROOT_BASH ]; then
|
||||
printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. ";
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION 106700" Boost/boost/version.hpp > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Boost
|
||||
CI_EXTRA_INNO_OPTIONS=""
|
||||
[ -n "$CI" ] && CI_EXTRA_INNO_OPTIONS="//SUPPRESSMSGBOXES //LOG='boost_install.log'"
|
||||
"${DEPS}/boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe" //DIR="${CWD_DRIVE_ROOT}" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS}
|
||||
mv "${CWD_DRIVE_ROOT_BASH}" "${BOOST_SDK}"
|
||||
fi
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}"
|
||||
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
||||
echo Done.
|
||||
else
|
||||
# Appveyor unstable has all the boost we need already
|
||||
if [ $MSVC_REAL_VER -eq 12 ]; then
|
||||
BOOST_SDK="c:/Libraries/boost_1_58_0"
|
||||
else
|
||||
BOOST_SDK="c:/Libraries/boost_1_67_0"
|
||||
fi
|
||||
if [ $MSVC_REAL_VER -eq 15 ]; then
|
||||
LIB_SUFFIX="1"
|
||||
else
|
||||
LIB_SUFFIX="0"
|
||||
fi
|
||||
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
|
||||
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
||||
|
||||
echo Done.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# Bullet
|
||||
printf "Bullet 2.86... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d Bullet ]; then
|
||||
printf -- "Exists. (No version checking) "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Bullet
|
||||
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
||||
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
|
||||
fi
|
||||
export BULLET_ROOT="$(real_pwd)/Bullet"
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# FFmpeg
|
||||
printf "FFmpeg 3.2.4... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf FFmpeg
|
||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
|
||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP
|
||||
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
|
||||
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
|
||||
rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
|
||||
fi
|
||||
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
|
||||
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
|
||||
if [ $BITS -eq 32 ]; then
|
||||
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
||||
fi
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# MyGUI
|
||||
printf "MyGUI 3.2.2... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d MyGUI ] && \
|
||||
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
|
||||
then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf MyGUI
|
||||
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
||||
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
|
||||
fi
|
||||
export MYGUI_HOME="$(real_pwd)/MyGUI"
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="_d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# OpenAL
|
||||
printf "OpenAL-Soft 1.17.2... "
|
||||
{
|
||||
if [ -d openal-soft-1.17.2-bin ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf openal-soft-1.17.2-bin
|
||||
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
|
||||
fi
|
||||
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
|
||||
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
|
||||
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
|
||||
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# OSG
|
||||
printf "OSG 3.4.1-scrawl... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d OSG ] && \
|
||||
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
|
||||
grep "OPENSCENEGRAPH_PATCH_VERSION 1" OSG/include/osg/Version > /dev/null
|
||||
then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf OSG
|
||||
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
||||
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
|
||||
fi
|
||||
OSG_SDK="$(real_pwd)/OSG"
|
||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
|
||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll
|
||||
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
|
||||
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Qt 5.7.0... "
|
||||
else
|
||||
printf "Qt 5.10 AppVeyor... "
|
||||
fi
|
||||
{
|
||||
if [ $BITS -eq 64 ]; then
|
||||
SUFFIX="_64"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
|
||||
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Qt
|
||||
cp "${DEPS}/qt-5-install.qs" qt-install.qs
|
||||
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
|
||||
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
|
||||
printf -- "(Installation might take a while) "
|
||||
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
|
||||
mv qt-install.qs Qt/
|
||||
echo Done.
|
||||
printf " Cleaning up extraneous data... "
|
||||
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
|
||||
fi
|
||||
cd $QT_SDK
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||
echo Done.
|
||||
else
|
||||
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||
echo Done.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
# SDL2
|
||||
printf "SDL 2.0.7... "
|
||||
{
|
||||
if [ -d SDL2-2.0.7 ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf SDL2-2.0.7
|
||||
eval 7z x -y SDL2-2.0.7.zip $STRIP
|
||||
fi
|
||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
|
||||
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||
echo Done.
|
||||
}
|
||||
echo
|
||||
cd $DEPS_INSTALL/..
|
||||
echo
|
||||
echo "Setting up OpenMW build..."
|
||||
add_cmake_opts -DBUILD_BSATOOL=no \
|
||||
-DBUILD_ESMTOOL=no \
|
||||
-DBUILD_MYGUI_PLUGIN=no \
|
||||
-DOPENMW_MP_BUILD=on
|
||||
if [ ! -z $CI ]; then
|
||||
case $STEP in
|
||||
components )
|
||||
echo " Building subproject: Components."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENCS=no \
|
||||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
openmw )
|
||||
echo " Building subproject: OpenMW."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENCS=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
opencs )
|
||||
echo " Building subproject: OpenCS."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
misc )
|
||||
echo " Building subprojects: Misc."
|
||||
add_cmake_opts -DBUILD_OPENCS=no \
|
||||
-DBUILD_OPENMW=no
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
# NOTE: Disable this when/if we want to run test cases
|
||||
#if [ -z $CI ]; then
|
||||
echo "- Copying Runtime DLLs..."
|
||||
mkdir -p $BUILD_CONFIG
|
||||
for DLL in $RUNTIME_DLLS; do
|
||||
TARGET="$(basename "$DLL")"
|
||||
if [[ "$DLL" == *":"* ]]; then
|
||||
IFS=':'; SPLIT=( ${DLL} ); unset IFS
|
||||
DLL=${SPLIT[0]}
|
||||
TARGET=${SPLIT[1]}
|
||||
fi
|
||||
echo " ${TARGET}."
|
||||
cp "$DLL" "$BUILD_CONFIG/$TARGET"
|
||||
done
|
||||
echo
|
||||
echo "- OSG Plugin DLLs..."
|
||||
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
|
||||
for DLL in $OSG_PLUGINS; do
|
||||
echo " $(basename $DLL)."
|
||||
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
|
||||
done
|
||||
echo
|
||||
echo "- Qt Platform DLLs..."
|
||||
mkdir -p ${BUILD_CONFIG}/platforms
|
||||
for DLL in $QT_PLATFORMS; do
|
||||
echo " $(basename $DLL)"
|
||||
cp "$DLL" "${BUILD_CONFIG}/platforms"
|
||||
done
|
||||
echo
|
||||
#fi
|
||||
if [ -z $VERBOSE ]; then
|
||||
printf -- "- Configuring... "
|
||||
else
|
||||
echo "- cmake .. $CMAKE_OPTS"
|
||||
fi
|
||||
run_cmd cmake .. $CMAKE_OPTS
|
||||
RET=$?
|
||||
if [ -z $VERBOSE ]; then
|
||||
if [ $RET -eq 0 ]; then
|
||||
echo Done.
|
||||
else
|
||||
echo Failed.
|
||||
fi
|
||||
fi
|
||||
exit $RET
|
@ -1,21 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH=`brew --prefix qt`
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake \
|
||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \
|
||||
-D CMAKE_OSX_SYSROOT="macosx10.13" \
|
||||
-D CMAKE_BUILD_TYPE=Release \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D DESIRED_QT_VERSION=5 \
|
||||
-D BUILD_ESMTOOL=FALSE \
|
||||
-D BUILD_MYGUI_PLUGIN=FALSE \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..
|
||||
|
@ -1,91 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
APPVEYOR=""
|
||||
CI=""
|
||||
|
||||
PACKAGE=""
|
||||
PLATFORM=""
|
||||
CONFIGURATION=""
|
||||
VS_VERSION=""
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM=`uname -m`
|
||||
fi
|
||||
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
CONFIGURATION="Debug"
|
||||
fi
|
||||
|
||||
case $VS_VERSION in
|
||||
14|14.0|2015 )
|
||||
GENERATOR="Visual Studio 14 2015"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_VER="14.0"
|
||||
;;
|
||||
|
||||
# 12|2013|
|
||||
* )
|
||||
GENERATOR="Visual Studio 12 2013"
|
||||
MSVC_YEAR="2013"
|
||||
MVSC_VER="12.0"
|
||||
;;
|
||||
esac
|
||||
|
||||
case $PLATFORM in
|
||||
x64|x86_64|x86-64|win64|Win64 )
|
||||
BITS=64
|
||||
;;
|
||||
|
||||
x32|x86|i686|i386|win32|Win32 )
|
||||
BITS=32
|
||||
;;
|
||||
esac
|
||||
|
||||
case $CONFIGURATION in
|
||||
debug|Debug|DEBUG )
|
||||
CONFIGURATION=Debug
|
||||
;;
|
||||
|
||||
release|Release|RELEASE )
|
||||
CONFIGURATION=Release
|
||||
;;
|
||||
|
||||
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
|
||||
CONFIGURATION=RelWithDebInfo
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
echo "Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build outside of Appveyor."
|
||||
|
||||
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
cd $(dirname "$DIR")/..
|
||||
else
|
||||
echo "Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build in Appveyor."
|
||||
|
||||
cd $APPVEYOR_BUILD_FOLDER
|
||||
fi
|
||||
|
||||
BUILD_DIR="MSVC${MSVC_YEAR}_${BITS}"
|
||||
cd ${BUILD_DIR}
|
||||
|
||||
which msbuild > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
msbuild() {
|
||||
/c/Program\ Files\ \(x86\)/MSBuild/${MSVC_VER}/Bin/MSBuild.exe "$@"
|
||||
}
|
||||
fi
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
msbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8
|
||||
else
|
||||
msbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8 //logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
fi
|
||||
|
||||
RET=$?
|
||||
if [ $RET -eq 0 ] && [ ! -z $PACKAGE ]; then
|
||||
msbuild PACKAGE.vcxproj //t:Build //m:8
|
||||
RET=$?
|
||||
fi
|
||||
|
||||
exit $RET
|
@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
OUTPUT=$(grep -nRP '\t' --include=\*.{cpp,hpp,c,h} --exclude=ui_\* apps components)
|
||||
|
||||
if [[ $OUTPUT ]] ; then
|
||||
echo "Error: Tab characters found!"
|
||||
echo $OUTPUT
|
||||
exit 1
|
||||
fi
|
@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd build
|
||||
|
||||
DATE=`date +'%d%m%Y'`
|
||||
SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}`
|
||||
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg"
|
||||
|
||||
if ! curl --ssl -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}" --silent | grep $SHORT_COMMIT > /dev/null; then
|
||||
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}"
|
||||
fi
|
File diff suppressed because it is too large
Load Diff
@ -1,115 +0,0 @@
|
||||
How to contribute to OpenMW
|
||||
=======================
|
||||
|
||||
Not sure what to do with all your free time? Pick out a task from here:
|
||||
|
||||
https://gitlab.com/OpenMW/openmw/issues
|
||||
|
||||
Currently, we are focused on completing the MW game experience and general polishing. Features out of this scope may be approved in some cases, but you should probably start a discussion first.
|
||||
|
||||
Note:
|
||||
* Tasks set to 'openmw-future' are usually out of the current scope of the project and can't be started yet.
|
||||
* Bugs that are not 'Confirmed' should be confirmed first.
|
||||
* Often, it's best to start a discussion about possible solutions before you jump into coding, especially for larger features.
|
||||
|
||||
Aside from coding, you can also help by triaging the issues list. Check for bugs that are 'Unconfirmed' and try to confirm them on your end, working out any details that may be necessary. Check for bugs that do not conform to [Bug reporting guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) and improve them to do so!
|
||||
|
||||
There are various [Tools](https://wiki.openmw.org/index.php?title=Tools) to facilitate testing/development.
|
||||
|
||||
Pull Request Guidelines
|
||||
=======================
|
||||
|
||||
To facilitate the review process, your pull request description should include the following, if applicable:
|
||||
|
||||
* A link back to the bug report or forum discussion that prompted the change. Note: when linking bugs, use the syntax ```[Bug #xyz](https://bugs.openmw.org/issues/#xyz)``` to create a clickable link. Writing only 'Bug #xyz' will unfortunately create a link to the Github pull request with that number instead.
|
||||
* Summary of the changes made
|
||||
* Reasoning / motivation behind the change
|
||||
* What testing you have carried out to verify the change
|
||||
|
||||
Furthermore, we advise to:
|
||||
|
||||
* Avoid stuffing unrelated commits into one pull request. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.
|
||||
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
|
||||
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).
|
||||
* Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway.
|
||||
* Reference the bug / feature ticket(s) in your commit message (e.g. 'Bug #123') to make it easier to keep track of what we changed for what reason. Our bugtracker will show those commits next to the ticket. If your commit message includes 'Fixes #123', that bug/feature will automatically be set to 'Closed' when your commit is merged.
|
||||
* When pulling changes from master, prefer rebase over merge. Consider using a merge if there are conflicts or for long-running PRs.
|
||||
|
||||
Guidelines for original engine "fixes"
|
||||
=================================
|
||||
|
||||
From time to time you may be tempted to "fix" what you think was a "bug" in the original game engine.
|
||||
|
||||
Unfortunately, the definition of what is a "bug" is not so clear. Consider that your "bug" is actually a feature unless proven otherwise:
|
||||
|
||||
* We have no way of knowing what the original developers really intended (short of asking them, good luck with that).
|
||||
* What may seem like an illogical mechanic can actually be part of an attempt to balance the game.
|
||||
* Many people will actually <i>like</i> these "bugs" because that is what they remember the game for.
|
||||
* Exploits may be part of the fun of an open-world game - they reward knowledge with power. There are too many of them to plug them all, anyway.
|
||||
|
||||
OpenMW, in its default configuration, is meant to be a faithful reimplementation of Morrowind, minus things like crash bugs, stability issues and severe design errors. However, we try to avoid touching anything that affects the core gameplay, the balancing of the game or introduces incompatibilities with existing mod content.
|
||||
|
||||
That said, we may sometimes evaluate such issues on an individual basis. Common exceptions to the above would be:
|
||||
|
||||
* Issues so glaring that they would severely limit the capabilities of the engine in the future (for example, the scripting engine not being allowed to access objects in remote cells)
|
||||
* Bugs where the intent is very obvious, and that have little to no balancing impact (e.g. the bug were being tired made it easier to repair items, instead of harder)
|
||||
* Bugs that were fixed in an official patch for Morrowind
|
||||
|
||||
Feature additions policy
|
||||
=====================
|
||||
|
||||
We get it, you have waited so long for feature XYZ to be available in Morrowind and now that OpenMW is here you can not wait to implement your ingenious idea and share it with the world.
|
||||
|
||||
Unfortunately, since maintaining features comes at a cost and our resources are limited, we have to be a little selective in what features we allow into the main repository. Generally:
|
||||
|
||||
* Features should be as generic and non-redundant as possible.
|
||||
* Any feature that is also possible with modding should be done as a mod instead.
|
||||
* In the future, OpenMW plans to expand the scope of what is possible with modding, e.g. by moving certain game logic into editable scripts.
|
||||
* Currently, modders can edit OpenMW's GUI skins and layout XML files, although there are still a few missing hooks (e.g. scripting support) in order to make this into a powerful way of modding.
|
||||
* If a feature introduces new game UI strings, that reduces its chance of being accepted because we do not currently have any way of localizing these to the user's Morrowind installation language.
|
||||
|
||||
If you are in doubt of your feature being within our scope, it is probably best to start a forum discussion first. See the [settings documentation](https://openmw.readthedocs.io/en/stable/reference/modding/settings/index.html) and [Features list](https://wiki.openmw.org/index.php?title=Features) for some examples of features that were deemed acceptable.
|
||||
|
||||
Reviewing pull requests
|
||||
=======================
|
||||
|
||||
We welcome any help in reviewing open PRs. You don't need to be a developer to comment on new features. We also encourage ["junior" developers to review senior's work](https://pagefault.blog/2018/04/08/why-junior-devs-should-review-seniors-commits/).
|
||||
|
||||
This review process is divided into two sections because complaining about code or style issues hardly makes sense until the functionality of the PR is deemed OK. Anyone can help with the Functionality Review while most parts of the Code Review require you to have programming experience.
|
||||
|
||||
In addition to the checklist below, make sure to check that the Pull Request Guidelines (first half of this document) were followed.
|
||||
|
||||
First review
|
||||
============
|
||||
|
||||
1. Ask for missing information or clarifications. Compare against the project's design goals and roadmap.
|
||||
2. Check if the automated tests are passing. If they are not, make the PR author aware of the issue and potentially link to the error line on Travis CI or Appveyor. If the error appears unrelated to the PR and/or the master branch is failing with the same error, our CI has broken and needs to be fixed independently of any open PRs. Raise this issue on the forums, bug tracker or with the relevant maintainer. The PR can be merged in this case as long as you've built it yourself to make sure it does build.
|
||||
3. Make sure that the new code has been tested thoroughly, either by asking the author or, preferably, testing yourself. In a complex project like OpenMW, it is easy to make mistakes, typos, etc. Therefore, prefer testing all code changes, no matter how trivial they look. When you have tested a PR that no one has tested so far, post a comment letting us know.
|
||||
4. On long running PRs, request the author to update its description with the current state or a checklist of things left to do.
|
||||
|
||||
Code Review
|
||||
===========
|
||||
|
||||
1. Carefully review each line for issues the author may not have thought of, paying special attention to 'special' cases. Often, people build their code with a particular mindset and forget about other configurations or unexpected interactions.
|
||||
2. If any changes are workarounds for an issue in an upstream library, make sure the issue was reported upstream so we can eventually drop the workaround when the issue is fixed and the new version of that library is a build dependency.
|
||||
3. Make sure PRs do not turn into arguments about hardly related issues. If the PR author disagrees with an established part of the project (e.g. supported build environments), they should open a forum discussion or bug report and in the meantime adjust the PR to adhere to the established way, rather than leaving the PR hanging on a dispute.
|
||||
4. Check if the code matches our style guidelines.
|
||||
5. Check to make sure the commit history is clean. Squashing should be considered if the review process has made the commit history particularly long. Commits that don't build should be avoided because they are a nuisance for ```git bisect```.
|
||||
|
||||
Merging
|
||||
=======
|
||||
|
||||
To be able to merge PRs, commit priviledges are required. If you do not have the priviledges, just ping someone that does have them with a short comment like "Looks good to me @user".
|
||||
|
||||
The person to merge the PR may either use github's Merge button or if using the command line, use the ```--no-ff``` flag (so a merge commit is created, just like with Github's merge button) and include the pull request number in the commit description.
|
||||
|
||||
Dealing with regressions
|
||||
========================
|
||||
|
||||
The master branch should always be in a working state that is not worse than the previous release in any way. If a regression is found, the first and foremost priority should be to get the regression fixed quickly, either by reverting the change that caused it or finding a better solution. Please avoid leaving the project in the 'broken' state for an extensive period of time while proper solutions are found. If the solution takes more than a day or so then it is usually better to revert the offending change first and reapply it later when fixed.
|
||||
|
||||
Other resources
|
||||
===============
|
||||
|
||||
[GitHub blog - how to write the perfect pull request](https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/)
|
||||
|
@ -1,100 +0,0 @@
|
||||
|
||||
set (CMAKE_CXX_STANDARD 14)
|
||||
|
||||
set(BROWSER_UI
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/Main.ui
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/ServerInfo.ui
|
||||
)
|
||||
set(BROWSER
|
||||
main.cpp
|
||||
MainWindow.cpp
|
||||
ServerModel.cpp
|
||||
ServerInfoDialog.cpp
|
||||
MySortFilterProxyModel.cpp
|
||||
netutils/HTTPNetwork.cpp
|
||||
netutils/Utils.cpp
|
||||
netutils/QueryClient.cpp
|
||||
PingUpdater.cpp
|
||||
PingHelper.cpp
|
||||
QueryHelper.cpp
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/browser.rc
|
||||
)
|
||||
|
||||
set(BROWSER_HEADER_MOC
|
||||
MainWindow.hpp
|
||||
ServerModel.hpp
|
||||
ServerInfoDialog.hpp
|
||||
MySortFilterProxyModel.hpp
|
||||
PingUpdater.hpp
|
||||
PingHelper.hpp
|
||||
QueryHelper.hpp
|
||||
)
|
||||
|
||||
set(BROWSER_HEADER
|
||||
${BROWSER_HEADER_MOC}
|
||||
netutils/HTTPNetwork.hpp
|
||||
netutils/Utils.hpp
|
||||
netutils/QueryClient.hpp
|
||||
Types.hpp
|
||||
)
|
||||
|
||||
source_group(browser FILES ${BROWSER} ${BROWSER_HEADER})
|
||||
|
||||
set(QT_USE_QTGUI 1)
|
||||
|
||||
# Set some platform specific settings
|
||||
if(WIN32)
|
||||
set(GUI_TYPE WIN32)
|
||||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
message(SEND_ERROR "QT4 is not supported.")
|
||||
#include(${QT_USE_FILE})
|
||||
#QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||
#QT4_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
|
||||
#QT4_WRAP_UI(UI_HDRS ${BROWSER_UI})
|
||||
else()
|
||||
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||
QT5_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
|
||||
QT5_WRAP_UI(UI_HDRS ${BROWSER_UI})
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
if(NOT WIN32)
|
||||
include_directories(${LIBUNSHIELD_INCLUDE_DIR})
|
||||
endif(NOT WIN32)
|
||||
|
||||
# Main executable
|
||||
add_executable(tes3mp-browser
|
||||
${GUI_TYPE}
|
||||
${BROWSER}
|
||||
${BROWSER_HEADER}
|
||||
${RCC_SRCS}
|
||||
${MOC_SRCS}
|
||||
${UI_HDRS}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
INSTALL(TARGETS tes3mp-browser RUNTIME DESTINATION ".")
|
||||
endif (WIN32)
|
||||
|
||||
target_link_libraries(tes3mp-browser
|
||||
${SDL2_LIBRARY_ONLY}
|
||||
${RakNet_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
# target_link_libraries(tes3mp-browser ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY})
|
||||
# if(WIN32)
|
||||
# target_link_libraries(tes3mp-browser ${QT_QTMAIN_LIBRARY})
|
||||
# endif(WIN32)
|
||||
else()
|
||||
qt5_use_modules(tes3mp-browser Widgets Core)
|
||||
endif()
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(tes3mp-browser gcov)
|
||||
endif()
|
@ -1,246 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 06.01.17.
|
||||
//
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
#include "QueryHelper.hpp"
|
||||
#include "PingHelper.hpp"
|
||||
#include "ServerInfoDialog.hpp"
|
||||
#include "components/files/configurationmanager.hpp"
|
||||
#include <qdebug.h>
|
||||
#include <QInputDialog>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
|
||||
|
||||
using namespace Process;
|
||||
using namespace std;
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
mGameInvoker = new ProcessInvoker();
|
||||
|
||||
browser = new ServerModel;
|
||||
favorites = new ServerModel;
|
||||
proxyModel = new MySortFilterProxyModel(this);
|
||||
proxyModel->setSourceModel(browser);
|
||||
tblServerBrowser->setModel(proxyModel);
|
||||
tblFavorites->setModel(proxyModel);
|
||||
|
||||
// Remove Favorites tab while it remains broken
|
||||
tabWidget->removeTab(1);
|
||||
|
||||
tblServerBrowser->hideColumn(ServerData::ADDR);
|
||||
tblFavorites->hideColumn(ServerData::ADDR);
|
||||
|
||||
PingHelper::Get().SetModel((ServerModel*)proxyModel->sourceModel());
|
||||
queryHelper = new QueryHelper(proxyModel->sourceModel());
|
||||
connect(queryHelper, &QueryHelper::started, [this](){actionRefresh->setEnabled(false);});
|
||||
connect(queryHelper, &QueryHelper::finished, [this](){actionRefresh->setEnabled(true);});
|
||||
|
||||
connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSwitched(int)));
|
||||
connect(actionAdd, SIGNAL(triggered(bool)), this, SLOT(addServer()));
|
||||
connect(actionAdd_by_IP, SIGNAL(triggered(bool)), this, SLOT(addServerByIP()));
|
||||
connect(actionDelete, SIGNAL(triggered(bool)), this, SLOT(deleteServer()));
|
||||
connect(actionRefresh, SIGNAL(triggered(bool)), queryHelper, SLOT(refresh()));
|
||||
connect(actionPlay, SIGNAL(triggered(bool)), this, SLOT(play()));
|
||||
connect(tblServerBrowser, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
|
||||
connect(tblFavorites, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
|
||||
connect(tblFavorites, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
||||
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
||||
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
|
||||
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));
|
||||
connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool)));
|
||||
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
|
||||
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
|
||||
loadFavorites();
|
||||
queryHelper->refresh();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete mGameInvoker;
|
||||
}
|
||||
|
||||
void MainWindow::addServerAndUpdate(const QString &addr)
|
||||
{
|
||||
favorites->insertRow(0);
|
||||
QModelIndex mi = favorites->index(0, ServerData::ADDR);
|
||||
favorites->setData(mi, addr, Qt::EditRole);
|
||||
//NetController::get()->updateInfo(favorites, mi);
|
||||
//QueryClient::Update(RakNet::SystemAddress())
|
||||
/*auto data = QueryClient::Get().Query();
|
||||
if (data.empty())
|
||||
return;
|
||||
transform(data.begin(), data.end(), back_inserter());*/
|
||||
}
|
||||
|
||||
void MainWindow::addServer()
|
||||
{
|
||||
int id = tblServerBrowser->selectionModel()->currentIndex().row();
|
||||
|
||||
if (id >= 0)
|
||||
{
|
||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
||||
favorites->myData.push_back(browser->myData[sourceId]);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::addServerByIP()
|
||||
{
|
||||
bool ok;
|
||||
QString text = QInputDialog::getText(this, tr("Add Server by address"), tr("Address:"), QLineEdit::Normal, "", &ok);
|
||||
if (ok && !text.isEmpty())
|
||||
addServerAndUpdate(text);
|
||||
}
|
||||
|
||||
void MainWindow::deleteServer()
|
||||
{
|
||||
if (tabWidget->currentIndex() != 1)
|
||||
return;
|
||||
int id = tblFavorites->selectionModel()->currentIndex().row();
|
||||
if (id >= 0)
|
||||
{
|
||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
||||
favorites->removeRow(sourceId);
|
||||
if (favorites->myData.isEmpty())
|
||||
{
|
||||
actionPlay->setEnabled(false);
|
||||
actionDelete->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::play()
|
||||
{
|
||||
QTableView *curTable = tabWidget->currentIndex() ? tblFavorites : tblServerBrowser;
|
||||
int id = curTable->selectionModel()->currentIndex().row();
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
|
||||
ServerModel *sm = ((ServerModel*)proxyModel->sourceModel());
|
||||
|
||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
||||
ServerInfoDialog infoDialog(sm->myData[sourceId].addr, this);
|
||||
|
||||
if (!infoDialog.exec())
|
||||
return;
|
||||
|
||||
if (!infoDialog.isUpdated())
|
||||
return;
|
||||
|
||||
QStringList arguments;
|
||||
arguments.append(QLatin1String("--connect=") + sm->myData[sourceId].addr.toLatin1());
|
||||
|
||||
if (sm->myData[sourceId].GetPassword() == 1)
|
||||
{
|
||||
bool ok;
|
||||
QString passw = QInputDialog::getText(this, tr("Connecting to: ") + sm->myData[sourceId].addr, tr("Password: "),
|
||||
QLineEdit::Password, "", &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
arguments.append(QLatin1String("--password=") + passw.toLatin1());
|
||||
}
|
||||
|
||||
if (mGameInvoker->startProcess(QLatin1String("tes3mp"), arguments, true))
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
void MainWindow::tabSwitched(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
proxyModel->setSourceModel(browser);
|
||||
actionDelete->setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyModel->setSourceModel(favorites);
|
||||
}
|
||||
actionPlay->setEnabled(false);
|
||||
actionAdd->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::serverSelected()
|
||||
{
|
||||
actionPlay->setEnabled(true);
|
||||
if (tabWidget->currentIndex() == 0)
|
||||
actionAdd->setEnabled(true);
|
||||
if (tabWidget->currentIndex() == 1)
|
||||
actionDelete->setEnabled(true);
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
Files::ConfigurationManager cfgMgr;
|
||||
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
|
||||
|
||||
QJsonArray saveData;
|
||||
for (auto server : favorites->myData)
|
||||
saveData.push_back(server.addr);
|
||||
|
||||
QFile file(cfgPath);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qDebug() << "Cannot save " << cfgPath;
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(QJsonDocument(saveData).toJson());
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::loadFavorites()
|
||||
{
|
||||
Files::ConfigurationManager cfgMgr;
|
||||
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
|
||||
|
||||
QFile file(cfgPath);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "Cannot open " << cfgPath;
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(file.readAll()));
|
||||
|
||||
for (auto server : jsonDoc.array())
|
||||
addServerAndUpdate(server.toString());
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
void MainWindow::notFullSwitch(bool state)
|
||||
{
|
||||
proxyModel->filterFullServer(state);
|
||||
}
|
||||
|
||||
void MainWindow::havePlayersSwitch(bool state)
|
||||
{
|
||||
proxyModel->filterEmptyServers(state);
|
||||
}
|
||||
|
||||
void MainWindow::noPasswordSwitch(bool state)
|
||||
{
|
||||
proxyModel->filterPassworded(state);
|
||||
}
|
||||
|
||||
void MainWindow::maxLatencyChanged(int index)
|
||||
{
|
||||
int maxLatency = index * 50;
|
||||
proxyModel->pingLessThan(maxLatency);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::gamemodeChanged(const QString &text)
|
||||
{
|
||||
proxyModel->setFilterFixedString(text);
|
||||
proxyModel->setFilterKeyColumn(ServerData::MODNAME);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 06.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_MAIN_HPP
|
||||
#define NEWLAUNCHER_MAIN_HPP
|
||||
|
||||
|
||||
#include "ui_Main.h"
|
||||
#include "ServerModel.hpp"
|
||||
#include "MySortFilterProxyModel.hpp"
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
class QueryHelper;
|
||||
|
||||
class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
protected:
|
||||
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
|
||||
void addServerAndUpdate(const QString &addr);
|
||||
protected slots:
|
||||
void tabSwitched(int index);
|
||||
void addServer();
|
||||
void addServerByIP();
|
||||
void deleteServer();
|
||||
void play();
|
||||
void serverSelected();
|
||||
void notFullSwitch(bool state);
|
||||
void havePlayersSwitch(bool state);
|
||||
void noPasswordSwitch(bool state);
|
||||
void maxLatencyChanged(int index);
|
||||
void gamemodeChanged(const QString &text);
|
||||
private:
|
||||
QueryHelper *queryHelper;
|
||||
Process::ProcessInvoker *mGameInvoker;
|
||||
ServerModel *browser, *favorites;
|
||||
MySortFilterProxyModel *proxyModel;
|
||||
void loadFavorites();
|
||||
};
|
||||
|
||||
|
||||
#endif //NEWLAUNCHER_MAIN_HPP
|
@ -1,84 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 30.01.17.
|
||||
//
|
||||
|
||||
#include "MySortFilterProxyModel.hpp"
|
||||
#include "ServerModel.hpp"
|
||||
|
||||
#include <qdebug.h>
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
|
||||
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
|
||||
QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
|
||||
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
|
||||
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);
|
||||
QModelIndex passwordIndex = sourceModel()->index(sourceRow, ServerData::PASSW, sourceParent);
|
||||
|
||||
bool pingOk;
|
||||
int ping = sourceModel()->data(pingIndex).toInt(&pingOk);
|
||||
int players = sourceModel()->data(plIndex).toInt();
|
||||
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
|
||||
|
||||
if (maxPing > 0 && (ping == -1 || ping > maxPing || !pingOk))
|
||||
return false;
|
||||
if (filterEmpty && players == 0)
|
||||
return false;
|
||||
if (filterFull && players >= maxPlayers)
|
||||
return false;
|
||||
if(filterPasswEnabled && sourceModel()->data(passwordIndex).toString() == "Yes")
|
||||
return false;
|
||||
|
||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
||||
}
|
||||
|
||||
bool MySortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||
{
|
||||
if(sortColumn() == ServerData::PING)
|
||||
{
|
||||
bool valid;
|
||||
|
||||
int pingright = sourceModel()->data(source_right).toInt(&valid);
|
||||
pingright = valid ? pingright : PING_UNREACHABLE;
|
||||
|
||||
int pingleft = sourceModel()->data(source_left).toInt(&valid);
|
||||
pingleft = valid ? pingleft : PING_UNREACHABLE;
|
||||
return pingleft < pingright;
|
||||
}
|
||||
else
|
||||
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||
}
|
||||
|
||||
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
filterEmpty = false;
|
||||
filterFull = false;
|
||||
filterPasswEnabled = false;
|
||||
maxPing = 0;
|
||||
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::filterEmptyServers(bool state)
|
||||
{
|
||||
filterEmpty = state;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::filterFullServer(bool state)
|
||||
{
|
||||
filterFull = state;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::pingLessThan(int maxPing)
|
||||
{
|
||||
this->maxPing = maxPing;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::filterPassworded(bool state)
|
||||
{
|
||||
filterPasswEnabled = state;
|
||||
invalidateFilter();
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 30.01.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
||||
#define OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
||||
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class MySortFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
|
||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const Q_DECL_FINAL;
|
||||
public:
|
||||
explicit MySortFilterProxyModel(QObject *parent);
|
||||
void filterFullServer(bool state);
|
||||
void filterEmptyServers(bool state);
|
||||
void filterPassworded(bool state);
|
||||
void pingLessThan(int maxPing);
|
||||
private:
|
||||
bool filterEmpty, filterFull, filterPasswEnabled;
|
||||
int maxPing;
|
||||
};
|
||||
|
||||
|
||||
#endif //OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
@ -1,56 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 03.05.17.
|
||||
//
|
||||
|
||||
#include "PingHelper.hpp"
|
||||
#include "ServerModel.hpp"
|
||||
#include <QDebug>
|
||||
#include "PingUpdater.hpp"
|
||||
|
||||
void PingHelper::Add(int row, const AddrPair &addrPair)
|
||||
{
|
||||
pingUpdater->addServer(row, addrPair);
|
||||
if (!pingThread->isRunning())
|
||||
pingThread->start();
|
||||
}
|
||||
|
||||
void PingHelper::Reset()
|
||||
{
|
||||
//if (pingThread->isRunning())
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PingHelper::Stop()
|
||||
{
|
||||
emit pingUpdater->stop();
|
||||
}
|
||||
|
||||
void PingHelper::SetModel(QAbstractTableModel *model)
|
||||
{
|
||||
this->model = model;
|
||||
}
|
||||
|
||||
void PingHelper::update(int row, unsigned ping)
|
||||
{
|
||||
model->setData(model->index(row, ServerData::PING), ping);
|
||||
}
|
||||
|
||||
PingHelper &PingHelper::Get()
|
||||
{
|
||||
static PingHelper helper;
|
||||
return helper;
|
||||
}
|
||||
|
||||
PingHelper::PingHelper() : QObject()
|
||||
{
|
||||
pingThread = new QThread;
|
||||
pingUpdater = new PingUpdater;
|
||||
pingUpdater->moveToThread(pingThread);
|
||||
|
||||
connect(pingThread, SIGNAL(started()), pingUpdater, SLOT(process()));
|
||||
connect(pingUpdater, SIGNAL(start()), pingThread, SLOT(start()));
|
||||
connect(pingUpdater, SIGNAL(finished()), pingThread, SLOT(quit()));
|
||||
connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));
|
||||
//connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));
|
||||
connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 03.05.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_PINGHELPER_HPP
|
||||
#define OPENMW_PINGHELPER_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractTableModel>
|
||||
#include <QThread>
|
||||
#include "Types.hpp"
|
||||
|
||||
class PingUpdater;
|
||||
|
||||
class PingHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
void Reset();
|
||||
void Add(int row, const AddrPair &addrPair);
|
||||
void Stop();
|
||||
void SetModel(QAbstractTableModel *model);
|
||||
//void UpdateImmedialy(PingUpdater::AddrPair addrPair);
|
||||
static PingHelper &Get();
|
||||
|
||||
PingHelper(const PingHelper&) = delete;
|
||||
PingHelper& operator=(const PingHelper&) = delete;
|
||||
private:
|
||||
PingHelper();
|
||||
signals:
|
||||
void stop();
|
||||
public slots:
|
||||
void update(int row, unsigned ping);
|
||||
private:
|
||||
QThread *pingThread;
|
||||
PingUpdater *pingUpdater;
|
||||
QAbstractTableModel *model;
|
||||
};
|
||||
|
||||
|
||||
#endif //OPENMW_PINGHELPER_HPP
|
@ -1,50 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 02.05.17.
|
||||
//
|
||||
|
||||
#include "PingUpdater.hpp"
|
||||
#include "netutils/Utils.hpp"
|
||||
#include <QDebug>
|
||||
#include <QModelIndex>
|
||||
#include <QThread>
|
||||
|
||||
void PingUpdater::stop()
|
||||
{
|
||||
servers.clear();
|
||||
run = false;
|
||||
}
|
||||
|
||||
void PingUpdater::addServer(int row, const AddrPair &addr)
|
||||
{
|
||||
servers.push_back({row, addr});
|
||||
run = true;
|
||||
emit start();
|
||||
}
|
||||
|
||||
void PingUpdater::process()
|
||||
{
|
||||
while (run)
|
||||
{
|
||||
if (servers.count() == 0)
|
||||
{
|
||||
QThread::msleep(1000);
|
||||
if (servers.count() == 0)
|
||||
{
|
||||
qDebug() << "PingUpdater stopped due to inactivity";
|
||||
run = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ServerRow server = servers.back();
|
||||
servers.pop_back();
|
||||
|
||||
unsigned ping = PingRakNetServer(server.second.first.toLatin1(), server.second.second);
|
||||
|
||||
qDebug() << "Pong from" << server.second.first + "|" + QString::number(server.second.second)
|
||||
<< ":" << ping << "ms" << "Sizeof servers: " << servers.size();
|
||||
|
||||
emit updateModel(server.first, ping);
|
||||
}
|
||||
emit finished();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 02.05.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_PINGUPDATER_HPP
|
||||
#define OPENMW_PINGUPDATER_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
#include "Types.hpp"
|
||||
|
||||
class PingUpdater : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void addServer(int row, const AddrPair &addrPair);
|
||||
public slots:
|
||||
void stop();
|
||||
void process();
|
||||
signals:
|
||||
void start();
|
||||
void updateModel(int row, unsigned ping);
|
||||
void finished();
|
||||
private:
|
||||
QVector<ServerRow> servers;
|
||||
bool run;
|
||||
};
|
||||
|
||||
|
||||
#endif //OPENMW_PINGUPDATER_HPP
|
@ -1,87 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 27.05.17.
|
||||
//
|
||||
|
||||
#include "netutils/QueryClient.hpp"
|
||||
#include "netutils/Utils.hpp"
|
||||
#include "QueryHelper.hpp"
|
||||
#include "PingHelper.hpp"
|
||||
|
||||
QueryUpdate *queryUpdate;
|
||||
|
||||
|
||||
QueryHelper::QueryHelper(QAbstractItemModel *model)
|
||||
{
|
||||
qRegisterMetaType<QueryData>("QueryData");
|
||||
queryThread = new QThread;
|
||||
queryUpdate = new QueryUpdate;
|
||||
_model = model;
|
||||
connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));
|
||||
connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));
|
||||
connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});
|
||||
connect(queryUpdate, SIGNAL(updateModel(const QString&, unsigned short, const QueryData&)),
|
||||
this, SLOT(update(const QString&, unsigned short, const QueryData&)));
|
||||
queryUpdate->moveToThread(queryThread);
|
||||
}
|
||||
|
||||
void QueryHelper::refresh()
|
||||
{
|
||||
if (!queryThread->isRunning())
|
||||
{
|
||||
_model->removeRows(0, _model->rowCount());
|
||||
PingHelper::Get().Stop();
|
||||
queryThread->start();
|
||||
emit started();
|
||||
}
|
||||
}
|
||||
|
||||
void QueryHelper::terminate()
|
||||
{
|
||||
queryThread->terminate();
|
||||
}
|
||||
|
||||
void QueryHelper::update(const QString &addr, unsigned short port, const QueryData& data)
|
||||
{
|
||||
ServerModel *model = ((ServerModel*)_model);
|
||||
model->insertRow(model->rowCount());
|
||||
int row = model->rowCount() - 1;
|
||||
|
||||
QModelIndex mi = model->index(row, ServerData::ADDR);
|
||||
model->setData(mi, addr + ":" + QString::number(port));
|
||||
|
||||
mi = model->index(row, ServerData::PLAYERS);
|
||||
model->setData(mi, (int)data.players.size());
|
||||
|
||||
mi = model->index(row, ServerData::MAX_PLAYERS);
|
||||
model->setData(mi, data.GetMaxPlayers());
|
||||
|
||||
mi = model->index(row, ServerData::HOSTNAME);
|
||||
model->setData(mi, data.GetName());
|
||||
|
||||
mi = model->index(row, ServerData::MODNAME);
|
||||
model->setData(mi, data.GetGameMode());
|
||||
|
||||
mi = model->index(row, ServerData::VERSION);
|
||||
model->setData(mi, data.GetVersion());
|
||||
|
||||
mi = model->index(row, ServerData::PASSW);
|
||||
model->setData(mi, data.GetPassword() == 1);
|
||||
|
||||
mi = model->index(row, ServerData::PING);
|
||||
model->setData(mi, PING_UNREACHABLE);
|
||||
PingHelper::Get().Add(row, {addr, port});
|
||||
}
|
||||
|
||||
void QueryUpdate::process()
|
||||
{
|
||||
auto data = QueryClient::Get().Query();
|
||||
if (QueryClient::Get().Status() != ID_MASTER_QUERY)
|
||||
{
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &server : data)
|
||||
emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);
|
||||
emit finished();
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 27.05.17.
|
||||
//
|
||||
|
||||
|
||||
#ifndef OPENMW_QUERYHELPER_HPP
|
||||
#define OPENMW_QUERYHELPER_HPP
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <vector>
|
||||
#include <QAbstractItemModel>
|
||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||
|
||||
Q_DECLARE_METATYPE(QueryData)
|
||||
|
||||
class QueryHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QueryHelper(QAbstractItemModel *model);
|
||||
public slots:
|
||||
void refresh();
|
||||
void terminate();
|
||||
private slots:
|
||||
void update(const QString &addr, unsigned short port, const QueryData& data);
|
||||
signals:
|
||||
void finished();
|
||||
void started();
|
||||
private:
|
||||
QThread *queryThread;
|
||||
QAbstractItemModel *_model;
|
||||
};
|
||||
|
||||
class QueryUpdate : public QObject
|
||||
{
|
||||
friend class QueryHelper;
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void finished();
|
||||
void updateModel(const QString &addr, unsigned short port, const QueryData& data);
|
||||
public slots:
|
||||
void process();
|
||||
};
|
||||
|
||||
#endif //OPENMW_QUERYHELPER_HPP
|
@ -1,107 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#include <apps/browser/netutils/QueryClient.hpp>
|
||||
#include "qdebug.h"
|
||||
|
||||
#include "ServerInfoDialog.hpp"
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <QThread>
|
||||
|
||||
using namespace std;
|
||||
using namespace RakNet;
|
||||
|
||||
ThrWorker::ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port): addr(std::move(addr)), port(port), stopped(false)
|
||||
{
|
||||
this->dialog = dialog;
|
||||
}
|
||||
|
||||
void ThrWorker::process()
|
||||
{
|
||||
stopped = false;
|
||||
auto newSD = QueryClient::Get().Update(SystemAddress(addr.toUtf8(), port));
|
||||
if (dialog != nullptr)
|
||||
dialog->setData(newSD);
|
||||
stopped = true;
|
||||
emit finished();
|
||||
}
|
||||
|
||||
ServerInfoDialog::ServerInfoDialog(const QString &addr, QWidget *parent): QDialog(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
refreshThread = new QThread;
|
||||
|
||||
QStringList list = addr.split(':');
|
||||
worker = new ThrWorker(this, list[0].toLatin1(), list[1].toUShort());
|
||||
worker->moveToThread(refreshThread);
|
||||
connect(refreshThread, SIGNAL(started()), worker, SLOT(process()));
|
||||
connect(worker, SIGNAL(finished()), refreshThread, SLOT(quit()));
|
||||
connect(refreshThread, SIGNAL(finished()), this, SLOT(refresh()));
|
||||
|
||||
connect(btnRefresh, &QPushButton::clicked, [this]{
|
||||
if (!refreshThread->isRunning())
|
||||
refreshThread->start();
|
||||
});
|
||||
}
|
||||
|
||||
ServerInfoDialog::~ServerInfoDialog()
|
||||
{
|
||||
worker->dialog = nullptr;
|
||||
if (!refreshThread->isRunning())
|
||||
refreshThread->terminate();
|
||||
}
|
||||
|
||||
bool ServerInfoDialog::isUpdated()
|
||||
{
|
||||
return sd.first != UNASSIGNED_SYSTEM_ADDRESS;
|
||||
}
|
||||
|
||||
void ServerInfoDialog::setData(std::pair<RakNet::SystemAddress, QueryData> &newSD)
|
||||
{
|
||||
sd = newSD;
|
||||
}
|
||||
|
||||
void ServerInfoDialog::refresh()
|
||||
{
|
||||
if (sd.first != UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
leAddr->setText(sd.first.ToString(true, ':'));
|
||||
lblName->setText(sd.second.GetName());
|
||||
int ping = PingRakNetServer(sd.first.ToString(false), sd.first.GetPort());
|
||||
lblPing->setNum(ping);
|
||||
btnConnect->setDisabled(ping == PING_UNREACHABLE);
|
||||
|
||||
listPlayers->clear();
|
||||
for (const auto &player : sd.second.players)
|
||||
listPlayers->addItem(QString::fromStdString(player));
|
||||
|
||||
listPlugins->clear();
|
||||
for (const auto &plugin : sd.second.plugins)
|
||||
listPlugins->addItem(QString::fromStdString(plugin.name));
|
||||
|
||||
listRules->clear();
|
||||
const static vector<std::string> defaultRules {"gamemode", "maxPlayers", "name", "passw", "players", "version"};
|
||||
for (auto &rule : sd.second.rules)
|
||||
{
|
||||
if (::find(defaultRules.begin(), defaultRules.end(), rule.first) != defaultRules.end())
|
||||
continue;
|
||||
QString ruleStr = QString::fromStdString(rule.first) + " : ";
|
||||
if (rule.second.type == 's')
|
||||
ruleStr += QString::fromStdString(rule.second.str);
|
||||
else
|
||||
ruleStr += QString::number(rule.second.val);
|
||||
listRules->addItem(ruleStr);
|
||||
}
|
||||
|
||||
lblPlayers->setText(QString::number(sd.second.players.size()) + " / " + QString::number(sd.second.GetMaxPlayers()));
|
||||
}
|
||||
}
|
||||
|
||||
int ServerInfoDialog::exec()
|
||||
{
|
||||
if (!refreshThread->isRunning())
|
||||
refreshThread->start();
|
||||
return QDialog::exec();
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_SERVERINFODIALOG_HPP
|
||||
#define NEWLAUNCHER_SERVERINFODIALOG_HPP
|
||||
|
||||
#include "ui_ServerInfo.h"
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
#include <RakNetTypes.h>
|
||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||
|
||||
class ThrWorker;
|
||||
|
||||
class ServerInfoDialog : public QDialog, public Ui::Dialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerInfoDialog(const QString &addr, QWidget *parent = nullptr);
|
||||
~ServerInfoDialog() override;
|
||||
bool isUpdated();
|
||||
void setData(std::pair<RakNet::SystemAddress, QueryData> &newSD);
|
||||
public slots:
|
||||
void refresh();
|
||||
int exec() Q_DECL_OVERRIDE;
|
||||
private:
|
||||
QThread *refreshThread;
|
||||
ThrWorker* worker;
|
||||
std::pair<RakNet::SystemAddress, QueryData> sd;
|
||||
};
|
||||
|
||||
class ThrWorker: public QObject
|
||||
{
|
||||
friend class ServerInfoDialog;
|
||||
Q_OBJECT
|
||||
public:
|
||||
ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port);
|
||||
|
||||
public slots:
|
||||
void process();
|
||||
signals:
|
||||
void finished();
|
||||
private:
|
||||
QString addr;
|
||||
unsigned short port;
|
||||
bool stopped;
|
||||
ServerInfoDialog *dialog;
|
||||
};
|
||||
|
||||
|
||||
#endif //NEWLAUNCHER_SERVERINFODIALOG_HPP
|
@ -1,191 +0,0 @@
|
||||
#include <qmessagebox.h>
|
||||
#include "ServerModel.hpp"
|
||||
#include <qdebug.h>
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
|
||||
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
|
||||
{
|
||||
}
|
||||
/*QHash<int, QByteArray> ServerModel::roleNames() const
|
||||
{
|
||||
return roles;
|
||||
}*/
|
||||
|
||||
QVariant ServerModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
if (index.row() < 0 || index.row() > myData.size())
|
||||
return QVariant();
|
||||
|
||||
const ServerData &sd = myData.at(index.row());
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
QVariant var;
|
||||
switch (index.column())
|
||||
{
|
||||
case ServerData::ADDR:
|
||||
var = sd.addr;
|
||||
break;
|
||||
case ServerData::PASSW:
|
||||
var = (int)(sd.rules.at("passw").val) == 1 ? "Yes" : "No";
|
||||
break;
|
||||
case ServerData::VERSION:
|
||||
var = QString(sd.rules.at("version").str.c_str());
|
||||
break;
|
||||
case ServerData::PLAYERS:
|
||||
var = (int) sd.rules.at("players").val;
|
||||
break;
|
||||
case ServerData::MAX_PLAYERS:
|
||||
var = (int) sd.rules.at("maxPlayers").val;
|
||||
break;
|
||||
case ServerData::HOSTNAME:
|
||||
var = QString(sd.rules.at("name").str.c_str());
|
||||
break;
|
||||
case ServerData::PING:
|
||||
var = sd.ping == PING_UNREACHABLE ? QVariant("Unreachable") : sd.ping;
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
if (sd.rules.at("gamemode").str == "")
|
||||
var = "default";
|
||||
else
|
||||
var = QString(sd.rules.at("gamemode").str.c_str());
|
||||
break;
|
||||
}
|
||||
return var;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QVariant var;
|
||||
if (orientation == Qt::Horizontal)
|
||||
{
|
||||
if (role == Qt::SizeHintRole)
|
||||
{
|
||||
/*if (section == ServerData::HOSTNAME)
|
||||
var = QSize(200, 25);*/
|
||||
}
|
||||
else if (role == Qt::DisplayRole)
|
||||
{
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case ServerData::ADDR:
|
||||
var = "Address";
|
||||
break;
|
||||
case ServerData::PASSW:
|
||||
var = "Password";
|
||||
break;
|
||||
case ServerData::VERSION:
|
||||
var = "Version";
|
||||
break;
|
||||
case ServerData::HOSTNAME:
|
||||
var = "Host name";
|
||||
break;
|
||||
case ServerData::PLAYERS:
|
||||
var = "Players";
|
||||
break;
|
||||
case ServerData::MAX_PLAYERS:
|
||||
var = "Max players";
|
||||
break;
|
||||
case ServerData::PING:
|
||||
var = "Ping";
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
var = "Game mode";
|
||||
}
|
||||
}
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
int ServerModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return myData.size();
|
||||
}
|
||||
|
||||
int ServerModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return ServerData::LAST;
|
||||
}
|
||||
|
||||
bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (index.isValid() && role == Qt::EditRole)
|
||||
{
|
||||
int row = index.row();
|
||||
int col = index.column();
|
||||
|
||||
ServerData &sd = myData[row];
|
||||
bool ok = true;
|
||||
switch(col)
|
||||
{
|
||||
case ServerData::ADDR:
|
||||
sd.addr = value.toString();
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PASSW:
|
||||
sd.SetPassword(value.toBool());
|
||||
break;
|
||||
case ServerData::VERSION:
|
||||
sd.SetVersion(value.toString().toUtf8());
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PLAYERS:
|
||||
sd.SetPlayers(value.toInt(&ok));
|
||||
break;
|
||||
case ServerData::MAX_PLAYERS:
|
||||
sd.SetMaxPlayers(value.toInt(&ok));
|
||||
break;
|
||||
case ServerData::HOSTNAME:
|
||||
sd.SetName(value.toString().toUtf8());
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PING:
|
||||
sd.ping = value.toInt(&ok);
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
sd.SetGameMode(value.toString().toUtf8());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (ok)
|
||||
emit(dataChanged(index, index));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServerModel::insertRows(int position, int count, const QModelIndex &index)
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
beginInsertRows(QModelIndex(), position, position + count - 1);
|
||||
|
||||
myData.insert(position, count, {});
|
||||
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServerModel::removeRows(int position, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
beginRemoveRows(parent, position, position + count - 1);
|
||||
myData.erase(myData.begin()+position, myData.begin() + position + count);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QModelIndex ServerModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
|
||||
QModelIndex index = QAbstractTableModel::index(row, column, parent);
|
||||
return index;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#ifndef SERVERMODEL_FONTMODEL_HPP
|
||||
#define SERVERMODEL_FONTMODEL_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include <QAbstractTableModel>
|
||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||
|
||||
struct ServerData : public QueryData
|
||||
{
|
||||
QString addr;
|
||||
int ping;
|
||||
enum IDS
|
||||
{
|
||||
ADDR,
|
||||
HOSTNAME,
|
||||
PLAYERS,
|
||||
MAX_PLAYERS,
|
||||
PASSW,
|
||||
MODNAME,
|
||||
PING,
|
||||
VERSION,
|
||||
LAST
|
||||
};
|
||||
};
|
||||
|
||||
class ServerModel: public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerModel(QObject *parent = nullptr);
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
||||
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_FINAL;
|
||||
|
||||
bool insertRows(int row, int count, const QModelIndex &index = QModelIndex()) Q_DECL_FINAL;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_FINAL;
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
||||
|
||||
|
||||
public:
|
||||
//QHash<int, QByteArray> roles;
|
||||
QVector<ServerData> myData;
|
||||
};
|
||||
|
||||
|
||||
#endif //SERVERMODEL_FONTMODEL_HPP
|
@ -1,15 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 07.05.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_TYPES_HPP
|
||||
#define OPENMW_TYPES_HPP
|
||||
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
|
||||
typedef QPair <QString, unsigned short> AddrPair;
|
||||
typedef QPair <int, AddrPair> ServerRow;
|
||||
|
||||
|
||||
#endif //OPENMW_TYPES_HPP
|
@ -1,53 +0,0 @@
|
||||
#include <QApplication>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <apps/browser/netutils/QueryClient.hpp>
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
std::string loadSettings (Settings::Manager & settings)
|
||||
{
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
// Create the settings manager and load default settings file
|
||||
const std::string localdefault = (mCfgMgr.getLocalPath() / "tes3mp-client-default.cfg").string();
|
||||
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "tes3mp-client-default.cfg").string();
|
||||
|
||||
// prefer local
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
settings.loadDefault(localdefault);
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
settings.loadDefault(globaldefault);
|
||||
else
|
||||
throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed.");
|
||||
|
||||
// load user settings if they exist
|
||||
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "tes3mp-client.cfg").string();
|
||||
if (boost::filesystem::exists(settingspath))
|
||||
settings.loadUser(settingspath);
|
||||
|
||||
return settingspath;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Settings::Manager mgr;
|
||||
|
||||
loadSettings(mgr);
|
||||
|
||||
std::string addr = mgr.getString("address", "Master");
|
||||
int port = mgr.getInt("port", "Master");
|
||||
|
||||
// Is this an attempt to connect to the official master server at the old port? If so,
|
||||
// redirect it to the correct port for the currently used fork of RakNet
|
||||
if (Misc::StringUtils::ciEqual(addr, "master.tes3mp.com") && port == 25560)
|
||||
port = 25561;
|
||||
|
||||
// initialize resources, if needed
|
||||
// Q_INIT_RESOURCE(resfile);
|
||||
|
||||
QueryClient::Get().SetServer(addr, port);
|
||||
QApplication app(argc, argv);
|
||||
MainWindow d;
|
||||
|
||||
d.show();
|
||||
return app.exec();
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#include <RakPeer.h>
|
||||
#include <HTTPConnection2.h>
|
||||
#include <TCPInterface.h>
|
||||
#include <RakSleep.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "HTTPNetwork.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
HTTPNetwork::HTTPNetwork(std::string addr, unsigned short port) : address(addr), port(port)
|
||||
{
|
||||
httpConnection = HTTPConnection2::GetInstance();
|
||||
tcpInterface = new TCPInterface;
|
||||
tcpInterface->Start(0, 64);
|
||||
tcpInterface->AttachPlugin(httpConnection);
|
||||
}
|
||||
|
||||
HTTPNetwork::~HTTPNetwork()
|
||||
{
|
||||
delete tcpInterface;
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::answer()
|
||||
{
|
||||
RakNet::SystemAddress sa;
|
||||
RakNet::Packet *packet;
|
||||
RakNet::SystemAddress hostReceived;
|
||||
RakNet::RakString response;
|
||||
RakNet::RakString transmitted, hostTransmitted;
|
||||
int contentOffset = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// This is kind of crappy, but for TCP plugins, always do HasCompletedConnectionAttempt,
|
||||
// then Receive(), then HasFailedConnectionAttempt(),HasLostConnection()
|
||||
sa = tcpInterface->HasCompletedConnectionAttempt();
|
||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
||||
printf("Connected to master server: %s\n", sa.ToString());
|
||||
|
||||
sa = tcpInterface->HasFailedConnectionAttempt();
|
||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
printf("Failed to connect to master server: %s\n", sa.ToString());
|
||||
return "FAIL_CONNECT";
|
||||
}
|
||||
sa = tcpInterface->HasLostConnection();
|
||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
printf("Lost connection to master server: %s\n", sa.ToString());
|
||||
return "LOST_CONNECTION";
|
||||
}
|
||||
|
||||
for (packet = tcpInterface->Receive(); packet; tcpInterface->DeallocatePacket(
|
||||
packet), packet = tcpInterface->Receive());
|
||||
|
||||
if (httpConnection->GetResponse(transmitted, hostTransmitted, response, hostReceived, contentOffset))
|
||||
{
|
||||
if (contentOffset < 0)
|
||||
return "NO_CONTENT"; // no content
|
||||
tcpInterface->CloseConnection(sa);
|
||||
|
||||
return (response.C_String() + contentOffset);
|
||||
}
|
||||
RakSleep(30);
|
||||
}
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::getData(const char *uri)
|
||||
{
|
||||
RakNet::RakString createRequest = RakNet::RakString::FormatForGET(uri);
|
||||
|
||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
||||
return "UNKNOWN_ADDRESS";
|
||||
return answer();
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::getDataPOST(const char *uri, const char* body, const char* contentType)
|
||||
{
|
||||
RakNet::RakString createRequest = RakNet::RakString::FormatForPOST(uri, contentType, body);
|
||||
|
||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
||||
return "UNKNOWN_ADDRESS";
|
||||
return answer();
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::getDataPUT(const char *uri, const char* body, const char* contentType)
|
||||
{
|
||||
RakNet::RakString createRequest = RakNet::RakString::FormatForPUT(uri, contentType, body);
|
||||
|
||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
||||
return "UNKNOWN_ADDRESS";
|
||||
return answer();
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_HTTPNETWORK_HPP
|
||||
#define NEWLAUNCHER_HTTPNETWORK_HPP
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
class TCPInterface;
|
||||
class HTTPConnection2;
|
||||
}
|
||||
|
||||
class HTTPNetwork
|
||||
{
|
||||
public:
|
||||
HTTPNetwork(std::string addr, unsigned short port);
|
||||
~HTTPNetwork();
|
||||
std::string getData(const char *uri);
|
||||
std::string getDataPOST(const char *uri, const char* body, const char* contentType = "application/json");
|
||||
std::string getDataPUT(const char *uri, const char* body, const char* contentType = "application/json");
|
||||
|
||||
protected:
|
||||
RakNet::TCPInterface *tcpInterface;
|
||||
RakNet::HTTPConnection2 *httpConnection;
|
||||
std::string address;
|
||||
unsigned short port;
|
||||
std::string answer();
|
||||
};
|
||||
|
||||
|
||||
#endif //NEWLAUNCHER_HTTPNETWORK_HPP
|
@ -1,212 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 24.04.17.
|
||||
//
|
||||
|
||||
#include "QueryClient.hpp"
|
||||
#include <RakSleep.h>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include <iostream>
|
||||
#include <components/openmw-mp/Version.hpp>
|
||||
#include <qdebug.h>
|
||||
|
||||
using namespace RakNet;
|
||||
using namespace std;
|
||||
using namespace mwmp;
|
||||
|
||||
QueryClient::QueryClient()
|
||||
{
|
||||
peer = RakPeerInterface::GetInstance();
|
||||
pmq = new PacketMasterQuery(peer);
|
||||
pmu = new PacketMasterUpdate(peer);
|
||||
RakNet::SocketDescriptor sd;
|
||||
peer->Startup(8, &sd, 1);
|
||||
status = -1;
|
||||
}
|
||||
|
||||
QueryClient::~QueryClient()
|
||||
{
|
||||
delete pmq;
|
||||
delete pmu;
|
||||
RakPeerInterface::DestroyInstance(peer);
|
||||
}
|
||||
|
||||
void QueryClient::SetServer(const string &addr, unsigned short port)
|
||||
{
|
||||
masterAddr = SystemAddress(addr.c_str(), port);
|
||||
}
|
||||
|
||||
QueryClient &QueryClient::Get()
|
||||
{
|
||||
static QueryClient myInstance;
|
||||
return myInstance;
|
||||
}
|
||||
|
||||
map<SystemAddress, QueryData> QueryClient::Query()
|
||||
{
|
||||
map<SystemAddress, QueryData> query;
|
||||
BitStream bs;
|
||||
bs.Write((unsigned char) (ID_MASTER_QUERY));
|
||||
qDebug() << "Locking mutex in QueryClient::Query()";
|
||||
mxServers.lock();
|
||||
status = -1;
|
||||
int attempts = 3;
|
||||
do
|
||||
{
|
||||
if (Connect() == IS_NOT_CONNECTED)
|
||||
{
|
||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
||||
mxServers.unlock();
|
||||
return query;
|
||||
}
|
||||
|
||||
int code = peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
||||
|
||||
if (code == 0)
|
||||
{
|
||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
||||
mxServers.unlock();
|
||||
return query;
|
||||
}
|
||||
|
||||
pmq->SetServers(&query);
|
||||
status = GetAnswer(ID_MASTER_QUERY);
|
||||
RakSleep(100);
|
||||
}
|
||||
while(status != ID_MASTER_QUERY && attempts-- > 0);
|
||||
if(status != ID_MASTER_QUERY)
|
||||
qDebug() << "Getting query was failed";
|
||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
||||
peer->CloseConnection(masterAddr, true);
|
||||
mxServers.unlock();
|
||||
qDebug() <<"Answer" << (status == ID_MASTER_QUERY ? "ok." : "wrong.");
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
pair<SystemAddress, QueryData> QueryClient::Update(const RakNet::SystemAddress &addr)
|
||||
{
|
||||
qDebug() << "Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||
pair<SystemAddress, QueryData> server;
|
||||
BitStream bs;
|
||||
bs.Write((unsigned char) (ID_MASTER_UPDATE));
|
||||
bs.Write(addr);
|
||||
|
||||
mxServers.lock();
|
||||
status = -1;
|
||||
int attempts = 3;
|
||||
pmu->SetServer(&server);
|
||||
do
|
||||
{
|
||||
if (Connect() == IS_NOT_CONNECTED)
|
||||
{
|
||||
qDebug() << IS_NOT_CONNECTED;
|
||||
qDebug() << "Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||
mxServers.unlock();
|
||||
return server;
|
||||
}
|
||||
|
||||
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
||||
status = GetAnswer(ID_MASTER_UPDATE);
|
||||
RakSleep(100);
|
||||
}
|
||||
while(status != ID_MASTER_UPDATE && attempts-- > 0);
|
||||
if(status != ID_MASTER_UPDATE)
|
||||
qDebug() << "Getting update was failed";
|
||||
peer->CloseConnection(masterAddr, true);
|
||||
qDebug() << "Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||
mxServers.unlock();
|
||||
return server;
|
||||
}
|
||||
|
||||
MASTER_PACKETS QueryClient::GetAnswer(MASTER_PACKETS waitingPacket)
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
bool update = true;
|
||||
unsigned char pid = 0;
|
||||
int id = -1;
|
||||
while (update)
|
||||
{
|
||||
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
||||
{
|
||||
BitStream data(packet->data, packet->length, false);
|
||||
pmq->SetReadStream(&data);
|
||||
pmu->SetReadStream(&data);
|
||||
data.Read(pid);
|
||||
switch(pid)
|
||||
{
|
||||
case ID_CONNECTION_LOST:
|
||||
qDebug() << "ID_CONNECTION_LOST";
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
qDebug() << "Disconnected";
|
||||
update = false;
|
||||
break;
|
||||
case ID_MASTER_QUERY:
|
||||
qDebug() << "ID_MASTER_QUERY";
|
||||
if (waitingPacket == ID_MASTER_QUERY)
|
||||
pmq->Read();
|
||||
else
|
||||
qDebug() << "Got wrong packet";
|
||||
update = false;
|
||||
id = pid;
|
||||
break;
|
||||
case ID_MASTER_UPDATE:
|
||||
qDebug() << "ID_MASTER_UPDATE";
|
||||
if (waitingPacket == ID_MASTER_UPDATE)
|
||||
pmu->Read();
|
||||
else
|
||||
qDebug() << "Got wrong packet";
|
||||
update = false;
|
||||
id = pid;
|
||||
break;
|
||||
case ID_MASTER_ANNOUNCE:
|
||||
qDebug() << "ID_MASTER_ANNOUNCE";
|
||||
update = false;
|
||||
id = pid;
|
||||
break;
|
||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||
qDebug() << "ID_CONNECTION_REQUEST_ACCEPTED";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
RakSleep(500);
|
||||
}
|
||||
return (MASTER_PACKETS)(id);
|
||||
}
|
||||
|
||||
ConnectionState QueryClient::Connect()
|
||||
{
|
||||
|
||||
ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,
|
||||
strlen(TES3MP_MASTERSERVER_PASSW), nullptr, 0, 5, 500);
|
||||
|
||||
while (true)
|
||||
{
|
||||
ConnectionState state = peer->GetConnectionState(masterAddr);
|
||||
switch (state)
|
||||
{
|
||||
case IS_CONNECTED:
|
||||
qDebug() << "Connected";
|
||||
return IS_CONNECTED;
|
||||
case IS_NOT_CONNECTED:
|
||||
case IS_DISCONNECTED:
|
||||
case IS_SILENTLY_DISCONNECTING:
|
||||
case IS_DISCONNECTING:
|
||||
{
|
||||
qDebug() << "Cannot connect to the master server. Code:"<< state;
|
||||
return IS_NOT_CONNECTED;
|
||||
}
|
||||
case IS_PENDING:
|
||||
case IS_CONNECTING:
|
||||
qDebug() << "Pending";
|
||||
break;
|
||||
}
|
||||
RakSleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
int QueryClient::Status()
|
||||
{
|
||||
return status;
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 24.04.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_QUERYCLIENT_HPP
|
||||
#define OPENMW_QUERYCLIENT_HPP
|
||||
|
||||
#include <string>
|
||||
#include <RakPeerInterface.h>
|
||||
#include <components/openmw-mp/Master/PacketMasterQuery.hpp>
|
||||
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
|
||||
#include <apps/browser/ServerModel.hpp>
|
||||
#include <mutex>
|
||||
|
||||
class QueryClient
|
||||
{
|
||||
public:
|
||||
QueryClient(QueryClient const &) = delete;
|
||||
QueryClient(QueryClient &&) = delete;
|
||||
QueryClient &operator=(QueryClient const &) = delete;
|
||||
QueryClient &operator=(QueryClient &&) = delete;
|
||||
|
||||
static QueryClient &Get();
|
||||
void SetServer(const std::string &addr, unsigned short port);
|
||||
std::map<RakNet::SystemAddress, QueryData> Query();
|
||||
std::pair<RakNet::SystemAddress, QueryData> Update(const RakNet::SystemAddress &addr);
|
||||
int Status();
|
||||
private:
|
||||
RakNet::ConnectionState Connect();
|
||||
MASTER_PACKETS GetAnswer(MASTER_PACKETS packet);
|
||||
protected:
|
||||
QueryClient();
|
||||
~QueryClient();
|
||||
private:
|
||||
int status;
|
||||
RakNet::RakPeerInterface *peer;
|
||||
RakNet::SystemAddress masterAddr;
|
||||
mwmp::PacketMasterQuery *pmq;
|
||||
mwmp::PacketMasterUpdate *pmu;
|
||||
std::pair<RakNet::SystemAddress, ServerData> server;
|
||||
std::mutex mxServers;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //OPENMW_QUERYCLIENT_HPP
|
@ -1,167 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#include <RakPeer.h>
|
||||
#include <MessageIdentifiers.h>
|
||||
#include <RakSleep.h>
|
||||
#include <GetTime.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <components/openmw-mp/Version.hpp>
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
unsigned int PingRakNetServer(const char *addr, unsigned short port)
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
bool done = false;
|
||||
RakNet::TimeMS time = PING_UNREACHABLE;
|
||||
|
||||
RakNet::SocketDescriptor socketDescriptor{0, ""};
|
||||
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
|
||||
peer->Startup(1, &socketDescriptor, 1);
|
||||
if (!peer->Ping(addr, port, false))
|
||||
return time;
|
||||
RakNet::TimeMS start = RakNet::GetTimeMS();
|
||||
while (!done)
|
||||
{
|
||||
RakNet::TimeMS now = RakNet::GetTimeMS();
|
||||
if (now - start >= PING_UNREACHABLE)
|
||||
break;
|
||||
|
||||
packet = peer->Receive();
|
||||
if (!packet)
|
||||
continue;
|
||||
|
||||
switch (packet->data[0])
|
||||
{
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
case ID_CONNECTION_LOST:
|
||||
done = true;
|
||||
break;
|
||||
case ID_CONNECTED_PING:
|
||||
case ID_UNCONNECTED_PONG:
|
||||
{
|
||||
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
||||
bsIn.Read(time);
|
||||
time = now - time;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
peer->DeallocatePacket(packet);
|
||||
}
|
||||
|
||||
peer->Shutdown(0);
|
||||
RakNet::RakPeerInterface::DestroyInstance(peer);
|
||||
return time > PING_UNREACHABLE ? PING_UNREACHABLE : time;
|
||||
}
|
||||
|
||||
ServerExtendedData getExtendedData(const char *addr, unsigned short port)
|
||||
{
|
||||
ServerExtendedData data;
|
||||
RakNet::SocketDescriptor socketDescriptor = {0, ""};
|
||||
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
|
||||
peer->Startup(1, &socketDescriptor, 1);
|
||||
|
||||
stringstream sstr;
|
||||
sstr << TES3MP_VERSION;
|
||||
sstr << TES3MP_PROTO_VERSION;
|
||||
|
||||
std::string msg;
|
||||
|
||||
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), nullptr, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
|
||||
msg = "Connection attempt failed.\n";
|
||||
|
||||
|
||||
int queue = 0;
|
||||
while (queue == 0)
|
||||
{
|
||||
for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(
|
||||
packet), packet = peer->Receive())
|
||||
{
|
||||
switch (packet->data[0])
|
||||
{
|
||||
case ID_CONNECTION_ATTEMPT_FAILED:
|
||||
{
|
||||
msg = "Connection failed.\n"
|
||||
"Either the IP address is wrong or a firewall on either system is blocking\n"
|
||||
"UDP packets on the port you have chosen.";
|
||||
queue = -1;
|
||||
break;
|
||||
}
|
||||
case ID_INVALID_PASSWORD:
|
||||
{
|
||||
msg = "Connection failed.\n"
|
||||
"The client or server is outdated.\n";
|
||||
queue = -1;
|
||||
break;
|
||||
}
|
||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||
{
|
||||
msg = "Connection accepted.\n";
|
||||
queue = 1;
|
||||
break;
|
||||
}
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
throw runtime_error("ID_DISCONNECTION_NOTIFICATION.\n");
|
||||
case ID_CONNECTION_BANNED:
|
||||
throw runtime_error("ID_CONNECTION_BANNED.\n");
|
||||
case ID_CONNECTION_LOST:
|
||||
throw runtime_error("ID_CONNECTION_LOST.\n");
|
||||
default:
|
||||
printf("Connection message with identifier %i has arrived in initialization.\n", packet->data[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
puts(msg.c_str());
|
||||
|
||||
if (queue == -1) // connection is failed
|
||||
return data;
|
||||
|
||||
{
|
||||
RakNet::BitStream bs;
|
||||
bs.Write((unsigned char) (ID_USER_PACKET_ENUM + 1));
|
||||
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
|
||||
RakNet::Packet *packet;
|
||||
bool done = false;
|
||||
while (!done)
|
||||
{
|
||||
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
||||
{
|
||||
if (packet->data[0] == (ID_USER_PACKET_ENUM + 1))
|
||||
{
|
||||
RakNet::BitStream bs(packet->data, packet->length, false);
|
||||
bs.IgnoreBytes(1);
|
||||
size_t length = 0;
|
||||
bs.Read(length);
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
RakNet::RakString str;
|
||||
bs.Read(str);
|
||||
data.players.emplace_back(str.C_String());
|
||||
}
|
||||
bs.Read(length);
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
RakNet::RakString str;
|
||||
bs.Read(str);
|
||||
data.plugins.emplace_back(str.C_String());
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
peer->Shutdown(0);
|
||||
RakSleep(10);
|
||||
RakNet::RakPeerInterface::DestroyInstance(peer);
|
||||
return data;
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_PING_HPP
|
||||
#define NEWLAUNCHER_PING_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
#define PING_UNREACHABLE 999
|
||||
|
||||
unsigned int PingRakNetServer(const char *addr, unsigned short port);
|
||||
|
||||
struct ServerExtendedData
|
||||
{
|
||||
std::vector<std::string> players;
|
||||
std::vector<std::string> plugins;
|
||||
};
|
||||
|
||||
ServerExtendedData getExtendedData(const char *addr, unsigned short port);
|
||||
|
||||
#endif //NEWLAUNCHER_PING_HPP
|
@ -1,50 +0,0 @@
|
||||
set(ESSIMPORTER_FILES
|
||||
main.cpp
|
||||
importer.cpp
|
||||
importplayer.cpp
|
||||
importnpcc.cpp
|
||||
importcrec.cpp
|
||||
importcellref.cpp
|
||||
importacdt.cpp
|
||||
importinventory.cpp
|
||||
importklst.cpp
|
||||
importcntc.cpp
|
||||
importgame.cpp
|
||||
importinfo.cpp
|
||||
importdial.cpp
|
||||
importques.cpp
|
||||
importjour.cpp
|
||||
importscri.cpp
|
||||
importscpt.cpp
|
||||
importproj.cpp
|
||||
importsplm.cpp
|
||||
importercontext.cpp
|
||||
converter.cpp
|
||||
convertacdt.cpp
|
||||
convertnpcc.cpp
|
||||
convertinventory.cpp
|
||||
convertcrec.cpp
|
||||
convertcntc.cpp
|
||||
convertscri.cpp
|
||||
convertscpt.cpp
|
||||
convertplayer.cpp
|
||||
)
|
||||
|
||||
openmw_add_executable(openmw-essimporter
|
||||
${ESSIMPORTER_FILES}
|
||||
)
|
||||
|
||||
target_link_libraries(openmw-essimporter
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(openmw-essimporter gcov)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".")
|
||||
endif(WIN32)
|
@ -1,103 +0,0 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "convertacdt.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
int translateDynamicIndex(int mwIndex)
|
||||
{
|
||||
if (mwIndex == 1)
|
||||
return 2;
|
||||
else if (mwIndex == 2)
|
||||
return 1;
|
||||
return mwIndex;
|
||||
}
|
||||
|
||||
void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats)
|
||||
{
|
||||
for (int i=0; i<3; ++i)
|
||||
{
|
||||
int writeIndex = translateDynamicIndex(i);
|
||||
cStats.mDynamic[writeIndex].mBase = acdt.mDynamic[i][1];
|
||||
cStats.mDynamic[writeIndex].mMod = acdt.mDynamic[i][1];
|
||||
cStats.mDynamic[writeIndex].mCurrent = acdt.mDynamic[i][0];
|
||||
}
|
||||
for (int i=0; i<8; ++i)
|
||||
{
|
||||
cStats.mAttributes[i].mBase = static_cast<int>(acdt.mAttributes[i][1]);
|
||||
cStats.mAttributes[i].mMod = static_cast<int>(acdt.mAttributes[i][0]);
|
||||
cStats.mAttributes[i].mCurrent = static_cast<int>(acdt.mAttributes[i][0]);
|
||||
}
|
||||
cStats.mGoldPool = acdt.mGoldPool;
|
||||
cStats.mTalkedTo = (acdt.mFlags & TalkedToPlayer) != 0;
|
||||
cStats.mAttacked = (acdt.mFlags & Attacked) != 0;
|
||||
}
|
||||
|
||||
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats)
|
||||
{
|
||||
cStats.mDead = (acsc.mFlags & Dead) != 0;
|
||||
}
|
||||
|
||||
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats)
|
||||
{
|
||||
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||
{
|
||||
npcStats.mSkills[i].mMod = actorData.mSkills[i][1];
|
||||
npcStats.mSkills[i].mCurrent = actorData.mSkills[i][1];
|
||||
npcStats.mSkills[i].mBase = actorData.mSkills[i][0];
|
||||
}
|
||||
|
||||
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
|
||||
}
|
||||
|
||||
void convertANIS (const ANIS& anis, ESM::AnimationState& state)
|
||||
{
|
||||
static const char* animGroups[] =
|
||||
{
|
||||
"Idle", "Idle2", "Idle3", "Idle4", "Idle5", "Idle6", "Idle7", "Idle8", "Idle9", "Idlehh", "Idle1h", "Idle2c",
|
||||
"Idle2w", "IdleSwim", "IdleSpell", "IdleCrossbow", "IdleSneak", "IdleStorm", "Torch", "Hit1", "Hit2", "Hit3",
|
||||
"Hit4", "Hit5", "SwimHit1", "SwimHit2", "SwimHit3", "Death1", "Death2", "Death3", "Death4", "Death5",
|
||||
"DeathKnockDown", "DeathKnockOut", "KnockDown", "KnockOut", "SwimDeath", "SwimDeath2", "SwimDeath3",
|
||||
"SwimDeathKnockDown", "SwimDeathKnockOut", "SwimKnockOut", "SwimKnockDown", "SwimWalkForward",
|
||||
"SwimWalkBack", "SwimWalkLeft", "SwimWalkRight", "SwimRunForward", "SwimRunBack", "SwimRunLeft",
|
||||
"SwimRunRight", "SwimTurnLeft", "SwimTurnRight", "WalkForward", "WalkBack", "WalkLeft", "WalkRight",
|
||||
"TurnLeft", "TurnRight", "RunForward", "RunBack", "RunLeft", "RunRight", "SneakForward", "SneakBack",
|
||||
"SneakLeft", "SneakRight", "Jump", "WalkForwardhh", "WalkBackhh", "WalkLefthh", "WalkRighthh",
|
||||
"TurnLefthh", "TurnRighthh", "RunForwardhh", "RunBackhh", "RunLefthh", "RunRighthh", "SneakForwardhh",
|
||||
"SneakBackhh", "SneakLefthh", "SneakRighthh", "Jumphh", "WalkForward1h", "WalkBack1h", "WalkLeft1h",
|
||||
"WalkRight1h", "TurnLeft1h", "TurnRight1h", "RunForward1h", "RunBack1h", "RunLeft1h", "RunRight1h",
|
||||
"SneakForward1h", "SneakBack1h", "SneakLeft1h", "SneakRight1h", "Jump1h", "WalkForward2c", "WalkBack2c",
|
||||
"WalkLeft2c", "WalkRight2c", "TurnLeft2c", "TurnRight2c", "RunForward2c", "RunBack2c", "RunLeft2c",
|
||||
"RunRight2c", "SneakForward2c", "SneakBack2c", "SneakLeft2c", "SneakRight2c", "Jump2c", "WalkForward2w",
|
||||
"WalkBack2w", "WalkLeft2w", "WalkRight2w", "TurnLeft2w", "TurnRight2w", "RunForward2w", "RunBack2w",
|
||||
"RunLeft2w", "RunRight2w", "SneakForward2w", "SneakBack2w", "SneakLeft2w", "SneakRight2w", "Jump2w",
|
||||
"SpellCast", "SpellTurnLeft", "SpellTurnRight", "Attack1", "Attack2", "Attack3", "SwimAttack1",
|
||||
"SwimAttack2", "SwimAttack3", "HandToHand", "Crossbow", "BowAndArrow", "ThrowWeapon", "WeaponOneHand",
|
||||
"WeaponTwoHand", "WeaponTwoWide", "Shield", "PickProbe", "InventoryHandToHand", "InventoryWeaponOneHand",
|
||||
"InventoryWeaponTwoHand", "InventoryWeaponTwoWide"
|
||||
};
|
||||
|
||||
if (anis.mGroupIndex < (sizeof(animGroups) / sizeof(*animGroups)))
|
||||
{
|
||||
std::string group(animGroups[anis.mGroupIndex]);
|
||||
Misc::StringUtils::lowerCaseInPlace(group);
|
||||
|
||||
ESM::AnimationState::ScriptedAnimation scriptedAnim;
|
||||
scriptedAnim.mGroup = group;
|
||||
scriptedAnim.mTime = anis.mTime;
|
||||
scriptedAnim.mAbsolute = true;
|
||||
// Neither loop count nor queueing seems to be supported by the ess format.
|
||||
scriptedAnim.mLoopCount = std::numeric_limits<size_t>::max();
|
||||
state.mScriptedAnims.push_back(scriptedAnim);
|
||||
}
|
||||
else
|
||||
// TODO: Handle 0xFF index, which seems to be used for finished animations.
|
||||
std::cerr << "unknown animation group index: " << static_cast<unsigned int>(anis.mGroupIndex) << std::endl;
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTACDT_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTACDT_H
|
||||
|
||||
#include <components/esm/creaturestats.hpp>
|
||||
#include <components/esm/npcstats.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
#include <components/esm/animationstate.hpp>
|
||||
|
||||
#include "importacdt.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
// OpenMW uses Health,Magicka,Fatigue, MW uses Health,Fatigue,Magicka
|
||||
int translateDynamicIndex(int mwIndex);
|
||||
|
||||
|
||||
void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats);
|
||||
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
|
||||
|
||||
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
|
||||
|
||||
void convertANIS (const ANIS& anis, ESM::AnimationState& state);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,13 +0,0 @@
|
||||
#include "convertcntc.hpp"
|
||||
|
||||
#include "convertinventory.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertCNTC(const CNTC &cntc, ESM::ContainerState &state)
|
||||
{
|
||||
convertInventory(cntc.mInventory, state.mInventory);
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTCNTC_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTCNTC_H
|
||||
|
||||
#include "importcntc.hpp"
|
||||
|
||||
#include <components/esm/containerstate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertCNTC(const CNTC& cntc, ESM::ContainerState& state);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,13 +0,0 @@
|
||||
#include "convertcrec.hpp"
|
||||
|
||||
#include "convertinventory.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertCREC(const CREC &crec, ESM::CreatureState &state)
|
||||
{
|
||||
convertInventory(crec.mInventory, state.mInventory);
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTCREC_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTCREC_H
|
||||
|
||||
#include "importcrec.hpp"
|
||||
|
||||
#include <components/esm/creaturestate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertCREC(const CREC& crec, ESM::CreatureState& state);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,521 +0,0 @@
|
||||
#include "converter.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include <osgDB/WriteFile>
|
||||
|
||||
#include <components/esm/creaturestate.hpp>
|
||||
#include <components/esm/containerstate.hpp>
|
||||
|
||||
#include "convertcrec.hpp"
|
||||
#include "convertcntc.hpp"
|
||||
#include "convertscri.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void convertImage(char* data, int size, int width, int height, GLenum pf, const std::string& out)
|
||||
{
|
||||
osg::ref_ptr<osg::Image> image (new osg::Image);
|
||||
image->allocateImage(width, height, 1, pf, GL_UNSIGNED_BYTE);
|
||||
memcpy(image->data(), data, size);
|
||||
image->flipVertical();
|
||||
|
||||
osgDB::writeImageFile(*image, out);
|
||||
}
|
||||
|
||||
|
||||
void convertCellRef(const ESSImport::CellRef& cellref, ESM::ObjectState& objstate)
|
||||
{
|
||||
objstate.mEnabled = cellref.mEnabled;
|
||||
objstate.mPosition = cellref.mPos;
|
||||
objstate.mRef.mRefNum = cellref.mRefNum;
|
||||
if (cellref.mDeleted)
|
||||
objstate.mCount = 0;
|
||||
convertSCRI(cellref.mSCRI, objstate.mLocals);
|
||||
objstate.mHasLocals = !objstate.mLocals.mVariables.empty();
|
||||
|
||||
if (cellref.mHasANIS)
|
||||
convertANIS(cellref.mANIS, objstate.mAnimationState);
|
||||
}
|
||||
|
||||
bool isIndexedRefId(const std::string& indexedRefId)
|
||||
{
|
||||
if (indexedRefId.size() <= 8)
|
||||
return false;
|
||||
|
||||
if (indexedRefId.find_first_not_of("0123456789") == std::string::npos)
|
||||
return false; // entirely numeric refid, this is a reference to
|
||||
// a dynamically created record e.g. player-enchanted weapon
|
||||
|
||||
std::string index = indexedRefId.substr(indexedRefId.size()-8);
|
||||
if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::hex << indexedRefId.substr(indexedRefId.size()-8,8);
|
||||
stream >> refIndex;
|
||||
|
||||
refId = indexedRefId.substr(0,indexedRefId.size()-8);
|
||||
}
|
||||
|
||||
int convertActorId(const std::string& indexedRefId, ESSImport::Context& context)
|
||||
{
|
||||
if (isIndexedRefId(indexedRefId))
|
||||
{
|
||||
int refIndex;
|
||||
std::string refId;
|
||||
splitIndexedRefId(indexedRefId, refIndex, refId);
|
||||
|
||||
auto it = context.mActorIdMap.find(std::make_pair(refIndex, refId));
|
||||
if (it == context.mActorIdMap.end())
|
||||
return -1;
|
||||
return it->second;
|
||||
}
|
||||
else if (indexedRefId == "PlayerSaveGame")
|
||||
{
|
||||
return context.mPlayer.mObject.mCreatureStats.mActorId;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
|
||||
struct MAPH
|
||||
{
|
||||
unsigned int size;
|
||||
unsigned int value;
|
||||
};
|
||||
|
||||
void ConvertFMAP::read(ESM::ESMReader &esm)
|
||||
{
|
||||
MAPH maph;
|
||||
esm.getHNT(maph, "MAPH");
|
||||
std::vector<char> data;
|
||||
esm.getSubNameIs("MAPD");
|
||||
esm.getSubHeader();
|
||||
data.resize(esm.getSubSize());
|
||||
esm.getExact(&data[0], data.size());
|
||||
|
||||
mGlobalMapImage = new osg::Image;
|
||||
mGlobalMapImage->allocateImage(maph.size, maph.size, 1, GL_RGB, GL_UNSIGNED_BYTE);
|
||||
memcpy(mGlobalMapImage->data(), &data[0], data.size());
|
||||
|
||||
// to match openmw size
|
||||
// FIXME: filtering?
|
||||
mGlobalMapImage->scaleImage(maph.size*2, maph.size*2, 1, GL_UNSIGNED_BYTE);
|
||||
}
|
||||
|
||||
void ConvertFMAP::write(ESM::ESMWriter &esm)
|
||||
{
|
||||
int numcells = mGlobalMapImage->s() / 18; // NB truncating, doesn't divide perfectly
|
||||
// with the 512x512 map the game has by default
|
||||
int cellSize = mGlobalMapImage->s()/numcells;
|
||||
|
||||
// Note the upper left corner of the (0,0) cell should be at (width/2, height/2)
|
||||
|
||||
mContext->mGlobalMapState.mBounds.mMinX = -numcells/2;
|
||||
mContext->mGlobalMapState.mBounds.mMaxX = (numcells-1)/2;
|
||||
mContext->mGlobalMapState.mBounds.mMinY = -(numcells-1)/2;
|
||||
mContext->mGlobalMapState.mBounds.mMaxY = numcells/2;
|
||||
|
||||
osg::ref_ptr<osg::Image> image2 (new osg::Image);
|
||||
int width = cellSize*numcells;
|
||||
int height = cellSize*numcells;
|
||||
std::vector<unsigned char> data;
|
||||
data.resize(width*height*4, 0);
|
||||
|
||||
image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
memcpy(image2->data(), &data[0], data.size());
|
||||
|
||||
for (std::set<std::pair<int, int> >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it)
|
||||
{
|
||||
if (it->first > mContext->mGlobalMapState.mBounds.mMaxX
|
||||
|| it->first < mContext->mGlobalMapState.mBounds.mMinX
|
||||
|| it->second > mContext->mGlobalMapState.mBounds.mMaxY
|
||||
|| it->second < mContext->mGlobalMapState.mBounds.mMinY)
|
||||
{
|
||||
// out of bounds, I think this could happen, since the original engine had a fixed-size map
|
||||
continue;
|
||||
}
|
||||
|
||||
int imageLeftSrc = mGlobalMapImage->s()/2;
|
||||
int imageTopSrc = mGlobalMapImage->t()/2;
|
||||
imageLeftSrc += it->first * cellSize;
|
||||
imageTopSrc -= it->second * cellSize;
|
||||
int imageLeftDst = width/2;
|
||||
int imageTopDst = height/2;
|
||||
imageLeftDst += it->first * cellSize;
|
||||
imageTopDst -= it->second * cellSize;
|
||||
for (int x=0; x<cellSize; ++x)
|
||||
for (int y=0; y<cellSize; ++y)
|
||||
{
|
||||
unsigned int col = *(unsigned int*)mGlobalMapImage->data(imageLeftSrc+x, imageTopSrc+y, 0);
|
||||
*(unsigned int*)image2->data(imageLeftDst+x, imageTopDst+y, 0) = col;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ostream;
|
||||
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png");
|
||||
if (!readerwriter)
|
||||
{
|
||||
std::cerr << "Error: can't write global map image, no png readerwriter found" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
image2->flipVertical();
|
||||
|
||||
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image2, ostream);
|
||||
if (!result.success())
|
||||
{
|
||||
std::cerr << "Error: can't write global map image: " << result.message() << " code " << result.status() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string outData = ostream.str();
|
||||
mContext->mGlobalMapState.mImageData = std::vector<char>(outData.begin(), outData.end());
|
||||
|
||||
esm.startRecord(ESM::REC_GMAP);
|
||||
mContext->mGlobalMapState.save(esm);
|
||||
esm.endRecord(ESM::REC_GMAP);
|
||||
}
|
||||
|
||||
void ConvertCell::read(ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::Cell cell;
|
||||
bool isDeleted = false;
|
||||
|
||||
cell.load(esm, isDeleted, false);
|
||||
|
||||
// I wonder what 0x40 does?
|
||||
if (cell.isExterior() && cell.mData.mFlags & 0x20)
|
||||
{
|
||||
mContext->mGlobalMapState.mMarkers.insert(std::make_pair(cell.mData.mX, cell.mData.mY));
|
||||
}
|
||||
|
||||
// note if the player is in a nameless exterior cell, we will assign the cellId later based on player position
|
||||
if (cell.mName == mContext->mPlayerCellName)
|
||||
{
|
||||
mContext->mPlayer.mCellId = cell.getCellId();
|
||||
}
|
||||
|
||||
Cell newcell;
|
||||
newcell.mCell = cell;
|
||||
|
||||
// fog of war
|
||||
// seems to be a 1-bit pixel format, 16*16 pixels
|
||||
// TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures,
|
||||
// MW handles it when rendering only)
|
||||
unsigned char nam8[32];
|
||||
// exterior has 1 NAM8, interior can have multiple ones, and have an extra 4 byte flag at the start
|
||||
// (probably offset of that specific fog texture?)
|
||||
while (esm.isNextSub("NAM8"))
|
||||
{
|
||||
if (cell.isExterior()) // TODO: NAM8 occasionally exists for cells that haven't been explored.
|
||||
// are there any flags marking explored cells?
|
||||
mContext->mExploredCells.insert(std::make_pair(cell.mData.mX, cell.mData.mY));
|
||||
|
||||
esm.getSubHeader();
|
||||
|
||||
if (esm.getSubSize() == 36)
|
||||
{
|
||||
// flag on interiors
|
||||
esm.skip(4);
|
||||
}
|
||||
|
||||
esm.getExact(nam8, 32);
|
||||
|
||||
newcell.mFogOfWar.reserve(16*16);
|
||||
for (int x=0; x<16; ++x)
|
||||
{
|
||||
for (int y=0; y<16; ++y)
|
||||
{
|
||||
size_t pos = x*16+y;
|
||||
size_t bytepos = pos/8;
|
||||
assert(bytepos<32);
|
||||
int bit = pos%8;
|
||||
newcell.mFogOfWar.push_back(((nam8[bytepos] >> bit) & (0x1)) ? 0xffffffff : 0x000000ff);
|
||||
}
|
||||
}
|
||||
|
||||
if (cell.isExterior())
|
||||
{
|
||||
std::ostringstream filename;
|
||||
filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga";
|
||||
|
||||
convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, GL_RGBA, filename.str());
|
||||
}
|
||||
}
|
||||
|
||||
// moved reference, not handled yet
|
||||
// NOTE: MVRF can also occur in within normal references (importcellref.cpp)?
|
||||
// this does not match the ESM file implementation,
|
||||
// verify if that can happen with ESM files too
|
||||
while (esm.isNextSub("MVRF"))
|
||||
{
|
||||
esm.skipHSub(); // skip MVRF
|
||||
esm.getSubName();
|
||||
esm.skipHSub(); // skip CNDT
|
||||
}
|
||||
|
||||
std::vector<CellRef> cellrefs;
|
||||
while (esm.hasMoreSubs() && esm.isNextSub("FRMR"))
|
||||
{
|
||||
CellRef ref;
|
||||
ref.load (esm);
|
||||
cellrefs.push_back(ref);
|
||||
}
|
||||
|
||||
while (esm.isNextSub("MPCD"))
|
||||
{
|
||||
float notepos[3];
|
||||
esm.getHT(notepos, 3*sizeof(float));
|
||||
|
||||
// Markers seem to be arranged in a 32*32 grid, notepos has grid-indices.
|
||||
// This seems to be the reason markers can't be placed everywhere in interior cells,
|
||||
// i.e. when the grid is exceeded.
|
||||
// Converting the interior markers correctly could be rather tricky, but is probably similar logic
|
||||
// as used for the FoW texture placement, which we need to figure out anyway
|
||||
notepos[1] += 31.f;
|
||||
notepos[0] += 0.5;
|
||||
notepos[1] += 0.5;
|
||||
notepos[0] = 8192 * notepos[0] / 32.f;
|
||||
notepos[1] = 8192 * notepos[1] / 32.f;
|
||||
if (cell.isExterior())
|
||||
{
|
||||
notepos[0] += 8192 * cell.mData.mX;
|
||||
notepos[1] += 8192 * cell.mData.mY;
|
||||
}
|
||||
// TODO: what encoding is this in?
|
||||
std::string note = esm.getHNString("MPNT");
|
||||
ESM::CustomMarker marker;
|
||||
marker.mWorldX = notepos[0];
|
||||
marker.mWorldY = notepos[1];
|
||||
marker.mNote = note;
|
||||
marker.mCell = cell.getCellId();
|
||||
mMarkers.push_back(marker);
|
||||
}
|
||||
|
||||
newcell.mRefs = cellrefs;
|
||||
|
||||
|
||||
if (cell.isExterior())
|
||||
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
|
||||
else
|
||||
mIntCells[cell.mName] = newcell;
|
||||
}
|
||||
|
||||
void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm)
|
||||
{
|
||||
ESM::Cell esmcell = cell.mCell;
|
||||
esm.startRecord(ESM::REC_CSTA);
|
||||
ESM::CellState csta;
|
||||
csta.mHasFogOfWar = 0;
|
||||
csta.mId = esmcell.getCellId();
|
||||
csta.mId.save(esm);
|
||||
// TODO csta.mLastRespawn;
|
||||
// shouldn't be needed if we respawn on global schedule like in original MW
|
||||
csta.mWaterLevel = esmcell.mWater;
|
||||
csta.save(esm);
|
||||
|
||||
for (std::vector<CellRef>::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt)
|
||||
{
|
||||
const CellRef& cellref = *refIt;
|
||||
ESM::CellRef out (cellref);
|
||||
|
||||
// TODO: use mContext->mCreatures/mNpcs
|
||||
|
||||
if (!isIndexedRefId(cellref.mIndexedRefId))
|
||||
{
|
||||
// non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it
|
||||
// this could be any type of object really (even creatures/npcs too)
|
||||
out.mRefID = cellref.mIndexedRefId;
|
||||
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
|
||||
|
||||
ESM::ObjectState objstate;
|
||||
objstate.blank();
|
||||
objstate.mRef = out;
|
||||
objstate.mRef.mRefID = idLower;
|
||||
objstate.mHasCustomState = false;
|
||||
convertCellRef(cellref, objstate);
|
||||
esm.writeHNT ("OBJE", 0);
|
||||
objstate.save(esm);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
int refIndex;
|
||||
splitIndexedRefId(cellref.mIndexedRefId, refIndex, out.mRefID);
|
||||
|
||||
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
|
||||
|
||||
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
|
||||
std::make_pair(refIndex, out.mRefID));
|
||||
if (npccIt != mContext->mNpcChanges.end())
|
||||
{
|
||||
ESM::NpcState objstate;
|
||||
objstate.blank();
|
||||
objstate.mRef = out;
|
||||
objstate.mRef.mRefID = idLower;
|
||||
// TODO: need more micromanagement here so we don't overwrite values
|
||||
// from the ESM with default values
|
||||
if (cellref.mHasACDT)
|
||||
convertACDT(cellref.mACDT, objstate.mCreatureStats);
|
||||
if (cellref.mHasACSC)
|
||||
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
||||
convertNpcData(cellref, objstate.mNpcStats);
|
||||
convertNPCC(npccIt->second, objstate);
|
||||
convertCellRef(cellref, objstate);
|
||||
|
||||
objstate.mCreatureStats.mActorId = mContext->generateActorId();
|
||||
mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));
|
||||
|
||||
esm.writeHNT ("OBJE", ESM::REC_NPC_);
|
||||
objstate.save(esm);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::map<std::pair<int, std::string>, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find(
|
||||
std::make_pair(refIndex, out.mRefID));
|
||||
if (cntcIt != mContext->mContainerChanges.end())
|
||||
{
|
||||
ESM::ContainerState objstate;
|
||||
objstate.blank();
|
||||
objstate.mRef = out;
|
||||
objstate.mRef.mRefID = idLower;
|
||||
convertCNTC(cntcIt->second, objstate);
|
||||
convertCellRef(cellref, objstate);
|
||||
esm.writeHNT ("OBJE", ESM::REC_CONT);
|
||||
objstate.save(esm);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::map<std::pair<int, std::string>, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find(
|
||||
std::make_pair(refIndex, out.mRefID));
|
||||
if (crecIt != mContext->mCreatureChanges.end())
|
||||
{
|
||||
ESM::CreatureState objstate;
|
||||
objstate.blank();
|
||||
objstate.mRef = out;
|
||||
objstate.mRef.mRefID = idLower;
|
||||
// TODO: need more micromanagement here so we don't overwrite values
|
||||
// from the ESM with default values
|
||||
if (cellref.mHasACDT)
|
||||
convertACDT(cellref.mACDT, objstate.mCreatureStats);
|
||||
if (cellref.mHasACSC)
|
||||
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
||||
convertCREC(crecIt->second, objstate);
|
||||
convertCellRef(cellref, objstate);
|
||||
|
||||
objstate.mCreatureStats.mActorId = mContext->generateActorId();
|
||||
mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));
|
||||
|
||||
esm.writeHNT ("OBJE", ESM::REC_CREA);
|
||||
objstate.save(esm);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream error;
|
||||
error << "Can't find type for " << cellref.mIndexedRefId << std::endl;
|
||||
throw std::runtime_error(error.str());
|
||||
}
|
||||
}
|
||||
|
||||
esm.endRecord(ESM::REC_CSTA);
|
||||
}
|
||||
|
||||
void ConvertCell::write(ESM::ESMWriter &esm)
|
||||
{
|
||||
for (std::map<std::string, Cell>::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it)
|
||||
writeCell(it->second, esm);
|
||||
|
||||
for (std::map<std::pair<int, int>, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it)
|
||||
writeCell(it->second, esm);
|
||||
|
||||
for (std::vector<ESM::CustomMarker>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it)
|
||||
{
|
||||
esm.startRecord(ESM::REC_MARK);
|
||||
it->save(esm);
|
||||
esm.endRecord(ESM::REC_MARK);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertPROJ::read(ESM::ESMReader& esm)
|
||||
{
|
||||
mProj.load(esm);
|
||||
}
|
||||
|
||||
void ConvertPROJ::write(ESM::ESMWriter& esm)
|
||||
{
|
||||
for (const PROJ::PNAM& pnam : mProj.mProjectiles)
|
||||
{
|
||||
if (!pnam.isMagic())
|
||||
{
|
||||
ESM::ProjectileState out;
|
||||
convertBaseState(out, pnam);
|
||||
|
||||
out.mBowId = pnam.mBowId.toString();
|
||||
out.mVelocity = pnam.mVelocity;
|
||||
out.mAttackStrength = pnam.mAttackStrength;
|
||||
|
||||
esm.startRecord(ESM::REC_PROJ);
|
||||
out.save(esm);
|
||||
esm.endRecord(ESM::REC_PROJ);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESM::MagicBoltState out;
|
||||
convertBaseState(out, pnam);
|
||||
|
||||
auto it = std::find_if(mContext->mActiveSpells.begin(), mContext->mActiveSpells.end(),
|
||||
[&pnam](const SPLM::ActiveSpell& spell) -> bool { return spell.mIndex == pnam.mSplmIndex; });
|
||||
|
||||
if (it == mContext->mActiveSpells.end())
|
||||
{
|
||||
std::cerr << "Warning: Skipped conversion for magic projectile \"" << pnam.mArrowId.toString() << "\" (invalid spell link)" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
out.mSpellId = it->mSPDT.mId.toString();
|
||||
out.mSpeed = pnam.mSpeed * 0.001f; // not sure where this factor comes from
|
||||
|
||||
esm.startRecord(ESM::REC_MPRJ);
|
||||
out.save(esm);
|
||||
esm.endRecord(ESM::REC_MPRJ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertPROJ::convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam)
|
||||
{
|
||||
base.mId = pnam.mArrowId.toString();
|
||||
base.mPosition = pnam.mPosition;
|
||||
|
||||
osg::Quat orient;
|
||||
orient.makeRotate(osg::Vec3f(0,1,0), pnam.mVelocity);
|
||||
base.mOrientation = orient;
|
||||
|
||||
base.mActorId = convertActorId(pnam.mActorId.toString(), *mContext);
|
||||
}
|
||||
|
||||
void ConvertSPLM::read(ESM::ESMReader& esm)
|
||||
{
|
||||
mSPLM.load(esm);
|
||||
mContext->mActiveSpells = mSPLM.mActiveSpells;
|
||||
}
|
||||
|
||||
void ConvertSPLM::write(ESM::ESMWriter& esm)
|
||||
{
|
||||
std::cerr << "Warning: Skipped active spell conversion (not implemented)" << std::endl;
|
||||
}
|
||||
|
||||
}
|
@ -1,622 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTER_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTER_H
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <osg/Image>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/esm/loadbook.hpp>
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loadglob.hpp>
|
||||
#include <components/esm/cellstate.hpp>
|
||||
#include <components/esm/loadfact.hpp>
|
||||
#include <components/esm/dialoguestate.hpp>
|
||||
#include <components/esm/custommarkerstate.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/weatherstate.hpp>
|
||||
#include <components/esm/globalscript.hpp>
|
||||
#include <components/esm/queststate.hpp>
|
||||
#include <components/esm/stolenitems.hpp>
|
||||
#include <components/esm/projectilestate.hpp>
|
||||
|
||||
#include "importcrec.hpp"
|
||||
#include "importcntc.hpp"
|
||||
|
||||
#include "importercontext.hpp"
|
||||
#include "importcellref.hpp"
|
||||
#include "importklst.hpp"
|
||||
#include "importgame.hpp"
|
||||
#include "importinfo.hpp"
|
||||
#include "importdial.hpp"
|
||||
#include "importques.hpp"
|
||||
#include "importjour.hpp"
|
||||
#include "importscpt.hpp"
|
||||
#include "importproj.h"
|
||||
#include "importsplm.h"
|
||||
|
||||
#include "convertacdt.hpp"
|
||||
#include "convertnpcc.hpp"
|
||||
#include "convertscpt.hpp"
|
||||
#include "convertplayer.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
class Converter
|
||||
{
|
||||
public:
|
||||
/// @return the order for writing this converter's records to the output file, in relation to other converters
|
||||
virtual int getStage() { return 1; }
|
||||
|
||||
virtual ~Converter() {}
|
||||
|
||||
void setContext(Context& context) { mContext = &context; }
|
||||
|
||||
/// @note The load method of ESM records accept the deleted flag as a parameter.
|
||||
/// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored.
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
}
|
||||
|
||||
/// Called after the input file has been read in completely, which may be necessary
|
||||
/// if the conversion process relies on information in other records
|
||||
virtual void write(ESM::ESMWriter& esm)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
Context* mContext;
|
||||
};
|
||||
|
||||
/// Default converter: simply reads the record and writes it unmodified to the output
|
||||
template <typename T>
|
||||
class DefaultConverter : public Converter
|
||||
{
|
||||
public:
|
||||
virtual int getStage() { return 0; }
|
||||
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
T record;
|
||||
bool isDeleted = false;
|
||||
|
||||
record.load(esm, isDeleted);
|
||||
mRecords[record.mId] = record;
|
||||
}
|
||||
|
||||
virtual void write(ESM::ESMWriter& esm)
|
||||
{
|
||||
for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
|
||||
{
|
||||
esm.startRecord(T::sRecordId);
|
||||
it->second.save(esm);
|
||||
esm.endRecord(T::sRecordId);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::map<std::string, T> mRecords;
|
||||
};
|
||||
|
||||
class ConvertNPC : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::NPC npc;
|
||||
bool isDeleted = false;
|
||||
|
||||
npc.load(esm, isDeleted);
|
||||
if (npc.mId != "player")
|
||||
{
|
||||
// Handles changes to the NPC struct, but since there is no index here
|
||||
// it will apply to ALL instances of the class. seems to be the reason for the
|
||||
// "feature" in MW where changing AI settings of one guard will change it for all guards of that refID.
|
||||
mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc;
|
||||
}
|
||||
else
|
||||
{
|
||||
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt.mLevel;
|
||||
mContext->mPlayerBase = npc;
|
||||
ESM::SpellState::SpellParams empty;
|
||||
// FIXME: player start spells and birthsign spells aren't listed here,
|
||||
// need to fix openmw to account for this
|
||||
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
|
||||
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty;
|
||||
|
||||
// Clear the list now that we've written it, this prevents issues cropping up with
|
||||
// ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal.
|
||||
mContext->mPlayerBase.mSpells.mList.clear();
|
||||
|
||||
// Same with inventory. Actually it's strange this would contain something, since there's already an
|
||||
// inventory list in NPCC. There seems to be a fair amount of redundancy in this format.
|
||||
mContext->mPlayerBase.mInventory.mList.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertCREA : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
// See comment in ConvertNPC
|
||||
ESM::Creature creature;
|
||||
bool isDeleted = false;
|
||||
|
||||
creature.load(esm, isDeleted);
|
||||
mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature;
|
||||
}
|
||||
};
|
||||
|
||||
// Do we need ConvertCONT?
|
||||
// I've seen a CONT record in a certain save file, but the container contents in it
|
||||
// were identical to a corresponding CNTC record. See previous comment about redundancy...
|
||||
|
||||
class ConvertGlobal : public DefaultConverter<ESM::Global>
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::Global global;
|
||||
bool isDeleted = false;
|
||||
|
||||
global.load(esm, isDeleted);
|
||||
if (Misc::StringUtils::ciEqual(global.mId, "gamehour"))
|
||||
mContext->mHour = global.mValue.getFloat();
|
||||
if (Misc::StringUtils::ciEqual(global.mId, "day"))
|
||||
mContext->mDay = global.mValue.getInteger();
|
||||
if (Misc::StringUtils::ciEqual(global.mId, "month"))
|
||||
mContext->mMonth = global.mValue.getInteger();
|
||||
if (Misc::StringUtils::ciEqual(global.mId, "year"))
|
||||
mContext->mYear = global.mValue.getInteger();
|
||||
mRecords[global.mId] = global;
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertClass : public DefaultConverter<ESM::Class>
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::Class class_;
|
||||
bool isDeleted = false;
|
||||
|
||||
class_.load(esm, isDeleted);
|
||||
if (class_.mId == "NEWCLASSID_CHARGEN")
|
||||
mContext->mCustomPlayerClassName = class_.mName;
|
||||
|
||||
mRecords[class_.mId] = class_;
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertBook : public DefaultConverter<ESM::Book>
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::Book book;
|
||||
bool isDeleted = false;
|
||||
|
||||
book.load(esm, isDeleted);
|
||||
if (book.mData.mSkillId == -1)
|
||||
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
|
||||
|
||||
mRecords[book.mId] = book;
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertNPCC : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
NPCC npcc;
|
||||
npcc.load(esm);
|
||||
if (id == "PlayerSaveGame")
|
||||
{
|
||||
convertNPCC(npcc, mContext->mPlayer.mObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = npcc.mNPDT.mIndex;
|
||||
mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertREFR : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
REFR refr;
|
||||
refr.load(esm);
|
||||
assert(refr.mRefID == "PlayerSaveGame");
|
||||
mContext->mPlayer.mObject.mPosition = refr.mPos;
|
||||
|
||||
ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats;
|
||||
convertACDT(refr.mActorData.mACDT, cStats);
|
||||
|
||||
ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats;
|
||||
convertNpcData(refr.mActorData, npcStats);
|
||||
|
||||
mSelectedSpell = refr.mActorData.mSelectedSpell;
|
||||
if (!refr.mActorData.mSelectedEnchantItem.empty())
|
||||
{
|
||||
ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory;
|
||||
|
||||
for (unsigned int i=0; i<invState.mItems.size(); ++i)
|
||||
{
|
||||
// FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
|
||||
if (Misc::StringUtils::ciEqual(invState.mItems[i].mRef.mRefID, refr.mActorData.mSelectedEnchantItem))
|
||||
invState.mSelectedEnchantItem = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void write(ESM::ESMWriter& esm)
|
||||
{
|
||||
esm.startRecord(ESM::REC_ASPL);
|
||||
esm.writeHNString("ID__", mSelectedSpell);
|
||||
esm.endRecord(ESM::REC_ASPL);
|
||||
}
|
||||
private:
|
||||
std::string mSelectedSpell;
|
||||
};
|
||||
|
||||
class ConvertPCDT : public Converter
|
||||
{
|
||||
public:
|
||||
ConvertPCDT()
|
||||
: mFirstPersonCam(true),
|
||||
mTeleportingEnabled(true),
|
||||
mLevitationEnabled(true)
|
||||
{}
|
||||
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
PCDT pcdt;
|
||||
pcdt.load(esm);
|
||||
|
||||
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);
|
||||
}
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
{
|
||||
esm.startRecord(ESM::REC_ENAB);
|
||||
esm.writeHNT("TELE", mTeleportingEnabled);
|
||||
esm.writeHNT("LEVT", mLevitationEnabled);
|
||||
esm.endRecord(ESM::REC_ENAB);
|
||||
|
||||
esm.startRecord(ESM::REC_CAM_);
|
||||
esm.writeHNT("FIRS", mFirstPersonCam);
|
||||
esm.endRecord(ESM::REC_CAM_);
|
||||
}
|
||||
private:
|
||||
bool mFirstPersonCam;
|
||||
bool mTeleportingEnabled;
|
||||
bool mLevitationEnabled;
|
||||
};
|
||||
|
||||
class ConvertCNTC : public Converter
|
||||
{
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
CNTC cntc;
|
||||
cntc.load(esm);
|
||||
mContext->mContainerChanges.insert(std::make_pair(std::make_pair(cntc.mIndex,id), cntc));
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertCREC : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
CREC crec;
|
||||
crec.load(esm);
|
||||
mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec));
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertFMAP : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm);
|
||||
virtual void write(ESM::ESMWriter &esm);
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Image> mGlobalMapImage;
|
||||
};
|
||||
|
||||
class ConvertCell : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm);
|
||||
virtual void write(ESM::ESMWriter& esm);
|
||||
|
||||
private:
|
||||
struct Cell
|
||||
{
|
||||
ESM::Cell mCell;
|
||||
std::vector<CellRef> mRefs;
|
||||
std::vector<unsigned int> mFogOfWar;
|
||||
};
|
||||
|
||||
std::map<std::string, Cell> mIntCells;
|
||||
std::map<std::pair<int, int>, Cell> mExtCells;
|
||||
|
||||
std::vector<ESM::CustomMarker> mMarkers;
|
||||
|
||||
void writeCell(const Cell& cell, ESM::ESMWriter &esm);
|
||||
};
|
||||
|
||||
class ConvertKLST : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
KLST klst;
|
||||
klst.load(esm);
|
||||
mKillCounter = klst.mKillCounter;
|
||||
|
||||
mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills;
|
||||
}
|
||||
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
{
|
||||
esm.startRecord(ESM::REC_DCOU);
|
||||
for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
|
||||
{
|
||||
esm.writeHNString("ID__", it->first);
|
||||
esm.writeHNT ("COUN", it->second);
|
||||
}
|
||||
esm.endRecord(ESM::REC_DCOU);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, int> mKillCounter;
|
||||
};
|
||||
|
||||
class ConvertFACT : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
ESM::Faction faction;
|
||||
bool isDeleted = false;
|
||||
|
||||
faction.load(esm, isDeleted);
|
||||
std::string id = Misc::StringUtils::lowerCase(faction.mId);
|
||||
|
||||
for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
|
||||
{
|
||||
std::string faction2 = Misc::StringUtils::lowerCase(it->first);
|
||||
mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Stolen items
|
||||
class ConvertSTLN : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
std::string itemid = esm.getHNString("NAME");
|
||||
Misc::StringUtils::lowerCaseInPlace(itemid);
|
||||
|
||||
while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
|
||||
{
|
||||
if (esm.retSubName().toString() == "FNAM")
|
||||
{
|
||||
std::string factionid = esm.getHString();
|
||||
mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ownerid = esm.getHString();
|
||||
mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
{
|
||||
ESM::StolenItems items;
|
||||
for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
|
||||
{
|
||||
std::map<std::pair<std::string, bool>, int> owners;
|
||||
for (std::set<Owner>::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt)
|
||||
{
|
||||
owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second)
|
||||
// Since OpenMW doesn't suffer from the owner contamination bug,
|
||||
// it needs a count argument. But for legacy savegames, we don't know
|
||||
// this count, so must assume all items of that ID are stolen,
|
||||
// like vanilla MW did.
|
||||
,std::numeric_limits<int>::max()));
|
||||
}
|
||||
|
||||
items.mStolenItems.insert(std::make_pair(it->first, owners));
|
||||
}
|
||||
|
||||
esm.startRecord(ESM::REC_STLN);
|
||||
items.write(esm);
|
||||
esm.endRecord(ESM::REC_STLN);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::pair<std::string, bool> Owner; // <owner id, bool isFaction>
|
||||
|
||||
std::map<std::string, std::set<Owner> > mStolenItems;
|
||||
};
|
||||
|
||||
/// Seen responses for a dialogue topic?
|
||||
/// Each DIAL record is followed by a number of INFO records, I believe, just like in ESMs
|
||||
/// Dialogue conversion problems:
|
||||
/// - Journal is stored in one continuous HTML markup rather than each entry separately with associated info ID.
|
||||
/// - Seen dialogue responses only store the INFO id, rather than the fulltext.
|
||||
/// - Quest stages only store the INFO id, rather than the journal entry fulltext.
|
||||
class ConvertINFO : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
INFO info;
|
||||
info.load(esm);
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertDIAL : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
DIAL dial;
|
||||
dial.load(esm);
|
||||
if (dial.mIndex > 0)
|
||||
mDials[id] = dial;
|
||||
}
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
{
|
||||
for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)
|
||||
{
|
||||
esm.startRecord(ESM::REC_QUES);
|
||||
ESM::QuestState state;
|
||||
state.mFinished = 0;
|
||||
state.mState = it->second.mIndex;
|
||||
state.mTopic = Misc::StringUtils::lowerCase(it->first);
|
||||
state.save(esm);
|
||||
esm.endRecord(ESM::REC_QUES);
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::map<std::string, DIAL> mDials;
|
||||
};
|
||||
|
||||
class ConvertQUES : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
QUES quest;
|
||||
quest.load(esm);
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertJOUR : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
{
|
||||
JOUR journal;
|
||||
journal.load(esm);
|
||||
}
|
||||
};
|
||||
|
||||
class ConvertGAME : public Converter
|
||||
{
|
||||
public:
|
||||
ConvertGAME() : mHasGame(false) {}
|
||||
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
mGame.load(esm);
|
||||
mHasGame = true;
|
||||
}
|
||||
|
||||
int validateWeatherID(int weatherID)
|
||||
{
|
||||
if(weatherID >= -1 && weatherID < 10)
|
||||
{
|
||||
return weatherID;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Invalid weather ID:" << weatherID << std::endl;
|
||||
throw std::runtime_error(error.str());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
{
|
||||
if (!mHasGame)
|
||||
return;
|
||||
esm.startRecord(ESM::REC_WTHR);
|
||||
ESM::WeatherState weather;
|
||||
weather.mTimePassed = 0.0f;
|
||||
weather.mFastForward = false;
|
||||
weather.mWeatherUpdateTime = mGame.mGMDT.mTimeOfNextTransition - mContext->mHour;
|
||||
weather.mTransitionFactor = 1 - (mGame.mGMDT.mWeatherTransition / 100.0f);
|
||||
weather.mCurrentWeather = validateWeatherID(mGame.mGMDT.mCurrentWeather);
|
||||
weather.mNextWeather = validateWeatherID(mGame.mGMDT.mNextWeather);
|
||||
weather.mQueuedWeather = -1;
|
||||
// TODO: Determine how ModRegion modifiers are saved in Morrowind.
|
||||
weather.save(esm);
|
||||
esm.endRecord(ESM::REC_WTHR);
|
||||
}
|
||||
|
||||
private:
|
||||
bool mHasGame;
|
||||
GAME mGame;
|
||||
};
|
||||
|
||||
/// Running global script
|
||||
class ConvertSCPT : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
SCPT script;
|
||||
script.load(esm);
|
||||
ESM::GlobalScript out;
|
||||
convertSCPT(script, out);
|
||||
mScripts.push_back(out);
|
||||
}
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
{
|
||||
for (std::vector<ESM::GlobalScript>::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it)
|
||||
{
|
||||
esm.startRecord(ESM::REC_GSCR);
|
||||
it->save(esm);
|
||||
esm.endRecord(ESM::REC_GSCR);
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<ESM::GlobalScript> mScripts;
|
||||
};
|
||||
|
||||
/// Projectile converter
|
||||
class ConvertPROJ : public Converter
|
||||
{
|
||||
public:
|
||||
virtual int getStage() override { return 2; }
|
||||
virtual void read(ESM::ESMReader& esm) override;
|
||||
virtual void write(ESM::ESMWriter& esm) override;
|
||||
private:
|
||||
void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
|
||||
PROJ mProj;
|
||||
};
|
||||
|
||||
class ConvertSPLM : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm) override;
|
||||
virtual void write(ESM::ESMWriter& esm) override;
|
||||
private:
|
||||
SPLM mSPLM;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
#include "convertinventory.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertInventory(const Inventory &inventory, ESM::InventoryState &state)
|
||||
{
|
||||
int index = 0;
|
||||
for (std::vector<Inventory::InventoryItem>::const_iterator it = inventory.mItems.begin();
|
||||
it != inventory.mItems.end(); ++it)
|
||||
{
|
||||
ESM::ObjectState objstate;
|
||||
objstate.blank();
|
||||
objstate.mRef = *it;
|
||||
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId);
|
||||
objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile
|
||||
// openmw handles them differently, so no need to set any flags
|
||||
state.mItems.push_back(objstate);
|
||||
if (it->mRelativeEquipmentSlot != -1)
|
||||
// Note we should really write the absolute slot here, which we do not know about
|
||||
// Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when
|
||||
// an item could be equipped in two different slots (e.g. equipped two rings)
|
||||
state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTINVENTORY_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTINVENTORY_H
|
||||
|
||||
#include "importinventory.hpp"
|
||||
|
||||
#include <components/esm/inventorystate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertInventory (const Inventory& inventory, ESM::InventoryState& state);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,15 +0,0 @@
|
||||
#include "convertnpcc.hpp"
|
||||
|
||||
#include "convertinventory.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState)
|
||||
{
|
||||
npcState.mNpcStats.mDisposition = npcc.mNPDT.mDisposition;
|
||||
npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation;
|
||||
|
||||
convertInventory(npcc.mInventory, npcState.mInventory);
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTNPCC_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTNPCC_H
|
||||
|
||||
#include "importnpcc.hpp"
|
||||
|
||||
#include <components/esm/npcstate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertNPCC (const NPCC& npcc, ESM::NpcState& npcState);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,88 +0,0 @@
|
||||
#include "convertplayer.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls)
|
||||
{
|
||||
out.mBirthsign = pcdt.mBirthsign;
|
||||
out.mObject.mNpcStats.mBounty = pcdt.mBounty;
|
||||
for (std::vector<PCDT::FNAM>::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it)
|
||||
{
|
||||
ESM::NpcStats::Faction faction;
|
||||
faction.mExpelled = (it->mFlags & 0x2) != 0;
|
||||
faction.mRank = it->mRank;
|
||||
faction.mReputation = it->mReputation;
|
||||
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
|
||||
}
|
||||
for (int i=0; i<3; ++i)
|
||||
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
|
||||
for (int i=0; i<8; ++i)
|
||||
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
|
||||
for (int i=0; i<27; ++i)
|
||||
out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
|
||||
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
|
||||
|
||||
if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawn)
|
||||
out.mObject.mCreatureStats.mDrawState = 1;
|
||||
if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawn)
|
||||
out.mObject.mCreatureStats.mDrawState = 2;
|
||||
|
||||
firstPersonCam = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ThirdPerson);
|
||||
teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);
|
||||
levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);
|
||||
|
||||
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
|
||||
it != pcdt.mKnownDialogueTopics.end(); ++it)
|
||||
{
|
||||
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
|
||||
}
|
||||
|
||||
controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;
|
||||
controls.mControlsDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ControlsDisabled;
|
||||
controls.mJumpingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_JumpingDisabled;
|
||||
controls.mLookingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LookingDisabled;
|
||||
controls.mVanityModeDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_VanityModeDisabled;
|
||||
controls.mWeaponDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawingDisabled;
|
||||
controls.mSpellDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawingDisabled;
|
||||
|
||||
if (pcdt.mHasMark)
|
||||
{
|
||||
out.mHasMark = 1;
|
||||
|
||||
const PCDT::PNAM::MarkLocation& mark = pcdt.mPNAM.mMarkLocation;
|
||||
|
||||
ESM::CellId cell;
|
||||
cell.mWorldspace = ESM::CellId::sDefaultWorldspace;
|
||||
cell.mPaged = true;
|
||||
|
||||
cell.mIndex.mX = mark.mCellX;
|
||||
cell.mIndex.mY = mark.mCellY;
|
||||
|
||||
// TODO: Figure out a better way to detect interiors. (0, 0) is a valid exterior cell.
|
||||
if (mark.mCellX == 0 && mark.mCellY == 0)
|
||||
{
|
||||
cell.mWorldspace = pcdt.mMNAM;
|
||||
cell.mPaged = false;
|
||||
}
|
||||
|
||||
out.mMarkedCell = cell;
|
||||
out.mMarkedPosition.pos[0] = mark.mX;
|
||||
out.mMarkedPosition.pos[1] = mark.mY;
|
||||
out.mMarkedPosition.pos[2] = mark.mZ;
|
||||
out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f;
|
||||
out.mMarkedPosition.rot[2] = mark.mRotZ;
|
||||
}
|
||||
|
||||
if (pcdt.mHasENAM)
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize;
|
||||
out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize;
|
||||
out.mLastKnownExteriorPosition[2] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTPLAYER_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTPLAYER_H
|
||||
|
||||
#include "importplayer.hpp"
|
||||
|
||||
#include <components/esm/player.hpp>
|
||||
#include <components/esm/controlsstate.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,17 +0,0 @@
|
||||
#include "convertscpt.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "convertscri.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out)
|
||||
{
|
||||
out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString());
|
||||
out.mRunning = scpt.mRunning;
|
||||
convertSCRI(scpt.mSCRI, out.mLocals);
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTSCPT_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTSCPT_H
|
||||
|
||||
#include <components/esm/globalscript.hpp>
|
||||
|
||||
#include "importscpt.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertSCPT(const SCPT& scpt, ESM::GlobalScript& out);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,32 +0,0 @@
|
||||
#include "convertscri.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename T, ESM::VarType VariantType>
|
||||
void storeVariables(const std::vector<T>& variables, ESM::Locals& locals, const std::string& scriptname)
|
||||
{
|
||||
for (typename std::vector<T>::const_iterator it = variables.begin(); it != variables.end(); ++it)
|
||||
{
|
||||
ESM::Variant val(*it);
|
||||
val.setType(VariantType);
|
||||
locals.mVariables.push_back(std::make_pair(std::string(), val));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertSCRI(const SCRI &scri, ESM::Locals &locals)
|
||||
{
|
||||
// order *is* important, as we do not have variable names available in this format
|
||||
storeVariables<short, ESM::VT_Short> (scri.mShorts, locals, scri.mScript);
|
||||
storeVariables<int, ESM::VT_Int> (scri.mLongs, locals, scri.mScript);
|
||||
storeVariables<float, ESM::VT_Float> (scri.mFloats, locals, scri.mScript);
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTSCRI_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTSCRI_H
|
||||
|
||||
#include "importscri.hpp"
|
||||
|
||||
#include <components/esm/locals.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Convert script variable assignments
|
||||
void convertSCRI (const SCRI& scri, ESM::Locals& locals);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,135 +0,0 @@
|
||||
#include "importacdt.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void ActorData::load(ESM::ESMReader &esm)
|
||||
{
|
||||
if (esm.isNextSub("ACTN"))
|
||||
{
|
||||
/*
|
||||
Activation flags:
|
||||
ActivationFlag_UseEnabled = 1
|
||||
ActivationFlag_OnActivate = 2
|
||||
ActivationFlag_OnDeath = 10h
|
||||
ActivationFlag_OnKnockout = 20h
|
||||
ActivationFlag_OnMurder = 40h
|
||||
ActivationFlag_DoorOpening = 100h
|
||||
ActivationFlag_DoorClosing = 200h
|
||||
ActivationFlag_DoorJammedOpening = 400h
|
||||
ActivationFlag_DoorJammedClosing = 800h
|
||||
*/
|
||||
esm.skipHSub();
|
||||
}
|
||||
|
||||
if (esm.isNextSub("STPR"))
|
||||
esm.skipHSub();
|
||||
|
||||
if (esm.isNextSub("MNAM"))
|
||||
esm.skipHSub();
|
||||
|
||||
bool isDeleted = false;
|
||||
ESM::CellRef::loadData(esm, isDeleted);
|
||||
|
||||
mHasACDT = false;
|
||||
if (esm.isNextSub("ACDT"))
|
||||
{
|
||||
mHasACDT = true;
|
||||
esm.getHT(mACDT);
|
||||
}
|
||||
|
||||
mHasACSC = false;
|
||||
if (esm.isNextSub("ACSC"))
|
||||
{
|
||||
mHasACSC = true;
|
||||
esm.getHT(mACSC);
|
||||
}
|
||||
|
||||
if (esm.isNextSub("ACSL"))
|
||||
esm.skipHSubSize(112);
|
||||
|
||||
if (esm.isNextSub("CSTN"))
|
||||
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||
|
||||
if (esm.isNextSub("LSTN"))
|
||||
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||
|
||||
// unsure at which point between LSTN and TGTN
|
||||
if (esm.isNextSub("CSHN"))
|
||||
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||
|
||||
// unsure if before or after CSTN/LSTN
|
||||
if (esm.isNextSub("LSHN"))
|
||||
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||
|
||||
while (esm.isNextSub("TGTN"))
|
||||
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||
|
||||
while (esm.isNextSub("FGTN"))
|
||||
esm.getHString(); // fight target?
|
||||
|
||||
// unsure at which point between TGTN and CRED
|
||||
if (esm.isNextSub("AADT"))
|
||||
{
|
||||
// occurred when a creature was in the middle of its attack, 44 bytes
|
||||
esm.skipHSub();
|
||||
}
|
||||
|
||||
// unsure at which point between FGTN and CHRD
|
||||
if (esm.isNextSub("PWPC"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("PWPS"))
|
||||
esm.skipHSub();
|
||||
|
||||
if (esm.isNextSub("WNAM"))
|
||||
{
|
||||
std::string id = esm.getHString();
|
||||
|
||||
if (esm.isNextSub("XNAM"))
|
||||
mSelectedEnchantItem = esm.getHString();
|
||||
else
|
||||
mSelectedSpell = id;
|
||||
|
||||
if (esm.isNextSub("YNAM"))
|
||||
esm.skipHSub(); // 4 byte, 0
|
||||
}
|
||||
|
||||
while (esm.isNextSub("APUD"))
|
||||
{
|
||||
// used power
|
||||
esm.getSubHeader();
|
||||
std::string id = esm.getString(32);
|
||||
(void)id;
|
||||
// timestamp can't be used: this is the total hours passed, calculated by
|
||||
// timestamp = 24 * (365 * year + cumulativeDays[month] + day)
|
||||
// unfortunately cumulativeDays[month] is not clearly defined,
|
||||
// in the (non-MCP) vanilla version the first month was missing, but MCP added it.
|
||||
double timestamp;
|
||||
esm.getT(timestamp);
|
||||
}
|
||||
|
||||
// FIXME: not all actors have this, add flag
|
||||
if (esm.isNextSub("CHRD")) // npc only
|
||||
esm.getHExact(mSkills, 27*2*sizeof(int));
|
||||
|
||||
if (esm.isNextSub("CRED")) // creature only
|
||||
esm.getHExact(mCombatStats, 3*2*sizeof(int));
|
||||
|
||||
mSCRI.load(esm);
|
||||
|
||||
if (esm.isNextSub("ND3D"))
|
||||
esm.skipHSub();
|
||||
|
||||
mHasANIS = false;
|
||||
if (esm.isNextSub("ANIS"))
|
||||
{
|
||||
mHasANIS = true;
|
||||
esm.getHT(mANIS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_ACDT_H
|
||||
#define OPENMW_ESSIMPORT_ACDT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include "importscri.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
enum ACDTFlags
|
||||
{
|
||||
TalkedToPlayer = 0x4,
|
||||
Attacked = 0x100,
|
||||
Unknown = 0x200
|
||||
};
|
||||
enum ACSCFlags
|
||||
{
|
||||
Dead = 0x2
|
||||
};
|
||||
|
||||
/// Actor data, shared by (at least) REFR and CellRef
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
struct ACDT
|
||||
{
|
||||
// Note, not stored at *all*:
|
||||
// - Level changes are lost on reload, except for the player (there it's in the NPC record).
|
||||
unsigned char mUnknown[12];
|
||||
unsigned int mFlags;
|
||||
float mBreathMeter; // Seconds left before drowning
|
||||
unsigned char mUnknown2[20];
|
||||
float mDynamic[3][2];
|
||||
unsigned char mUnknown3[16];
|
||||
float mAttributes[8][2];
|
||||
float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes
|
||||
unsigned char mUnknown4[4];
|
||||
unsigned int mGoldPool;
|
||||
unsigned char mCountDown; // seen the same value as in ACSC.mCorpseClearCountdown, maybe
|
||||
// this one is for respawning?
|
||||
unsigned char mUnknown5[3];
|
||||
};
|
||||
struct ACSC
|
||||
{
|
||||
unsigned char mUnknown1[17];
|
||||
unsigned char mFlags; // ACSCFlags
|
||||
unsigned char mUnknown2[22];
|
||||
unsigned char mCorpseClearCountdown; // hours?
|
||||
unsigned char mUnknown3[71];
|
||||
};
|
||||
struct ANIS
|
||||
{
|
||||
unsigned char mGroupIndex;
|
||||
unsigned char mUnknown[3];
|
||||
float mTime;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct ActorData : public ESM::CellRef
|
||||
{
|
||||
bool mHasACDT;
|
||||
ACDT mACDT;
|
||||
|
||||
bool mHasACSC;
|
||||
ACSC mACSC;
|
||||
|
||||
int mSkills[27][2]; // skills, base and modified
|
||||
|
||||
// creature combat stats, base and modified
|
||||
// I think these can be ignored in the conversion, because it is not possible
|
||||
// to change them ingame
|
||||
int mCombatStats[3][2];
|
||||
|
||||
std::string mSelectedSpell;
|
||||
std::string mSelectedEnchantItem;
|
||||
|
||||
SCRI mSCRI;
|
||||
|
||||
bool mHasANIS;
|
||||
ANIS mANIS; // scripted animation state
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,57 +0,0 @@
|
||||
#include "importcellref.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void CellRef::load(ESM::ESMReader &esm)
|
||||
{
|
||||
blank();
|
||||
|
||||
// (FRMR subrecord name is already read by the loop in ConvertCell)
|
||||
esm.getHT(mRefNum.mIndex); // FRMR
|
||||
|
||||
// this is required since openmw supports more than 255 content files
|
||||
int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24;
|
||||
mRefNum.mContentFile = pluginIndex-1;
|
||||
mRefNum.mIndex &= 0x00ffffff;
|
||||
|
||||
mIndexedRefId = esm.getHNString("NAME");
|
||||
|
||||
ActorData::load(esm);
|
||||
if (esm.isNextSub("LVCR"))
|
||||
{
|
||||
// occurs on levelled creature spawner references
|
||||
// probably some identifier for the creature that has been spawned?
|
||||
unsigned char lvcr;
|
||||
esm.getHT(lvcr);
|
||||
//std::cout << "LVCR: " << (int)lvcr << std::endl;
|
||||
}
|
||||
|
||||
mEnabled = true;
|
||||
esm.getHNOT(mEnabled, "ZNAM");
|
||||
|
||||
// DATA should occur for all references, except levelled creature spawners
|
||||
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird
|
||||
// alarmvoi0000.ess
|
||||
esm.getHNOT(mPos, "DATA", 24);
|
||||
esm.getHNOT(mPos, "DATA", 24);
|
||||
|
||||
mDeleted = 0;
|
||||
if (esm.isNextSub("DELE"))
|
||||
{
|
||||
unsigned int deleted;
|
||||
esm.getHT(deleted);
|
||||
mDeleted = ((deleted >> 24) & 0x2) != 0; // the other 3 bytes seem to be uninitialized garbage
|
||||
}
|
||||
|
||||
if (esm.isNextSub("MVRF"))
|
||||
{
|
||||
esm.skipHSub();
|
||||
esm.getSubName();
|
||||
esm.skipHSub();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CELLREF_H
|
||||
#define OPENMW_ESSIMPORT_CELLREF_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include "importacdt.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct CellRef : public ActorData
|
||||
{
|
||||
std::string mIndexedRefId;
|
||||
|
||||
std::string mScript;
|
||||
|
||||
bool mEnabled;
|
||||
|
||||
bool mDeleted;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,16 +0,0 @@
|
||||
#include "importcntc.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void CNTC::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mIndex = 0;
|
||||
esm.getHNT(mIndex, "INDX");
|
||||
|
||||
mInventory.load(esm);
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTCNTC_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTCNTC_H
|
||||
|
||||
#include "importinventory.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Changed container contents
|
||||
struct CNTC
|
||||
{
|
||||
int mIndex;
|
||||
|
||||
Inventory mInventory;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -1,25 +0,0 @@
|
||||
#include "importcrec.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void CREC::load(ESM::ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mIndex, "INDX");
|
||||
|
||||
// equivalent of ESM::Creature XSCL? probably don't have to convert this,
|
||||
// since the value can't be changed
|
||||
float scale;
|
||||
esm.getHNOT(scale, "XSCL");
|
||||
|
||||
|
||||
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|
||||
|| esm.isNextSub("AI_A"))
|
||||
mAiPackages.add(esm);
|
||||
|
||||
mInventory.load(esm);
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CREC_H
|
||||
#define OPENMW_ESSIMPORT_CREC_H
|
||||
|
||||
#include "importinventory.hpp"
|
||||
#include <components/esm/aipackage.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Creature changes
|
||||
struct CREC
|
||||
{
|
||||
int mIndex;
|
||||
|
||||
Inventory mInventory;
|
||||
ESM::AIPackageList mAiPackages;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,23 +0,0 @@
|
||||
#include "importdial.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void DIAL::load(ESM::ESMReader &esm)
|
||||
{
|
||||
// See ESM::Dialogue::Type enum, not sure why we would need this here though
|
||||
int type = 0;
|
||||
esm.getHNOT(type, "DATA");
|
||||
|
||||
// Deleted dialogue in a savefile. No clue what this means...
|
||||
int deleted = 0;
|
||||
esm.getHNOT(deleted, "DELE");
|
||||
|
||||
mIndex = 0;
|
||||
// *should* always occur except when the dialogue is deleted, but leaving it optional just in case...
|
||||
esm.getHNOT(mIndex, "XIDX");
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTDIAL_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTDIAL_H
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct DIAL
|
||||
{
|
||||
int mIndex; // Journal index
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,448 +0,0 @@
|
||||
#include "importer.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osg/ImageUtils>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/defs.hpp>
|
||||
|
||||
#include <components/esm/savedgame.hpp>
|
||||
#include <components/esm/player.hpp>
|
||||
|
||||
#include <components/esm/loadalch.hpp>
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loadarmo.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/loadclot.hpp>
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/loadlevlist.hpp>
|
||||
#include <components/esm/loadglob.hpp>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "importercontext.hpp"
|
||||
|
||||
#include "converter.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void writeScreenshot(const ESM::Header& fileHeader, ESM::SavedGame& out)
|
||||
{
|
||||
if (fileHeader.mSCRS.size() != 128*128*4)
|
||||
{
|
||||
std::cerr << "Error: unexpected screenshot size " << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Image> image (new osg::Image);
|
||||
image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE);
|
||||
|
||||
// need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise
|
||||
std::vector<unsigned char>::const_iterator it = fileHeader.mSCRS.begin();
|
||||
for (int y=0; y<128; ++y)
|
||||
{
|
||||
for (int x=0; x<128; ++x)
|
||||
{
|
||||
assert(image->data(x,y));
|
||||
*(image->data(x,y)+2) = *it++;
|
||||
*(image->data(x,y)+1) = *it++;
|
||||
*image->data(x,y) = *it++;
|
||||
++it; // skip alpha
|
||||
}
|
||||
}
|
||||
|
||||
image->flipVertical();
|
||||
|
||||
std::stringstream ostream;
|
||||
|
||||
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg");
|
||||
if (!readerwriter)
|
||||
{
|
||||
std::cerr << "Error: can't write screenshot: no jpg readerwriter found" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image, ostream);
|
||||
if (!result.success())
|
||||
{
|
||||
std::cerr << "Error: can't write screenshot: " << result.message() << " code " << result.status() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string data = ostream.str();
|
||||
out.mScreenshot = std::vector<char>(data.begin(), data.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
Importer::Importer(const std::string &essfile, const std::string &outfile, const std::string &encoding)
|
||||
: mEssFile(essfile)
|
||||
, mOutFile(outfile)
|
||||
, mEncoding(encoding)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct File
|
||||
{
|
||||
struct Subrecord
|
||||
{
|
||||
std::string mName;
|
||||
size_t mFileOffset;
|
||||
std::vector<unsigned char> mData;
|
||||
};
|
||||
|
||||
struct Record
|
||||
{
|
||||
std::string mName;
|
||||
size_t mFileOffset;
|
||||
std::vector<Subrecord> mSubrecords;
|
||||
};
|
||||
|
||||
std::vector<Record> mRecords;
|
||||
};
|
||||
|
||||
void read(const std::string& filename, File& file)
|
||||
{
|
||||
ESM::ESMReader esm;
|
||||
esm.open(filename);
|
||||
|
||||
while (esm.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = esm.getRecName();
|
||||
esm.getRecHeader();
|
||||
|
||||
File::Record rec;
|
||||
rec.mName = n.toString();
|
||||
rec.mFileOffset = esm.getFileOffset();
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
File::Subrecord sub;
|
||||
esm.getSubName();
|
||||
esm.getSubHeader();
|
||||
sub.mFileOffset = esm.getFileOffset();
|
||||
sub.mName = esm.retSubName().toString();
|
||||
sub.mData.resize(esm.getSubSize());
|
||||
esm.getExact(&sub.mData[0], sub.mData.size());
|
||||
rec.mSubrecords.push_back(sub);
|
||||
}
|
||||
file.mRecords.push_back(rec);
|
||||
}
|
||||
}
|
||||
|
||||
void Importer::compare()
|
||||
{
|
||||
// data that always changes (and/or is already fully decoded) should be blacklisted
|
||||
std::set<std::pair<std::string, std::string> > blacklist;
|
||||
blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour
|
||||
blacklist.insert(std::make_pair("REFR", "DATA")); // player position
|
||||
blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war
|
||||
blacklist.insert(std::make_pair("GAME", "GMDT")); // weather data, current time always changes
|
||||
blacklist.insert(std::make_pair("CELL", "DELE")); // first 3 bytes are uninitialized
|
||||
|
||||
// this changes way too often, name suggests some renderer internal data?
|
||||
blacklist.insert(std::make_pair("CELL", "ND3D"));
|
||||
blacklist.insert(std::make_pair("REFR", "ND3D"));
|
||||
|
||||
File file1;
|
||||
read(mEssFile, file1);
|
||||
File file2;
|
||||
read(mOutFile, file2); // todo rename variable
|
||||
|
||||
// FIXME: use max(size1, size2)
|
||||
for (unsigned int i=0; i<file1.mRecords.size(); ++i)
|
||||
{
|
||||
File::Record rec = file1.mRecords[i];
|
||||
|
||||
if (i >= file2.mRecords.size())
|
||||
{
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl;
|
||||
std::cout.flags(f);
|
||||
return;
|
||||
}
|
||||
|
||||
File::Record rec2 = file2.mRecords[i];
|
||||
|
||||
if (rec.mName != rec2.mName)
|
||||
{
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl;
|
||||
std::cout.flags(f);
|
||||
return; // TODO: try to recover
|
||||
}
|
||||
|
||||
// FIXME: use max(size1, size2)
|
||||
for (unsigned int j=0; j<rec.mSubrecords.size(); ++j)
|
||||
{
|
||||
File::Subrecord sub = rec.mSubrecords[j];
|
||||
|
||||
if (j >= rec2.mSubrecords.size())
|
||||
{
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl;
|
||||
std::cout.flags(f);
|
||||
return;
|
||||
}
|
||||
|
||||
File::Subrecord sub2 = rec2.mSubrecords[j];
|
||||
|
||||
if (sub.mName != sub2.mName)
|
||||
{
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset
|
||||
<< " (2) 0x" << sub2.mFileOffset << std::endl;
|
||||
std::cout.flags(f);
|
||||
break; // TODO: try to recover
|
||||
}
|
||||
|
||||
if (sub.mData != sub2.mData)
|
||||
{
|
||||
if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end())
|
||||
continue;
|
||||
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
|
||||
std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset
|
||||
<< " (2) 0x" << sub2.mFileOffset << std::endl;
|
||||
|
||||
std::cout << "Data 1:" << std::endl;
|
||||
for (unsigned int k=0; k<sub.mData.size(); ++k)
|
||||
{
|
||||
bool different = false;
|
||||
if (k >= sub2.mData.size() || sub2.mData[k] != sub.mData[k])
|
||||
different = true;
|
||||
|
||||
if (different)
|
||||
std::cout << "\033[033m";
|
||||
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub.mData[k] << " ";
|
||||
if (different)
|
||||
std::cout << "\033[0m";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "Data 2:" << std::endl;
|
||||
for (unsigned int k=0; k<sub2.mData.size(); ++k)
|
||||
{
|
||||
bool different = false;
|
||||
if (k >= sub.mData.size() || sub.mData[k] != sub2.mData[k])
|
||||
different = true;
|
||||
|
||||
if (different)
|
||||
std::cout << "\033[033m";
|
||||
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub2.mData[k] << " ";
|
||||
if (different)
|
||||
std::cout << "\033[0m";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout.flags(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Importer::run()
|
||||
{
|
||||
ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding));
|
||||
ESM::ESMReader esm;
|
||||
esm.open(mEssFile);
|
||||
esm.setEncoder(&encoder);
|
||||
|
||||
Context context;
|
||||
|
||||
const ESM::Header& header = esm.getHeader();
|
||||
context.mPlayerCellName = header.mGameData.mCurrentCell.toString();
|
||||
|
||||
const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value;
|
||||
const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value;
|
||||
const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value;
|
||||
const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value;
|
||||
const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;
|
||||
const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value;
|
||||
const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value;
|
||||
const unsigned int recSPLM = ESM::FourCC<'S','P','L','M'>::value;
|
||||
|
||||
std::map<unsigned int, std::shared_ptr<Converter> > converters;
|
||||
converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal());
|
||||
converters[ESM::REC_BOOK] = std::shared_ptr<Converter>(new ConvertBook());
|
||||
converters[ESM::REC_NPC_] = std::shared_ptr<Converter>(new ConvertNPC());
|
||||
converters[ESM::REC_CREA] = std::shared_ptr<Converter>(new ConvertCREA());
|
||||
converters[ESM::REC_NPCC] = std::shared_ptr<Converter>(new ConvertNPCC());
|
||||
converters[ESM::REC_CREC] = std::shared_ptr<Converter>(new ConvertCREC());
|
||||
converters[recREFR ] = std::shared_ptr<Converter>(new ConvertREFR());
|
||||
converters[recPCDT ] = std::shared_ptr<Converter>(new ConvertPCDT());
|
||||
converters[recFMAP ] = std::shared_ptr<Converter>(new ConvertFMAP());
|
||||
converters[recKLST ] = std::shared_ptr<Converter>(new ConvertKLST());
|
||||
converters[recSTLN ] = std::shared_ptr<Converter>(new ConvertSTLN());
|
||||
converters[recGAME ] = std::shared_ptr<Converter>(new ConvertGAME());
|
||||
converters[ESM::REC_CELL] = std::shared_ptr<Converter>(new ConvertCell());
|
||||
converters[ESM::REC_ALCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
|
||||
converters[ESM::REC_CLAS] = std::shared_ptr<Converter>(new ConvertClass());
|
||||
converters[ESM::REC_SPEL] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Spell>());
|
||||
converters[ESM::REC_ARMO] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Armor>());
|
||||
converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||
converters[ESM::REC_CLOT] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Clothing>());
|
||||
converters[ESM::REC_ENCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Enchantment>());
|
||||
converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||
converters[ESM::REC_LEVC] = std::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());
|
||||
converters[ESM::REC_LEVI] = std::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());
|
||||
converters[ESM::REC_CNTC] = std::shared_ptr<Converter>(new ConvertCNTC());
|
||||
converters[ESM::REC_FACT] = std::shared_ptr<Converter>(new ConvertFACT());
|
||||
converters[ESM::REC_INFO] = std::shared_ptr<Converter>(new ConvertINFO());
|
||||
converters[ESM::REC_DIAL] = std::shared_ptr<Converter>(new ConvertDIAL());
|
||||
converters[ESM::REC_QUES] = std::shared_ptr<Converter>(new ConvertQUES());
|
||||
converters[recJOUR ] = std::shared_ptr<Converter>(new ConvertJOUR());
|
||||
converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT());
|
||||
converters[ESM::REC_PROJ] = std::shared_ptr<Converter>(new ConvertPROJ());
|
||||
converters[recSPLM] = std::shared_ptr<Converter>(new ConvertSPLM());
|
||||
|
||||
// TODO:
|
||||
// - REGN (weather in certain regions?)
|
||||
// - VFXM
|
||||
// - SPLM (active spell effects)
|
||||
|
||||
std::set<unsigned int> unknownRecords;
|
||||
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
{
|
||||
it->second->setContext(context);
|
||||
}
|
||||
|
||||
while (esm.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = esm.getRecName();
|
||||
esm.getRecHeader();
|
||||
|
||||
std::map<unsigned int, std::shared_ptr<Converter> >::iterator it = converters.find(n.intval);
|
||||
if (it != converters.end())
|
||||
{
|
||||
it->second->read(esm);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unknownRecords.insert(n.intval).second)
|
||||
{
|
||||
std::ios::fmtflags f(std::cerr.flags());
|
||||
std::cerr << "Error: unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
|
||||
std::cerr.flags(f);
|
||||
}
|
||||
|
||||
esm.skipRecord();
|
||||
}
|
||||
}
|
||||
|
||||
ESM::ESMWriter writer;
|
||||
|
||||
writer.setFormat (ESM::SavedGame::sCurrentFormat);
|
||||
|
||||
std::ofstream stream(mOutFile.c_str(), std::ios::binary);
|
||||
// all unused
|
||||
writer.setVersion(0);
|
||||
writer.setType(0);
|
||||
writer.setAuthor("");
|
||||
writer.setDescription("");
|
||||
writer.setRecordCount (0);
|
||||
|
||||
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
|
||||
it != header.mMaster.end(); ++it)
|
||||
writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0
|
||||
|
||||
writer.save (stream);
|
||||
|
||||
ESM::SavedGame profile;
|
||||
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
|
||||
it != header.mMaster.end(); ++it)
|
||||
{
|
||||
profile.mContentFiles.push_back(it->name);
|
||||
}
|
||||
profile.mDescription = esm.getDesc();
|
||||
profile.mInGameTime.mDay = context.mDay;
|
||||
profile.mInGameTime.mGameHour = context.mHour;
|
||||
profile.mInGameTime.mMonth = context.mMonth;
|
||||
profile.mInGameTime.mYear = context.mYear;
|
||||
profile.mPlayerCell = header.mGameData.mCurrentCell.toString();
|
||||
if (context.mPlayerBase.mClass == "NEWCLASSID_CHARGEN")
|
||||
profile.mPlayerClassName = context.mCustomPlayerClassName;
|
||||
else
|
||||
profile.mPlayerClassId = context.mPlayerBase.mClass;
|
||||
profile.mPlayerLevel = context.mPlayerBase.mNpdt.mLevel;
|
||||
profile.mPlayerName = header.mGameData.mPlayerName.toString();
|
||||
|
||||
writeScreenshot(header, profile);
|
||||
|
||||
writer.startRecord (ESM::REC_SAVE);
|
||||
profile.save (writer);
|
||||
writer.endRecord (ESM::REC_SAVE);
|
||||
|
||||
// Writing order should be Dynamic Store -> Cells -> Player,
|
||||
// so that references to dynamic records can be recognized when loading
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
{
|
||||
if (it->second->getStage() != 0)
|
||||
continue;
|
||||
it->second->write(writer);
|
||||
}
|
||||
|
||||
writer.startRecord(ESM::REC_NPC_);
|
||||
context.mPlayerBase.mId = "player";
|
||||
context.mPlayerBase.save(writer);
|
||||
writer.endRecord(ESM::REC_NPC_);
|
||||
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
{
|
||||
if (it->second->getStage() != 1)
|
||||
continue;
|
||||
it->second->write(writer);
|
||||
}
|
||||
|
||||
writer.startRecord(ESM::REC_PLAY);
|
||||
if (context.mPlayer.mCellId.mPaged)
|
||||
{
|
||||
// exterior cell -> determine cell coordinates based on position
|
||||
const int cellSize = 8192;
|
||||
int cellX = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize));
|
||||
int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize));
|
||||
context.mPlayer.mCellId.mIndex.mX = cellX;
|
||||
context.mPlayer.mCellId.mIndex.mY = cellY;
|
||||
}
|
||||
context.mPlayer.save(writer);
|
||||
writer.endRecord(ESM::REC_PLAY);
|
||||
|
||||
writer.startRecord(ESM::REC_ACTC);
|
||||
writer.writeHNT("COUN", context.mNextActorId);
|
||||
writer.endRecord(ESM::REC_ACTC);
|
||||
|
||||
// Stage 2 requires cell references to be written / actors IDs assigned
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
{
|
||||
if (it->second->getStage() != 2)
|
||||
continue;
|
||||
it->second->write(writer);
|
||||
}
|
||||
|
||||
writer.startRecord (ESM::REC_DIAS);
|
||||
context.mDialogueState.save(writer);
|
||||
writer.endRecord(ESM::REC_DIAS);
|
||||
|
||||
writer.startRecord(ESM::REC_INPU);
|
||||
context.mControlsState.save(writer);
|
||||
writer.endRecord(ESM::REC_INPU);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORTER_IMPORTER_H
|
||||
#define OPENMW_ESSIMPORTER_IMPORTER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
class Importer
|
||||
{
|
||||
public:
|
||||
Importer(const std::string& essfile, const std::string& outfile, const std::string& encoding);
|
||||
|
||||
void run();
|
||||
|
||||
void compare();
|
||||
|
||||
private:
|
||||
std::string mEssFile;
|
||||
std::string mOutFile;
|
||||
std::string mEncoding;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,97 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONTEXT_H
|
||||
#define OPENMW_ESSIMPORT_CONTEXT_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/player.hpp>
|
||||
#include <components/esm/dialoguestate.hpp>
|
||||
#include <components/esm/globalmap.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/controlsstate.hpp>
|
||||
|
||||
#include "importnpcc.hpp"
|
||||
#include "importcrec.hpp"
|
||||
#include "importcntc.hpp"
|
||||
#include "importplayer.hpp"
|
||||
#include "importsplm.h"
|
||||
|
||||
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct Context
|
||||
{
|
||||
// set from the TES3 header
|
||||
std::string mPlayerCellName;
|
||||
|
||||
ESM::Player mPlayer;
|
||||
ESM::NPC mPlayerBase;
|
||||
std::string mCustomPlayerClassName;
|
||||
|
||||
ESM::DialogueState mDialogueState;
|
||||
|
||||
ESM::ControlsState mControlsState;
|
||||
|
||||
// cells which should show an explored overlay on the global map
|
||||
std::set<std::pair<int, int> > mExploredCells;
|
||||
|
||||
ESM::GlobalMap mGlobalMapState;
|
||||
|
||||
int mDay, mMonth, mYear;
|
||||
float mHour;
|
||||
|
||||
// key <refIndex, refId>
|
||||
std::map<std::pair<int, std::string>, CREC> mCreatureChanges;
|
||||
std::map<std::pair<int, std::string>, NPCC> mNpcChanges;
|
||||
std::map<std::pair<int, std::string>, CNTC> mContainerChanges;
|
||||
|
||||
std::map<std::pair<int, std::string>, int> mActorIdMap;
|
||||
int mNextActorId;
|
||||
|
||||
std::map<std::string, ESM::Creature> mCreatures;
|
||||
std::map<std::string, ESM::NPC> mNpcs;
|
||||
|
||||
std::vector<SPLM::ActiveSpell> mActiveSpells;
|
||||
|
||||
Context()
|
||||
: mDay(0)
|
||||
, mMonth(0)
|
||||
, mYear(0)
|
||||
, mHour(0.f)
|
||||
, mNextActorId(0)
|
||||
{
|
||||
mPlayer.mAutoMove = 0;
|
||||
ESM::CellId playerCellId;
|
||||
playerCellId.mPaged = true;
|
||||
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
||||
mPlayer.mCellId = playerCellId;
|
||||
mPlayer.mLastKnownExteriorPosition[0]
|
||||
= mPlayer.mLastKnownExteriorPosition[1]
|
||||
= mPlayer.mLastKnownExteriorPosition[2]
|
||||
= 0.0f;
|
||||
mPlayer.mHasMark = 0;
|
||||
mPlayer.mCurrentCrimeId = -1; // TODO
|
||||
mPlayer.mPaidCrimeId = -1;
|
||||
mPlayer.mObject.blank();
|
||||
mPlayer.mObject.mEnabled = true;
|
||||
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
|
||||
mPlayer.mObject.mCreatureStats.mActorId = generateActorId();
|
||||
|
||||
mGlobalMapState.mBounds.mMinX = 0;
|
||||
mGlobalMapState.mBounds.mMaxX = 0;
|
||||
mGlobalMapState.mBounds.mMinY = 0;
|
||||
mGlobalMapState.mBounds.mMaxY = 0;
|
||||
}
|
||||
|
||||
int generateActorId()
|
||||
{
|
||||
return mNextActorId++;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,29 +0,0 @@
|
||||
#include "importgame.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void GAME::load(ESM::ESMReader &esm)
|
||||
{
|
||||
esm.getSubNameIs("GMDT");
|
||||
esm.getSubHeader();
|
||||
if (esm.getSubSize() == 92)
|
||||
{
|
||||
esm.getExact(&mGMDT, 92);
|
||||
mGMDT.mSecundaPhase = 0;
|
||||
}
|
||||
else if (esm.getSubSize() == 96)
|
||||
{
|
||||
esm.getT(mGMDT);
|
||||
}
|
||||
else
|
||||
esm.fail("unexpected subrecord size for GAME.GMDT");
|
||||
|
||||
mGMDT.mWeatherTransition &= (0x000000ff);
|
||||
mGMDT.mSecundaPhase &= (0x000000ff);
|
||||
mGMDT.mMasserPhase &= (0x000000ff);
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_GAME_H
|
||||
#define OPENMW_ESSIMPORT_GAME_H
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Weather data
|
||||
struct GAME
|
||||
{
|
||||
struct GMDT
|
||||
{
|
||||
char mCellName[64];
|
||||
int mFogColour;
|
||||
float mFogDensity;
|
||||
int mCurrentWeather, mNextWeather;
|
||||
int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage
|
||||
float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition
|
||||
int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage
|
||||
};
|
||||
|
||||
GMDT mGMDT;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,14 +0,0 @@
|
||||
#include "importinfo.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void INFO::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mInfo = esm.getHNString("INAM");
|
||||
mActorRefId = esm.getHNString("ACDT");
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTINFO_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTINFO_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct INFO
|
||||
{
|
||||
std::string mInfo;
|
||||
std::string mActorRefId;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,79 +0,0 @@
|
||||
#include "importinventory.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
#include <components/esm/loadcont.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void Inventory::load(ESM::ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("NPCO"))
|
||||
{
|
||||
ESM::ContItem contItem;
|
||||
esm.getHT(contItem);
|
||||
|
||||
InventoryItem item;
|
||||
item.mId = contItem.mItem.toString();
|
||||
item.mCount = contItem.mCount;
|
||||
item.mRelativeEquipmentSlot = -1;
|
||||
item.mLockLevel = 0;
|
||||
|
||||
unsigned int itemCount = std::abs(item.mCount);
|
||||
bool separateStacks = false;
|
||||
for (unsigned int i=0;i<itemCount;++i)
|
||||
{
|
||||
bool newStack = esm.isNextSub("XIDX");
|
||||
if (newStack)
|
||||
{
|
||||
unsigned int idx;
|
||||
esm.getHT(idx);
|
||||
separateStacks = true;
|
||||
item.mCount = 1;
|
||||
}
|
||||
|
||||
item.mSCRI.load(esm);
|
||||
|
||||
// for XSOL and XCHG seen so far, but probably others too
|
||||
bool isDeleted = false;
|
||||
item.ESM::CellRef::loadData(esm, isDeleted);
|
||||
|
||||
int charge=-1;
|
||||
esm.getHNOT(charge, "XHLT");
|
||||
item.mChargeInt = charge;
|
||||
|
||||
if (newStack)
|
||||
mItems.push_back(item);
|
||||
}
|
||||
|
||||
if (!separateStacks)
|
||||
mItems.push_back(item);
|
||||
}
|
||||
|
||||
// equipped items
|
||||
while (esm.isNextSub("WIDX"))
|
||||
{
|
||||
// note: same item can be equipped 2 items (e.g. 2 rings)
|
||||
// and will be *stacked* in the NPCO list, unlike openmw!
|
||||
// this is currently not handled properly.
|
||||
|
||||
esm.getSubHeader();
|
||||
int itemIndex; // index of the item in the NPCO list
|
||||
esm.getT(itemIndex);
|
||||
|
||||
if (itemIndex < 0 || itemIndex >= int(mItems.size()))
|
||||
esm.fail("equipment item index out of range");
|
||||
|
||||
// appears to be a relative index for only the *possible* slots this item can be equipped in,
|
||||
// i.e. 0 most of the time
|
||||
int slotIndex;
|
||||
esm.getT(slotIndex);
|
||||
|
||||
mItems[itemIndex].mRelativeEquipmentSlot = slotIndex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTINVENTORY_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTINVENTORY_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include "importscri.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct Inventory
|
||||
{
|
||||
struct InventoryItem : public ESM::CellRef
|
||||
{
|
||||
std::string mId;
|
||||
int mCount;
|
||||
int mRelativeEquipmentSlot;
|
||||
SCRI mSCRI;
|
||||
};
|
||||
std::vector<InventoryItem> mItems;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,13 +0,0 @@
|
||||
#include "importjour.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void JOUR::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mText = esm.getHNString("NAME");
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTJOUR_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTJOUR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Journal
|
||||
struct JOUR
|
||||
{
|
||||
// The entire journal, in HTML
|
||||
std::string mText;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,22 +0,0 @@
|
||||
#include "importklst.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void KLST::load(ESM::ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("KNAM"))
|
||||
{
|
||||
std::string refId = esm.getHString();
|
||||
int count;
|
||||
esm.getHNT(count, "CNAM");
|
||||
mKillCounter[refId] = count;
|
||||
}
|
||||
|
||||
mWerewolfKills = 0;
|
||||
esm.getHNOT(mWerewolfKills, "INTV");
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_KLST_H
|
||||
#define OPENMW_ESSIMPORT_KLST_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Kill Stats
|
||||
struct KLST
|
||||
{
|
||||
void load(ESM::ESMReader& esm);
|
||||
|
||||
/// RefId, kill count
|
||||
std::map<std::string, int> mKillCounter;
|
||||
|
||||
int mWerewolfKills;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,19 +0,0 @@
|
||||
#include "importnpcc.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void NPCC::load(ESM::ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mNPDT, "NPDT");
|
||||
|
||||
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|
||||
|| esm.isNextSub("AI_A"))
|
||||
mAiPackages.add(esm);
|
||||
|
||||
mInventory.load(esm);
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_NPCC_H
|
||||
#define OPENMW_ESSIMPORT_NPCC_H
|
||||
|
||||
#include <components/esm/loadcont.hpp>
|
||||
|
||||
#include <components/esm/aipackage.hpp>
|
||||
|
||||
#include "importinventory.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct NPCC
|
||||
{
|
||||
struct NPDT
|
||||
{
|
||||
unsigned char mDisposition;
|
||||
unsigned char unknown;
|
||||
unsigned char mReputation;
|
||||
unsigned char unknown2;
|
||||
int mIndex;
|
||||
} mNPDT;
|
||||
|
||||
Inventory mInventory;
|
||||
ESM::AIPackageList mAiPackages;
|
||||
|
||||
void load(ESM::ESMReader &esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,103 +0,0 @@
|
||||
#include "importplayer.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void REFR::load(ESM::ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mRefNum.mIndex, "FRMR");
|
||||
|
||||
mRefID = esm.getHNString("NAME");
|
||||
|
||||
mActorData.load(esm);
|
||||
|
||||
esm.getHNOT(mPos, "DATA", 24);
|
||||
}
|
||||
|
||||
void PCDT::load(ESM::ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("DNAM"))
|
||||
{
|
||||
mKnownDialogueTopics.push_back(esm.getHString());
|
||||
}
|
||||
|
||||
mHasMark = false;
|
||||
if (esm.isNextSub("MNAM"))
|
||||
{
|
||||
mHasMark = true;
|
||||
mMNAM = esm.getHString();
|
||||
}
|
||||
|
||||
esm.getHNT(mPNAM, "PNAM");
|
||||
|
||||
if (esm.isNextSub("SNAM"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("NAM9"))
|
||||
esm.skipHSub();
|
||||
|
||||
// Rest state. You shouldn't even be able to save during rest, but skip just in case.
|
||||
if (esm.isNextSub("RNAM"))
|
||||
/*
|
||||
int hoursLeft;
|
||||
float x, y, z; // resting position
|
||||
*/
|
||||
esm.skipHSub(); // 16 bytes
|
||||
|
||||
mBounty = 0;
|
||||
esm.getHNOT(mBounty, "CNAM");
|
||||
|
||||
mBirthsign = esm.getHNOString("BNAM");
|
||||
|
||||
// Holds the names of the last used Alchemy apparatus. Don't need to import this ATM,
|
||||
// because our GUI auto-selects the best apparatus.
|
||||
if (esm.isNextSub("NAM0"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("NAM1"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("NAM2"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("NAM3"))
|
||||
esm.skipHSub();
|
||||
|
||||
mHasENAM = false;
|
||||
if (esm.isNextSub("ENAM"))
|
||||
{
|
||||
mHasENAM = true;
|
||||
esm.getHT(mENAM);
|
||||
}
|
||||
|
||||
if (esm.isNextSub("LNAM"))
|
||||
esm.skipHSub();
|
||||
|
||||
while (esm.isNextSub("FNAM"))
|
||||
{
|
||||
FNAM fnam;
|
||||
esm.getHT(fnam);
|
||||
mFactions.push_back(fnam);
|
||||
}
|
||||
|
||||
mHasAADT = false;
|
||||
if (esm.isNextSub("AADT")) // Attack animation data?
|
||||
{
|
||||
mHasAADT = true;
|
||||
esm.getHT(mAADT);
|
||||
}
|
||||
|
||||
if (esm.isNextSub("KNAM"))
|
||||
esm.skipHSub(); // assigned Quick Keys, I think
|
||||
|
||||
if (esm.isNextSub("ANIS"))
|
||||
esm.skipHSub(); // 16 bytes
|
||||
|
||||
if (esm.isNextSub("WERE"))
|
||||
{
|
||||
// some werewolf data, 152 bytes
|
||||
// maybe current skills and attributes for werewolf form
|
||||
esm.getSubHeader();
|
||||
esm.skip(152);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_PLAYER_H
|
||||
#define OPENMW_ESSIMPORT_PLAYER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm/esmcommon.hpp>
|
||||
|
||||
#include "importacdt.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Player-agnostic player data
|
||||
struct REFR
|
||||
{
|
||||
ActorData mActorData;
|
||||
|
||||
std::string mRefID;
|
||||
ESM::Position mPos;
|
||||
ESM::RefNum mRefNum;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
/// Other player data
|
||||
struct PCDT
|
||||
{
|
||||
int mBounty;
|
||||
std::string mBirthsign;
|
||||
|
||||
std::vector<std::string> mKnownDialogueTopics;
|
||||
|
||||
enum PlayerFlags
|
||||
{
|
||||
PlayerFlags_ViewSwitchDisabled = 0x1,
|
||||
PlayerFlags_ControlsDisabled = 0x4,
|
||||
PlayerFlags_Sleeping = 0x10,
|
||||
PlayerFlags_Waiting = 0x40,
|
||||
PlayerFlags_WeaponDrawn = 0x80,
|
||||
PlayerFlags_SpellDrawn = 0x100,
|
||||
PlayerFlags_InJail = 0x200,
|
||||
PlayerFlags_JumpingDisabled = 0x1000,
|
||||
PlayerFlags_LookingDisabled = 0x2000,
|
||||
PlayerFlags_VanityModeDisabled = 0x4000,
|
||||
PlayerFlags_WeaponDrawingDisabled = 0x8000,
|
||||
PlayerFlags_SpellDrawingDisabled = 0x10000,
|
||||
PlayerFlags_ThirdPerson = 0x20000,
|
||||
PlayerFlags_TeleportingDisabled = 0x40000,
|
||||
PlayerFlags_LevitationDisabled = 0x80000
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
struct FNAM
|
||||
{
|
||||
unsigned char mRank;
|
||||
unsigned char mUnknown1[3];
|
||||
int mReputation;
|
||||
unsigned char mFlags; // 0x1: unknown, 0x2: expelled
|
||||
unsigned char mUnknown2[3];
|
||||
ESM::NAME32 mFactionName;
|
||||
};
|
||||
|
||||
struct PNAM
|
||||
{
|
||||
struct MarkLocation
|
||||
{
|
||||
float mX, mY, mZ; // worldspace position
|
||||
float mRotZ; // Z angle in radians
|
||||
int mCellX, mCellY; // grid coordinates; for interior cells this is always (0, 0)
|
||||
};
|
||||
|
||||
int mPlayerFlags; // controls, camera and draw state
|
||||
unsigned int mLevelProgress;
|
||||
float mSkillProgress[27]; // skill progress, non-uniform scaled
|
||||
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
|
||||
int mTelekinesisRangeBonus; // in units; seems redundant
|
||||
float mVisionBonus; // range: <0.0, 1.0>; affected by light spells and Get/Mod/SetPCVisionBonus
|
||||
int mDetectKeyMagnitude; // seems redundant
|
||||
int mDetectEnchantmentMagnitude; // seems redundant
|
||||
int mDetectAnimalMagnitude; // seems redundant
|
||||
MarkLocation mMarkLocation;
|
||||
unsigned char mUnknown3[40];
|
||||
unsigned char mSpecIncreases[3]; // number of skill increases for each specialization
|
||||
unsigned char mUnknown4;
|
||||
};
|
||||
|
||||
struct ENAM
|
||||
{
|
||||
int mCellX;
|
||||
int mCellY;
|
||||
};
|
||||
|
||||
struct AADT // 44 bytes
|
||||
{
|
||||
int animGroupIndex; // See convertANIS() for the mapping.
|
||||
unsigned char mUnknown5[40];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
std::vector<FNAM> mFactions;
|
||||
PNAM mPNAM;
|
||||
|
||||
bool mHasMark;
|
||||
std::string mMNAM; // mark cell name; can also be sDefaultCellname or region name
|
||||
|
||||
bool mHasENAM;
|
||||
ENAM mENAM; // last exterior cell
|
||||
|
||||
bool mHasAADT;
|
||||
AADT mAADT;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,18 +0,0 @@
|
||||
#include "importproj.h"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void ESSImport::PROJ::load(ESM::ESMReader& esm)
|
||||
{
|
||||
while (esm.isNextSub("PNAM"))
|
||||
{
|
||||
PNAM pnam;
|
||||
esm.getHT(pnam);
|
||||
mProjectiles.push_back(pnam);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTPROJ_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTPROJ_H
|
||||
|
||||
#include <vector>
|
||||
#include <components/esm/esmcommon.hpp>
|
||||
#include <components/esm/util.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct PROJ
|
||||
{
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
struct PNAM // 184 bytes
|
||||
{
|
||||
float mAttackStrength;
|
||||
float mSpeed;
|
||||
unsigned char mUnknown[4*2];
|
||||
float mFlightTime;
|
||||
int mSplmIndex; // reference to a SPLM record (0 for ballistic projectiles)
|
||||
unsigned char mUnknown2[4];
|
||||
ESM::Vector3 mVelocity;
|
||||
ESM::Vector3 mPosition;
|
||||
unsigned char mUnknown3[4*9];
|
||||
ESM::NAME32 mActorId; // indexed refID (with the exception of "PlayerSaveGame")
|
||||
ESM::NAME32 mArrowId;
|
||||
ESM::NAME32 mBowId;
|
||||
|
||||
bool isMagic() const { return mSplmIndex != 0; }
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
std::vector<PNAM> mProjectiles;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,14 +0,0 @@
|
||||
#include "importques.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void QUES::load(ESM::ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("DATA"))
|
||||
mInfo.push_back(esm.getHString());
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTQUES_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTQUES_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// State for a quest
|
||||
/// Presumably this record only exists when Tribunal is installed,
|
||||
/// since pre-Tribunal there weren't any quest names in the data files.
|
||||
struct QUES
|
||||
{
|
||||
std::string mName; // NAME, should be assigned from outside as usual
|
||||
std::vector<std::string> mInfo; // list of journal entries for the quest
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,26 +0,0 @@
|
||||
#include "importscpt.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void SCPT::load(ESM::ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mSCHD, "SCHD");
|
||||
|
||||
mSCRI.load(esm);
|
||||
|
||||
mRefNum = -1;
|
||||
if (esm.isNextSub("RNAM"))
|
||||
{
|
||||
mRunning = true;
|
||||
esm.getHT(mRefNum);
|
||||
}
|
||||
else
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTSCPT_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTSCPT_H
|
||||
|
||||
#include "importscri.hpp"
|
||||
|
||||
#include <components/esm/loadscpt.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
// A running global script
|
||||
struct SCPT
|
||||
{
|
||||
ESM::Script::SCHD mSCHD;
|
||||
|
||||
// values of local variables
|
||||
SCRI mSCRI;
|
||||
|
||||
bool mRunning;
|
||||
int mRefNum; // Targeted reference, -1: no reference
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue