forked from mirror/openmw-tes3mp
Merge remote-tracking branch 'upstream/master'
commit
17ede44628
@ -0,0 +1,239 @@
|
||||
Contributors
|
||||
============
|
||||
|
||||
The OpenMW project was started in 2008 by Nicolay Korslund.
|
||||
In the course of years many people have contributed to the project.
|
||||
|
||||
If you feel your name is missing from this list, please notify a developer.
|
||||
|
||||
|
||||
Programmers
|
||||
-----------
|
||||
|
||||
Marc Zinnschlag (Zini) - Lead Programmer/Project Manager
|
||||
|
||||
Adam Hogan (aurix)
|
||||
Aesylwinn
|
||||
aegis
|
||||
Aleksandar Jovanov
|
||||
Alex Haddad (rainChu)
|
||||
Alex McKibben (WeirdSexy)
|
||||
alexanderkjall
|
||||
Alexander Nadeau (wareya)
|
||||
Alexander Olofsson (Ace)
|
||||
Allofich
|
||||
AnyOldName3
|
||||
Austin Salgat (Salgat)
|
||||
Artem Kotsynyak (greye)
|
||||
artemutin
|
||||
Arthur Moore (EmperorArthur)
|
||||
athile
|
||||
Ben Shealy (bentsherman)
|
||||
Bret Curtis (psi29a)
|
||||
Britt Mathis (galdor557)
|
||||
cc9cii
|
||||
Chris Boyce (slothlife)
|
||||
Chris Robinson (KittyCat)
|
||||
Cory F. Cohen (cfcohen)
|
||||
Cris Mihalache (Mirceam)
|
||||
darkf
|
||||
Dieho
|
||||
Dmitry Shkurskiy (endorph)
|
||||
Douglas Diniz (Dgdiniz)
|
||||
Douglas Mencken (dougmencken)
|
||||
dreamer-dead
|
||||
David Teviotdale (dteviot)
|
||||
Edmondo Tommasina (edmondo)
|
||||
Eduard Cot (trombonecot)
|
||||
Eli2
|
||||
Emanuel Guével (potatoesmaster)
|
||||
eroen
|
||||
escondida
|
||||
Evgeniy Mineev (sandstranger)
|
||||
Fil Krynicki (filkry)
|
||||
Gašper Sedej
|
||||
gugus/gus
|
||||
Hallfaer Tuilinn
|
||||
hristoast
|
||||
Internecine
|
||||
Jacob Essex (Yacoby)
|
||||
Jannik Heller (scrawl)
|
||||
Jason Hooks (jhooks)
|
||||
jeaye
|
||||
Jeffrey Haines (Jyby)
|
||||
Jengerer
|
||||
Jiří Kuneš (kunesj)
|
||||
Joe Wilkerson (neuralroberts)
|
||||
Joel Graff (graffy)
|
||||
John Blomberg (fstp)
|
||||
Jordan Ayers
|
||||
Jordan Milne
|
||||
Julien Voisin (jvoisin/ap0)
|
||||
Karl-Felix Glatzer (k1ll)
|
||||
Kevin Poitra (PuppyKevin)
|
||||
Koncord
|
||||
Lars Söderberg (Lazaroth)
|
||||
lazydev
|
||||
Leon Saunders (emoose)
|
||||
Lukasz Gromanowski (lgro)
|
||||
Manuel Edelmann (vorenon)
|
||||
Marc Bouvier (CramitDeFrog)
|
||||
Marcin Hulist (Gohan)
|
||||
Mark Siewert (mark76)
|
||||
Marco Melletti (mellotanica)
|
||||
Marco Schulze
|
||||
Mateusz Kołaczek (PL_kolek)
|
||||
Mateusz Malisz (malice)
|
||||
megaton
|
||||
Michael Hogan (Xethik)
|
||||
Michael Mc Donnell
|
||||
Michael Papageorgiou (werdanith)
|
||||
Michał Bień (Glorf)
|
||||
Michał Moroz (dragonee)
|
||||
Miroslav Puda (pakanek)
|
||||
MiroslavR
|
||||
naclander
|
||||
Narmo
|
||||
Nathan Jeffords (blunted2night)
|
||||
NeveHanter
|
||||
Nikolay Kasyanov (corristo)
|
||||
nobrakal
|
||||
Nolan Poe (nopoe)
|
||||
Paul Cercueil (pcercuei)
|
||||
Paul McElroy (Greendogo)
|
||||
Pi03k
|
||||
Pieter van der Kloet (pvdk)
|
||||
pkubik
|
||||
Radu-Marius Popovici (rpopovici)
|
||||
rdimesio
|
||||
riothamus
|
||||
Rob Cutmore (rcutmore)
|
||||
Robert MacGregor (Ragora)
|
||||
Rohit Nirmal
|
||||
Roman Melnik (Kromgart)
|
||||
Roman Proskuryakov (kpp)
|
||||
Sandy Carter (bwrsandman)
|
||||
Scott Howard
|
||||
Sebastian Wick (swick)
|
||||
Sergey Shambir
|
||||
sir_herrbatka
|
||||
smbas
|
||||
Stefan Galowicz (bogglez)
|
||||
Stanislav Bobrov (Jiub)
|
||||
svaante
|
||||
Sylvain Thesnieres (Garvek)
|
||||
t6
|
||||
terrorfisch
|
||||
Thomas Luppi (Digmaster)
|
||||
Tom Mason (wheybags)
|
||||
Torben Leif Carrington (TorbenC)
|
||||
viadanna
|
||||
Vincent Heuken
|
||||
vocollapse
|
||||
zelurker
|
||||
|
||||
Manual
|
||||
------
|
||||
|
||||
Bodillium
|
||||
Cramal
|
||||
Alejandro Sanchez (HiPhish)
|
||||
sir_herrbatka
|
||||
|
||||
Packagers
|
||||
---------
|
||||
|
||||
Alexander Olofsson (Ace) - Windows
|
||||
Bret Curtis (psi29a) - Ubuntu Linux
|
||||
Edmondo Tommasina (edmondo) - Gentoo Linux
|
||||
Julian Ospald (hasufell) - Gentoo Linux
|
||||
Karl-Felix Glatzer (k1ll) - Linux Binaries
|
||||
Kenny Armstrong (artorius) - Fedora Linux
|
||||
Nikolay Kasyanov (corristo) - Mac OS X
|
||||
Sandy Carter (bwrsandman) - Arch Linux
|
||||
|
||||
Public Relations and Translations
|
||||
---------------------------------
|
||||
|
||||
Alex McKibben (WeirdSexy) - Podcaster
|
||||
Artem Kotsynyak (greye) - Russian News Writer
|
||||
Jim Clauwaert (Zedd) - Public Outreach
|
||||
Julien Voisin (jvoisin/ap0) - French News Writer
|
||||
Tom Koenderink (Okulo) - English News Writer
|
||||
Lukasz Gromanowski (lgro) - English News Writer
|
||||
Mickey Lyle (raevol) - Release Manager
|
||||
Pithorn - Chinese News Writer
|
||||
sir_herrbatka - Polish News Writer
|
||||
Dawid Lakomy (Vedyimyn) - Polish News Writer
|
||||
|
||||
Website
|
||||
-------
|
||||
|
||||
Lukasz Gromanowski (Lgro) - Website Administrator
|
||||
Ryan Sardonic (Wry) - Wiki Editor
|
||||
sir_herrbatka - Forum Administrator
|
||||
|
||||
Formula Research
|
||||
----------------
|
||||
|
||||
Hrnchamd
|
||||
Epsilon
|
||||
fragonard
|
||||
Greendogo
|
||||
HiPhish
|
||||
modred11
|
||||
Myckel
|
||||
natirips
|
||||
Sadler
|
||||
|
||||
Artwork
|
||||
-------
|
||||
|
||||
Necrod - OpenMW Logo
|
||||
Mickey Lyle (raevol) - Wordpress Theme
|
||||
Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons
|
||||
|
||||
Inactive Contributors
|
||||
---------------------
|
||||
|
||||
Ardekantur
|
||||
Armin Preiml
|
||||
Berulacks
|
||||
Carl Maxwell
|
||||
Diggory Hardy
|
||||
Dmitry Marakasov (AMDmi3)
|
||||
ElderTroll
|
||||
guidoj
|
||||
Jan-Peter Nilsson (peppe)
|
||||
Jan Borsodi
|
||||
Josua Grawitter
|
||||
juanmnzsk8
|
||||
Kingpix
|
||||
Lordrea
|
||||
Michal Sciubidlo
|
||||
Nicolay Korslund
|
||||
Nekochan
|
||||
pchan3
|
||||
penguinroad
|
||||
psi29a
|
||||
sergoz
|
||||
spyboot
|
||||
Star-Demon
|
||||
Thoronador
|
||||
Yuri Krupenin
|
||||
|
||||
Additional Credits
|
||||
------------------
|
||||
In this section we would like to thank people not part of OpenMW for their work.
|
||||
|
||||
Thanks to Maxim Nikolaev,
|
||||
for allowing us to use his excellent Morrowind fan-art on our website and in other places.
|
||||
|
||||
Thanks to DokterDume,
|
||||
for kindly providing us with the Moon and Star logo, used as the application icon and project logo.
|
||||
|
||||
Thanks to Kevin Ryan,
|
||||
for creating the icon used for the Data Files tab of the OpenMW Launcher.
|
||||
|
||||
Thanks to DejaVu team,
|
||||
for their DejaVuLGCSansMono fontface, see DejaVu Font License.txt for their license terms.
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
brew tap openmw/openmw
|
||||
brew update
|
||||
brew unlink boost
|
||||
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg qt unshield
|
||||
brew rm cmake || true
|
||||
brew rm pkgconfig || true
|
||||
brew rm qt5 || true
|
||||
brew install cmake pkgconfig qt5
|
||||
|
||||
curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip
|
||||
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
|
@ -1,5 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
free -m
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
|
||||
export CODE_COVERAGE=1
|
||||
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
|
||||
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE
|
||||
|
@ -0,0 +1,671 @@
|
||||
#!/bin/bash
|
||||
|
||||
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 ;;
|
||||
|
||||
v )
|
||||
VS_VERSION=$1
|
||||
shift ;;
|
||||
|
||||
d )
|
||||
SKIP_DOWNLOAD=true ;;
|
||||
|
||||
e )
|
||||
SKIP_EXTRACT=true ;;
|
||||
|
||||
k )
|
||||
KEEP=true ;;
|
||||
|
||||
u )
|
||||
UNITY_BUILD=true ;;
|
||||
|
||||
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>
|
||||
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 $VS_VERSION ]; then
|
||||
VS_VERSION="2013"
|
||||
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
|
||||
VERSION="$(cat README.md | grep Version: | awk '{ print $3; }')-$(git rev-parse --short HEAD)"
|
||||
appveyor UpdateBuild -Version "$VERSION" > /dev/null &
|
||||
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 $@"
|
||||
}
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM=`uname -m`
|
||||
fi
|
||||
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
CONFIGURATION="Debug"
|
||||
fi
|
||||
|
||||
case $VS_VERSION in
|
||||
14|2015 )
|
||||
GENERATOR="Visual Studio 14 2015"
|
||||
XP_TOOLSET="v140_xp"
|
||||
;;
|
||||
|
||||
# 12|2013|
|
||||
* )
|
||||
GENERATOR="Visual Studio 12 2013"
|
||||
XP_TOOLSET="v120_xp"
|
||||
;;
|
||||
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\" -T$XP_TOOLSET"
|
||||
add_cmake_opts "-G\"$GENERATOR\"" -T$XP_TOOLSET
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown platform $PLATFORM."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! [ -z $UNITY_BUILD ]; then
|
||||
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
|
||||
fi
|
||||
|
||||
case $CONFIGURATION in
|
||||
debug|Debug|DEBUG )
|
||||
CONFIGURATION=Debug
|
||||
;;
|
||||
|
||||
release|Release|RELEASE )
|
||||
CONFIGURATION=Release
|
||||
;;
|
||||
|
||||
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
|
||||
CONFIGURATION=RelWithDebInfo
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo "=========================="
|
||||
echo "Starting prebuild on 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.58.0" \
|
||||
http://sourceforge.net/projects/boost/files/boost-binaries/1.58.0/boost_1_58_0-msvc-12.0-$BITS.exe \
|
||||
boost-1.58.0-win$BITS.exe
|
||||
fi
|
||||
|
||||
# Bullet
|
||||
download "Bullet 2.83.5" \
|
||||
http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.83.5-win$BITS.7z \
|
||||
Bullet-2.83.5-win$BITS.7z
|
||||
|
||||
# FFmpeg
|
||||
download "FFmpeg 2.5.2" \
|
||||
http://ffmpeg.zeranoe.com/builds/win$BITS/shared/ffmpeg-2.5.2-win$BITS-shared.7z \
|
||||
ffmpeg$BITS-2.5.2.7z \
|
||||
http://ffmpeg.zeranoe.com/builds/win$BITS/dev/ffmpeg-2.5.2-win$BITS-dev.7z \
|
||||
ffmpeg$BITS-2.5.2-dev.7z
|
||||
|
||||
# MyGUI
|
||||
download "MyGUI 3.2.2" \
|
||||
http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-win$BITS.7z \
|
||||
MyGUI-3.2.2-win$BITS.7z
|
||||
|
||||
# OpenAL
|
||||
download "OpenAL-Soft 1.16.0" \
|
||||
http://kcat.strangesoft.net/openal-binaries/openal-soft-1.16.0-bin.zip \
|
||||
OpenAL-Soft-1.16.0.zip
|
||||
|
||||
# OSG
|
||||
download "OpenSceneGraph 3.3.8" \
|
||||
http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.3.8-win$BITS.7z \
|
||||
OSG-3.3.8-win$BITS.7z
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "Qt 4.8.6" \
|
||||
http://sourceforge.net/projects/qt64ng/files/qt/$ARCHNAME/4.8.6/msvc2013/qt-4.8.6-x$ARCHSUFFIX-msvc2013.7z \
|
||||
qt$BITS-4.8.6.7z
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
download "SDL 2.0.3" \
|
||||
https://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip \
|
||||
SDL2-2.0.3.zip
|
||||
fi
|
||||
|
||||
cd .. #/..
|
||||
|
||||
# Set up dependencies
|
||||
if [ -z $KEEP ]; then
|
||||
echo
|
||||
printf "Preparing build directory... "
|
||||
|
||||
rm -rf Build_$BITS
|
||||
mkdir -p Build_$BITS/deps
|
||||
|
||||
echo Done.
|
||||
fi
|
||||
mkdir -p Build_$BITS/deps
|
||||
cd Build_$BITS/deps
|
||||
|
||||
DEPS_INSTALL=`pwd`
|
||||
cd $DEPS
|
||||
|
||||
echo
|
||||
echo "Extracting dependencies..."
|
||||
|
||||
|
||||
# Boost
|
||||
printf "Boost 1.58.0... "
|
||||
{
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
BOOST_SDK="`real_pwd`/Boost"
|
||||
|
||||
if [ -d Boost ] && grep "BOOST_VERSION 105800" Boost/boost/version.hpp > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Boost
|
||||
$DEPS/boost-1.58.0-win$BITS.exe //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent
|
||||
fi
|
||||
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
|
||||
|
||||
echo Done.
|
||||
else
|
||||
# Appveyor unstable has all the boost we need already
|
||||
BOOST_SDK="c:/Libraries/boost"
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
|
||||
|
||||
echo AppVeyor.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# Bullet
|
||||
printf "Bullet 2.83.5... "
|
||||
{
|
||||
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.83.5-win$BITS.7z $STRIP
|
||||
mv Bullet-2.83.5-win$BITS Bullet
|
||||
fi
|
||||
|
||||
export BULLET_ROOT="`real_pwd`/Bullet"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# FFmpeg
|
||||
printf "FFmpeg 2.5.2... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d FFmpeg ] && grep "FFmpeg version: 2.5.2" FFmpeg/README.txt > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf FFmpeg
|
||||
|
||||
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2.7z $STRIP
|
||||
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2-dev.7z $STRIP
|
||||
|
||||
mv ffmpeg-2.5.2-win$BITS-shared FFmpeg
|
||||
cp -r ffmpeg-2.5.2-win$BITS-dev/* FFmpeg/
|
||||
rm -rf ffmpeg-2.5.2-win$BITS-dev
|
||||
fi
|
||||
|
||||
export FFMPEG_HOME="`real_pwd`/FFmpeg"
|
||||
add_runtime_dlls `pwd`/FFmpeg/bin/{avcodec-56,avformat-56,avutil-54,swresample-1,swscale-3}.dll
|
||||
|
||||
if [ $BITS -eq 32 ]; then
|
||||
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
||||
fi
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# 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-win$BITS.7z $STRIP
|
||||
mv MyGUI-3.2.2-win$BITS MyGUI
|
||||
fi
|
||||
|
||||
MYGUI_SDK="`real_pwd`/MyGUI"
|
||||
|
||||
add_cmake_opts -DMYGUISDK="$MYGUI_SDK" \
|
||||
-DMYGUI_INCLUDE_DIRS="$MYGUI_SDK/include/MYGUI" \
|
||||
-DMYGUI_PREQUEST_FILE="$MYGUI_SDK/include/MYGUI/MyGUI_Prerequest.h"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="_d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls `pwd`/MyGUI/bin/$CONFIGURATION/MyGUIEngine$SUFFIX.dll
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# OpenAL
|
||||
printf "OpenAL-Soft 1.16.0... "
|
||||
{
|
||||
if [ -d openal-soft-1.16.0-bin ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf openal-soft-1.16.0-bin
|
||||
eval 7z x -y OpenAL-Soft-1.16.0.zip $STRIP
|
||||
fi
|
||||
|
||||
OPENAL_SDK="`real_pwd`/openal-soft-1.16.0-bin"
|
||||
|
||||
add_cmake_opts -DOPENAL_INCLUDE_DIR="$OPENAL_SDK/include/AL" \
|
||||
-DOPENAL_LIBRARY="$OPENAL_SDK/libs/Win$BITS/OpenAL32.lib"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# OSG
|
||||
printf "OSG 3.3.8... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d OSG ] && \
|
||||
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||
grep "OPENSCENEGRAPH_MINOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||
grep "OPENSCENEGRAPH_PATCH_VERSION 8" OSG/include/osg/Version > /dev/null
|
||||
then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf OSG
|
||||
eval 7z x -y $DEPS/OSG-3.3.8-win$BITS.7z $STRIP
|
||||
mv OSG-3.3.8-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}$SUFFIX.dll \
|
||||
`pwd`/OSG/bin/osg{,Animation,DB,FX,GA,Particle,Qt,Text,Util,Viewer}$SUFFIX.dll
|
||||
|
||||
add_osg_dlls `pwd`/OSG/bin/osgPlugins-3.3.8/osgdb_{bmp,dds,gif,jpeg,png,tga}$SUFFIX.dll
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Qt 4.8.6... "
|
||||
else
|
||||
printf "Qt 5.4... "
|
||||
fi
|
||||
{
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
QT_SDK="`real_pwd`/Qt"
|
||||
|
||||
if [ -d Qt ] && head -n2 Qt/BUILDINFO.txt | grep "4.8.6" > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Qt
|
||||
eval 7z x -y $DEPS/qt$BITS-4.8.6.7z $STRIP
|
||||
mv qt-4.8.6-* Qt
|
||||
cd Qt
|
||||
eval ./qtbinpatcher.exe $STRIP
|
||||
fi
|
||||
|
||||
cd $QT_SDK
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=4 \
|
||||
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d4"
|
||||
else
|
||||
SUFFIX="4"
|
||||
fi
|
||||
|
||||
add_runtime_dlls `pwd`/bin/Qt{Core,Gui,Network,OpenGL}$SUFFIX.dll
|
||||
|
||||
echo Done.
|
||||
else
|
||||
if [ $BITS -eq 32 ]; then
|
||||
QT_SDK="C:/Qt/5.4/msvc2013_opengl"
|
||||
else
|
||||
QT_SDK="C:/Qt/5.4/msvc2013_64_opengl"
|
||||
fi
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
|
||||
echo AppVeyor.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# SDL2
|
||||
printf "SDL 2.0.3... "
|
||||
{
|
||||
if [ -d SDL2-2.0.3 ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf SDL2-2.0.3
|
||||
eval 7z x -y SDL2-2.0.3.zip $STRIP
|
||||
fi
|
||||
|
||||
SDL_SDK="`real_pwd`/SDL2-2.0.3"
|
||||
add_cmake_opts -DSDL2_INCLUDE_DIR="$SDL_SDK/include" \
|
||||
-DSDL2MAIN_LIBRARY="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2main.lib" \
|
||||
-DSDL2_LIBRARY_PATH="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2.lib"
|
||||
|
||||
add_runtime_dlls `pwd`/SDL2-2.0.3/lib/x$ARCHSUFFIX/SDL2.dll
|
||||
|
||||
echo Done.
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
echo " (Outside of CI, doing full build.)"
|
||||
else
|
||||
case $STEP in
|
||||
components )
|
||||
echo " 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 " Subproject: OpenMW."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENCS=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
opencs )
|
||||
echo " Subproject: OpenCS."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
misc )
|
||||
echo " Subproject: Misc."
|
||||
add_cmake_opts -DBUILD_OPENCS=no \
|
||||
-DBUILD_OPENMW=no
|
||||
;;
|
||||
* )
|
||||
echo " Building everything."
|
||||
;;
|
||||
esac
|
||||
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
|
||||
|
||||
echo
|
||||
|
||||
# NOTE: Disable this when/if we want to run test cases
|
||||
if [ -z $CI ]; then
|
||||
echo "Copying Runtime DLLs..."
|
||||
mkdir -p $CONFIGURATION
|
||||
for DLL in $RUNTIME_DLLS; do
|
||||
echo " `basename $DLL`."
|
||||
cp "$DLL" $CONFIGURATION/
|
||||
done
|
||||
echo "OSG Plugin DLLs..."
|
||||
mkdir -p $CONFIGURATION/osgPlugins-3.3.8
|
||||
for DLL in $OSG_PLUGINS; do
|
||||
echo " `basename $DLL`."
|
||||
cp "$DLL" $CONFIGURATION/osgPlugins-3.3.8
|
||||
done
|
||||
echo
|
||||
|
||||
echo "Copying Runtime Resources/Config Files"
|
||||
|
||||
echo " gamecontrollerdb.txt"
|
||||
cp $CONFIGURATION/../gamecontrollerdb.txt $CONFIGURATION/gamecontrollerdb.txt
|
||||
echo " openmw.cfg"
|
||||
cp $CONFIGURATION/../openmw.cfg.install $CONFIGURATION/openmw.cfg
|
||||
echo " openmw-cs.cfg"
|
||||
cp $CONFIGURATION/../openmw-cs.cfg $CONFIGURATION/openmw-cs.cfg
|
||||
echo " settings-default.cfg"
|
||||
cp $CONFIGURATION/../settings-default.cfg $CONFIGURATION/settings-default.cfg
|
||||
echo " resources/"
|
||||
cp -r $CONFIGURATION/../resources $CONFIGURATION/resources
|
||||
fi
|
||||
|
||||
exit $RET
|
@ -1,5 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH="/usr/local/opt/qt5"
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
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" ..
|
||||
|
||||
cmake \
|
||||
-D CMAKE_EXE_LINKER_FLAGS="-lz" \
|
||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
|
||||
-D CMAKE_OSX_SYSROOT="macosx10.11" \
|
||||
-D CMAKE_BUILD_TYPE=Debug \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D DESIRED_QT_VERSION=5 \
|
||||
-D OSG_PLUGIN_LIB_SEARCH_PATH="$DEPENDENCIES_ROOT/lib/osgPlugins-3.4.0" \
|
||||
-D BUILD_ESMTOOL=FALSE \
|
||||
-D BUILD_MYGUI_PLUGIN=FALSE \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
|
@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM=`uname -m`
|
||||
fi
|
||||
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
CONFIGURATION="Debug"
|
||||
fi
|
||||
|
||||
case $PLATFORM in
|
||||
x32|x86|i686|i386|win32|Win32 )
|
||||
BITS=32
|
||||
PLATFORM=Win32
|
||||
;;
|
||||
|
||||
x64|x86_64|x86-64|win64|Win64 )
|
||||
BITS=64
|
||||
PLATFORM=x64
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown platform $PLATFORM."
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
echo "Running $BITS-bit $CONFIGURATION build outside of Appveyor."
|
||||
|
||||
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
cd $(dirname "$DIR")/..
|
||||
else
|
||||
echo "Running $BITS-bit $CONFIGURATION build in Appveyor."
|
||||
|
||||
cd $APPVEYOR_BUILD_FOLDER
|
||||
fi
|
||||
|
||||
cd build_$BITS
|
||||
|
||||
which msbuild > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
msbuild() {
|
||||
/c/Program\ Files\ \(x86\)/MSBuild/12.0/Bin/MSBuild.exe "$@"
|
||||
}
|
||||
fi
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
msbuild OpenMW.sln //t:Build //m:8
|
||||
else
|
||||
msbuild OpenMW.sln //t:Build //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
|
@ -0,0 +1,9 @@
|
||||
#!/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
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@
|
||||
Description
|
||||
===========
|
||||
|
||||
Your pull request description should include (if applicable):
|
||||
|
||||
* A link back to the bug report or forum discussion that prompted the change
|
||||
* Summary of the changes made
|
||||
* Reasoning / motivation behind the change
|
||||
* What testing you have carried out to verify the change
|
||||
|
||||
Other notes
|
||||
===========
|
||||
|
||||
* Separate your work into multiple pull requests whenever possible. 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).
|
@ -0,0 +1,107 @@
|
||||
OpenMW
|
||||
======
|
||||
|
||||
[![Build Status](https://api.travis-ci.org/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Build status](https://ci.appveyor.com/api/projects/status/e6bqw8oouy8ufd46?svg=true)](https://ci.appveyor.com/project/scrawl/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
|
||||
|
||||
OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
|
||||
|
||||
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
|
||||
|
||||
* Version: 0.39.0
|
||||
* License: GPL (see docs/license/GPL3.txt for more information)
|
||||
* Website: http://www.openmw.org
|
||||
* IRC: #openmw on irc.freenode.net
|
||||
|
||||
Font Licenses:
|
||||
* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information)
|
||||
|
||||
Current Status
|
||||
--------------
|
||||
|
||||
The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://bugs.openmw.org/versions/21) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces.
|
||||
|
||||
Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
* [Official forums](https://forum.openmw.org/)
|
||||
* [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions)
|
||||
* [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup)
|
||||
* [Testing the game](https://wiki.openmw.org/index.php?title=Testing)
|
||||
* [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted)
|
||||
* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug!
|
||||
* [Known issues](http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker)
|
||||
|
||||
The data path
|
||||
-------------
|
||||
|
||||
The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install).
|
||||
|
||||
Command line options
|
||||
--------------------
|
||||
|
||||
Syntax: openmw <options>
|
||||
Allowed options:
|
||||
--help print help message
|
||||
--version print version information and quit
|
||||
--data arg (=data) set data directories (later directories
|
||||
have higher priority)
|
||||
--data-local arg set local data directory (highest
|
||||
priority)
|
||||
--fallback-archive arg (=fallback-archive)
|
||||
set fallback BSA archives (later
|
||||
archives have higher priority)
|
||||
--resources arg (=resources) set resources directory
|
||||
--start arg set initial cell
|
||||
--content arg content file(s): esm/esp, or
|
||||
omwgame/omwaddon
|
||||
--no-sound [=arg(=1)] (=0) disable all sounds
|
||||
--script-verbose [=arg(=1)] (=0) verbose script output
|
||||
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
|
||||
scripts) at startup
|
||||
--script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup
|
||||
--script-console [=arg(=1)] (=0) enable console-only script
|
||||
functionality
|
||||
--script-run arg select a file containing a list of
|
||||
console commands that is executed on
|
||||
startup
|
||||
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
|
||||
scripts
|
||||
0 - ignore warning
|
||||
1 - show warning but consider script as
|
||||
correctly compiled anyway
|
||||
2 - treat warnings as errors
|
||||
--script-blacklist arg ignore the specified script (if the use
|
||||
of the blacklist is enabled)
|
||||
--script-blacklist-use [=arg(=1)] (=1)
|
||||
enable script blacklisting
|
||||
--load-savegame arg load a save game file on game startup
|
||||
(specify an absolute filename or a
|
||||
filename relative to the current
|
||||
working directory)
|
||||
--skip-menu [=arg(=1)] (=0) skip main menu on game startup
|
||||
--new-game [=arg(=1)] (=0) run new game sequence (ignored if
|
||||
skip-menu=0)
|
||||
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
|
||||
folding)
|
||||
--encoding arg (=win1252) Character encoding used in OpenMW game
|
||||
messages:
|
||||
|
||||
win1250 - Central and Eastern European
|
||||
such as Polish, Czech, Slovak,
|
||||
Hungarian, Slovene, Bosnian, Croatian,
|
||||
Serbian (Latin script), Romanian and
|
||||
Albanian languages
|
||||
|
||||
win1251 - Cyrillic alphabet such as
|
||||
Russian, Bulgarian, Serbian Cyrillic
|
||||
and other languages
|
||||
|
||||
win1252 - Western European (Latin)
|
||||
alphabet, used by default
|
||||
--fallback arg fallback values
|
||||
--no-grab Don't grab mouse cursor
|
||||
--export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG
|
||||
image and XML file in current directory
|
||||
--activate-dist arg (=-1) activation distance override
|
@ -0,0 +1,44 @@
|
||||
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
|
||||
importercontext.cpp
|
||||
converter.cpp
|
||||
convertacdt.cpp
|
||||
convertnpcc.cpp
|
||||
convertinventory.cpp
|
||||
convertcrec.cpp
|
||||
convertcntc.cpp
|
||||
convertscri.cpp
|
||||
convertscpt.cpp
|
||||
convertplayer.cpp
|
||||
)
|
||||
|
||||
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()
|
@ -0,0 +1,52 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
#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 "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);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,13 @@
|
||||
#include "convertcntc.hpp"
|
||||
|
||||
#include "convertinventory.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertCNTC(const CNTC &cntc, ESM::ContainerState &state)
|
||||
{
|
||||
convertInventory(cntc.mInventory, state.mInventory);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#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
|
@ -0,0 +1,13 @@
|
||||
#include "convertcrec.hpp"
|
||||
|
||||
#include "convertinventory.hpp"
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertCREC(const CREC &crec, ESM::CreatureState &state)
|
||||
{
|
||||
convertInventory(crec.mInventory, state.mInventory);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#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
|
@ -0,0 +1,413 @@
|
||||
#include "converter.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 << "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 << "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
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8);
|
||||
int refIndex;
|
||||
stream >> refIndex;
|
||||
|
||||
out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,587 @@
|
||||
#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 "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 "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.mNpdt52.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) {}
|
||||
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
{
|
||||
PCDT pcdt;
|
||||
pcdt.load(esm);
|
||||
|
||||
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam);
|
||||
}
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
{
|
||||
esm.startRecord(ESM::REC_CAM_);
|
||||
esm.writeHNT("FIRS", mFirstPersonCam);
|
||||
esm.endRecord(ESM::REC_CAM_);
|
||||
}
|
||||
private:
|
||||
bool mFirstPersonCam;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,31 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#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
|
@ -0,0 +1,15 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#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
|
@ -0,0 +1,42 @@
|
||||
#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)
|
||||
{
|
||||
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.mDrawState & PCDT::DrawState_Weapon)
|
||||
out.mObject.mCreatureStats.mDrawState = 1;
|
||||
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell)
|
||||
out.mObject.mCreatureStats.mDrawState = 2;
|
||||
|
||||
firstPersonCam = !(pcdt.mPNAM.mCameraFlags & PCDT::CameraFlag_ThirdPerson);
|
||||
|
||||
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
|
||||
it != pcdt.mKnownDialogueTopics.end(); ++it)
|
||||
{
|
||||
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#ifndef OPENMW_ESSIMPORT_CONVERTPLAYER_H
|
||||
#define OPENMW_ESSIMPORT_CONVERTPLAYER_H
|
||||
|
||||
#include "importplayer.hpp"
|
||||
|
||||
#include <components/esm/player.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,17 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#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
|
@ -0,0 +1,32 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#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
|
@ -0,0 +1,130 @@
|
||||
#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();
|
||||
if (esm.isNextSub("ANIS"))
|
||||
esm.skipHSub();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
#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];
|
||||
};
|
||||
#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;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,57 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
#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
|
@ -0,0 +1,16 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#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
|
@ -0,0 +1,25 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#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
|
@ -0,0 +1,23 @@
|
||||
#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");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
#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
|
@ -0,0 +1,428 @@
|
||||
#include "importer.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <boost/shared_ptr.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 << "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)
|
||||
{
|
||||
*(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 << "can't write screenshot: no jpg readerwriter found" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image, ostream);
|
||||
if (!result.success())
|
||||
{
|
||||
std::cerr << "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;
|
||||
|
||||
std::map<unsigned int, boost::shared_ptr<Converter> > converters;
|
||||
converters[ESM::REC_GLOB] = boost::shared_ptr<Converter>(new ConvertGlobal());
|
||||
converters[ESM::REC_BOOK] = boost::shared_ptr<Converter>(new ConvertBook());
|
||||
converters[ESM::REC_NPC_] = boost::shared_ptr<Converter>(new ConvertNPC());
|
||||
converters[ESM::REC_CREA] = boost::shared_ptr<Converter>(new ConvertCREA());
|
||||
converters[ESM::REC_NPCC] = boost::shared_ptr<Converter>(new ConvertNPCC());
|
||||
converters[ESM::REC_CREC] = boost::shared_ptr<Converter>(new ConvertCREC());
|
||||
converters[recREFR ] = boost::shared_ptr<Converter>(new ConvertREFR());
|
||||
converters[recPCDT ] = boost::shared_ptr<Converter>(new ConvertPCDT());
|
||||
converters[recFMAP ] = boost::shared_ptr<Converter>(new ConvertFMAP());
|
||||
converters[recKLST ] = boost::shared_ptr<Converter>(new ConvertKLST());
|
||||
converters[recSTLN ] = boost::shared_ptr<Converter>(new ConvertSTLN());
|
||||
converters[recGAME ] = boost::shared_ptr<Converter>(new ConvertGAME());
|
||||
converters[ESM::REC_CELL] = boost::shared_ptr<Converter>(new ConvertCell());
|
||||
converters[ESM::REC_ALCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
|
||||
converters[ESM::REC_CLAS] = boost::shared_ptr<Converter>(new ConvertClass());
|
||||
converters[ESM::REC_SPEL] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Spell>());
|
||||
converters[ESM::REC_ARMO] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Armor>());
|
||||
converters[ESM::REC_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||
converters[ESM::REC_CLOT] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Clothing>());
|
||||
converters[ESM::REC_ENCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Enchantment>());
|
||||
converters[ESM::REC_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||
converters[ESM::REC_LEVC] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());
|
||||
converters[ESM::REC_LEVI] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());
|
||||
converters[ESM::REC_CNTC] = boost::shared_ptr<Converter>(new ConvertCNTC());
|
||||
converters[ESM::REC_FACT] = boost::shared_ptr<Converter>(new ConvertFACT());
|
||||
converters[ESM::REC_INFO] = boost::shared_ptr<Converter>(new ConvertINFO());
|
||||
converters[ESM::REC_DIAL] = boost::shared_ptr<Converter>(new ConvertDIAL());
|
||||
converters[ESM::REC_QUES] = boost::shared_ptr<Converter>(new ConvertQUES());
|
||||
converters[recJOUR ] = boost::shared_ptr<Converter>(new ConvertJOUR());
|
||||
converters[ESM::REC_SCPT] = boost::shared_ptr<Converter>(new ConvertSCPT());
|
||||
|
||||
// TODO:
|
||||
// - REGN (weather in certain regions?)
|
||||
// - VFXM
|
||||
// - SPLM (active spell effects)
|
||||
// - PROJ (magic projectiles in air)
|
||||
|
||||
std::set<unsigned int> unknownRecords;
|
||||
|
||||
for (std::map<unsigned int, boost::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, boost::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 << "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.mNpdt52.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, boost::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, boost::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_DIAS);
|
||||
context.mDialogueState.save(writer);
|
||||
writer.endRecord(ESM::REC_DIAS);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#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
|
@ -0,0 +1,77 @@
|
||||
#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 "importnpcc.hpp"
|
||||
#include "importcrec.hpp"
|
||||
#include "importcntc.hpp"
|
||||
#include "importplayer.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
struct Context
|
||||
{
|
||||
// set from the TES3 header
|
||||
std::string mPlayerCellName;
|
||||
|
||||
ESM::Player mPlayer;
|
||||
ESM::NPC mPlayerBase;
|
||||
std::string mCustomPlayerClassName;
|
||||
|
||||
ESM::DialogueState mDialogueState;
|
||||
|
||||
// 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::string, ESM::Creature> mCreatures;
|
||||
std::map<std::string, ESM::NPC> mNpcs;
|
||||
|
||||
Context()
|
||||
: mDay(0)
|
||||
, mMonth(0)
|
||||
, mYear(0)
|
||||
, mHour(0.f)
|
||||
{
|
||||
mPlayer.mAutoMove = 0;
|
||||
ESM::CellId playerCellId;
|
||||
playerCellId.mPaged = true;
|
||||
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
||||
mPlayer.mCellId = playerCellId;
|
||||
//mPlayer.mLastKnownExteriorPosition
|
||||
mPlayer.mHasMark = 0; // TODO
|
||||
mPlayer.mCurrentCrimeId = 0; // TODO
|
||||
mPlayer.mObject.blank();
|
||||
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
|
||||
|
||||
mGlobalMapState.mBounds.mMinX = 0;
|
||||
mGlobalMapState.mBounds.mMaxX = 0;
|
||||
mGlobalMapState.mBounds.mMinY = 0;
|
||||
mGlobalMapState.mBounds.mMaxY = 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,29 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
#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
|
@ -0,0 +1,14 @@
|
||||
#include "importinfo.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void INFO::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mInfo = esm.getHNString("INAM");
|
||||
mActorRefId = esm.getHNString("ACDT");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#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
|
@ -0,0 +1,78 @@
|
||||
#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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#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
|
@ -0,0 +1,13 @@
|
||||
#include "importjour.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void JOUR::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mText = esm.getHNString("NAME");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#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
|
@ -0,0 +1,22 @@
|
||||
#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");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#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
|
@ -0,0 +1,19 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
#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
|
@ -0,0 +1,85 @@
|
||||
#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());
|
||||
}
|
||||
|
||||
if (esm.isNextSub("MNAM"))
|
||||
esm.skipHSub(); // If this field is here it seems to specify the interior cell the player is in,
|
||||
// but it's not always here, so it's kinda useless
|
||||
|
||||
esm.getHNT(mPNAM, "PNAM");
|
||||
|
||||
if (esm.isNextSub("SNAM"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("NAM9"))
|
||||
esm.skipHSub();
|
||||
|
||||
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();
|
||||
|
||||
if (esm.isNextSub("ENAM"))
|
||||
esm.skipHSub();
|
||||
|
||||
if (esm.isNextSub("LNAM"))
|
||||
esm.skipHSub();
|
||||
|
||||
while (esm.isNextSub("FNAM"))
|
||||
{
|
||||
FNAM fnam;
|
||||
esm.getHT(fnam);
|
||||
mFactions.push_back(fnam);
|
||||
}
|
||||
|
||||
if (esm.isNextSub("AADT"))
|
||||
esm.skipHSub(); // 44 bytes, no clue
|
||||
|
||||
if (esm.isNextSub("KNAM"))
|
||||
esm.skipHSub(); // assigned Quick Keys, I think
|
||||
|
||||
if (esm.isNextSub("WERE"))
|
||||
{
|
||||
// some werewolf data, 152 bytes
|
||||
// maybe current skills and attributes for werewolf form
|
||||
esm.getSubHeader();
|
||||
esm.skip(152);
|
||||
}
|
||||
|
||||
// unsure if before or after WERE
|
||||
if (esm.isNextSub("ANIS"))
|
||||
esm.skipHSub();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
#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 DrawState_
|
||||
{
|
||||
DrawState_Weapon = 0x80,
|
||||
DrawState_Spell = 0x100
|
||||
};
|
||||
enum CameraFlags
|
||||
{
|
||||
CameraFlag_ThirdPerson = 0x2
|
||||
};
|
||||
|
||||
#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
|
||||
{
|
||||
short mDrawState; // DrawState
|
||||
short mCameraFlags; // CameraFlags
|
||||
unsigned int mLevelProgress;
|
||||
float mSkillProgress[27]; // skill progress, non-uniform scaled
|
||||
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
|
||||
unsigned char mUnknown3[84];
|
||||
unsigned char mSpecIncreases[3]; // number of skill increases for each specialization
|
||||
unsigned char mUnknown4;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
std::vector<FNAM> mFactions;
|
||||
PNAM mPNAM;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,14 @@
|
||||
#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());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#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
|
@ -0,0 +1,26 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#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
|
@ -0,0 +1,55 @@
|
||||
#include "importscri.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
void SCRI::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
|
||||
int numShorts = 0, numLongs = 0, numFloats = 0;
|
||||
if (esm.isNextSub("SLCS"))
|
||||
{
|
||||
esm.getSubHeader();
|
||||
esm.getT(numShorts);
|
||||
esm.getT(numLongs);
|
||||
esm.getT(numFloats);
|
||||
}
|
||||
|
||||
if (esm.isNextSub("SLSD"))
|
||||
{
|
||||
esm.getSubHeader();
|
||||
for (int i=0; i<numShorts; ++i)
|
||||
{
|
||||
short val;
|
||||
esm.getT(val);
|
||||
mShorts.push_back(val);
|
||||
}
|
||||
}
|
||||
// I haven't seen Longs in a save file yet, but SLLD would make sense for the name
|
||||
// TODO: test this
|
||||
if (esm.isNextSub("SLLD"))
|
||||
{
|
||||
esm.getSubHeader();
|
||||
for (int i=0; i<numLongs; ++i)
|
||||
{
|
||||
int val;
|
||||
esm.getT(val);
|
||||
mLongs.push_back(val);
|
||||
}
|
||||
}
|
||||
if (esm.isNextSub("SLFD"))
|
||||
{
|
||||
esm.getSubHeader();
|
||||
for (int i=0; i<numFloats; ++i)
|
||||
{
|
||||
float val;
|
||||
esm.getT(val);
|
||||
mFloats.push_back(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
#ifndef OPENMW_ESSIMPORT_IMPORTSCRI_H
|
||||
#define OPENMW_ESSIMPORT_IMPORTSCRI_H
|
||||
|
||||
#include <components/esm/variant.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
/// Local variable assigments for a running script
|
||||
struct SCRI
|
||||
{
|
||||
std::string mScript;
|
||||
|
||||
std::vector<short> mShorts;
|
||||
std::vector<int> mLongs;
|
||||
std::vector<float> mFloats;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,77 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include "importer.hpp"
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
bpo::options_description desc("Syntax: openmw-essimporter <options> infile.ess outfile.omwsave\nAllowed options");
|
||||
bpo::positional_options_description p_desc;
|
||||
desc.add_options()
|
||||
("help,h", "produce help message")
|
||||
("mwsave,m", bpo::value<std::string>(), "morrowind .ess save file")
|
||||
("output,o", bpo::value<std::string>(), "output file (.omwsave)")
|
||||
("compare,c", "compare two .ess files")
|
||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"), "encoding of the save file")
|
||||
;
|
||||
p_desc.add("mwsave", 1).add("output", 1);
|
||||
|
||||
bpo::variables_map variables;
|
||||
|
||||
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
|
||||
.options(desc)
|
||||
.positional(p_desc)
|
||||
.run();
|
||||
|
||||
bpo::store(parsed, variables);
|
||||
|
||||
if(variables.count("help") || !variables.count("mwsave") || !variables.count("output")) {
|
||||
std::cout << desc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bpo::notify(variables);
|
||||
|
||||
Files::ConfigurationManager cfgManager(true);
|
||||
cfgManager.readConfiguration(variables, desc);
|
||||
|
||||
std::string essFile = variables["mwsave"].as<std::string>();
|
||||
std::string outputFile = variables["output"].as<std::string>();
|
||||
std::string encoding = variables["encoding"].as<std::string>();
|
||||
|
||||
ESSImport::Importer importer(essFile, outputFile, encoding);
|
||||
|
||||
if (variables.count("compare"))
|
||||
importer.compare();
|
||||
else
|
||||
{
|
||||
const std::string& ext = ".omwsave";
|
||||
if (boost::filesystem::exists(boost::filesystem::path(outputFile))
|
||||
&& (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext))
|
||||
{
|
||||
throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?");
|
||||
}
|
||||
importer.run();
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#include "graphicssettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
Launcher::GraphicsSettings::GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
Launcher::GraphicsSettings::~GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
bool Launcher::GraphicsSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
QMapIterator<QString, QString> i(settings);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
|
||||
QString prefix;
|
||||
QString key;
|
||||
|
||||
if (sectionRe.exactMatch(i.key())) {
|
||||
prefix = sectionRe.cap(1);
|
||||
key = sectionRe.cap(2);
|
||||
}
|
||||
|
||||
if (sectionPrefix != prefix) {
|
||||
sectionPrefix = prefix;
|
||||
stream << "\n[" << prefix << "]\n";
|
||||
}
|
||||
|
||||
stream << key << " = " << i.value() << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#ifndef GRAPHICSSETTINGS_HPP
|
||||
#define GRAPHICSSETTINGS_HPP
|
||||
|
||||
#include <components/config/settingsbase.hpp>
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
class GraphicsSettings : public Config::SettingsBase<QMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
GraphicsSettings();
|
||||
~GraphicsSettings();
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
}
|
||||
#endif // GRAPHICSSETTINGS_HPP
|
@ -0,0 +1,165 @@
|
||||
///Program to test .nif files both on the FileSystem and in BSA archives.
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <components/nif/niffile.hpp>
|
||||
#include <components/files/constrainedfilestream.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/bsaarchive.hpp>
|
||||
#include <components/vfs/filesystemarchive.hpp>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// Create local aliases for brevity
|
||||
namespace bpo = boost::program_options;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
///See if the file has the named extension
|
||||
bool hasExtension(std::string filename, std::string extensionToFind)
|
||||
{
|
||||
std::string extension = filename.substr(filename.find_last_of(".")+1);
|
||||
|
||||
//Convert strings to lower case for comparison
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
|
||||
std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);
|
||||
|
||||
if(extension == extensionToFind)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
///See if the file has the "nif" extension.
|
||||
bool isNIF(std::string filename)
|
||||
{
|
||||
return hasExtension(filename,"nif");
|
||||
}
|
||||
///See if the file has the "bsa" extension.
|
||||
bool isBSA(std::string filename)
|
||||
{
|
||||
return hasExtension(filename,"bsa");
|
||||
}
|
||||
|
||||
/// Check all the nif files in a given VFS::Archive
|
||||
/// \note Takes ownership!
|
||||
/// \note Can not read a bsa file inside of a bsa file.
|
||||
void readVFS(VFS::Archive* anArchive,std::string archivePath = "")
|
||||
{
|
||||
VFS::Manager myManager(true);
|
||||
myManager.addArchive(anArchive);
|
||||
myManager.buildIndex();
|
||||
|
||||
std::map<std::string, VFS::File*> files=myManager.getIndex();
|
||||
for(std::map<std::string, VFS::File*>::const_iterator it=files.begin(); it!=files.end(); ++it)
|
||||
{
|
||||
std::string name = it->first;
|
||||
|
||||
try{
|
||||
if(isNIF(name))
|
||||
{
|
||||
// std::cout << "Decoding: " << name << std::endl;
|
||||
Nif::NIFFile temp_nif(myManager.get(name),archivePath+name);
|
||||
}
|
||||
else if(isBSA(name))
|
||||
{
|
||||
if(!archivePath.empty() && !isBSA(archivePath))
|
||||
{
|
||||
// std::cout << "Reading BSA File: " << name << std::endl;
|
||||
readVFS(new VFS::BsaArchive(archivePath+name),archivePath+name+"/");
|
||||
// std::cout << "Done with BSA File: " << name << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> parseOptions (int argc, char** argv)
|
||||
{
|
||||
bpo::options_description desc("Ensure that OpenMW can use the provided NIF and BSA files\n\n"
|
||||
"Usages:\n"
|
||||
" niftool <nif files, BSA files, or directories>\n"
|
||||
" Scan the file or directories for nif errors.\n\n"
|
||||
"Allowed options");
|
||||
desc.add_options()
|
||||
("help,h", "print help message.")
|
||||
("input-file", bpo::value< std::vector<std::string> >(), "input file")
|
||||
;
|
||||
|
||||
//Default option if none provided
|
||||
bpo::positional_options_description p;
|
||||
p.add("input-file", -1);
|
||||
|
||||
bpo::variables_map variables;
|
||||
try
|
||||
{
|
||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).
|
||||
options(desc).positional(p).run();
|
||||
bpo::store(valid_opts, variables);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
|
||||
<< desc << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bpo::notify(variables);
|
||||
if (variables.count ("help"))
|
||||
{
|
||||
std::cout << desc << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
if (variables.count("input-file"))
|
||||
{
|
||||
return variables["input-file"].as< std::vector<std::string> >();
|
||||
}
|
||||
|
||||
std::cout << "No input files or directories specified!" << std::endl;
|
||||
std::cout << desc << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::vector<std::string> files = parseOptions (argc, argv);
|
||||
|
||||
// std::cout << "Reading Files" << std::endl;
|
||||
for(std::vector<std::string>::const_iterator it=files.begin(); it!=files.end(); ++it)
|
||||
{
|
||||
std::string name = *it;
|
||||
|
||||
try{
|
||||
if(isNIF(name))
|
||||
{
|
||||
//std::cout << "Decoding: " << name << std::endl;
|
||||
Nif::NIFFile temp_nif(Files::openConstrainedFileStream(name.c_str()),name);
|
||||
}
|
||||
else if(isBSA(name))
|
||||
{
|
||||
// std::cout << "Reading BSA File: " << name << std::endl;
|
||||
readVFS(new VFS::BsaArchive(name));
|
||||
}
|
||||
else if(bfs::is_directory(bfs::path(name)))
|
||||
{
|
||||
// std::cout << "Reading All Files in: " << name << std::endl;
|
||||
readVFS(new VFS::FileSystemArchive(name),name);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: \"" << name << "\" is not a nif file, bsa file, or directory!" << std::endl;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue