1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 17:19:56 +00:00

Merge branch 'master' into pathgrid-edit

Conflicts:
	apps/opencs/view/render/cell.cpp
	apps/opencs/view/render/cell.hpp
This commit is contained in:
cc9cii 2015-02-16 16:46:52 +11:00
commit 7f7c3d57bd
656 changed files with 17184 additions and 8865 deletions

View file

@ -5,7 +5,23 @@ language: cpp
branches:
only:
- master
- coverity_scan
- /openmw-.*$/
env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
addons:
coverity_scan:
project:
name: "OpenMW/openmw"
description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de
build_command_prepend: "cmake ."
build_command: "make -j3"
branch_pattern: coverity_scan
before_install:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi
@ -14,7 +30,8 @@ before_script:
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
script:
- cd ./build
- make -j4
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j4; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && ["${TRAVIS_OS_NAME}" = "osx"]; then make package; fi
after_script:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
notifications:

207
AUTHORS.md Normal file
View file

@ -0,0 +1,207 @@
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)
Aleksandar Jovanov
Alex Haddad (rainChu)
Alex McKibben (WeirdSexy)
Alexander Nadeau (wareya)
Alexander Olofsson (Ace)
Artem Kotsynyak (greye)
Arthur Moore (EmperorArthur)
athile
Bret Curtis (psi29a)
Britt Mathis (galdor557)
cc9cii
Chris Boyce (slothlife)
Chris Robinson (KittyCat)
Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam)
darkf
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
Fil Krynicki (filkry)
Gašper Sedej
gugus/gus
Hallfaer Tuilinn
Internecine
Jacob Essex (Yacoby)
Jannik Heller (scrawl)
Jason Hooks (jhooks)
jeaye
Jeffrey Haines (Jyby)
Jengerer
Joel Graff (graffy)
John Blomberg (fstp)
Jordan Ayers
Jordan Milne
Julien Voisin (jvoisin/ap0)
Karl-Felix Glatzer (k1ll)
Kevin Poitra (PuppyKevin)
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)
megaton
Michael Hogan (Xethik)
Michael Mc Donnell
Michael Papageorgiou (werdanith)
Michał Bień (Glorf)
Miroslav Puda (pakanek)
MiroslavR
naclander
Narmo
Nathan Jeffords (blunted2night)
NeveHanter
Nikolay Kasyanov (corristo)
nobrakal
Nolan Poe (nopoe)
Paul McElroy (Greendogo)
Pieter van der Kloet (pvdk)
Radu-Marius Popovici (rpopovici)
rdimesio
riothamus
Robert MacGregor (Ragora)
Rohit Nirmal
Roman Melnik (Kromgart)
Roman Proskuryakov (humbug)
sandstranger
Sandy Carter (bwrsandman)
Scott Howard
Sebastian Wick (swick)
Sergey Shambir
sir_herrbatka
Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub)
Sylvain Thesnieres (Garvek)
terrorfisch
Thomas Luppi (Digmaster)
Tom Mason (wheybags)
Torben Leif Carrington (TorbenC)
viadanna
Vincent Heuken
vocollapse
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.

1622
CHANGELOG.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -6,4 +6,4 @@ export CC=clang
brew tap openmw/openmw
brew update
brew unlink boost
brew install cmake openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg pkg-config qt unshield
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield

View file

@ -1,5 +1,12 @@
project(OpenMW)
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
FORCE)
ENDIF()
if (APPLE)
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
@ -12,37 +19,21 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 33)
set(OPENMW_VERSION_RELEASE 1)
set(OPENMW_VERSION_MINOR 35)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(GIT_CHECKOUT FALSE)
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
find_package(Git)
if(GIT_FOUND)
include(GetGitRevisionDescription)
get_git_tag_revision(TAGHASH --tags --max-count=1)
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if(MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" GIT_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_RELEASE "${VERSION}")
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
message(STATUS "OpenMW version ${OPENMW_VERSION}")
else(MATCH)
message(WARNING "Failed to get valid version information from Git")
endif(MATCH)
set(GIT_CHECKOUT TRUE)
else(GIT_FOUND)
message(WARNING "Git executable not found")
endif(GIT_FOUND)
@ -67,6 +58,8 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binarie
option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE)
option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE)
set(CUSTOM_OGRE_PLUGIN_DIR "" CACHE PATH "Specify a custom directory for Ogre plugins (autodetected by default)")
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
# Apps and tools
@ -74,10 +67,11 @@ option(BUILD_BSATOOL "build BSA extractor" ON)
option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
option(BUILD_WIZARD "build Installation Wizard" ON)
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest and GMock frameworks" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
option(BUILD_NIFTEST "build nif file tester" OFF)
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
@ -117,7 +111,7 @@ set(OENGINE_OGRE
set(OENGINE_GUI
${LIBS_DIR}/openengine/gui/loglistener.cpp
${LIBS_DIR}/openengine/gui/manager.cpp
${LIBS_DIR}/openengine/gui/layout.hpp
${LIBS_DIR}/openengine/gui/layout.cpp
)
set(OENGINE_BULLET
@ -338,8 +332,10 @@ if (APPLE AND OPENMW_OSX_DEPLOYMENT)
# make it empty so plugin loading code can check this and try to find plugins inside app bundle
add_definitions(-DOGRE_PLUGIN_DIR="")
else()
if (NOT DEFINED ${OGRE_PLUGIN_DIR})
if (CUSTOM_OGRE_PLUGIN_DIR STREQUAL "")
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL})
else()
set(OGRE_PLUGIN_DIR ${CUSTOM_OGRE_PLUGIN_DIR})
endif()
add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}")
@ -382,8 +378,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
"${OpenMW_BINARY_DIR}/openmw.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop
"${OpenMW_BINARY_DIR}/opencs.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
"${OpenMW_BINARY_DIR}/openmw-cs.desktop")
endif()
# Compiler settings
@ -395,13 +391,19 @@ if (CMAKE_COMPILER_IS_GNUCC)
if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
SET(CMAKE_CXX_FLAGS "-Wno-unused-but-set-parameter ${CMAKE_CXX_FLAGS}")
endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
elseif (MSVC)
# Enable link-time code generation globally for all linking
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG")
set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG")
endif (CMAKE_COMPILER_IS_GNUCC)
IF(NOT WIN32 AND NOT APPLE)
# Linux building
# Paths
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Where to install libraries")
SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries")
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix")
SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
@ -417,7 +419,7 @@ IF(NOT WIN32 AND NOT APPLE)
# Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER)
IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
@ -426,10 +428,13 @@ IF(NOT WIN32 AND NOT APPLE)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-cs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
@ -446,23 +451,23 @@ IF(NOT WIN32 AND NOT APPLE)
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
# Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS)
# Install global configuration files
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS)
# Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" COMPONENT "Resources")
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
ENDIF(NOT WIN32 AND NOT APPLE)
@ -470,8 +475,9 @@ if(WIN32)
FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll")
INSTALL(FILES ${dll_files} DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
INSTALL(FILES
"${OpenMW_SOURCE_DIR}/readme.txt"
"${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt"
"${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg"
@ -480,13 +486,16 @@ if(WIN32)
DESTINATION ".")
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" DESTINATION ".")
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-launcher.exe" DESTINATION ".")
ENDIF(BUILD_LAUNCHER)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" DESTINATION ".")
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-iniimporter.exe" DESTINATION ".")
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".")
ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".")
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
@ -507,26 +516,26 @@ if(WIN32)
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW")
IF(BUILD_LAUNCHER)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};omwlauncher;OpenMW Launcher")
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
ENDIF(BUILD_LAUNCHER)
IF(BUILD_OPENCS)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};opencs;OpenMW Construction Set")
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard")
ENDIF(BUILD_WIZARD)
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\README.txt'")
SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\"
")
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/README.md")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md")
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe")
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
@ -595,6 +604,10 @@ if (BUILD_MWINIIMPORTER)
add_subdirectory( apps/mwiniimporter )
endif()
if (BUILD_ESSIMPORTER)
add_subdirectory (apps/essimporter )
endif()
if (BUILD_OPENCS)
add_subdirectory (apps/opencs)
endif()
@ -696,7 +709,7 @@ if (WIN32)
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_WARNINGS} ${MT_BUILD}")
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_LAUNCHER)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_LAUNCHER)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_BSATOOL)
@ -711,10 +724,10 @@ if (WIN32)
if (BUILD_OPENCS)
# QT triggers an informational warning that the object layout may differ when compiled with /vd2
set(OPENCS_WARNINGS "${WARNINGS} ${MT_BUILD} /wd4435")
set_target_properties(opencs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS})
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS})
endif (BUILD_OPENCS)
if (BUILD_MWINIIMPORTER)
set_target_properties(mwiniimport PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_MWINIIMPORTER)
endif(MSVC)
@ -758,7 +771,7 @@ if (APPLE)
set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
set(OPENCS_BUNDLE_NAME "OpenCS.app")
set(OPENCS_BUNDLE_NAME "OpenMW-CS.app")
set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
set(ABSOLUTE_PLUGINS "")
@ -870,4 +883,3 @@ if (DOXYGEN_FOUND)
WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
endif ()

99
README.md Normal file
View file

@ -0,0 +1,99 @@
OpenMW
======
[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg?style=plastic)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
OpenMW is an attempt at recreating 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.
* Version: 0.35.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)
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

View file

@ -27,8 +27,8 @@ struct Arguments
void replaceAll(std::string& str, const std::string& needle, const std::string& substitute)
{
int pos = str.find(needle);
while(pos != -1)
size_t pos = str.find(needle);
while(pos != std::string::npos)
{
str.replace(pos, needle.size(), substitute);
pos = str.find(needle);
@ -138,8 +138,8 @@ bool parseOptions (int argc, char** argv, Arguments &info)
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
info.longformat = variables.count("long");
info.fullpath = variables.count("full-path");
info.longformat = variables.count("long") != 0;
info.fullpath = variables.count("full-path") != 0;
return true;
}
@ -150,33 +150,32 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info);
int main(int argc, char** argv)
{
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
// Open file
Bsa::BSAFile bsa;
try
{
bsa.open(info.filename);
}
catch(std::exception &e)
{
std::cout << "ERROR reading BSA archive '" << info.filename
<< "'\nDetails:\n" << e.what() << std::endl;
return 2;
}
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
if (info.mode == "list")
return list(bsa, info);
else if (info.mode == "extract")
return extract(bsa, info);
else if (info.mode == "extractall")
return extractAll(bsa, info);
else
// Open file
Bsa::BSAFile bsa;
bsa.open(info.filename);
if (info.mode == "list")
return list(bsa, info);
else if (info.mode == "extract")
return extract(bsa, info);
else if (info.mode == "extractall")
return extractAll(bsa, info);
else
{
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
return 1;
}
}
catch (std::exception& e)
{
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
return 1;
std::cerr << "ERROR reading BSA archive\nDetails:\n" << e.what() << std::endl;
return 2;
}
}
@ -189,9 +188,11 @@ int list(Bsa::BSAFile& bsa, Arguments& info)
if(info.longformat)
{
// Long format
std::ios::fmtflags f(std::cout.flags());
std::cout << std::setw(50) << std::left << files[i].name;
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize;
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl;
std::cout.flags(f);
}
else
std::cout << files[i].name << std::endl;

View file

@ -22,7 +22,7 @@ struct ESMData
{
std::string author;
std::string description;
int version;
unsigned int version;
std::vector<ESM::Header::MasterData> masters;
std::deque<EsmTool::RecordBase *> mRecords;
@ -48,9 +48,9 @@ const std::set<int> ESMData::sLabeledRec =
// Based on the legacy struct
struct Arguments
{
unsigned int raw_given;
unsigned int quiet_given;
unsigned int loadcells_given;
bool raw_given;
bool quiet_given;
bool loadcells_given;
bool plain_given;
std::string mode;
@ -59,6 +59,7 @@ struct Arguments
std::string outname;
std::vector<std::string> types;
std::string name;
ESMData data;
ESM::ESMReader reader;
@ -78,6 +79,8 @@ bool parseOptions (int argc, char** argv, Arguments &info)
("type,t", bpo::value< std::vector<std::string> >(),
"Show only records of this type (four character record code). May "
"be specified multiple times. Only affects dump mode.")
("name,n", bpo::value<std::string>(),
"Show only the record with this name. Only affects dump mode.")
("plain,p", "Print contents of dialogs, books and scripts. "
"(skipped by default)"
"Only affects dump mode.")
@ -148,7 +151,9 @@ bool parseOptions (int argc, char** argv, Arguments &info)
}
if (variables.count("type") > 0)
info.types = variables["type"].as< std::vector<std::string> >();
info.types = variables["type"].as< std::vector<std::string> >();
if (variables.count("name") > 0)
info.name = variables["name"].as<std::string>();
info.mode = variables["mode"].as<std::string>();
if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp"))
@ -177,10 +182,10 @@ bool parseOptions (int argc, char** argv, Arguments &info)
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
info.outname = variables["input-file"].as< std::vector<std::string> >()[1];
info.raw_given = variables.count ("raw");
info.quiet_given = variables.count ("quiet");
info.loadcells_given = variables.count ("loadcells");
info.plain_given = (variables.count("plain") > 0);
info.raw_given = variables.count ("raw") != 0;
info.quiet_given = variables.count ("quiet") != 0;
info.loadcells_given = variables.count ("loadcells") != 0;
info.plain_given = variables.count("plain") != 0;
// Font encoding settings
info.encoding = variables["encoding"].as<std::string>();
@ -203,19 +208,27 @@ int comp(Arguments& info);
int main(int argc, char**argv)
{
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
if (info.mode == "dump")
return load(info);
else if (info.mode == "clone")
return clone(info);
else if (info.mode == "comp")
return comp(info);
else
try
{
std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl;
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
if (info.mode == "dump")
return load(info);
else if (info.mode == "clone")
return clone(info);
else if (info.mode == "comp")
return comp(info);
else
{
std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl;
return 1;
}
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
return 1;
}
@ -253,10 +266,12 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
std::cout << " Faction: '" << ref.mFaction << "'" << std::endl;
std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl;
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
std::cout << " Uses/health: '" << ref.mCharge << "'\n";
std::cout << " Uses/health: '" << ref.mChargeInt << "'\n";
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl;
if (!ref.mKey.empty())
std::cout << " Key: '" << ref.mKey << "'" << std::endl;
}
}
@ -273,8 +288,10 @@ void printRaw(ESM::ESMReader &esm)
esm.getSubName();
esm.skipHSub();
n = esm.retSubName();
std::ios::fmtflags f(std::cout.flags());
std::cout << " " << n.toString() << " - " << esm.getSubSize()
<< " bytes @ 0x" << std::hex << offs << "\n";
std::cout.flags(f);
}
}
}
@ -348,6 +365,9 @@ int load(Arguments& info)
if (id.empty())
id = esm.getHNOString("INAM");
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id))
interested = false;
if(!quiet && interested)
std::cout << "\nRecord: " << n.toString()
<< " '" << id << "'\n";
@ -375,7 +395,7 @@ int load(Arguments& info)
record->load(esm);
if (!quiet && interested) record->print();
if (record->getType().val == ESM::REC_CELL && loadCells) {
if (record->getType().val == ESM::REC_CELL && loadCells && interested) {
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
}
@ -420,7 +440,7 @@ int clone(Arguments& info)
return 1;
}
int recordCount = info.data.mRecords.size();
size_t recordCount = info.data.mRecords.size();
int digitCount = 1; // For a nicer output
if (recordCount > 9) ++digitCount;
@ -491,9 +511,9 @@ int clone(Arguments& info)
if (!info.data.mCellRefs[ptr].empty()) {
typedef std::deque<ESM::CellRef> RefList;
RefList &refs = info.data.mCellRefs[ptr];
for (RefList::iterator it = refs.begin(); it != refs.end(); ++it)
for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt)
{
it->save(esm);
refIt->save(esm);
}
}
}
@ -501,7 +521,7 @@ int clone(Arguments& info)
esm.endRecord(name.toString());
saved++;
int perc = (saved / (float)recordCount)*100;
int perc = (int)((saved / (float)recordCount)*100);
if (perc % 10 == 0)
{
std::cerr << "\r" << perc << "%";

View file

@ -13,14 +13,13 @@
#include <components/esm/loadweap.hpp>
#include <components/esm/aipackage.hpp>
#include <iostream>
#include <boost/format.hpp>
std::string bodyPartLabel(int idx)
{
if (idx >= 0 && idx <= 26)
{
const char *bodyPartLabels[] = {
static const char *bodyPartLabels[] = {
"Head",
"Hair",
"Neck",
@ -59,7 +58,7 @@ std::string meshPartLabel(int idx)
{
if (idx >= 0 && idx <= ESM::BodyPart::MP_Tail)
{
const char *meshPartLabels[] = {
static const char *meshPartLabels[] = {
"Head",
"Hair",
"Neck",
@ -86,7 +85,7 @@ std::string meshTypeLabel(int idx)
{
if (idx >= 0 && idx <= ESM::BodyPart::MT_Armor)
{
const char *meshTypeLabels[] = {
static const char *meshTypeLabels[] = {
"Skin",
"Clothing",
"Armor"
@ -101,7 +100,7 @@ std::string clothingTypeLabel(int idx)
{
if (idx >= 0 && idx <= 9)
{
const char *clothingTypeLabels[] = {
static const char *clothingTypeLabels[] = {
"Pants",
"Shoes",
"Shirt",
@ -123,7 +122,7 @@ std::string armorTypeLabel(int idx)
{
if (idx >= 0 && idx <= 10)
{
const char *armorTypeLabels[] = {
static const char *armorTypeLabels[] = {
"Helmet",
"Cuirass",
"Left Pauldron",
@ -146,7 +145,7 @@ std::string dialogTypeLabel(int idx)
{
if (idx >= 0 && idx <= 4)
{
const char *dialogTypeLabels[] = {
static const char *dialogTypeLabels[] = {
"Topic",
"Voice",
"Greeting",
@ -165,7 +164,7 @@ std::string questStatusLabel(int idx)
{
if (idx >= 0 && idx <= 4)
{
const char *questStatusLabels[] = {
static const char *questStatusLabels[] = {
"None",
"Name",
"Finished",
@ -182,7 +181,7 @@ std::string creatureTypeLabel(int idx)
{
if (idx >= 0 && idx <= 3)
{
const char *creatureTypeLabels[] = {
static const char *creatureTypeLabels[] = {
"Creature",
"Daedra",
"Undead",
@ -198,7 +197,7 @@ std::string soundTypeLabel(int idx)
{
if (idx >= 0 && idx <= 7)
{
const char *soundTypeLabels[] = {
static const char *soundTypeLabels[] = {
"Left Foot",
"Right Foot",
"Swim Left",
@ -218,7 +217,7 @@ std::string weaponTypeLabel(int idx)
{
if (idx >= 0 && idx <= 13)
{
const char *weaponTypeLabels[] = {
static const char *weaponTypeLabels[] = {
"Short Blade One Hand",
"Long Blade One Hand",
"Long Blade Two Hand",

View file

@ -2,6 +2,8 @@
#include "labels.hpp"
#include <iostream>
#include <sstream>
#include <boost/format.hpp>
void printAIPackage(ESM::AIPackage p)
@ -25,7 +27,7 @@ void printAIPackage(ESM::AIPackage p)
{
std::cout << " Travel Coordinates: (" << p.mTravel.mX << ","
<< p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl;
std::cout << " Travel Unknown: " << (int)p.mTravel.mUnk << std::endl;
std::cout << " Travel Unknown: " << p.mTravel.mUnk << std::endl;
}
else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort)
{
@ -33,12 +35,12 @@ void printAIPackage(ESM::AIPackage p)
<< p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl;
std::cout << " Duration: " << p.mTarget.mDuration << std::endl;
std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl;
std::cout << " Unknown: " << (int)p.mTarget.mUnk << std::endl;
std::cout << " Unknown: " << p.mTarget.mUnk << std::endl;
}
else if (p.mType == ESM::AI_Activate)
{
std::cout << " Name: " << p.mActivate.mName.toString() << std::endl;
std::cout << " Activate Unknown: " << (int)p.mActivate.mUnk << std::endl;
std::cout << " Activate Unknown: " << p.mActivate.mUnk << std::endl;
}
else {
std::cout << " BadPackage: " << boost::format("0x%08x") % p.mType << std::endl;
@ -89,6 +91,7 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
case 'A': if (indicator == 'R') type_str = "Not Race"; break;
case 'B': if (indicator == 'L') type_str = "Not Cell"; break;
case 'C': if (indicator == 's') type_str = "Not Local"; break;
default: break;
}
// Append the variable name to the function string if any.
@ -110,6 +113,7 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
case '3': oper_str = ">="; break;
case '4': oper_str = "< "; break;
case '5': oper_str = "<="; break;
default: break;
}
std::ostringstream stream;
@ -430,7 +434,7 @@ void Record<ESM::Apparatus>::print()
std::cout << " Icon: " << mData.mIcon << std::endl;
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Type: " << apparatusTypeLabel(mData.mData.mType)
<< " (" << (int)mData.mData.mType << ")" << std::endl;
<< " (" << mData.mData.mType << ")" << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
@ -531,10 +535,10 @@ void Record<ESM::Class>::print()
std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization)
<< " (" << mData.mData.mSpecialization << ")" << std::endl;
for (int i = 0; i != 5; i++)
std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][0])
std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][0])
<< " (" << mData.mData.mSkills[i][0] << ")" << std::endl;
for (int i = 0; i != 5; i++)
std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][1])
std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1])
<< " (" << mData.mData.mSkills[i][1] << ")" << std::endl;
}
@ -750,7 +754,7 @@ void Record<ESM::DialInfo>::print()
if (mData.mCell != "")
std::cout << " Cell: " << mData.mCell << std::endl;
if (mData.mData.mDisposition > 0)
std::cout << " Disposition: " << mData.mData.mDisposition << std::endl;
std::cout << " Disposition/Journal index: " << mData.mData.mDisposition << std::endl;
if (mData.mData.mGender != ESM::DialInfo::NA)
std::cout << " Gender: " << mData.mData.mGender << std::endl;
if (mData.mSound != "")
@ -810,13 +814,12 @@ void Record<ESM::Land>::print()
{
std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl;
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
std::cout << " HasData: " << mData.mHasData << std::endl;
std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
// Seems like this should done with reference counting in the
// loader to me. But I'm not really knowledgable about this
// record type yet. --Cory
bool wasLoaded = mData.mDataLoaded;
bool wasLoaded = (mData.mDataLoaded != 0);
if (mData.mDataTypes) mData.loadData(mData.mDataTypes);
if (mData.mDataLoaded)
{
@ -834,7 +837,7 @@ void Record<ESM::CreatureLevList>::print()
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
std::vector<ESM::LevelledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Creature: Level: " << iit->mLevel
<< " Creature: " << iit->mId << std::endl;
@ -846,7 +849,7 @@ void Record<ESM::ItemLevList>::print()
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
std::vector<ESM::LevelledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Inventory: Level: " << iit->mLevel
<< " Item: " << iit->mId << std::endl;
@ -999,7 +1002,7 @@ void Record<ESM::NPC>::print()
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl;
std::cout << " Unknown3: "
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl;
std::cout << " Gold: " << (int)mData.mNpdt12.mGold << std::endl;
std::cout << " Gold: " << mData.mNpdt12.mGold << std::endl;
}
else {
std::cout << " Level: " << mData.mNpdt52.mLevel << std::endl;
@ -1021,7 +1024,7 @@ void Record<ESM::NPC>::print()
std::cout << " Skills:" << std::endl;
for (int i = 0; i != ESM::Skill::Length; i++)
std::cout << " " << skillLabel(i) << ": "
<< (int)((unsigned char)mData.mNpdt52.mSkills[i]) << std::endl;
<< (int)(mData.mNpdt52.mSkills[i]) << std::endl;
std::cout << " Health: " << mData.mNpdt52.mHealth << std::endl;
std::cout << " Magicka: " << mData.mNpdt52.mMana << std::endl;
@ -1123,9 +1126,9 @@ void Record<ESM::Race>::print()
std::cout << (male ? " Male:" : " Female:") << std::endl;
for (int i=0; i<8; ++i)
std::cout << " " << sAttributeNames[i] << ": "
<< mData.mData.mAttributeValues[i].getValue (male) << std::endl;
for (int j=0; j<8; ++j)
std::cout << " " << sAttributeNames[j] << ": "
<< mData.mData.mAttributeValues[j].getValue (male) << std::endl;
std::cout << " Height: " << mData.mData.mHeight.getValue (male) << std::endl;
std::cout << " Weight: " << mData.mData.mWeight.getValue (male) << std::endl;

View file

@ -19,7 +19,7 @@ namespace EsmTool
{
protected:
std::string mId;
int mFlags;
uint32_t mFlags;
ESM::NAME mType;
bool mPrintPlain;
@ -40,11 +40,11 @@ namespace EsmTool
mId = id;
}
int getFlags() const {
uint32_t getFlags() const {
return mFlags;
}
void setFlags(int flags) {
void setFlags(uint32_t flags) {
mFlags = flags;
}
@ -52,10 +52,6 @@ namespace EsmTool
return mType;
}
bool getPrintPlain() const {
return mPrintPlain;
}
void setPrintPlain(bool plain) {
mPrintPlain = plain;
}

View file

@ -0,0 +1,43 @@
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_LIBRARIES}
components
)
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(openmw-essimporter gcov)
endif()

View file

@ -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 = acdt.mAttributes[i][1];
cStats.mAttributes[i].mMod = acdt.mAttributes[i][0];
cStats.mAttributes[i].mCurrent = acdt.mAttributes[i][0];
}
cStats.mGoldPool = acdt.mGoldPool;
cStats.mTalkedTo = acdt.mFlags & TalkedToPlayer;
cStats.mAttacked = acdt.mFlags & Attacked;
}
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats)
{
cStats.mDead = acsc.mFlags & Dead;
}
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats)
{
for (int i=0; i<ESM::Skill::Length; ++i)
{
npcStats.mSkills[i].mRegular.mMod = actorData.mSkills[i][1];
npcStats.mSkills[i].mRegular.mCurrent = actorData.mSkills[i][1];
npcStats.mSkills[i].mRegular.mBase = actorData.mSkills[i][0];
}
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -0,0 +1,389 @@
#include "converter.hpp"
#include <stdexcept>
#include <OgreImage.h>
#include <OgreColourValue.h>
#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, Ogre::PixelFormat pf, const std::string& out)
{
Ogre::Image screenshot;
Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(data, size));
screenshot.loadRawData(stream, width, height, 1, pf);
screenshot.save(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());
Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(&data[0], data.size()));
mGlobalMapImage.loadRawData(stream, maph.size, maph.size, 1, Ogre::PF_BYTE_RGB);
// to match openmw size
mGlobalMapImage.resize(maph.size*2, maph.size*2, Ogre::Image::FILTER_BILINEAR);
}
void ConvertFMAP::write(ESM::ESMWriter &esm)
{
int numcells = mGlobalMapImage.getWidth() / 18; // NB truncating, doesn't divide perfectly
// with the 512x512 map the game has by default
int cellSize = mGlobalMapImage.getWidth()/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;
Ogre::Image image2;
std::vector<Ogre::uint8> data;
int width = cellSize*numcells;
int height = cellSize*numcells;
data.resize(width*height*4, 0);
image2.loadDynamicImage(&data[0], width, height, Ogre::PF_BYTE_RGBA);
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.getWidth()/2;
int imageTopSrc = mGlobalMapImage.getHeight()/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)
image2.setColourAt(mGlobalMapImage.getColourAt(imageLeftSrc+x, imageTopSrc+y, 0)
, imageLeftDst+x, imageTopDst+y, 0);
}
Ogre::DataStreamPtr encoded = image2.encode("png");
mContext->mGlobalMapState.mImageData.resize(encoded->size());
encoded->read(&mContext->mGlobalMapState.mImageData[0], encoded->size());
esm.startRecord(ESM::REC_GMAP);
mContext->mGlobalMapState.save(esm);
esm.endRecord(ESM::REC_GMAP);
}
void ConvertCell::read(ESM::ESMReader &esm)
{
ESM::Cell cell;
std::string id = esm.getHNString("NAME");
cell.mName = id;
cell.load(esm, 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 (id == 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, Ogre::PF_BYTE_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[id] = 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);
}
}
}

View file

@ -0,0 +1,597 @@
#ifndef OPENMW_ESSIMPORT_CONVERTER_H
#define OPENMW_ESSIMPORT_CONVERTER_H
#include <OgreImage.h>
#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; }
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)
{
std::string id = esm.getHNString("NAME");
T record;
record.load(esm);
mRecords[id] = 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);
esm.writeHNString("NAME", it->first);
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;
std::string id = esm.getHNString("NAME");
npc.load(esm);
if (id != "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(id)] = npc;
}
else
{
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
mContext->mPlayerBase = npc;
std::map<const int, float> 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;
std::string id = esm.getHNString("NAME");
creature.load(esm);
mContext->mCreatures[Misc::StringUtils::lowerCase(id)] = 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)
{
std::string id = esm.getHNString("NAME");
ESM::Global global;
global.load(esm);
if (Misc::StringUtils::ciEqual(id, "gamehour"))
mContext->mHour = global.mValue.getFloat();
if (Misc::StringUtils::ciEqual(id, "day"))
mContext->mDay = global.mValue.getInteger();
if (Misc::StringUtils::ciEqual(id, "month"))
mContext->mMonth = global.mValue.getInteger();
if (Misc::StringUtils::ciEqual(id, "year"))
mContext->mYear = global.mValue.getInteger();
mRecords[id] = global;
}
};
class ConvertClass : public DefaultConverter<ESM::Class>
{
public:
virtual void read(ESM::ESMReader &esm)
{
std::string id = esm.getHNString("NAME");
ESM::Class class_;
class_.load(esm);
if (id == "NEWCLASSID_CHARGEN")
mContext->mCustomPlayerClassName = class_.mName;
mRecords[id] = class_;
}
};
class ConvertBook : public DefaultConverter<ESM::Book>
{
public:
virtual void read(ESM::ESMReader &esm)
{
std::string id = esm.getHNString("NAME");
ESM::Book book;
book.load(esm);
if (book.mData.mSkillID == -1)
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id));
mRecords[id] = 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:
Ogre::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)
{
std::string id = esm.getHNString("NAME");
ESM::Faction faction;
faction.load(esm);
Misc::StringUtils::toLower(id);
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::toLower(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) {}
std::string toString(int weatherId)
{
switch (weatherId)
{
case 0:
return "clear";
case 1:
return "cloudy";
case 2:
return "foggy";
case 3:
return "overcast";
case 4:
return "rain";
case 5:
return "thunderstorm";
case 6:
return "ashstorm";
case 7:
return "blight";
case 8:
return "snow";
case 9:
return "blizzard";
case -1:
return "";
default:
{
std::stringstream error;
error << "unknown weather id: " << weatherId;
throw std::runtime_error(error.str());
}
}
}
virtual void read(ESM::ESMReader &esm)
{
mGame.load(esm);
mHasGame = true;
}
virtual void write(ESM::ESMWriter &esm)
{
if (!mHasGame)
return;
esm.startRecord(ESM::REC_WTHR);
ESM::WeatherState weather;
weather.mCurrentWeather = toString(mGame.mGMDT.mCurrentWeather);
weather.mNextWeather = toString(mGame.mGMDT.mNextWeather);
weather.mRemainingTransitionTime = mGame.mGMDT.mWeatherTransition/100.f*(0.015*24*3600);
weather.mHour = mContext->mHour;
weather.mWindSpeed = 0.f;
weather.mTimePassed = 0.0;
weather.mFirstUpdate = false;
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

View file

@ -0,0 +1,30 @@
#include "convertinventory.hpp"
#include <components/misc/stringops.hpp>
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;
}
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -0,0 +1,38 @@
#include "convertplayer.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<8; ++i)
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
for (int i=0; i<27; ++i)
out.mObject.mNpcStats.mSkills[i].mRegular.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.mCameraState == PCDT::CameraState_FirstPerson);
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it)
{
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
}
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -0,0 +1,115 @@
#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"))
esm.skipHSub();
if (esm.isNextSub("STPR"))
esm.skipHSub();
if (esm.isNextSub("MNAM"))
esm.skipHSub();
ESM::CellRef::loadData(esm);
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"))
{
// occured 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();
}
}

View file

@ -0,0 +1,81 @@
#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 mUnknown5[4];
};
struct ACSC
{
unsigned char mUnknown1[17];
unsigned char mFlags; // ACSCFlags
unsigned char mUnknown2[94];
};
#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

View file

@ -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; // the other 3 bytes seem to be uninitialized garbage
}
if (esm.isNextSub("MVRF"))
{
esm.skipHSub();
esm.getSubName();
esm.skipHSub();
}
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A"))
esm.skipHSub();
mInventory.load(esm);
}
}

View file

@ -0,0 +1,26 @@
#ifndef OPENMW_ESSIMPORT_CREC_H
#define OPENMW_ESSIMPORT_CREC_H
#include "importinventory.hpp"
namespace ESM
{
class ESMReader;
}
namespace ESSImport
{
/// Creature changes
struct CREC
{
int mIndex;
Inventory mInventory;
void load(ESM::ESMReader& esm);
};
}
#endif

View file

@ -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");
}
}

View file

@ -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

View file

@ -0,0 +1,378 @@
#include "importer.hpp"
#include <boost/shared_ptr.hpp>
#include <OgreRoot.h>
#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)
{
Ogre::Image screenshot;
std::vector<unsigned char> screenshotData = fileHeader.mSCRS; // MemoryDataStream doesn't work with const data :(
Ogre::DataStreamPtr screenshotStream (new Ogre::MemoryDataStream(&screenshotData[0], screenshotData.size()));
screenshot.loadRawData(screenshotStream, 128, 128, 1, Ogre::PF_BYTE_BGRA);
Ogre::DataStreamPtr encoded = screenshot.encode("jpg");
out.mScreenshot.resize(encoded->size());
encoded->read(&out.mScreenshot[0], encoded->size());
}
}
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::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl;
return;
}
File::Record rec2 = file2.mRecords[i];
if (rec.mName != rec2.mName)
{
std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl;
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::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl;
return;
}
File::Subrecord sub2 = rec2.mSubrecords[j];
if (sub.mName != sub2.mName)
{
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;
break; // TODO: try to recover
}
if (sub.mData != sub2.mData)
{
if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end())
continue;
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;
}
}
}
}
void Importer::run()
{
// construct Ogre::Root to gain access to image codecs
Ogre::LogManager logman;
Ogre::Root root;
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.val);
if (it != converters.end())
{
it->second->read(esm);
}
else
{
if (unknownRecords.insert(n.val).second)
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
esm.skipRecord();
}
}
ESM::ESMWriter writer;
writer.setFormat (ESM::Header::CurrentFormat);
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_);
writer.writeHNString("NAME", "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);
}
}

View file

@ -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

View file

View file

@ -0,0 +1,73 @@
#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()
{
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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

@ -0,0 +1,68 @@
#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;
// seems that a stack of items can have a set of subrecords for each item? rings0000.ess
// doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place?
// I guess we should double check the stacking logic in OpenMW
for (int i=0;i<std::abs(item.mCount);++i)
{
if (esm.isNextSub("XIDX")) // index in the stack?
esm.skipHSub();
item.mSCRI.load(esm);
// for XSOL and XCHG seen so far, but probably others too
item.ESM::CellRef::loadData(esm);
int charge=-1;
esm.getHNOT(charge, "XHLT");
item.mChargeInt = charge;
}
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;
}
}
}

View file

@ -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

View file

@ -0,0 +1,13 @@
#include "importjour.hpp"
#include <components/esm/esmreader.hpp>
namespace ESSImport
{
void JOUR::load(ESM::ESMReader &esm)
{
mText = esm.getHNString("NAME");
}
}

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

@ -0,0 +1,20 @@
#include "importnpcc.hpp"
#include <components/esm/esmreader.hpp>
namespace ESSImport
{
void NPCC::load(ESM::ESMReader &esm)
{
esm.getHNT(mNPDT, "NPDT");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A"))
esm.skipHSub();
mInventory.load(esm);
}
}

View file

@ -0,0 +1,36 @@
#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;
void load(ESM::ESMReader &esm);
};
}
#endif

View file

@ -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();
}
}

View file

@ -0,0 +1,83 @@
#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 CameraState
{
CameraState_FirstPerson = 0x8,
CameraState_ThirdPerson = 0xa
};
#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 mCameraState; // CameraState
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[88];
};
#pragma pack(pop)
std::vector<FNAM> mFactions;
PNAM mPNAM;
void load(ESM::ESMReader& esm);
};
}
#endif

View file

@ -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());
}
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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

View file

@ -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);
}
}
}
}

View file

@ -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

77
apps/essimporter/main.cpp Normal file
View file

@ -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;
}

View file

@ -78,7 +78,7 @@ if(NOT WIN32)
endif(NOT WIN32)
# Main executable
add_executable(omwlauncher
add_executable(openmw-launcher
${GUI_TYPE}
${LAUNCHER}
${LAUNCHER_HEADER}
@ -87,7 +87,7 @@ add_executable(omwlauncher
${UI_HDRS}
)
target_link_libraries(omwlauncher
target_link_libraries(openmw-launcher
${Boost_LIBRARIES}
${OGRE_LIBRARIES}
${OGRE_STATIC_PLUGINS}
@ -99,6 +99,6 @@ target_link_libraries(omwlauncher
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(omwlauncher gcov)
target_link_libraries(openmw-launcher gcov)
endif()

View file

@ -20,6 +20,9 @@
#include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp"
const char *Launcher::DataFilesPage::mDefaultContentListName = "Default";
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg)
, mGameSettings(gameSettings)
@ -30,7 +33,7 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
setObjectName ("DataFilesPage");
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
mProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this);
connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),
this, SLOT(updateOkButton(QString)));
@ -44,13 +47,13 @@ void Launcher::DataFilesPage::buildView()
ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
//tool buttons
ui.newProfileButton->setToolTip ("Create a new profile");
ui.deleteProfileButton->setToolTip ("Delete an existing profile");
ui.newProfileButton->setToolTip ("Create a new Content List");
ui.deleteProfileButton->setToolTip ("Delete an existing Content List");
//combo box
ui.profilesComboBox->addItem ("Default");
ui.profilesComboBox->setPlaceholderText (QString("Select a profile..."));
ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String("Default")));
ui.profilesComboBox->addItem(mDefaultContentListName);
ui.profilesComboBox->setPlaceholderText (QString("Select a Content List..."));
ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String(mDefaultContentListName)));
// Add the actions to the toolbuttons
ui.newProfileButton->setDefaultAction (ui.newProfileAction);
@ -69,21 +72,8 @@ void Launcher::DataFilesPage::buildView()
bool Launcher::DataFilesPage::loadSettings()
{
QStringList paths = mGameSettings.getDataDirs();
foreach (const QString &path, paths)
mSelector->addFiles(path);
mDataLocal = mGameSettings.getDataLocal();
if (!mDataLocal.isEmpty())
mSelector->addFiles(mDataLocal);
paths.insert (0, mDataLocal);
PathIterator pathIterator (paths);
QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/"));
QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile");
QStringList profiles = mLauncherSettings.getContentLists();
QString currentProfile = mLauncherSettings.getCurrentContentListName();
qDebug() << "current profile is: " << currentProfile;
@ -94,20 +84,41 @@ bool Launcher::DataFilesPage::loadSettings()
if (!currentProfile.isEmpty())
addProfile(currentProfile, true);
QStringList files = mLauncherSettings.values(QString("Profiles/") + currentProfile + QString("/content"), Qt::MatchExactly);
return true;
}
void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
{
QStringList paths = mGameSettings.getDataDirs();
foreach(const QString &path, paths)
mSelector->addFiles(path);
mDataLocal = mGameSettings.getDataLocal();
if (!mDataLocal.isEmpty())
mSelector->addFiles(mDataLocal);
paths.insert(0, mDataLocal);
PathIterator pathIterator(paths);
mSelector->setProfileContent(filesInProfile(contentModelName, pathIterator));
}
QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName, PathIterator& pathIterator)
{
QStringList files = mLauncherSettings.getContentListFiles(profileName);
QStringList filepaths;
foreach (const QString &file, files)
foreach(const QString& file, files)
{
QString filepath = pathIterator.findFirstPath (file);
QString filepath = pathIterator.findFirstPath(file);
if (!filepath.isEmpty())
filepaths << filepath;
}
mSelector->setProfileContent (filepaths);
return true;
return filepaths;
}
void Launcher::DataFilesPage::saveSettings(const QString &profile)
@ -120,24 +131,20 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
//retrieve the files selected for the profile
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
removeProfile (profileName);
mGameSettings.remove(QString("content"));
//set the value of the current profile (not necessarily the profile being saved!)
mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText());
mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText());
QStringList fileNames;
foreach(const ContentSelectorModel::EsmFile *item, items) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/content"), item->fileName());
mGameSettings.setMultiValue(QString("content"), item->fileName());
fileNames.append(item->fileName());
}
mLauncherSettings.setContentList(profileName, fileNames);
mGameSettings.setContentList(fileNames);
}
void Launcher::DataFilesPage::removeProfile(const QString &profile)
{
mLauncherSettings.remove(QString("Profiles/") + profile);
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/content"));
mLauncherSettings.removeContentList(profile);
}
QAbstractItemModel *Launcher::DataFilesPage::profilesModel() const
@ -154,9 +161,11 @@ void Launcher::DataFilesPage::setProfile(int index, bool savePrevious)
{
if (index >= -1 && index < ui.profilesComboBox->count())
{
QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex());
QString previous = mPreviousProfile;
QString current = ui.profilesComboBox->itemText(index);
mPreviousProfile = current;
setProfile (previous, current, savePrevious);
}
}
@ -167,15 +176,12 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString
if (previous == current)
return;
if (previous.isEmpty())
return;
if (!previous.isEmpty() && savePrevious)
saveSettings (previous);
ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current));
loadSettings();
populateFileViews(current);
checkForDefaultProfile();
}
@ -207,12 +213,16 @@ void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const
void Launcher::DataFilesPage::slotProfileChanged(int index)
{
// in case the event was triggered externally
if (ui.profilesComboBox->currentIndex() != index)
ui.profilesComboBox->setCurrentIndex(index);
setProfile (index, true);
}
void Launcher::DataFilesPage::on_newProfileAction_triggered()
{
if (!mProfileDialog->exec() == QDialog::Accepted)
if (mProfileDialog->exec() != QDialog::Accepted)
return;
QString profile = mProfileDialog->lineEdit()->text();
@ -222,15 +232,9 @@ void Launcher::DataFilesPage::on_newProfileAction_triggered()
saveSettings();
mSelector->clearCheckStates();
mLauncherSettings.setCurrentContentListName(profile);
addProfile(profile, true);
mSelector->setGameFile();
saveSettings();
emit signalProfileChanged (ui.profilesComboBox->findText(profile));
}
void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent)
@ -238,10 +242,8 @@ void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurr
if (profile.isEmpty())
return;
if (ui.profilesComboBox->findText (profile) != -1)
return;
ui.profilesComboBox->addItem (profile);
if (ui.profilesComboBox->findText (profile) == -1)
ui.profilesComboBox->addItem (profile);
if (setAsCurrent)
setProfile (ui.profilesComboBox->findText (profile), false);
@ -257,14 +259,14 @@ void Launcher::DataFilesPage::on_deleteProfileAction_triggered()
if (!showDeleteMessageBox (profile))
return;
// Remove the profile from the combobox
ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile));
// this should work since the Default profile can't be deleted and is always index 0
int next = ui.profilesComboBox->currentIndex()-1;
// changing the profile forces a reload of plugin file views.
ui.profilesComboBox->setCurrentIndex(next);
removeProfile(profile);
saveSettings();
loadSettings();
ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile));
checkForDefaultProfile();
}
@ -285,7 +287,7 @@ void Launcher::DataFilesPage::updateOkButton(const QString &text)
void Launcher::DataFilesPage::checkForDefaultProfile()
{
//don't allow deleting "Default" profile
bool success = (ui.profilesComboBox->currentText() != "Default");
bool success = (ui.profilesComboBox->currentText() != mDefaultContentListName);
ui.deleteProfileAction->setEnabled (success);
ui.profilesComboBox->setEditEnabled (success);
@ -294,10 +296,10 @@ void Launcher::DataFilesPage::checkForDefaultProfile()
bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text)
{
QMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Delete Profile"));
msgBox.setWindowTitle(tr("Delete Content List"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(tr("Are you sure you want to delete <b>%0</b>?").arg(text));
msgBox.setText(tr("Are you sure you want to delete <b>%1</b>?").arg(text));
QAbstractButton *deleteButton =
msgBox.addButton(tr("Delete"), QMessageBox::ActionRole);

View file

@ -58,6 +58,10 @@ namespace Launcher
void on_newProfileAction_triggered();
void on_deleteProfileAction_triggered();
public:
/// Content List that is always present
const static char *mDefaultContentListName;
private:
TextInputDialog *mProfileDialog;
@ -67,6 +71,8 @@ namespace Launcher
Config::GameSettings &mGameSettings;
Config::LauncherSettings &mLauncherSettings;
QString mPreviousProfile;
QString mDataLocal;
void setPluginsCheckstates(Qt::CheckState state);
@ -80,6 +86,7 @@ namespace Launcher
bool showDeleteMessageBox (const QString &text);
void addProfile (const QString &profile, bool setAsCurrent);
void checkForDefaultProfile();
void populateFileViews(const QString& contentModelName);
class PathIterator
{
@ -132,6 +139,8 @@ namespace Launcher
}
};
QStringList filesInProfile(const QString& profileName, PathIterator& pathIterator);
};
}
#endif

View file

@ -10,7 +10,10 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h>
#include <SDL_video.h>
#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <boost/math/common_factor.hpp>
@ -64,7 +67,7 @@ bool Launcher::GraphicsPage::setupOgre()
}
catch(Ogre::Exception &ex)
{
QString ogreError = QString::fromStdString(ex.getFullDescription().c_str());
QString ogreError = QString::fromUtf8(ex.getFullDescription().c_str());
QMessageBox msgBox;
msgBox.setWindowTitle("Error creating Ogre::Root");
msgBox.setIcon(QMessageBox::Critical);
@ -132,7 +135,7 @@ bool Launcher::GraphicsPage::setupSDL()
msgBox.setWindowTitle(tr("Error receiving number of screens"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>");
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromUtf8(SDL_GetError()) + "<br>");
msgBox.exec();
return false;
}
@ -159,6 +162,9 @@ bool Launcher::GraphicsPage::loadSettings()
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
fullScreenCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/window border")) == QLatin1String("true"))
windowBorderCheckBox->setCheckState(Qt::Checked);
int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing")));
if (aaIndex != -1)
antiAliasingComboBox->setCurrentIndex(aaIndex);
@ -193,6 +199,9 @@ void Launcher::GraphicsPage::saveSettings()
fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false"));
windowBorderCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/window border"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/window border"), QString("false"));
mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText());
mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText());
@ -228,7 +237,7 @@ QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre
opt_it != i->second.possibleValues.end(); ++opt_it, ++idx)
{
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) {
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified();
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromUtf8((*opt_it).c_str()).simplified();
}
}
}
@ -257,7 +266,7 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
msgBox.setWindowTitle(tr("Error receiving resolutions"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>");
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromUtf8(SDL_GetError()) + "<br>");
msgBox.exec();
return result;
}
@ -270,7 +279,7 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
msgBox.setWindowTitle(tr("Error receiving resolutions"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>SDL_GetDisplayMode failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>");
msgBox.setText(tr("<br><b>SDL_GetDisplayMode failed:</b><br><br>") + QString::fromUtf8(SDL_GetError()) + "<br>");
msgBox.exec();
return result;
}
@ -331,10 +340,12 @@ void Launcher::GraphicsPage::slotFullScreenChanged(int state)
customRadioButton->setEnabled(false);
customWidthSpinBox->setEnabled(false);
customHeightSpinBox->setEnabled(false);
windowBorderCheckBox->setEnabled(false);
} else {
customRadioButton->setEnabled(true);
customWidthSpinBox->setEnabled(true);
customHeightSpinBox->setEnabled(true);
windowBorderCheckBox->setEnabled(true);
}
}

View file

@ -3,14 +3,11 @@
#include <QWidget>
#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <components/ogreinit/ogreinit.hpp>
#include "ui_graphicspage.h"
namespace Ogre { class Root; class RenderSystem; }
namespace Files { struct ConfigurationManager; }

View file

@ -1,3 +1,6 @@
#include <iostream>
#include <csignal>
#include <QApplication>
#include <QTextCodec>
#include <QDir>
@ -15,53 +18,63 @@
int main(int argc, char *argv[])
{
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_VIDEO) != 0)
try
{
qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError());
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
qDebug() << "SDL_Init failed: " << QString::fromUtf8(SDL_GetError());
return 0;
}
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
QApplication app(argc, argv);
// Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
app.setLibraryPaths(libraryPaths);
#endif
QDir::setCurrent(dir.absolutePath());
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
Launcher::MainDialog mainWin;
if (!mainWin.showFirstRunDialog())
return 0;
// if (!mainWin.setup()) {
// return 0;
// }
mainWin.show();
int returnValue = app.exec();
SDL_Quit();
return returnValue;
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
return 0;
}
QApplication app(argc, argv);
// Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
app.setLibraryPaths(libraryPaths);
#endif
QDir::setCurrent(dir.absolutePath());
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
Launcher::MainDialog mainWin;
if (!mainWin.showFirstRunDialog())
return 0;
// if (!mainWin.setup()) {
// return 0;
// }
mainWin.show();
int returnValue = app.exec();
SDL_Quit();
return returnValue;
}

View file

@ -27,25 +27,6 @@ using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr), QMainWindow (parent)
{
// Install the stylesheet font
QFile file;
QFontDatabase fontDatabase;
const QStringList fonts = fontDatabase.families();
// Check if the font is installed
if (!fonts.contains("EB Garamond")) {
QString font = QString::fromUtf8(mCfgMgr.getGlobalDataPath().string().c_str()) + QString("resources/mygui/EBGaramond-Regular.ttf");
file.setFileName(font);
if (!file.exists()) {
font = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str()) + QString("resources/mygui/EBGaramond-Regular.ttf");
}
fontDatabase.addApplicationFont(font);
}
setupUi(this);
mGameInvoker = new ProcessInvoker();
@ -209,6 +190,8 @@ bool Launcher::MainDialog::setup()
if (!setupGameSettings())
return false;
mLauncherSettings.setContentList(mGameSettings);
if (!setupGraphicsSettings())
return false;
@ -232,6 +215,8 @@ bool Launcher::MainDialog::reloadSettings()
if (!setupGameSettings())
return false;
mLauncherSettings.setContentList(mGameSettings);
if (!setupGraphicsSettings())
return false;
@ -280,8 +265,8 @@ bool Launcher::MainDialog::setupLauncherSettings()
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QStringList paths;
paths.append(QString("launcher.cfg"));
paths.append(userPath + QString("launcher.cfg"));
paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName));
paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
@ -562,7 +547,7 @@ bool Launcher::MainDialog::writeSettings()
file.close();
// Launcher settings
file.setFileName(userPath + QString("launcher.cfg"));
file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created

View file

@ -11,6 +11,7 @@
#include <components/config/launchersettings.hpp>
#include "utils/textinputdialog.hpp"
#include "datafilespage.hpp"
using namespace Process;
@ -51,7 +52,7 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(importerFinished(int,QProcess::ExitStatus)));
mProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this);
connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)),
this, SLOT(updateOkButton(QString)));
@ -140,7 +141,7 @@ void Launcher::SettingsPage::on_importerButton_clicked()
qDebug() << "arguments " << arguments;
if (!mImporterInvoker->startProcess(QLatin1String("mwiniimport"), arguments, false))
if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false))
return;
}
@ -198,31 +199,33 @@ void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
return;
// Re-read the settings in their current state
// Importer may have changed settings, so refresh
mMain->reloadSettings();
// Import selected data files from openmw.cfg
if (addonsCheckBox->isChecked())
{
// Because we've reloaded settings, the current content list matches content in OpenMW.cfg
QString oldContentListName = mLauncherSettings.getCurrentContentListName();
if (mProfileDialog->exec() == QDialog::Accepted)
{
const QString profile(mProfileDialog->lineEdit()->text());
const QStringList files(mGameSettings.values(QLatin1String("content")));
qDebug() << "Profile " << profile << files;
// Doesn't quite work right now
mLauncherSettings.setValue(QLatin1String("Profiles/currentprofile"), profile);
foreach (const QString &file, files) {
mLauncherSettings.setMultiValue(QLatin1String("Profiles/") + profile + QLatin1String("/content"), file);
// remove the current content list to prevent duplication
//... except, not allowed to delete the Default content list
if (oldContentListName.compare(DataFilesPage::mDefaultContentListName) != 0)
{
mLauncherSettings.removeContentList(oldContentListName);
}
mGameSettings.remove(QLatin1String("content"));
const QString newContentListName(mProfileDialog->lineEdit()->text());
const QStringList files(mGameSettings.getContentList());
mLauncherSettings.setCurrentContentListName(newContentListName);
mLauncherSettings.setContentList(newContentListName, files);
// Make DataFiles Page load the new content list.
mMain->reloadSettings();
}
}
mMain->reloadSettings();
importerButton->setEnabled(true);
}
@ -234,7 +237,7 @@ void Launcher::SettingsPage::updateOkButton(const QString &text)
return;
}
const QStringList profiles(mLauncherSettings.subKeys(QString("Profiles/")));
const QStringList profiles(mLauncherSettings.getContentLists());
(profiles.contains(text))
? mProfileDialog->setOkButtonEnabled(false)

View file

@ -47,13 +47,13 @@ void ProfilesComboBox::setEditEnabled(bool editable)
void ProfilesComboBox::slotTextChanged(const QString &text)
{
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::red);
QPalette palette;
palette.setColor(QPalette::Text,Qt::red);
int index = findText(text);
if (text.isEmpty() || (index != -1 && index != currentIndex())) {
lineEdit()->setPalette(*palette);
lineEdit()->setPalette(palette);
} else {
lineEdit()->setPalette(QApplication::palette());
}

View file

@ -59,13 +59,13 @@ void Launcher::TextInputDialog::setOkButtonEnabled(bool enabled)
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
okButton->setEnabled(enabled);
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text, Qt::red);
QPalette palette;
palette.setColor(QPalette::Text, Qt::red);
if (enabled) {
mLineEdit->setPalette(QApplication::palette());
} else {
// Existing profile name, make the text red
mLineEdit->setPalette(*palette);
mLineEdit->setPalette(palette);
}
}

View file

@ -9,16 +9,16 @@ set(MWINIIMPORT_HEADER
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
add_executable(mwiniimport
add_executable(openmw-iniimporter
${MWINIIMPORT}
)
target_link_libraries(mwiniimport
target_link_libraries(openmw-iniimporter
${Boost_LIBRARIES}
components
)
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(mwiniimport gcov)
target_link_libraries(openmw-iniimporter gcov)
endif()

View file

@ -56,93 +56,87 @@ int wmain(int argc, wchar_t *wargv[]) {
char **argv = converter.get();
boost::filesystem::path::imbue(boost::locale::generator().generate(""));
#endif
bpo::options_description desc("Syntax: mwiniimporter <options> inifile configfile\nAllowed options");
bpo::positional_options_description p_desc;
desc.add_options()
("help,h", "produce help message")
("verbose,v", "verbose output")
("ini,i", bpo::value<std::string>(), "morrowind.ini file")
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
("game-files,g", "import esm and esp files")
("no-archives,A", "disable bsa archives import")
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
"Character encoding used in OpenMW game messages:\n"
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default")
;
p_desc.add("ini", 1).add("cfg", 1);
bpo::variables_map vm;
try
{
bpo::options_description desc("Syntax: openmw-iniimporter <options> inifile configfile\nAllowed options");
bpo::positional_options_description p_desc;
desc.add_options()
("help,h", "produce help message")
("verbose,v", "verbose output")
("ini,i", bpo::value<std::string>(), "morrowind.ini file")
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
("game-files,g", "import esm and esp files")
("no-archives,A", "disable bsa archives import")
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
"Character encoding used in OpenMW game messages:\n"
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default")
;
p_desc.add("ini", 1).add("cfg", 1);
bpo::variables_map vm;
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
.options(desc)
.positional(p_desc)
.run();
bpo::store(parsed, vm);
if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
std::cout << desc;
return 0;
}
bpo::notify(vm);
std::string iniFile = vm["ini"].as<std::string>();
std::string cfgFile = vm["cfg"].as<std::string>();
// if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>());
if(vm["output"].defaulted()) {
outputFile = vm["cfg"].as<std::string>();
}
if(!boost::filesystem::exists(iniFile)) {
std::cerr << "ini file does not exist" << std::endl;
return -3;
}
if(!boost::filesystem::exists(cfgFile))
std::cerr << "cfg file does not exist" << std::endl;
MwIniImporter importer;
importer.setVerbose(vm.count("verbose"));
// Font encoding settings
std::string encoding(vm["encoding"].as<std::string>());
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
importer.merge(cfg, ini);
importer.mergeFallback(cfg, ini);
if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini);
}
if(!vm.count("no-archives")) {
importer.importArchives(cfg, ini);
}
std::cout << "write to: " << outputFile << std::endl;
bfs::ofstream file((bfs::path(outputFile)));
importer.writeToFile(file, cfg);
}
catch(boost::program_options::unknown_option & x)
catch (std::exception& e)
{
std::cerr << "ERROR: " << x.what() << std::endl;
return false;
std::cerr << "ERROR: " << e.what() << std::endl;
}
catch(boost::program_options::invalid_command_line_syntax & x)
{
std::cerr << "ERROR: " << x.what() << std::endl;
return false;
}
if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
std::cout << desc;
return 0;
}
bpo::notify(vm);
std::string iniFile = vm["ini"].as<std::string>();
std::string cfgFile = vm["cfg"].as<std::string>();
// if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>());
if(vm["output"].defaulted()) {
outputFile = vm["cfg"].as<std::string>();
}
if(!boost::filesystem::exists(iniFile)) {
std::cerr << "ini file does not exist" << std::endl;
return -3;
}
if(!boost::filesystem::exists(cfgFile))
std::cerr << "cfg file does not exist" << std::endl;
MwIniImporter importer;
importer.setVerbose(vm.count("verbose"));
// Font encoding settings
std::string encoding(vm["encoding"].as<std::string>());
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
importer.merge(cfg, ini);
importer.mergeFallback(cfg, ini);
if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini);
}
if(!vm.count("no-archives")) {
importer.importArchives(cfg, ini);
}
std::cout << "write to: " << outputFile << std::endl;
bfs::ofstream file((bfs::path(outputFile)));
importer.writeToFile(file, cfg);
return 0;
}

View file

@ -4,8 +4,6 @@ set (OPENCS_SRC main.cpp
opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc
document operation saving documentmanager loader runner
)
@ -41,7 +39,7 @@ opencs_units (model/tools
opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referenceablecheck scriptcheck bodypartcheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
)
@ -147,7 +145,7 @@ set (OPENCS_UI
${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui
)
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})
source_group (openmw-cs FILES ${OPENCS_SRC} ${OPENCS_HDR})
if(WIN32)
set(QT_USE_QTMAIN TRUE)
@ -170,12 +168,12 @@ qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${BULLET_INCLUDE_DIRS})
if(APPLE)
set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/opencs.icns)
set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns)
else()
set (OPENCS_MAC_ICON "")
endif(APPLE)
add_executable(opencs
add_executable(openmw-cs
MACOSX_BUNDLE
${OENGINE_BULLET}
${OPENCS_SRC}
@ -186,10 +184,10 @@ add_executable(opencs
)
if(APPLE)
set_target_properties(opencs PROPERTIES
set_target_properties(openmw-cs PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
OUTPUT_NAME "OpenCS"
MACOSX_BUNDLE_ICON_FILE "opencs.icns"
OUTPUT_NAME "OpenMW-CS"
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
@ -200,7 +198,7 @@ if(APPLE)
MACOSX_PACKAGE_LOCATION Resources)
endif(APPLE)
target_link_libraries(opencs
target_link_libraries(openmw-cs
${OGRE_LIBRARIES}
${OGRE_Overlay_LIBRARIES}
${OGRE_STATIC_PLUGINS}
@ -212,5 +210,5 @@ target_link_libraries(opencs
)
if(APPLE)
INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
endif()

View file

@ -15,7 +15,7 @@
#include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp>
#include <components/ogreinit/ogreinit.hpp>
#include <components/nifogre/ogrenifloader.hpp>
#include <components/bsa/resources.hpp>
#include "model/doc/document.hpp"
@ -35,6 +35,8 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string());
NifOgre::Loader::setShowMarkers(true);
mOverlaySystem.reset (new CSVRender::OverlaySystem);
Bsa::registerResources (Files::Collections (config.first, !mFsStrict), config.second, true,
@ -75,7 +77,8 @@ CS::Editor::~Editor ()
mPidFile.close();
if(mServer && boost::filesystem::exists(mPid))
remove(mPid.string().c_str()); // ignore any error
static_cast<void> ( // silence coverity warning
remove(mPid.string().c_str())); // ignore any error
// cleanup global resources used by OEngine
delete OEngine::Physic::BulletShapeManager::getSingletonPtr();
@ -93,7 +96,7 @@ void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs)
std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig()
{
boost::program_options::variables_map variables;
boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options");
boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()->composing())
@ -236,6 +239,7 @@ void CS::Editor::showSettings()
if (mSettings.isHidden())
mSettings.show();
mSettings.move (QCursor::pos());
mSettings.raise();
mSettings.activateWindow();
}
@ -245,7 +249,7 @@ bool CS::Editor::makeIPCServer()
try
{
mPid = boost::filesystem::temp_directory_path();
mPid /= "opencs.pid";
mPid /= "openmw-cs.pid";
bool pidExists = boost::filesystem::exists(mPid);
mPidFile.open(mPid);

View file

@ -46,47 +46,55 @@ class Application : public QApplication
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
OgreInit::OgreInit ogreInit;
std::auto_ptr<sh::Factory> shinyFactory;
Application application (argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath());
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
application.setLibraryPaths(libraryPaths);
#endif
application.setWindowIcon (QIcon (":./opencs.png"));
CS::Editor editor (ogreInit);
if(!editor.makeIPCServer())
try
{
editor.connectToIPCServer();
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
OgreInit::OgreInit ogreInit;
std::auto_ptr<sh::Factory> shinyFactory;
Application application (argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath());
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
application.setLibraryPaths(libraryPaths);
#endif
application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor (ogreInit);
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
shinyFactory = editor.setupGraphics();
return editor.run();
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
return 0;
}
shinyFactory = editor.setupGraphics();
return editor.run();
}

View file

@ -27,7 +27,7 @@ bool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row,
QVariant data = table.data (index);
if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int &&
data.type()!=QVariant::UInt && data.type()!=static_cast<QVariant::Type> (QMetaType::Float))
data.type()!=QVariant::UInt)
return false;
double value = data.toDouble();

View file

@ -0,0 +1,110 @@
#include "referencecheck.hpp"
#include <boost/lexical_cast.hpp>
CSMTools::ReferenceCheckStage::ReferenceCheckStage(
const CSMWorld::RefCollection& references,
const CSMWorld::RefIdCollection& referencables,
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
const CSMWorld::IdCollection<ESM::Faction>& factions)
:
mReferences(references),
mReferencables(referencables),
mDataSet(referencables.getDataSet()),
mCells(cells),
mFactions(factions)
{
}
void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
const CSMWorld::Record<CSMWorld::CellRef>& record = mReferences.getRecord(stage);
if (record.isDeleted())
return;
const CSMWorld::CellRef& cellRef = record.get();
const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId);
// Check for empty reference id
if (cellRef.mRefID.empty()) {
messages.push_back(std::make_pair(id, " is an empty reference"));
} else {
// Check for non existing referenced object
if (mReferencables.searchId(cellRef.mRefID) == -1) {
messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID));
} else {
// Check if reference charge is valid for it's proper referenced type
CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID);
bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light;
if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) {
std::string str = " has invalid charge ";
if (localIndex.second == CSMWorld::UniversalId::Type_Light)
str += boost::lexical_cast<std::string>(cellRef.mChargeFloat);
else
str += boost::lexical_cast<std::string>(cellRef.mChargeInt);
messages.push_back(std::make_pair(id, id.getId() + str));
}
}
}
// Check if referenced object is in valid cell
if (mCells.searchId(cellRef.mCell) == -1)
messages.push_back(std::make_pair(id, " is referencing object from non existing cell " + cellRef.mCell));
// If object have owner, check if that owner reference is valid
if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1)
messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner));
// If object have creature soul trapped, check if that creature reference is valid
if (!cellRef.mSoul.empty())
if (mReferencables.searchId(cellRef.mSoul) == -1)
messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul));
bool hasFaction = !cellRef.mFaction.empty();
// If object have faction, check if that faction reference is valid
if (hasFaction)
if (mFactions.searchId(cellRef.mFaction) == -1)
messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction));
// Check item's faction rank
if (hasFaction && cellRef.mFactionRank < -1)
messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + cellRef.mFactionRank));
else if (!hasFaction && cellRef.mFactionRank != -2)
messages.push_back(std::make_pair(id, " has invalid faction rank " + cellRef.mFactionRank));
// If door have destination cell, check if that reference is valid
if (!cellRef.mDestCell.empty())
if (mCells.searchId(cellRef.mDestCell) == -1)
messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell));
// Check if scale isn't negative
if (cellRef.mScale < 0)
{
std::string str = " has negative scale ";
str += boost::lexical_cast<std::string>(cellRef.mScale);
messages.push_back(std::make_pair(id, id.getId() + str));
}
// Check if enchantement points aren't negative or are at full (-1)
if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1)
{
std::string str = " has negative enchantment points ";
str += boost::lexical_cast<std::string>(cellRef.mEnchantmentCharge);
messages.push_back(std::make_pair(id, id.getId() + str));
}
// Check if gold value isn't negative
if (cellRef.mGoldValue < 0)
{
std::string str = " has negative gold value ";
str += cellRef.mGoldValue;
messages.push_back(std::make_pair(id, id.getId() + str));
}
}
int CSMTools::ReferenceCheckStage::setup()
{
return mReferences.getSize();
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_REFERENCECHECK_H
#define CSM_TOOLS_REFERENCECHECK_H
#include "../doc/state.hpp"
#include "../doc/document.hpp"
namespace CSMTools
{
class ReferenceCheckStage : public CSMDoc::Stage
{
public:
ReferenceCheckStage (const CSMWorld::RefCollection& references,
const CSMWorld::RefIdCollection& referencables,
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
const CSMWorld::IdCollection<ESM::Faction>& factions);
virtual void perform(int stage, CSMDoc::Messages& messages);
virtual int setup();
private:
const CSMWorld::RefCollection& mReferences;
const CSMWorld::RefIdCollection& mReferencables;
const CSMWorld::RefIdData& mDataSet;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
};
}
#endif // CSM_TOOLS_REFERENCECHECK_H

View file

@ -23,6 +23,7 @@
#include "referenceablecheck.hpp"
#include "scriptcheck.hpp"
#include "bodypartcheck.hpp"
#include "referencecheck.hpp"
CSMDoc::Operation *CSMTools::Tools::get (int type)
{
@ -82,6 +83,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
mVerifier->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
mVerifier->appendStage (new ScriptCheckStage (mDocument));
mVerifier->appendStage(

View file

@ -1053,13 +1053,13 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mCharge;
return record.get().mChargeInt;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mCharge = data.toInt();
record2.mChargeInt = data.toInt();
record.setModified (record2);
}

View file

@ -1,4 +1,5 @@
#include "cell.hpp"
#include "idcollection.hpp"
#include "pathgrid.hpp"
#include <sstream>

View file

@ -6,11 +6,12 @@
#include <components/esm/loadpgrd.hpp>
#include "idcollection.hpp"
#include "cell.hpp"
namespace CSMWorld
{
struct Cell;
template<typename T, typename AT>
class IdCollection;
/// \brief Wrapper for Pathgrid record
///
/// \attention The mData.mX and mData.mY fields of the ESM::Pathgrid struct are not used.

View file

@ -223,7 +223,14 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD
}
CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns)
: ActorColumns (actorColumns)
: ActorColumns (actorColumns),
mType(NULL),
mSoul(NULL),
mScale(NULL),
mOriginal(NULL),
mCombat(NULL),
mMagic(NULL),
mStealth(NULL)
{}
CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns)
@ -431,7 +438,14 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
InventoryRefIdAdapter<ESM::Miscellaneous>::setData (column, data, index, value);
}
CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns) {}
CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)
: ActorColumns (actorColumns),
mRace(NULL),
mClass(NULL),
mFaction(NULL),
mHair(NULL),
mHead(NULL)
{}
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)
: ActorRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc, columns), mColumns (columns)
@ -587,4 +601,4 @@ void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData
else
EnchantableRefIdAdapter<ESM::Weapon>::setData (column, data, index, value);
}
}
}

View file

@ -1,10 +1,17 @@
#ifndef CSM_WOLRD_SUBCOLLECTION_H
#define CSM_WOLRD_SUBCOLLECTION_H
#include "idcollection.hpp"
namespace ESM
{
class ESMReader;
}
namespace CSMWorld
{
struct Cell;
template<typename T, typename AT>
class IdCollection;
/// \brief Single type collection of top level records that are associated with cells
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class SubCellCollection : public IdCollection<ESXRecordT, IdAccessorT>

View file

@ -131,6 +131,7 @@ namespace
}
CSMWorld::UniversalId::UniversalId (const std::string& universalId)
: mIndex(0)
{
std::string::size_type index = universalId.find (':');

View file

@ -18,7 +18,7 @@
#include "adjusterwidget.hpp"
CSVDoc::FileDialog::FileDialog(QWidget *parent) :
QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false)
QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false), mAction(ContentAction_Undefined)
{
ui.setupUi (this);
resize(400, 400);
@ -125,13 +125,17 @@ void CSVDoc::FileDialog::buildOpenFileView()
if(!mDialogBuilt)
{
connect (mSelector, SIGNAL (signalAddonFileSelected (int)), this, SLOT (slotUpdateAcceptButton (int)));
connect (mSelector, SIGNAL (signalAddonFileUnselected (int)), this, SLOT (slotUpdateAcceptButton (int)));
connect (mSelector, SIGNAL (signalAddonDataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (slotAddonDataChanged(const QModelIndex&, const QModelIndex&)));
}
connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile()));
}
void CSVDoc::FileDialog::slotUpdateAcceptButton (int)
void CSVDoc::FileDialog::slotAddonDataChanged(const QModelIndex &topleft, const QModelIndex &bottomright)
{
slotUpdateAcceptButton(0);
}
void CSVDoc::FileDialog::slotUpdateAcceptButton(int)
{
QString name = "";

View file

@ -70,6 +70,7 @@ namespace CSVDoc
void slotUpdateAcceptButton (int);
void slotUpdateAcceptButton (const QString &, bool);
void slotRejected();
void slotAddonDataChanged(const QModelIndex& topleft, const QModelIndex& bottomright);
};
}
#endif // FILEDIALOG_HPP

View file

@ -18,7 +18,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
}
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
: mDocument (document), mAborted (false), mMessages (0)
: mDocument (document), mAborted (false), mMessages (0), mTotalRecords (0)
{
setWindowTitle (("Opening " + document->getSavePath().filename().string()).c_str());
@ -104,7 +104,7 @@ void CSVDoc::LoadingDocument::nextRecord (int records)
void CSVDoc::LoadingDocument::abort (const std::string& error)
{
mAborted = true;
mError->setText (QString::fromUtf8 (("Loading failed: " + error).c_str()));
mError->setText (QString::fromUtf8 (("<font color=red>Loading failed: " + error + "</font>").c_str()));
mButtons->setStandardButtons (QDialogButtonBox::Close);
}
@ -199,4 +199,4 @@ void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string&
if (iter!=mDocuments.end())
iter->second->addMessage (message);
}
}

View file

@ -375,17 +375,20 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews)
{
QString width = CSMSettings::UserSettings::instance().settingValue
("window/default-width");
int width = CSMSettings::UserSettings::instance().settingValue
("window/default-width").toInt();
QString height = CSMSettings::UserSettings::instance().settingValue
("window/default-height");
int height = CSMSettings::UserSettings::instance().settingValue
("window/default-height").toInt();
width = std::max(width, 300);
height = std::max(height, 300);
// trick to get the window decorations and their sizes
show();
hide();
resize (width.toInt() - (frameGeometry().width() - geometry().width()),
height.toInt() - (frameGeometry().height() - geometry().height()));
resize (width - (frameGeometry().width() - geometry().width()),
height - (frameGeometry().height() - geometry().height()));
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);

View file

@ -120,7 +120,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics),
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics), mX(0), mY(0),
mPathgridId("")
{
mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
@ -137,15 +137,14 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
int landIndex = land.searchId(mId);
if (landIndex != -1)
{
mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true,
Terrain::Align_XY));
const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get();
mTerrain->loadCell(esmLand->mX,
esmLand->mY);
if(esmLand)
{
mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true,
Terrain::Align_XY));
mTerrain->loadCell(esmLand->mX,
esmLand->mY);
float verts = ESM::Land::LAND_SIZE;
float worldsize = ESM::Land::REAL_SIZE;
mX = esmLand->mX;
@ -160,6 +159,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
CSVRender::Cell::~Cell()
{
<<<<<<< .mine
// destroy manual objects
for(std::map<std::pair<int, int>, std::string>::iterator iter = mPgEdges.begin();
iter != mPgEdges.end(); ++iter)
@ -182,6 +182,30 @@ CSVRender::Cell::~Cell()
}
mPhysics->removeHeightField(mSceneMgr, mX, mY);
=======
if (mTerrain.get())
mPhysics->removeHeightField(mSceneMgr, mX, mY);
>>>>>>> .theirs
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)

View file

@ -9,8 +9,13 @@
#include <OgreVector3.h>
#ifndef Q_MOC_RUN
#include <components/terrain/terraingrid.hpp>
<<<<<<< .mine
#include <components/esm/loadpgrd.hpp> // FIXME: temporaty storage until saving to document
=======
#endif
>>>>>>> .theirs
#include "object.hpp"

View file

@ -422,7 +422,7 @@ namespace CSVRender
//plane X, upvector Y, mOffset x : x-z plane, wheel closer/further
std::pair<Ogre::Vector3, Ogre::Vector3> MouseState::planeAxis()
{
bool screenCoord = true;
const bool screenCoord = true;
Ogre::Vector3 dir = getCamera()->getDerivedDirection();
QString wheelDir = "Closer/Further";

View file

@ -35,7 +35,8 @@ void CSVRender::Object::clear()
{
mObject.setNull();
clearSceneNode (mBase);
if (mBase)
clearSceneNode (mBase);
}
void CSVRender::Object::update()
@ -156,11 +157,13 @@ CSVRender::Object::~Object()
{
clear();
if(mPhysics) // preview may not have physics enabled
mPhysics->removeObject(mBase->getName());
if (mBase)
{
if(mPhysics) // preview may not have physics enabled
mPhysics->removeObject(mBase->getName());
mBase->getCreator()->destroySceneNode (mBase);
}
}
bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft,

View file

@ -3,7 +3,9 @@
#include <boost/shared_ptr.hpp>
#ifndef Q_MOC_RUN
#include <components/nifogre/ogrenifloader.hpp>
#endif
class QModelIndex;

View file

@ -16,7 +16,7 @@ namespace CSVWorld
PhysicsSystem::PhysicsSystem()
{
// Create physics. shapeLoader is deleted by the physic engine
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(true);
mEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
}

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