Merge remote-tracking branch 'upstream/master' into pathfinding

Conflicts:
	apps/openmw/mwmechanics/aicombat.cpp
	apps/openmw/mwmechanics/aifollow.cpp
	apps/openmw/mwmechanics/aipackage.cpp
	apps/openmw/mwmechanics/aipackage.hpp
	apps/openmw/mwmechanics/aiwander.cpp
	apps/openmw/mwmechanics/pathfinding.hpp

Ogre::Vector3->osg::Vec3f; REACTION_INTERVAL->AI_REACTION_TIME; MakeOgreVec3->MakeOsgVec3
coverity_scan^2
mrcheko 9 years ago
commit d7d5cc6689

3
.gitignore vendored

@ -5,7 +5,7 @@ CMakeCache.txt
cmake_install.cmake
Makefile
makefile
build
build*
prebuilt
## doxygen
@ -41,7 +41,6 @@ resources
## generated objects
apps/openmw/config.hpp
components/version/version.hpp
docs/mainpage.hpp
moc_*.cxx
*.cxx_parameters

@ -1,6 +1,6 @@
os:
- linux
- osx
# - osx
language: cpp
branches:
only:
@ -18,8 +18,8 @@ addons:
name: "OpenMW/openmw"
description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de
build_command_prepend: "cmake ."
build_command: "make -j3"
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE"
build_command: "make"
branch_pattern: coverity_scan
matrix:
include:
@ -40,8 +40,8 @@ script:
- cd ./build
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j2; 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
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
notifications:
recipients:
- corrmage+travis-ci@gmail.com

@ -13,12 +13,14 @@ Programmers
Marc Zinnschlag (Zini) - Lead Programmer/Project Manager
Adam Hogan (aurix)
Aesylwinn
Aleksandar Jovanov
Alex Haddad (rainChu)
Alex McKibben (WeirdSexy)
Alexander Nadeau (wareya)
Alexander Olofsson (Ace)
Artem Kotsynyak (greye)
artemutin
Arthur Moore (EmperorArthur)
athile
Bret Curtis (psi29a)
@ -29,6 +31,7 @@ Programmers
Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam)
darkf
Dieho
Dmitry Shkurskiy (endorph)
Douglas Diniz (Dgdiniz)
Douglas Mencken (dougmencken)
@ -52,6 +55,7 @@ Programmers
jeaye
Jeffrey Haines (Jyby)
Jengerer
Jiří Kuneš (kunesj)
Joel Graff (graffy)
John Blomberg (fstp)
Jordan Ayers
@ -59,6 +63,7 @@ Programmers
Julien Voisin (jvoisin/ap0)
Karl-Felix Glatzer (k1ll)
Kevin Poitra (PuppyKevin)
Koncord
Lars Söderberg (Lazaroth)
lazydev
Leon Saunders (emoose)
@ -70,6 +75,7 @@ Programmers
Marco Melletti (mellotanica)
Marco Schulze
Mateusz Kołaczek (PL_kolek)
Mateusz Malisz (malice)
megaton
Michael Hogan (Xethik)
Michael Mc Donnell
@ -84,8 +90,10 @@ Programmers
Nikolay Kasyanov (corristo)
nobrakal
Nolan Poe (nopoe)
Paul Cercueil (pcercuei)
Paul McElroy (Greendogo)
Pieter van der Kloet (pvdk)
pkubik
Radu-Marius Popovici (rpopovici)
rdimesio
riothamus
@ -109,6 +117,7 @@ Programmers
viadanna
Vincent Heuken
vocollapse
zelurker
Manual
------

@ -1,3 +1,237 @@
0.37.0
------
Bug #385: Light emitting objects have a too short distance of activation
Bug #455: Animation doesn't resize creature's bounding box
Bug #602: Only collision model is updated when modifying objects trough console
Bug #639: Sky horizon at nighttime
Bug #672: incorrect trajectory of the moons
Bug #814: incorrect NPC width
Bug #827: Inaccurate raycasting for dead actors
Bug #996: Can see underwater clearly when at right height/angle
Bug #1317: Erene Llenim in Seyda Neen does not walk around
Bug #1330: Cliff racers fail to hit the player
Bug #1366: Combat AI can't aim down (in order to hit small creatures)
Bug #1511: View distance while under water is much too short
Bug #1563: Terrain positioned incorrectly and appears to vibrate in far-out cells
Bug #1612: First person models clip through walls
Bug #1647: Crash switching from full screen to windows mode - D3D9
Bug #1650: No textures with directx on windows
Bug #1730: Scripts names starting with digit(s) fail to compile
Bug #1738: Socucius Ergalla's greetings are doubled during the tutorial
Bug #1784: First person weapons always in the same position
Bug #1813: Underwater flora lighting up entire area.
Bug #1871: Handle controller extrapolation flags
Bug #1921: Footstep frequency and velocity do not immediately update when speed attribute changes
Bug #2001: OpenMW crashes on start with OpenGL 1.4 drivers
Bug #2014: Antialiasing setting does nothing on Linux
Bug #2037: Some enemies attack the air when spotting the player
Bug #2052: NIF rotation matrices including scales are not supported
Bug #2062: Crank in Old Mournhold: Forgotten Sewer turns about the wrong axis
Bug #2111: Raindrops in front of fire look wrong
Bug #2140: [OpenGL] Water effects, flames and parts of creatures solid black when observed through brazier flame
Bug #2147: Trueflame and Hopesfire flame effects not properly aligned with blade
Bug #2148: Verminous fabricants have little coloured box beneath their feet
Bug #2149: Sparks in Clockwork City should bounce off the floor
Bug #2151: Clockwork City dicer trap doesn't activate when you're too close
Bug #2186: Mini map contains scrambled pixels that cause the mini map to flicker
Bug #2187: NIF file with more than 255 NiBillboardNodes does not load
Bug #2191: Editor: Crash when trying to view cell in render view in OpenCS
Bug #2270: Objects flicker transparently
Bug #2280: Latest 32bit windows build of openmw runns out of vram
Bug #2281: NPCs don't scream when they die
Bug #2286: Jumping animation restarts when equipping mid-air
Bug #2287: Weapon idle animation stops when turning
Bug #2355: Light spell doesn't work in 1st person view
Bug #2362: Lantern glas opaque to flame effect from certain viewing angles
Bug #2364: Light spells are not as bright as in Morrowind
Bug #2383: Remove the alpha testing override list
Bug #2436: Crash on entering cell "Tower of Tel Fyr, Hall of Fyr"
Bug #2457: Player followers should not report crimes
Bug #2458: crash in some fighting situations
Bug #2464: Hiding an emitter node should make that emitter stop firing particles
Bug #2466: Can't load a save created with OpenMW-0.35.0-win64
Bug #2468: music from title screen continues after loading savegame
Bug #2494: Map not consistent between saves
Bug #2504: Dialog scroll should always start at the top
Bug #2506: Editor: Undo/Redo shortcuts do not work in script editor
Bug #2513: Mannequins in mods appear as dead bodies
Bug #2524: Editor: TopicInfo "custom" condition section is missing
Bug #2540: Editor: search and verification result table can not be sorted by clicking on the column names
Bug #2543: Editor: there is a problem with spell effects
Bug #2544: Editor fails to save NPC information correctly.
Bug #2545: Editor: delete record in Objects (referenceables) table messes up data
Bug #2546: Editor: race base attributes and skill boni are not displayed, thus not editable
Bug #2547: Editor: some NPC data is not displayed, thus not editable
Bug #2551: Editor: missing data in cell definition
Bug #2553: Editor: value filter does not work for float values
Bug #2555: Editor: undo leaves the record status as Modified
Bug #2559: Make Detect Enchantment marks appear on top of the player arrow
Bug #2563: position consoling npc doesn't work without cell reload
Bug #2564: Editor: Closing a subview from code does not clean up properly and will lead to crash on opening the next subview
Bug #2568: Editor: Setting default window size is ignored
Bug #2569: Editor: saving from an esp to omwaddon file results in data loss for TopicInfo
Bug #2575: Editor: Deleted record (with Added (ModifiedOnly) status) remains in the Dialog SubView
Bug #2576: Editor: Editor doesn't scroll to a newly opened subview, when ScrollBar Only mode is active
Bug #2578: Editor: changing Level or Reputation of an NPC crashes the editor
Bug #2579: Editor: filters not updated when adding or cloning records
Bug #2580: Editor: omwaddon makes OpenMW crash
Bug #2581: Editor: focus problems in edit subviews single- and multiline input fields
Bug #2582: Editor: object verifier should check for non-existing scripts being referenced
Bug #2583: Editor: applying filter to TopicInfo on mods that have added dialouge makes the Editor crash
Bug #2586: Editor: some dialogue only editable items do not refresh after undo
Bug #2588: Editor: Cancel button exits program
Bug #2589: Editor: Regions table - mapcolor does not change correctly
Bug #2591: Placeatme - spurious 5th parameter raises error
Bug #2593: COC command prints multiple times when GUI is hidden
Bug #2598: Editor: scene view of instances has to be zoomed out to displaying something - center camera instance please
Bug #2607: water behind an invisible NPC becomes invisible as well
Bug #2611: Editor: Sort problem in Objects table when few nested rows are added
Bug #2621: crash when a creature has no model
Bug #2624: Editor: missing columns in tables
Bug #2627: Character sheet doesn't properly update when backing out of CharGen
Bug #2642: Editor: endif without if - is not reported as error when "verify" was executed
Bug #2644: Editor: rebuild the list of available content files when opening the open/new dialogues
Bug #2656: OpenMW & OpenMW-CS: setting "Flies" flag for ghosts has no effect
Bug #2659: OpenMW & OpenMW-CS: savegame load fail due to script attached to NPCs
Bug #2668: Editor: reputation value in the input field is not stored
Bug #2696: Horkers use land idle animations under water
Bug #2705: Editor: Sort by Record Type (Objects table) is incorrect
Bug #2711: Map notes on an exterior cell that shows up with a map marker on the world map do not show up in the tooltip for that cell's marker on the world map
Bug #2714: Editor: Can't reorder rows with the same topic in different letter case
Bug #2720: Head tracking for creatures not implemented
Bug #2722: Alchemy should only include effects shared by at least 2 ingredients
Bug #2723: "ori" console command is not working
Bug #2726: Ashlanders in front of Ghostgate start wandering around
Bug #2727: ESM writer does not handle encoding when saving the TES3 header
Bug #2728: Editor: Incorrect position of an added row in Info tables
Bug #2731: Editor: Deleting a record triggers a Qt warning
Bug #2733: Editor: Undo doesn't restore the Modified status of a record when a nested data is changed
Bug #2734: Editor: The Search doesn't work
Bug #2738: Additive moon blending
Bug #2746: NIF node names should be case insensitive
Bug #2752: Fog depth/density not handled correctly
Bug #2753: Editor: line edit in dialogue subview tables shows after a single click
Bug #2755: Combat AI changes target too frequently
Bug #2761: Can't attack during block animations
Bug #2764: Player doesn't raise arm in 3rd person for weathertype 9
Bug #2768: Current screen resolution not selected in options when starting OpenMW
Bug #2773: Editor: Deleted scripts are editable
Bug #2776: ordinators still think I'm wearing their helm even though Khajiit and argonians can't
Bug #2779: Slider bars continue to move if you don't release mouse button
Bug #2781: sleep interruption is a little off (is this an added feature?)
Bug #2782: erroneously able to ready weapon/magic (+sheathe weapon/magic) while paralyzed
Bug #2785: Editor: Incorrect GMSTs for newly created omwgame files
Bug #2786: Kwama Queen head is inverted under OpenMW
Bug #2788: additem and removeitem incorrect gold behavior
Bug #2790: --start doesn't trace down
Bug #2791: Editor: Listed attributes and skill should not be based on number of NPC objects.
Bug #2792: glitched merchantile/infinite free items
Bug #2794: Need to ignore quotes in names of script function
Bug #2797: Editor: Crash when removing the first row in a nested table
Bug #2800: Show an error message when S3TC support is missing
Bug #2811: Targetted Open spell effect persists.
Bug #2819: Editor: bodypart's race filter not displayed correctly
Bug #2820: Editor: table sorting is inverted
Bug #2821: Editor: undo/redo command labels are incorrect
Bug #2826: locking beds that have been locked via magic psuedo-freezes the game
Bug #2830: Script compiler does not accept IDs as instruction/functions arguments if the ID is also a keyword
Bug #2832: Cell names are not localized on the world map
Bug #2833: [cosmetic] Players swimming at water's surface are slightly too low.
Bug #2840: Save/load menu is not entirely localized
Bug #2853: [exploit/bug] disintegrate weapon incorrectly applying to lockpicks, probes. creates unbreakable lockpicks
Bug #2855: Mouse wheel in journal is not disabled by "Options" panel.
Bug #2856: Heart of Lorkhan doesn't visually respond to attacks
Bug #2863: Inventory highlights wrong category after load
Bug #2864: Illuminated Order 1.0c Bug The teleport amulet is not placed in the PC inventory.
Bug #2866: Editor: use checkbox instead of combobox for boolean values
Bug #2875: special cases of fSleepRandMod not behaving properly.
Bug #2878: Editor: Verify reports "creature has non-positive level" but there is no level setting
Bug #2879: Editor: entered value of field "Buys *" is not saved for a creature
Bug #2880: OpenMW & OpenMW-CS: having a scale value of 0.000 makes the game laggy
Bug #2882: Freeze when entering cell "Guild of Fighters (Ald'ruhn)" after dropping some items inside
Bug #2883: game not playable if mod providing a spell is removed but the list of known spells still contains it
Bug #2884: NPC chats about wrong player race
Bug #2886: Adding custom races breaks existing numbering of PcRace
Bug #2888: Editor: value entered in "AI Wander Idle" is not kept
Bug #2889: Editor: creatures made with the CS (not cloned) are always dead
Bug #2890: Editor: can't make NPC say a specific "Hello" voice-dialouge
Bug #2893: Editor: making a creature use textual dialogue doesn't work.
Bug #2901: Editor: gold for trading can not be set for creatures
Bug #2907: looking from uderwater part of the PC that is below the surface looks like it would be above the water
Bug #2914: Magicka not recalculated on character generation
Bug #2915: When paralyzed, you can still enter and exit sneak
Bug #2917: chameleon does not work for creatures
Bug #2927: Editor: in the automatic script checker local variable caches are not invalidated/updated on modifications of other scripts
Bug #2930: Editor: AIWander Idle can not be set for a creature
Bug #2932: Editor: you can add rows to "Creature Attack" but you can not enter values
Bug #2938: Editor: Can't add a start script.
Bug #2944: Spell chance for power to show as 0 on hud when used
Bug #2953: Editor: rightclick in an empty place in the menu bar shows an unnamed checkbox
Bug #2956: Editor: freezes while editing Filter
Bug #2959: space character in field enchantment (of an amulet) prevents rendering of surroundings
Bug #2962: OpenMW: Assertion `it != invStore.end()' failed
Bug #2964: Recursive script execution can corrupt script runtime data
Bug #2973: Editor: placing a chest in the game world and activating it heavily blurrs the character portrait
Bug #2978: Editor: Cannot edit alchemy ingredient properties
Bug #2980: Editor: Attribute and Skill can be selected for spells that do not require these parameters, leading to non-functional spells
Bug #2990: Compiling a script with warning mode 2 and enabled error downgrading leads to infinite recursion
Bug #2992: [Mod: Great House Dagoth] Killing Dagoth Gares freezes the game
Bug #3007: PlaceItem takes radians instead of degrees + angle reliability
Feature #706: Editor: Script Editor enhancements
Feature #872: Editor: Colour values in tables
Feature #880: Editor: ID auto-complete
Feature #928: Editor: Partial sorting in info tables
Feature #942: Editor: Dialogue for editing/viewing content file meta information
Feature #1057: NiStencilProperty
Feature #1278: Editor: Mouse picking in worldspace widget
Feature #1280: Editor: Cell border arrows
Feature #1401: Editor: Cloning enhancements
Feature #1463: Editor: Fine grained configuration of extended revert/delete commands
Feature #1591: Editor: Make fields in creation bar drop targets where applicable
Feature #1998: Editor: Magic effect record verifier
Feature #1999: Editor Sound Gen record verifier
Feature #2000: Editor: Pathgrid record verifier
Feature #2528: Game Time Tracker
Feature #2534: Editor: global search does not auomatically focus the search input field
Feature #2535: OpenMW: allow comments in openmw.cfg
Feature #2541: Editor: provide a go to the very bottom button for TopicInfo and JournalInfo
Feature #2549: Editor: add a horizontal slider to scroll between opened tables
Feature #2558: Editor: provide a shortcut for closing the subview that has the focus
Feature #2565: Editor: add context menu for dialogue sub view fields with an item matching "Edit 'x'" from the table subview context menu
Feature #2585: Editor: Ignore mouse wheel input for numeric values unless the respective widget has the focus
Feature #2620: Editor: make the verify-view refreshable
Feature #2622: Editor: Make double click behaviour in result tables configurable (see ID tables)
Feature #2717: Editor: Add severity column to report tables
Feature #2729: Editor: Various dialogue button bar improvements
Feature #2739: Profiling overlay
Feature #2740: Resource manager optimizations
Feature #2741: Make NIF files into proper resources
Feature #2742: Use the skinning data in NIF files as-is
Feature #2743: Small feature culling
Feature #2744: Configurable near clip distance
Feature #2745: GUI scaling option
Feature #2747: Support anonymous textures
Feature #2749: Loading screen optimizations
Feature #2751: Character preview optimization
Feature #2804: Editor: Merge Tool
Feature #2818: Editor: allow copying a record ID to the clipboard
Feature #2946: Editor: add script line number in results of search
Feature #2963: Editor: Mouse button bindings in 3D scene
Feature #2983: Sun Glare fader
Feature #2999: Scaling of journal and books
Task #2665: Support building with Qt5
Task #2725: Editor: Remove Display_YesNo
Task #2730: Replace hardcoded column numbers in SimpleDialogueSubView/DialogueSubView
Task #2750: Bullet shape instancing optimization
Task #2793: Replace grid size setting with half grid size setting
Task #3003: Support FFMPEG 2.9 (Debian request)
0.36.1
------
Bug #2590: Start scripts not added correctly
0.36.0
------

@ -10,11 +10,13 @@ fi
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
echo "yes" | sudo apt-add-repository ppa:openmw/openmw
echo "yes" | sudo apt-add-repository ppa:boost-latest/ppa
sudo apt-get update -qq
sudo apt-get install -qq libgtest-dev google-mock
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libboost-wave-dev
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
sudo apt-get install -qq libboost-filesystem1.55-dev libboost-program-options1.55-dev libboost-system1.55-dev libboost-thread1.55-dev
sudo apt-get install -qq ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
sudo apt-get install -qq cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi
sudo mkdir /usr/src/gtest/build
cd /usr/src/gtest/build

@ -0,0 +1,9 @@
#!/bin/bash
OUTPUT=$(grep -nRP '\t' --include=\*.{cpp,hpp,c,h} --exclude=ui_\* apps components)
if [[ $OUTPUT ]] ; then
echo "Error: Tab characters found!"
echo $OUTPUT
exit 1
fi

@ -3,8 +3,9 @@ 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."
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel."
FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS None Debug Release RelWithDebInfo MinSizeRel)
ENDIF()
if (APPLE)
@ -19,7 +20,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 36)
set(OPENMW_VERSION_MINOR 37)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
@ -45,24 +46,20 @@ endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
# Macros
include(OpenMWMacros)
if (ANDROID)
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
endif (ANDROID)
# doxygen main page
configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_BINARY_DIR}/docs/mainpage.hpp")
option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE)
option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE)
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(OSG_STATIC "Link static build of OpenSceneGraph into the binaries" FALSE)
option(QT_STATIC "Link static build of QT into the binaries" FALSE)
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
# Apps and tools
option(BUILD_OPENMW "build OpenMW" ON)
option(BUILD_BSATOOL "build BSA extractor" ON)
option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON)
@ -78,13 +75,33 @@ option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with
# OS X deployment
option(OPENMW_OSX_DEPLOYMENT OFF)
# Location of morrowind data files
if (MSVC)
option(OPENMW_MP_BUILD "Build OpenMW with /MP flag" OFF)
option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF)
endif()
# Set up common paths
if (APPLE)
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files")
elseif(UNIX)
set(MORROWIND_DATA_FILES "${CMAKE_INSTALL_PREFIX}/share/games/openmw/data/" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "${CMAKE_INSTALL_PREFIX}/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
# Paths
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
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")
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr")
SET(GLOBAL_CONFIG_PATH "/etc/" CACHE PATH "Set config dir prefix")
ELSE()
SET(GLOBAL_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/etc/" CACHE PATH "Set config dir prefix")
ENDIF()
SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir")
set(MORROWIND_DATA_FILES "${DATADIR}/data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "${DATADIR}/resources" CACHE PATH "location of OpenMW resources files")
else()
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
@ -102,24 +119,13 @@ unset(FFMPEG_LIBRARIES CACHE)
find_package(FFmpeg REQUIRED)
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY})
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARIES})
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND )
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND OR NOT SWRESAMPLE_FOUND)
message(FATAL_ERROR "FFmpeg component required, but not found!")
endif()
set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIRS})
if( SWRESAMPLE_FOUND )
add_definitions(-DHAVE_LIBSWRESAMPLE)
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWRESAMPLE_LIBRARIES})
else()
if( AVRESAMPLE_FOUND )
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${AVRESAMPLE_LIBRARIES})
else()
message(FATAL_ERROR "Install either libswresample (FFmpeg) or libavresample (Libav).")
endif()
endif()
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES})
# Required for building the FFmpeg headers
add_definitions(-D__STDC_CONSTANT_MACROS)
# TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
@ -140,32 +146,49 @@ endif()
if (WIN32)
if(NOT MINGW)
set(Boost_USE_STATIC_LIBS ON)
set(PLATFORM_INCLUDE_DIR "platform")
add_definitions(-DBOOST_ALL_NO_LIB)
endif(NOT MINGW)
# Suppress WinMain(), provided by SDL
add_definitions(-DSDL_MAIN_HANDLED)
else (WIN32)
set(PLATFORM_INCLUDE_DIR "")
endif (WIN32)
if (MSVC10)
set(PLATFORM_INCLUDE_DIR "")
# Get rid of useless crud from windows.h
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
endif()
# Dependencies
if (ANDROID)
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE)
endif (ANDROID)
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
option(OPENGL_ES "enable opengl es support" FALSE )
if (DESIRED_QT_VERSION MATCHES 4)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork)
if (OPENGL_ES)
add_definitions(-DOPENGL_ES)
endif(OPENGL_ES)
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
set(USE_QT TRUE)
endif()
# Dependencies
if (USE_QT)
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
if (DESIRED_QT_VERSION MATCHES 4)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
# Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON)
endif()
endif()
# Fix for not visible pthreads functions for linker with glibc 2.15
@ -188,7 +211,7 @@ if (HAVE_UNORDERED_MAP)
endif ()
set(BOOST_COMPONENTS system filesystem program_options thread wave)
set(BOOST_COMPONENTS system filesystem program_options thread)
if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
endif(WIN32)
@ -197,9 +220,70 @@ IF(BOOST_STATIC)
set(Boost_USE_STATIC_LIBS ON)
endif()
find_package(OGRE REQUIRED)
if (${OGRE_VERSION} VERSION_LESS "1.9")
message(FATAL_ERROR "OpenMW requires Ogre 1.9 or later, please install the latest stable version from http://ogre3d.org")
if (USE_QT)
set (OSG_QT osgQt)
endif()
find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle ${OSG_QT} osgUtil osgFX)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
if(OSG_STATIC)
macro(use_static_osg_plugin_library PLUGIN_NAME)
set(PLUGIN_NAME_DBG ${PLUGIN_NAME}d ${PLUGIN_NAME}D ${PLUGIN_NAME}_d ${PLUGIN_NAME}_D ${PLUGIN_NAME}_debug ${PLUGIN_NAME})
# For now, users wishing to do a static build will need to pass the path to where the plugins reside
# More clever logic would need to deduce the path, probably installed under <OpenSceneGraph>/lib/osgPlugins-<X.X.X>
find_library(${PLUGIN_NAME}_LIBRARY_REL NAMES ${PLUGIN_NAME} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH})
find_library(${PLUGIN_NAME}_LIBRARY_DBG NAMES ${PLUGIN_NAME_DBG} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH})
make_library_set(${PLUGIN_NAME}_LIBRARY)
if("${${PLUGIN_NAME}_LIBRARY}" STREQUAL "")
message(FATAL_ERROR "Unable to find static OpenSceneGraph plugin: ${PLUGIN_NAME}")
endif()
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY})
endmacro()
macro(use_static_osg_plugin_dep DEPENDENCY)
find_package(${DEPENDENCY} REQUIRED)
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${DEPENDENCY}_LIBRARIES})
endmacro()
add_definitions(-DOSG_LIBRARY_STATIC)
set(PLUGIN_LIST
osgdb_png # depends on libpng, zlib
osgdb_tga
osgdb_dds
osgdb_jpeg # depends on libjpeg
)
foreach(PLUGIN ${PLUGIN_LIST})
use_static_osg_plugin_library(${PLUGIN})
endforeach()
# OSG static plugins need to linked against their respective dependencies
set(PLUGIN_DEPS_LIST
PNG # needed by osgdb_png
ZLIB # needed by osgdb_png
JPEG # needed by osgdb_jpeg
)
foreach(DEPENDENCY ${PLUGIN_DEPS_LIST})
use_static_osg_plugin_dep(${DEPENDENCY})
endforeach()
endif()
if(QT_STATIC)
if(WIN32)
if(DESIRED_QT_VERSION MATCHES 4)
# QtCore needs WSAAsyncSelect from Ws2_32.lib
set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib)
message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}")
endif()
endif()
endif()
find_package(MyGUI REQUIRED)
@ -212,88 +296,23 @@ find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED)
find_package(Bullet REQUIRED)
set(OGRE_PLUGIN_INCLUDE_DIRS "")
set(OGRE_STATIC_PLUGINS "")
macro(add_static_ogre_plugin PLUGIN)
if(OGRE_${PLUGIN}_FOUND)
# strip RenderSystem_ or Plugin_ prefix from plugin name
string(REPLACE "RenderSystem_" "" PLUGIN_TEMP ${PLUGIN})
string(REPLACE "Plugin_" "" PLUGIN_NAME ${PLUGIN_TEMP})
add_definitions(-DENABLE_PLUGIN_${PLUGIN_NAME})
list(APPEND OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_${PLUGIN}_INCLUDE_DIRS})
list(APPEND OGRE_STATIC_PLUGINS ${OGRE_${PLUGIN}_LIBRARIES})
endif(OGRE_${PLUGIN}_FOUND)
endmacro(add_static_ogre_plugin)
if(OGRE_STATIC)
# set up OGRE_PLUGIN_INCLUDE_DIRS and OGRE_STATIC_PLUGINS
add_static_ogre_plugin(Plugin_OctreeSceneManager)
add_static_ogre_plugin(Plugin_ParticleFX)
find_package(Cg)
if(Cg_FOUND)
add_static_ogre_plugin(Plugin_CgProgramManager)
list(APPEND OGRE_STATIC_PLUGINS ${Cg_LIBRARIES})
endif(Cg_FOUND)
if (ANDROID)
add_static_ogre_plugin(RenderSystem_GLES2)
else ()
add_static_ogre_plugin(RenderSystem_GL)
endif ()
if(WIN32)
add_static_ogre_plugin(RenderSystem_Direct3D9)
endif(WIN32)
endif(OGRE_STATIC)
include_directories("." ${LIBS_DIR}
include_directories("."
SYSTEM
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_INCLUDE_DIRS} ${OGRE_PLUGIN_INCLUDE_DIRS}
${OGRE_INCLUDE_DIR}/Overlay ${OGRE_Overlay_INCLUDE_DIR}
${SDL2_INCLUDE_DIR}
${Boost_INCLUDE_DIR}
${PLATFORM_INCLUDE_DIR}
${MYGUI_INCLUDE_DIRS}
${MYGUI_PLATFORM_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR}
${BULLET_INCLUDE_DIRS}
)
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MYGUI_LIB_DIR})
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${MYGUI_LIB_DIR})
if(MYGUI_STATIC)
add_definitions(-DMYGUI_STATIC)
endif (MYGUI_STATIC)
if (APPLE)
# List used Ogre plugins
SET(USED_OGRE_PLUGINS ${OGRE_RenderSystem_GL_LIBRARY_REL}
${OGRE_Plugin_ParticleFX_LIBRARY_REL})
# Actually we must use OGRE_Plugin_CgProgramManager_FOUND but it's
# not reliable and equals TRUE even if there's no Ogre Cg plugin
if (Cg_FOUND)
set(USED_OGRE_PLUGINS ${USED_OGRE_PLUGINS}
${OGRE_Plugin_CgProgramManager_LIBRARY_REL})
endif ()
if (${OGRE_PLUGIN_DIR_REL}})
set(OGRE_PLUGINS_REL_FOUND TRUE)
endif ()
if (${OGRE_PLUGIN_DIR_DBG})
set(OGRE_PLUGINS_DBG_FOUND TRUE)
endif ()
if (${OGRE_PLUGINS_REL_FOUND})
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL})
else ()
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG})
endif ()
configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist
configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw-Info.plist.in
"${APP_BUNDLE_DIR}/Contents/Info.plist")
configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns
@ -303,36 +322,17 @@ endif (APPLE)
# Set up DEBUG define
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1)
# Set up Ogre plugin folder & debug suffix
if (APPLE)
# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt)
add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="")
else ()
add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d")
endif()
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 (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}")
endif()
add_subdirectory(files/)
add_subdirectory(files/mygui)
# Specify build paths
if (APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS")
if (OPENMW_OSX_DEPLOYMENT)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif()
else (APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
@ -343,17 +343,14 @@ endif (APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
"${OpenMW_BINARY_DIR}/settings-default.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.ini
"${OpenMW_BINARY_DIR}/opencs.ini")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
"${OpenMW_BINARY_DIR}/openmw-cs.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)
@ -364,6 +361,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt
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/openmw.appdata.xml
"${OpenMW_BINARY_DIR}/openmw.appdata.xml")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
"${OpenMW_BINARY_DIR}/openmw-cs.desktop")
endif()
@ -387,31 +386,23 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
endif(CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${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")
if (OPENMW_LTO_BUILD)
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()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
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${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")
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr")
SET(GLOBAL_CONFIG_PATH "/etc/" CACHE PATH "Set config dir prefix")
ELSE()
SET(GLOBAL_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/etc/" CACHE PATH "Set config dir prefix")
ENDIF()
SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir")
# Linux installation
# Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_OPENMW)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENMW)
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER)
@ -421,6 +412,9 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL)
IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
ENDIF(BUILD_NIFTEST)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
@ -430,23 +424,20 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-cs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
ENDIF(BUILD_NIFTEST)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" )
ENDIF(BUILD_WIZARD)
if(BUILD_MYGUI_PLUGIN)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Plugin_MyGUI_OpenMW_Resources.so" DESTINATION "${LIBDIR}" )
ENDIF(BUILD_MYGUI_PLUGIN)
#if(BUILD_MYGUI_PLUGIN)
# INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Plugin_MyGUI_OpenMW_Resources.so" DESTINATION "${LIBDIR}" )
#ENDIF(BUILD_MYGUI_PLUGIN)
# Install licenses
INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
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" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/appdata" COMPONENT "openmw")
IF(BUILD_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")
@ -454,12 +445,12 @@ IF(NOT WIN32 AND NOT APPLE)
# Install global configuration files
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")
INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS)
# Install resources
@ -477,7 +468,6 @@ if(WIN32)
"${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt"
"${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt"
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
DESTINATION ".")
@ -493,7 +483,7 @@ if(WIN32)
ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".")
@ -503,6 +493,8 @@ if(WIN32)
ENDIF(BUILD_MYGUI_PLUGIN)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
FILE(GLOB plugin_dir "${OpenMW_BINARY_DIR}/Release/osgPlugins-*")
INSTALL(DIRECTORY ${plugin_dir} DESTINATION ".")
SET(CPACK_GENERATOR "NSIS")
SET(CPACK_PACKAGE_NAME "OpenMW")
@ -563,31 +555,22 @@ if(WIN32)
include(CPack)
endif(WIN32)
# Libs
include_directories(libs)
add_subdirectory(libs/openengine)
# Extern
add_subdirectory (extern/shiny)
add_subdirectory (extern/ogre-ffmpeg-videoplayer)
add_subdirectory (extern/osg-ffmpeg-videoplayer)
add_subdirectory (extern/oics)
add_subdirectory (extern/sdl4ogre)
# Components
add_subdirectory (components)
# Plugins
if (BUILD_MYGUI_PLUGIN)
add_subdirectory(plugins/mygui_resource_plugin)
endif()
#Testing
if (BUILD_NIFTEST)
add_subdirectory(components/nif/tests/)
endif(BUILD_NIFTEST)
#if (BUILD_MYGUI_PLUGIN)
# add_subdirectory(plugins/mygui_resource_plugin)
#endif()
# Apps and tools
add_subdirectory( apps/openmw )
if (BUILD_OPENMW)
add_subdirectory( apps/openmw )
endif()
if (BUILD_BSATOOL)
add_subdirectory( apps/bsatool )
@ -617,6 +600,10 @@ if (BUILD_WIZARD)
add_subdirectory(apps/wizard)
endif()
if (BUILD_NIFTEST)
add_subdirectory(apps/niftest)
endif(BUILD_NIFTEST)
# UnitTests
if (BUILD_UNITTESTS)
add_subdirectory( apps/openmw_test_suite )
@ -624,9 +611,9 @@ endif()
if (WIN32)
if (MSVC)
if (MULTITHREADED_BUILD)
if (OPENMW_MP_BUILD)
set( MT_BUILD "/MP")
endif (MULTITHREADED_BUILD)
endif()
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
@ -634,20 +621,22 @@ if (WIN32)
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "$(ProjectDir)$(Configuration)" )
endforeach( OUTPUTCONFIG )
if (USE_DEBUG_CONSOLE)
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
else()
elseif (BUILD_OPENMW)
# Turn off debug console, debug output will be written to visual studio output instead
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
endif()
# Release builds use the debug console
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
if (BUILD_OPENMW)
# Release builds use the debug console
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
endif()
# Play a bit with the warning levels
@ -656,8 +645,8 @@ if (WIN32)
set(WARNINGS_DISABLE
# Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library files
4061 4263 4264 4266 4350 4371 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
# Not going to bother commenting them as they tend to warn on every standard library file
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
# Warnings that are thrown on standard libraries and not OpenMW
4347 # Non-template function with same name and parameter count as template function
@ -669,12 +658,6 @@ if (WIN32)
4987 # nonstandard extension used (triggered by setjmp.h)
4996 # Function was declared deprecated
# cause by ogre extensivly
4193 # #pragma warning(pop) : no matching '#pragma warning(push)'
4251 # class 'XXXX' needs to have dll-interface to be used by clients of class 'YYYY'
4275 # non dll-interface struct 'XXXX' used as base for dll-interface class 'YYYY'
4315 # undocumented, 'this' pointer for member might not be aligned (OgreMemoryStlAllocator.h)
# caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
@ -685,6 +668,7 @@ if (WIN32)
4127 # Conditional expression is constant
4242 # Storing value in a variable of a smaller type, possible loss of data
4244 # Storing value of one type in variable of another (size_t in int, for example)
4245 # Signed/unsigned mismatch
4267 # Conversion from 'size_t' to 'int', possible loss of data
4305 # Truncating value (double to float, for example)
4309 # Variable overflow, trying to store 128 in a signed char for example
@ -700,21 +684,42 @@ if (WIN32)
set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNINGS} ${MT_BUILD}")
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
# oics uses tinyxml, which has an initialized but unused variable
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${WARNINGS} /wd4189 ${MT_BUILD}")
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_BSATOOL)
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
# boost::wave has a few issues with signed / unsigned conversions, so we suppress those here
set(SHINY_WARNINGS "${WARNINGS} /wd4245")
set_target_properties(shiny PROPERTIES COMPILE_FLAGS "${SHINY_WARNINGS} ${MT_BUILD}")
if (BUILD_ESMTOOL)
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
# oics uses tinyxml, which has an initialized but unused variable
set(OICS_WARNINGS "${WARNINGS} /wd4189")
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_WARNINGS} ${MT_BUILD}")
if (BUILD_ESSIMPORTER)
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_LAUNCHER)
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_MWINIIMPORTER)
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
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(openmw-cs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS})
endif (BUILD_OPENCS)
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_OPENMW)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_WIZARD)
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
endif(MSVC)
# TODO: At some point release builds should not use the console but rather write to a log file
@ -724,6 +729,18 @@ endif()
# Apple bundling
if (APPLE)
get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)
get_filename_component(QT_COCOA_PLUGIN_DIR "${QT_COCOA_PLUGIN_PATH}" DIRECTORY)
get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME)
get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME)
configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
if (BUILD_OPENCS)
get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME)
set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app")
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
endif ()
set(INSTALL_SUBDIR OpenMW)
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
@ -731,8 +748,7 @@ if (APPLE)
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
@ -740,27 +756,35 @@ if (APPLE)
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
set(OPENCS_BUNDLE_NAME "OpenMW-CS.app")
set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
set(ABSOLUTE_PLUGINS "")
foreach (PLUGIN ${USED_OGRE_PLUGINS})
get_filename_component(PLUGIN_ABS ${PLUGIN} REALPATH)
set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})
endforeach ()
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
install(CODE "
set(BU_CHMOD_BUNDLE_ITEMS ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
include(BundleUtilities)
cmake_minimum_required(VERSION 3.1)
" COMPONENT Runtime)
set(ABSOLUTE_PLUGINS "")
set(USED_OSG_PLUGINS
osgdb_dds
osgdb_jpeg
osgdb_png
osgdb_tga
)
foreach (PLUGIN_NAME ${USED_OSG_PLUGINS})
set(PLUGIN_ABS "${OSG_PLUGIN_LIB_SEARCH_PATH}/${PLUGIN_NAME}.so")
set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})
endforeach ()
get_filename_component(OSG_PLUGIN_PREFIX_DIR "${OSG_PLUGIN_LIB_SEARCH_PATH}" NAME)
# installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX})
# and returns list of install paths for all installed plugins
function (install_plugins_for_bundle bundle_path plugins_var)
set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/Frameworks")
set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/PlugIns/${OSG_PLUGIN_PREFIX_DIR}")
set(PLUGINS "")
set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${RELATIVE_PLUGIN_INSTALL_BASE}")
@ -769,11 +793,11 @@ if (APPLE)
get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME)
get_filename_component(PLUGIN_RELATIVE_WE ${PLUGIN} NAME_WE)
set(PLUGIN_DYLIB_IN_BUNDLE "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}/${PLUGIN_RELATIVE_WE}")
set(PLUGIN_DYLIB_IN_BUNDLE "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}")
set(PLUGINS ${PLUGINS} "${PLUGIN_DYLIB_IN_BUNDLE}")
install(CODE "
copy_resolved_framework_into_bundle(\"${PLUGIN}/${PLUGIN_RELATIVE_WE}\" \"${PLUGIN_DYLIB_IN_BUNDLE}\")
copy_resolved_item_into_bundle(\"${PLUGIN}\" \"${PLUGIN_DYLIB_IN_BUNDLE}\")
" COMPONENT Runtime)
endforeach ()
@ -783,53 +807,22 @@ if (APPLE)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
#For now, search unresolved dependencies only in default system paths, so if you put unresolveable (i.e. with @executable_path in id name) lib or framework somewhere else, it would fail
set(DIRS "")
# Overriding item resolving during installation, it needed if
# some library already has been "fixed up", i.e. its id name contains @executable_path,
# but library is not embedded in bundle. For example, it's Ogre.framework from Ogre SDK.
# Current implementation of GetPrerequsities/BundleUtilities doesn't handle that case.
#
# Current limitations:
# 1. Handles only frameworks, not simple libs
INSTALL(CODE "
cmake_policy(SET CMP0009 OLD)
set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_SYSTEM_FRAMEWORK_PATH ${CMAKE_SYSTEM_FRAMEWORK_PATH})
set(OPENMW_RESOLVED_ITEMS \"\")
function(gp_resolve_item_override context item exepath dirs resolved_item_var resolved_var)
if(item MATCHES \"@executable_path\" AND NOT \${\${resolved_var}})
if (item MATCHES \"Frameworks\") # if it is a framework
# get last segment of path
get_filename_component(fname \"\${item}\" NAME_WE)
find_library(ri NAMES \${fname} PATHS \${exepath} \${dirs} \${CMAKE_SYSTEM_FRAMEWORK_PATH})
if (ri)
string(REGEX REPLACE \"^.*/Frameworks/.*\\\\.framework\" \"\" item_part \${item})
set(ri \"\${ri}\${item_part}\")
set(\${resolved_item_var} \${ri} PARENT_SCOPE)
set(\${resolved_var} 1 PARENT_SCOPE)
endif()
else()
# code path for standard (non-framework) libs (ogre & qt pugins)
get_filename_component(fname \"\${item}\" NAME_WE)
string(REGEX REPLACE \"^lib\" \"\" fname \${fname})
find_library(ri NAMES \${fname} PATHS \${exepath} \${dirs} /usr/lib /usr/local/lib)
if (ri)
set(\${resolved_item_var} \${ri} PARENT_SCOPE)
set(\${resolved_var} 1 PARENT_SCOPE)
endif ()
endif()
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
install(CODE "
function(gp_item_default_embedded_path_override item default_embedded_path_var)
if (\${item} MATCHES ${OSG_PLUGIN_PREFIX_DIR})
set(path \"@executable_path/../PlugIns/${OSG_PLUGIN_PREFIX_DIR}\")
set(\${default_embedded_path_var} \"\${path}\" PARENT_SCOPE)
endif()
endfunction(gp_resolve_item_override)
endfunction()
fixup_bundle(\"${OPENMW_APP}\" \"${PLUGINS}\" \"${DIRS}\")
fixup_bundle(\"${OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"${DIRS}\")
cmake_policy(SET CMP0009 OLD)
fixup_bundle(\"${INSTALLED_OPENMW_APP}\" \"${PLUGINS}\" \"\")
fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\")
" COMPONENT Runtime)
include(CPack)
include(CPack)
endif (APPLE)
# Doxygen Target -- simply run 'make doc' or 'make doc_pages'

@ -0,0 +1,16 @@
Description
===========
Your pull request description should include (if applicable):
* A link back to the bug report or forum discussion that prompted the change
* Summary of the changes made
* Reasoning / motivation behind the change
* What testing you have carried out to verify the change
Other notes
===========
* Separate your work into multiple pull requests whenever possible. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).

@ -3,10 +3,11 @@ OpenMW
[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](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.
OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
* Version: 0.36.0
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
* Version: 0.37.0
* License: GPL (see docs/license/GPL3.txt for more information)
* Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net
@ -14,6 +15,13 @@ Morrowind by Bethesda Softworks. You need to own and install the original game f
Font Licenses:
* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information)
Current Status
--------------
The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://bugs.openmw.org/versions/21) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces.
Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page.
Getting Started
---------------

@ -1,4 +1,5 @@
#include <iostream>
#include <iomanip>
#include <vector>
#include <exception>
@ -237,12 +238,14 @@ int extract(Bsa::BSAFile& bsa, Arguments& info)
}
// Get a stream for the file to extract
Ogre::DataStreamPtr data = bsa.getFile(archivePath.c_str());
Files::IStreamPtr stream = bsa.getFile(archivePath.c_str());
bfs::ofstream out(target, std::ios::binary);
// Write the file to disk
std::cout << "Extracting " << info.extractfile << " to " << target << std::endl;
out.write(data->getAsString().c_str(), data->size());
out << stream->rdbuf();
out.close();
return 0;
@ -276,12 +279,12 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info)
// Get a stream for the file to extract
// (inefficient because getFile iter on the list again)
Ogre::DataStreamPtr data = bsa.getFile(archivePath);
Files::IStreamPtr data = bsa.getFile(archivePath);
bfs::ofstream out(target, std::ios::binary);
// Write the file to disk
std::cout << "Extracting " << target << std::endl;
out.write(data->getAsString().c_str(), data->size());
out << data->rdbuf();
out.close();
}

@ -4,6 +4,7 @@
#include <list>
#include <map>
#include <set>
#include <fstream>
#include <boost/program_options.hpp>
@ -26,7 +27,8 @@ struct ESMData
std::vector<ESM::Header::MasterData> masters;
std::deque<EsmTool::RecordBase *> mRecords;
std::map<ESM::Cell *, std::deque<ESM::CellRef> > mCellRefs;
// Value: (Reference, Deleted flag)
std::map<ESM::Cell *, std::deque<std::pair<ESM::CellRef, bool> > > mCellRefs;
std::map<int, int> mRecordStats;
static const std::set<int> sLabeledRec;
@ -254,7 +256,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
while(cell.getNextRef(esm, ref, deleted))
{
if (save) {
info.data.mCellRefs[&cell].push_back(ref);
info.data.mCellRefs[&cell].push_back(std::make_pair(ref, deleted));
}
if(quiet) continue;
@ -284,7 +286,7 @@ void printRaw(ESM::ESMReader &esm)
esm.getRecHeader();
while(esm.hasMoreSubs())
{
uint64_t offs = esm.getOffset();
size_t offs = esm.getFileOffset();
esm.getSubName();
esm.skipHSub();
n = esm.retSubName();
@ -351,61 +353,58 @@ int load(Arguments& info)
uint32_t flags;
esm.getRecHeader(flags);
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == 0)
{
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end())
{
std::cout << "Skipping " << n.toString() << " records." << std::endl;
skipped.push_back(n.val);
}
esm.skipRecord();
if (quiet) break;
std::cout << " Skipping\n";
continue;
}
record->setFlags(static_cast<int>(flags));
record->setPrintPlain(info.plain_given);
record->load(esm);
// Is the user interested in this record type?
bool interested = true;
if (!info.types.empty())
{
std::vector<std::string>::iterator match;
match = std::find(info.types.begin(), info.types.end(),
n.toString());
match = std::find(info.types.begin(), info.types.end(), n.toString());
if (match == info.types.end()) interested = false;
}
std::string id = esm.getHNOString("NAME");
if (id.empty())
id = esm.getHNOString("INAM");
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id))
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, record->getId()))
interested = false;
if(!quiet && interested)
std::cout << "\nRecord: " << n.toString()
<< " '" << id << "'\n";
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == 0) {
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end())
{
std::cout << "Skipping " << n.toString() << " records." << std::endl;
skipped.push_back(n.val);
}
{
std::cout << "\nRecord: " << n.toString() << " '" << record->getId() << "'\n";
record->print();
}
esm.skipRecord();
if (quiet) break;
std::cout << " Skipping\n";
} else {
if (record->getType().val == ESM::REC_GMST) {
// preset id for GameSetting record
record->cast<ESM::GameSetting>()->get().mId = id;
}
record->setId(id);
record->setFlags((int) flags);
record->setPrintPlain(info.plain_given);
record->load(esm);
if (!quiet && interested) record->print();
if (record->getType().val == ESM::REC_CELL && loadCells && interested) {
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
}
if (record->getType().val == ESM::REC_CELL && loadCells && interested)
{
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
}
if (save) {
info.data.mRecords.push_back(record);
} else {
delete record;
}
++info.data.mRecordStats[n.val];
if (save)
{
info.data.mRecords.push_back(record);
}
else
{
delete record;
}
++info.data.mRecordStats[n.val];
}
} catch(std::exception &e) {
@ -492,28 +491,19 @@ int clone(Arguments& info)
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
{
EsmTool::RecordBase *record = *it;
name.val = record->getType().val;
esm.startRecord(name.toString(), record->getFlags());
// TODO wrap this with std::set
if (ESMData::sLabeledRec.count(name.val) > 0) {
esm.writeHNCString("NAME", record->getId());
} else {
esm.writeHNOString("NAME", record->getId());
}
record->save(esm);
if (name.val == ESM::REC_CELL) {
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
if (!info.data.mCellRefs[ptr].empty()) {
typedef std::deque<ESM::CellRef> RefList;
typedef std::deque<std::pair<ESM::CellRef, bool> > RefList;
RefList &refs = info.data.mCellRefs[ptr];
for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt)
{
refIt->save(esm);
refIt->first.save(esm, refIt->second);
}
}
}

@ -405,6 +405,7 @@ void Record<ESM::Activator>::print()
std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -419,6 +420,7 @@ void Record<ESM::Potion>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl;
printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -447,6 +449,7 @@ void Record<ESM::Armor>::print()
if (pit->mFemale != "")
std::cout << " Female Name: " << pit->mFemale << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -461,6 +464,7 @@ void Record<ESM::Apparatus>::print()
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -474,6 +478,7 @@ void Record<ESM::BodyPart>::print()
std::cout << " Part: " << meshPartLabel(mData.mData.mPart)
<< " (" << (int)mData.mData.mPart << ")" << std::endl;
std::cout << " Vampire: " << (int)mData.mData.mVampire << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -493,15 +498,16 @@ void Record<ESM::Book>::print()
std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl;
if (mPrintPlain)
{
std::cout << " Text:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
std::cout << " Text:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Text: [skipped]" << std::endl;
std::cout << " Text: [skipped]" << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -513,6 +519,7 @@ void Record<ESM::BirthSign>::print()
std::vector<std::string>::iterator pit;
for (pit = mData.mPowers.mList.begin(); pit != mData.mPowers.mList.end(); ++pit)
std::cout << " Power: " << *pit << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -541,6 +548,7 @@ void Record<ESM::Cell>::print()
std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl;
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
@ -563,6 +571,7 @@ void Record<ESM::Class>::print()
for (int i = 0; i != 5; i++)
std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1])
<< " (" << mData.mData.mSkills[i][1] << ")" << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -589,6 +598,7 @@ void Record<ESM::Clothing>::print()
if (pit->mFemale != "")
std::cout << " Female Name: " << pit->mFemale << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -604,6 +614,7 @@ void Record<ESM::Container>::print()
for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit)
std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount
<< " Item: " << cit->mItem.toString() << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -670,6 +681,7 @@ void Record<ESM::Creature>::print()
std::vector<ESM::AIPackage>::iterator pit;
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
printAIPackage(*pit);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -677,6 +689,7 @@ void Record<ESM::Dialogue>::print()
{
std::cout << " Type: " << dialogTypeLabel(mData.mType)
<< " (" << (int)mData.mType << ")" << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
// Sadly, there are no DialInfos, because the loader dumps as it
// loads, rather than loading and then dumping. :-( Anyone mind if
// I change this?
@ -693,6 +706,7 @@ void Record<ESM::Door>::print()
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " OpenSound: " << mData.mOpenSound << std::endl;
std::cout << " CloseSound: " << mData.mCloseSound << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -704,6 +718,7 @@ void Record<ESM::Enchantment>::print()
std::cout << " Charge: " << mData.mData.mCharge << std::endl;
std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl;
printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -737,12 +752,14 @@ void Record<ESM::Faction>::print()
std::map<std::string, int>::iterator rit;
for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); ++rit)
std::cout << " Reaction: " << rit->second << " = " << rit->first << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
void Record<ESM::Global>::print()
{
std::cout << " " << mData.mValue << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -799,16 +816,17 @@ void Record<ESM::DialInfo>::print()
{
if (mPrintPlain)
{
std::cout << " Result Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mResultScript << std::endl;
std::cout << "END----------------------------------------" << std::endl;
std::cout << " Result Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mResultScript << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Result Script: [skipped]" << std::endl;
std::cout << " Result Script: [skipped]" << std::endl;
}
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -832,6 +850,7 @@ void Record<ESM::Ingredient>::print()
std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i])
<< " (" << mData.mData.mAttributes[i] << ")" << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -841,19 +860,15 @@ void Record<ESM::Land>::print()
std::cout << " Flags: " << landFlags(mData.mFlags) << 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 != 0);
if (mData.mDataTypes) mData.loadData(mData.mDataTypes);
if (mData.mDataLoaded)
if (const ESM::Land::LandData *data = mData.getLandData (mData.mDataTypes))
{
std::cout << " Height Offset: " << mData.mLandData->mHeightOffset << std::endl;
std::cout << " Height Offset: " << data->mHeightOffset << std::endl;
// Lots of missing members.
std::cout << " Unknown1: " << mData.mLandData->mUnk1 << std::endl;
std::cout << " Unknown2: " << mData.mLandData->mUnk2 << std::endl;
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
std::cout << " Unknown2: " << data->mUnk2 << std::endl;
}
if (!wasLoaded) mData.unloadData();
mData.unloadData();
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -866,6 +881,7 @@ void Record<ESM::CreatureLevList>::print()
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Creature: Level: " << iit->mLevel
<< " Creature: " << iit->mId << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -878,6 +894,7 @@ void Record<ESM::ItemLevList>::print()
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Inventory: Level: " << iit->mLevel
<< " Item: " << iit->mId << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -898,6 +915,7 @@ void Record<ESM::Light>::print()
std::cout << " Duration: " << mData.mData.mTime << std::endl;
std::cout << " Radius: " << mData.mData.mRadius << std::endl;
std::cout << " Color: " << mData.mData.mColor << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -912,6 +930,7 @@ void Record<ESM::Lockpick>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Uses: " << mData.mData.mUses << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -926,6 +945,7 @@ void Record<ESM::Probe>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Uses: " << mData.mData.mUses << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -940,6 +960,7 @@ void Record<ESM::Repair>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Uses: " << mData.mData.mUses << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -948,6 +969,7 @@ void Record<ESM::LandTexture>::print()
std::cout << " Id: " << mData.mId << std::endl;
std::cout << " Index: " << mData.mIndex << std::endl;
std::cout << " Texture: " << mData.mTexture << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -998,6 +1020,7 @@ void Record<ESM::Miscellaneous>::print()
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Is Key: " << mData.mData.mIsKey << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1083,6 +1106,8 @@ void Record<ESM::NPC>::print()
std::vector<ESM::AIPackage>::iterator pit;
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
printAIPackage(*pit);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1117,6 +1142,8 @@ void Record<ESM::Pathgrid>::print()
std::cout << " BAD POINT IN EDGE!" << std::endl;
i++;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1157,6 +1184,8 @@ void Record<ESM::Race>::print()
std::vector<std::string>::iterator sit;
for (sit = mData.mPowers.mList.begin(); sit != mData.mPowers.mList.end(); ++sit)
std::cout << " Power: " << *sit << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1207,15 +1236,17 @@ void Record<ESM::Script>::print()
if (mPrintPlain)
{
std::cout << " Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mScriptText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
std::cout << " Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mScriptText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Script: [skipped]" << std::endl;
std::cout << " Script: [skipped]" << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1239,6 +1270,7 @@ void Record<ESM::SoundGenerator>::print()
std::cout << " Sound: " << mData.mSound << std::endl;
std::cout << " Type: " << soundTypeLabel(mData.mType)
<< " (" << mData.mType << ")" << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1249,6 +1281,7 @@ void Record<ESM::Sound>::print()
if (mData.mData.mMinRange != 0 && mData.mData.mMaxRange != 0)
std::cout << " Range: " << (int)mData.mData.mMinRange << " - "
<< (int)mData.mData.mMaxRange << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1260,13 +1293,15 @@ void Record<ESM::Spell>::print()
std::cout << " Flags: " << spellFlags(mData.mData.mFlags) << std::endl;
std::cout << " Cost: " << mData.mData.mCost << std::endl;
printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
void Record<ESM::StartScript>::print()
{
std::cout << "Start Script: " << mData.mId << std::endl;
std::cout << "Start Data: " << mData.mData << std::endl;
std::cout << " Start Script: " << mData.mId << std::endl;
std::cout << " Start Data: " << mData.mData << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1307,6 +1342,37 @@ void Record<ESM::Weapon>::print()
if (mData.mData.mThrust[0] != 0 && mData.mData.mThrust[1] != 0)
std::cout << " Thrust: " << (int)mData.mData.mThrust[0] << "-"
<< (int)mData.mData.mThrust[1] << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
std::string Record<ESM::Cell>::getId() const
{
return mData.mName;
}
template<>
std::string Record<ESM::Land>::getId() const
{
return ""; // No ID for Land record
}
template<>
std::string Record<ESM::MagicEffect>::getId() const
{
return ""; // No ID for MagicEffect record
}
template<>
std::string Record<ESM::Pathgrid>::getId() const
{
return ""; // No ID for Pathgrid record
}
template<>
std::string Record<ESM::Skill>::getId() const
{
return ""; // No ID for Skill record
}
} // end namespace

@ -32,13 +32,7 @@ namespace EsmTool
virtual ~RecordBase() {}
const std::string &getId() const {
return mId;
}
void setId(const std::string &id) {
mId = id;
}
virtual std::string getId() const = 0;
uint32_t getFlags() const {
return mFlags;
@ -53,7 +47,7 @@ namespace EsmTool
}
void setPrintPlain(bool plain) {
mPrintPlain = plain;
mPrintPlain = plain;
}
virtual void load(ESM::ESMReader &esm) = 0;
@ -73,22 +67,37 @@ namespace EsmTool
class Record : public RecordBase
{
T mData;
bool mIsDeleted;
public:
Record()
: mIsDeleted(false)
{}
std::string getId() const {
return mData.mId;
}
T &get() {
return mData;
}
void save(ESM::ESMWriter &esm) {
mData.save(esm);
mData.save(esm, mIsDeleted);
}
void load(ESM::ESMReader &esm) {
mData.load(esm);
mData.load(esm, mIsDeleted);
}
void print();
};
template<> std::string Record<ESM::Cell>::getId() const;
template<> std::string Record<ESM::Land>::getId() const;
template<> std::string Record<ESM::MagicEffect>::getId() const;
template<> std::string Record<ESM::Pathgrid>::getId() const;
template<> std::string Record<ESM::Skill>::getId() const;
template<> void Record<ESM::Activator>::print();
template<> void Record<ESM::Potion>::print();

@ -41,9 +41,9 @@ namespace ESSImport
{
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.mSkills[i].mMod = actorData.mSkills[i][1];
npcStats.mSkills[i].mCurrent = actorData.mSkills[i][1];
npcStats.mSkills[i].mBase = actorData.mSkills[i][0];
}
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;

@ -2,8 +2,7 @@
#include <stdexcept>
#include <OgreImage.h>
#include <OgreColourValue.h>
#include <osgDB/WriteFile>
#include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp>
@ -15,12 +14,14 @@
namespace
{
void convertImage(char* data, int size, int width, int height, Ogre::PixelFormat pf, const std::string& out)
void convertImage(char* data, int size, int width, int height, GLenum 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);
osg::ref_ptr<osg::Image> image (new osg::Image);
image->allocateImage(width, height, 1, pf, GL_UNSIGNED_BYTE);
memcpy(image->data(), data, size);
image->flipVertical();
osgDB::writeImageFile(*image, out);
}
@ -71,17 +72,20 @@ namespace ESSImport
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);
mGlobalMapImage = new osg::Image;
mGlobalMapImage->allocateImage(maph.size, maph.size, 1, GL_RGB, GL_UNSIGNED_BYTE);
memcpy(mGlobalMapImage->data(), &data[0], data.size());
// to match openmw size
mGlobalMapImage.resize(maph.size*2, maph.size*2, Ogre::Image::FILTER_BILINEAR);
// FIXME: filtering?
mGlobalMapImage->scaleImage(maph.size*2, maph.size*2, 1, GL_UNSIGNED_BYTE);
}
void ConvertFMAP::write(ESM::ESMWriter &esm)
{
int numcells = mGlobalMapImage.getWidth() / 18; // NB truncating, doesn't divide perfectly
int numcells = mGlobalMapImage->s() / 18; // NB truncating, doesn't divide perfectly
// with the 512x512 map the game has by default
int cellSize = mGlobalMapImage.getWidth()/numcells;
int cellSize = mGlobalMapImage->s()/numcells;
// Note the upper left corner of the (0,0) cell should be at (width/2, height/2)
@ -90,12 +94,14 @@ namespace ESSImport
mContext->mGlobalMapState.mBounds.mMinY = -(numcells-1)/2;
mContext->mGlobalMapState.mBounds.mMaxY = numcells/2;
Ogre::Image image2;
std::vector<Ogre::uint8> data;
osg::ref_ptr<osg::Image> image2 (new osg::Image);
int width = cellSize*numcells;
int height = cellSize*numcells;
std::vector<unsigned char> data;
data.resize(width*height*4, 0);
image2.loadDynamicImage(&data[0], width, height, Ogre::PF_BYTE_RGBA);
image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
memcpy(image2->data(), &data[0], data.size());
for (std::set<std::pair<int, int> >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it)
{
@ -108,8 +114,8 @@ namespace ESSImport
continue;
}
int imageLeftSrc = mGlobalMapImage.getWidth()/2;
int imageTopSrc = mGlobalMapImage.getHeight()/2;
int imageLeftSrc = mGlobalMapImage->s()/2;
int imageTopSrc = mGlobalMapImage->t()/2;
imageLeftSrc += it->first * cellSize;
imageTopSrc -= it->second * cellSize;
int imageLeftDst = width/2;
@ -118,13 +124,31 @@ namespace ESSImport
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);
{
unsigned int col = *(unsigned int*)mGlobalMapImage->data(imageLeftSrc+x, imageTopSrc+y, 0);
*(unsigned int*)image2->data(imageLeftDst+x, imageTopDst+y, 0) = col;
}
}
Ogre::DataStreamPtr encoded = image2.encode("png");
mContext->mGlobalMapState.mImageData.resize(encoded->size());
encoded->read(&mContext->mGlobalMapState.mImageData[0], encoded->size());
std::stringstream ostream;
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png");
if (!readerwriter)
{
std::cerr << "can't write global map image, no png readerwriter found" << std::endl;
return;
}
image2->flipVertical();
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image2, ostream);
if (!result.success())
{
std::cerr << "can't write global map image: " << result.message() << " code " << result.status() << std::endl;
return;
}
std::string outData = ostream.str();
mContext->mGlobalMapState.mImageData = std::vector<char>(outData.begin(), outData.end());
esm.startRecord(ESM::REC_GMAP);
mContext->mGlobalMapState.save(esm);
@ -134,9 +158,9 @@ namespace ESSImport
void ConvertCell::read(ESM::ESMReader &esm)
{
ESM::Cell cell;
std::string id = esm.getHNString("NAME");
cell.mName = id;
cell.load(esm, false);
bool isDeleted = false;
cell.load(esm, isDeleted, false);
// I wonder what 0x40 does?
if (cell.isExterior() && cell.mData.mFlags & 0x20)
@ -145,7 +169,7 @@ namespace ESSImport
}
// note if the player is in a nameless exterior cell, we will assign the cellId later based on player position
if (id == mContext->mPlayerCellName)
if (cell.mName == mContext->mPlayerCellName)
{
mContext->mPlayer.mCellId = cell.getCellId();
}
@ -194,7 +218,7 @@ namespace ESSImport
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());
convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, GL_RGBA, filename.str());
}
}
@ -253,7 +277,7 @@ namespace ESSImport
if (cell.isExterior())
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
else
mIntCells[id] = newcell;
mIntCells[cell.mName] = newcell;
}
void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm)

@ -1,7 +1,10 @@
#ifndef OPENMW_ESSIMPORT_CONVERTER_H
#define OPENMW_ESSIMPORT_CONVERTER_H
#include <OgreImage.h>
#include <limits>
#include <osg/Image>
#include <osg/ref_ptr>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
@ -51,6 +54,8 @@ public:
void setContext(Context& context) { mContext = &context; }
/// @note The load method of ESM records accept the deleted flag as a parameter.
/// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored.
virtual void read(ESM::ESMReader& esm)
{
}
@ -75,10 +80,11 @@ public:
virtual void read(ESM::ESMReader& esm)
{
std::string id = esm.getHNString("NAME");
T record;
record.load(esm);
mRecords[id] = record;
bool isDeleted = false;
record.load(esm, isDeleted);
mRecords[record.mId] = record;
}
virtual void write(ESM::ESMWriter& esm)
@ -86,7 +92,6 @@ public:
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);
}
@ -102,14 +107,15 @@ public:
virtual void read(ESM::ESMReader &esm)
{
ESM::NPC npc;
std::string id = esm.getHNString("NAME");
npc.load(esm);
if (id != "player")
bool isDeleted = false;
npc.load(esm, isDeleted);
if (npc.mId != "player")
{
// Handles changes to the NPC struct, but since there is no index here
// it will apply to ALL instances of the class. seems to be the reason for the
// "feature" in MW where changing AI settings of one guard will change it for all guards of that refID.
mContext->mNpcs[Misc::StringUtils::lowerCase(id)] = npc;
mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc;
}
else
{
@ -139,9 +145,10 @@ public:
{
// See comment in ConvertNPC
ESM::Creature creature;
std::string id = esm.getHNString("NAME");
creature.load(esm);
mContext->mCreatures[Misc::StringUtils::lowerCase(id)] = creature;
bool isDeleted = false;
creature.load(esm, isDeleted);
mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature;
}
};
@ -154,18 +161,19 @@ 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"))
bool isDeleted = false;
global.load(esm, isDeleted);
if (Misc::StringUtils::ciEqual(global.mId, "gamehour"))
mContext->mHour = global.mValue.getFloat();
if (Misc::StringUtils::ciEqual(id, "day"))
if (Misc::StringUtils::ciEqual(global.mId, "day"))
mContext->mDay = global.mValue.getInteger();
if (Misc::StringUtils::ciEqual(id, "month"))
if (Misc::StringUtils::ciEqual(global.mId, "month"))
mContext->mMonth = global.mValue.getInteger();
if (Misc::StringUtils::ciEqual(id, "year"))
if (Misc::StringUtils::ciEqual(global.mId, "year"))
mContext->mYear = global.mValue.getInteger();
mRecords[id] = global;
mRecords[global.mId] = global;
}
};
@ -174,14 +182,14 @@ 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);
bool isDeleted = false;
if (id == "NEWCLASSID_CHARGEN")
class_.load(esm, isDeleted);
if (class_.mId == "NEWCLASSID_CHARGEN")
mContext->mCustomPlayerClassName = class_.mName;
mRecords[id] = class_;
mRecords[class_.mId] = class_;
}
};
@ -190,13 +198,14 @@ 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);
bool isDeleted = false;
book.load(esm, isDeleted);
if (book.mData.mSkillID == -1)
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id));
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
mRecords[id] = book;
mRecords[book.mId] = book;
}
};
@ -311,7 +320,7 @@ public:
virtual void write(ESM::ESMWriter &esm);
private:
Ogre::Image mGlobalMapImage;
osg::ref_ptr<osg::Image> mGlobalMapImage;
};
class ConvertCell : public Converter
@ -368,11 +377,12 @@ class ConvertFACT : public Converter
public:
virtual void read(ESM::ESMReader& esm)
{
std::string id = esm.getHNString("NAME");
ESM::Faction faction;
faction.load(esm);
bool isDeleted = false;
faction.load(esm, isDeleted);
std::string id = Misc::StringUtils::lowerCase(faction.mId);
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);
@ -388,7 +398,7 @@ public:
virtual void read(ESM::ESMReader &esm)
{
std::string itemid = esm.getHNString("NAME");
Misc::StringUtils::toLower(itemid);
Misc::StringUtils::lowerCaseInPlace(itemid);
while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
{
@ -504,60 +514,40 @@ 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;
}
int validateWeatherID(int weatherID)
{
if(weatherID >= -1 && weatherID < 10)
{
return weatherID;
}
else
{
std::stringstream error;
error << "Invalid weather ID:" << weatherID << std::endl;
throw std::runtime_error(error.str());
}
}
virtual void write(ESM::ESMWriter &esm)
{
if (!mHasGame)
return;
esm.startRecord(ESM::REC_WTHR);
ESM::WeatherState weather;
weather.mCurrentWeather = toString(mGame.mGMDT.mCurrentWeather);
weather.mNextWeather = toString(mGame.mGMDT.mNextWeather);
weather.mRemainingTransitionTime = mGame.mGMDT.mWeatherTransition/100.f*(0.015f*24*3600);
weather.mHour = mContext->mHour;
weather.mWindSpeed = 0.f;
weather.mTimePassed = 0.0;
weather.mFirstUpdate = false;
weather.mTimePassed = 0.0f;
weather.mFastForward = false;
weather.mWeatherUpdateTime = mGame.mGMDT.mTimeOfNextTransition - mContext->mHour;
weather.mTransitionFactor = 1 - (mGame.mGMDT.mWeatherTransition / 100.0f);
weather.mCurrentWeather = validateWeatherID(mGame.mGMDT.mCurrentWeather);
weather.mNextWeather = validateWeatherID(mGame.mGMDT.mNextWeather);
weather.mQueuedWeather = -1;
// TODO: Determine how ModRegion modifiers are saved in Morrowind.
weather.save(esm);
esm.endRecord(ESM::REC_WTHR);
}

@ -1,5 +1,7 @@
#include "convertplayer.hpp"
#include <components/misc/stringops.hpp>
namespace ESSImport
{
@ -18,7 +20,7 @@ namespace ESSImport
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.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon)

@ -10,7 +10,21 @@ namespace ESSImport
void ActorData::load(ESM::ESMReader &esm)
{
if (esm.isNextSub("ACTN"))
{
/*
Activation flags:
ActivationFlag_UseEnabled = 1
ActivationFlag_OnActivate = 2
ActivationFlag_OnDeath = 10h
ActivationFlag_OnKnockout = 20h
ActivationFlag_OnMurder = 40h
ActivationFlag_DoorOpening = 100h
ActivationFlag_DoorClosing = 200h
ActivationFlag_DoorJammedOpening = 400h
ActivationFlag_DoorJammedClosing = 800h
*/
esm.skipHSub();
}
if (esm.isNextSub("STPR"))
esm.skipHSub();
@ -18,7 +32,8 @@ namespace ESSImport
if (esm.isNextSub("MNAM"))
esm.skipHSub();
ESM::CellRef::loadData(esm);
bool isDeleted = false;
ESM::CellRef::loadData(esm, isDeleted);
mHasACDT = false;
if (esm.isNextSub("ACDT"))

@ -1,7 +1,11 @@
#include "importer.hpp"
#include <iomanip>
#include <boost/shared_ptr.hpp>
#include <OgreRoot.h>
#include <osgDB/ReadFile>
#include <osg/ImageUtils>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
@ -32,13 +36,48 @@ 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());
if (fileHeader.mSCRS.size() != 128*128*4)
{
std::cerr << "unexpected screenshot size " << std::endl;
return;
}
osg::ref_ptr<osg::Image> image (new osg::Image);
image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE);
// need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise
std::vector<unsigned char>::const_iterator it = fileHeader.mSCRS.begin();
for (int y=0; y<128; ++y)
{
for (int x=0; x<128; ++x)
{
*(image->data(x,y)+2) = *it++;
*(image->data(x,y)+1) = *it++;
*image->data(x,y) = *it++;
it++; // skip alpha
}
}
image->flipVertical();
std::stringstream ostream;
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg");
if (!readerwriter)
{
std::cerr << "can't write screenshot: no jpg readerwriter found" << std::endl;
return;
}
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image, ostream);
if (!result.success())
{
std::cerr << "can't write screenshot: " << result.message() << " code " << result.status() << std::endl;
return;
}
std::string data = ostream.str();
out.mScreenshot = std::vector<char>(data.begin(), data.end());
}
}
@ -127,7 +166,9 @@ namespace ESSImport
if (i >= file2.mRecords.size())
{
std::ios::fmtflags f(std::cout.flags());
std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl;
std::cout.flags(f);
return;
}
@ -135,7 +176,9 @@ namespace ESSImport
if (rec.mName != rec2.mName)
{
std::ios::fmtflags f(std::cout.flags());
std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl;
std::cout.flags(f);
return; // TODO: try to recover
}
@ -146,7 +189,9 @@ namespace ESSImport
if (j >= rec2.mSubrecords.size())
{
std::ios::fmtflags f(std::cout.flags());
std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl;
std::cout.flags(f);
return;
}
@ -154,8 +199,10 @@ namespace ESSImport
if (sub.mName != sub2.mName)
{
std::ios::fmtflags f(std::cout.flags());
std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset
<< " (2) 0x" << sub2.mFileOffset << std::endl;
std::cout.flags(f);
break; // TODO: try to recover
}
@ -164,6 +211,8 @@ namespace ESSImport
if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end())
continue;
std::ios::fmtflags f(std::cout.flags());
std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset
<< " (2) 0x" << sub2.mFileOffset << std::endl;
@ -196,6 +245,7 @@ namespace ESSImport
std::cout << "\033[0m";
}
std::cout << std::endl;
std::cout.flags(f);
}
}
}
@ -203,10 +253,6 @@ namespace ESSImport
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);
@ -284,7 +330,11 @@ namespace ESSImport
else
{
if (unknownRecords.insert(n.val).second)
{
std::ios::fmtflags f(std::cerr.flags());
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
std::cerr.flags(f);
}
esm.skipRecord();
}
@ -292,7 +342,7 @@ namespace ESSImport
ESM::ESMWriter writer;
writer.setFormat (ESM::Header::CurrentFormat);
writer.setFormat (ESM::SavedGame::sCurrentFormat);
std::ofstream stream(mOutFile.c_str(), std::ios::binary);
// all unused
@ -344,7 +394,7 @@ namespace ESSImport
}
writer.startRecord(ESM::REC_NPC_);
writer.writeHNString("NAME", "player");
context.mPlayerBase.mId = "player";
context.mPlayerBase.save(writer);
writer.endRecord(ESM::REC_NPC_);

@ -49,6 +49,10 @@ namespace ESSImport
std::map<std::string, ESM::NPC> mNpcs;
Context()
: mDay(0)
, mMonth(0)
, mYear(0)
, mHour(0.f)
{
mPlayer.mAutoMove = 0;
ESM::CellId playerCellId;

@ -32,7 +32,8 @@ namespace ESSImport
item.mSCRI.load(esm);
// for XSOL and XCHG seen so far, but probably others too
item.ESM::CellRef::loadData(esm);
bool isDeleted = false;
item.ESM::CellRef::loadData(esm, isDeleted);
int charge=-1;
esm.getHNOT(charge, "XHLT");

@ -7,8 +7,6 @@ set(LAUNCHER
textslotmsgbox.cpp
settingspage.cpp
settings/graphicssettings.cpp
utils/profilescombobox.cpp
utils/textinputdialog.cpp
utils/lineedit.cpp
@ -24,8 +22,6 @@ set(LAUNCHER_HEADER
textslotmsgbox.hpp
settingspage.hpp
settings/graphicssettings.hpp
utils/profilescombobox.hpp
utils/textinputdialog.hpp
utils/lineedit.hpp
@ -92,8 +88,6 @@ add_executable(openmw-launcher
)
target_link_libraries(openmw-launcher
${OGRE_LIBRARIES}
${OGRE_STATIC_PLUGINS}
${SDL2_LIBRARY_ONLY}
components
)

@ -75,7 +75,7 @@ bool Launcher::DataFilesPage::loadSettings()
QStringList profiles = mLauncherSettings.getContentLists();
QString currentProfile = mLauncherSettings.getCurrentContentListName();
qDebug() << "current profile is: " << currentProfile;
qDebug() << "The current profile is: " << currentProfile;
foreach (const QString &item, profiles)
addProfile (item, false);

@ -12,16 +12,13 @@
#include <SDL_video.h>
#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <boost/math/common_factor.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/contentselector/model/naturalsort.hpp>
#include "settings/graphicssettings.hpp"
#include <components/settings/settings.hpp>
QString getAspect(int x, int y)
{
@ -35,14 +32,10 @@ QString getAspect(int x, int y)
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
}
Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent)
: QWidget(parent)
, mOgre(NULL)
, mSelectedRenderSystem(NULL)
, mOpenGLRenderSystem(NULL)
, mDirect3DRenderSystem(NULL)
, mCfgMgr(cfg)
, mGraphicsSettings(graphicsSetting)
, mEngineSettings(engineSettings)
{
setObjectName ("GraphicsPage");
setupUi(this);
@ -52,79 +45,12 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsS
customWidthSpinBox->setMaximum(res.width());
customHeightSpinBox->setMaximum(res.height());
connect(rendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int)));
connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));
connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int)));
}
bool Launcher::GraphicsPage::setupOgre()
{
try
{
mOgre = mOgreInit.init(mCfgMgr.getLogPath().string() + "/launcherOgre.log");
}
catch(Ogre::Exception &ex)
{
QString ogreError = QString::fromUtf8(ex.getFullDescription().c_str());
QMessageBox msgBox;
msgBox.setWindowTitle("Error creating Ogre::Root");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Failed to create the Ogre::Root object</b><br><br> \
Press \"Show Details...\" for more information.<br>"));
msgBox.setDetailedText(ogreError);
msgBox.exec();
qCritical("Error creating Ogre::Root, the error reported was:\n %s", qPrintable(ogreError));
return false;
}
// Get the available renderers and put them in the combobox
const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers();
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
mSelectedRenderSystem = *r;
rendererComboBox->addItem((*r)->getName().c_str());
}
QString openGLName = QString("OpenGL Rendering Subsystem");
QString direct3DName = QString("Direct3D9 Rendering Subsystem");
// Create separate rendersystems
mOpenGLRenderSystem = mOgre->getRenderSystemByName(openGLName.toStdString());
mDirect3DRenderSystem = mOgre->getRenderSystemByName(direct3DName.toStdString());
if (!mOpenGLRenderSystem && !mDirect3DRenderSystem) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error creating renderer"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \
Please make sure Ogre plugins were installed correctly.<br>"));
msgBox.exec();
return false;
}
// Now fill the GUI elements
int index = rendererComboBox->findText(mGraphicsSettings.value(QString("Video/render system")));
if ( index != -1) {
rendererComboBox->setCurrentIndex(index);
} else {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
rendererComboBox->setCurrentIndex(rendererComboBox->findText(direct3DName));
#else
rendererComboBox->setCurrentIndex(rendererComboBox->findText(openGLName));
#endif
}
antiAliasingComboBox->clear();
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
return true;
}
bool Launcher::GraphicsPage::setupSDL()
{
int displays = SDL_GetNumVideoDisplays();
@ -153,28 +79,27 @@ bool Launcher::GraphicsPage::loadSettings()
{
if (!setupSDL())
return false;
if (!mOgre && !setupOgre())
return false;
if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))
if (mEngineSettings.getBool("vsync", "Video"))
vSyncCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
if (mEngineSettings.getBool("fullscreen", "Video"))
fullScreenCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/window border")) == QLatin1String("true"))
if (mEngineSettings.getBool("window border", "Video"))
windowBorderCheckBox->setCheckState(Qt::Checked);
int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing")));
// aaValue is the actual value (0, 1, 2, 4, 8, 16)
int aaValue = mEngineSettings.getInt("antialiasing", "Video");
// aaIndex is the index into the allowed values in the pull down.
int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue));
if (aaIndex != -1)
antiAliasingComboBox->setCurrentIndex(aaIndex);
QString width = mGraphicsSettings.value(QString("Video/resolution x"));
QString height = mGraphicsSettings.value(QString("Video/resolution y"));
QString resolution = width + QString(" x ") + height;
QString screen = mGraphicsSettings.value(QString("Video/screen"));
screenComboBox->setCurrentIndex(screen.toInt());
int width = mEngineSettings.getInt("resolution x", "Video");
int height = mEngineSettings.getInt("resolution y", "Video");
QString resolution = QString::number(width) + QString(" x ") + QString::number(height);
screenComboBox->setCurrentIndex(mEngineSettings.getInt("screen", "Video"));
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
@ -183,9 +108,8 @@ bool Launcher::GraphicsPage::loadSettings()
resolutionComboBox->setCurrentIndex(resIndex);
} else {
customRadioButton->toggle();
customWidthSpinBox->setValue(width.toInt());
customHeightSpinBox->setValue(height.toInt());
customWidthSpinBox->setValue(width);
customHeightSpinBox->setValue(height);
}
return true;
@ -193,65 +117,46 @@ bool Launcher::GraphicsPage::loadSettings()
void Launcher::GraphicsPage::saveSettings()
{
vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/vsync"), QString("false"));
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());
// Ensure we only set the new settings if they changed. This is to avoid cluttering the
// user settings file (which by definition should only contain settings the user has touched)
bool cVSync = vSyncCheckBox->checkState();
if (cVSync != mEngineSettings.getBool("vsync", "Video"))
mEngineSettings.setBool("vsync", "Video", cVSync);
bool cFullScreen = fullScreenCheckBox->checkState();
if (cFullScreen != mEngineSettings.getBool("fullscreen", "Video"))
mEngineSettings.setBool("fullscreen", "Video", cFullScreen);
bool cWindowBorder = windowBorderCheckBox->checkState();
if (cWindowBorder != mEngineSettings.getBool("window border", "Video"))
mEngineSettings.setBool("window border", "Video", cWindowBorder);
int cAAValue = antiAliasingComboBox->currentText().toInt();
if (cAAValue != mEngineSettings.getInt("antialiasing", "Video"))
mEngineSettings.setInt("antialiasing", "Video", cAAValue);
int cWidth = 0;
int cHeight = 0;
if (standardRadioButton->isChecked()) {
QRegExp resolutionRe(QString("(\\d+) x (\\d+).*"));
if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) {
mGraphicsSettings.setValue(QString("Video/resolution x"), resolutionRe.cap(1));
mGraphicsSettings.setValue(QString("Video/resolution y"), resolutionRe.cap(2));
cWidth = resolutionRe.cap(1).toInt();
cHeight = resolutionRe.cap(2).toInt();
}
} else {
mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value()));
mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->value()));
}
mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex()));
}
QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
{
QStringList result;
uint row = 0;
Ogre::ConfigOptionMap options = renderer->getConfigOptions();
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); ++i, ++row)
{
Ogre::StringVector::iterator opt_it;
uint idx = 0;
for (opt_it = i->second.possibleValues.begin();
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::fromUtf8((*opt_it).c_str()).simplified();
}
}
cWidth = customWidthSpinBox->value();
cHeight = customHeightSpinBox->value();
}
// Sort ascending
qSort(result.begin(), result.end(), naturalSortLessThanCI);
if (cWidth != mEngineSettings.getInt("resolution x", "Video"))
mEngineSettings.setInt("resolution x", "Video", cWidth);
// Replace the zero option with Off
int index = result.indexOf("MSAA 0");
if (cHeight != mEngineSettings.getInt("resolution y", "Video"))
mEngineSettings.setInt("resolution y", "Video", cHeight);
if (index != -1)
result.replace(index, tr("Off"));
return result;
int cScreen = screenComboBox->currentIndex();
if (cScreen != mEngineSettings.getInt("screen", "Video"))
mEngineSettings.setInt("screen", "Video", cScreen);
}
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
@ -316,15 +221,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution()
return max;
}
void Launcher::GraphicsPage::rendererChanged(const QString &renderer)
{
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
antiAliasingComboBox->clear();
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
}
void Launcher::GraphicsPage::screenChanged(int screen)
{
if (screen >= 0) {

@ -3,11 +3,9 @@
#include <QWidget>
#include <components/ogreinit/ogreinit.hpp>
#include "ui_graphicspage.h"
namespace Ogre { class Root; class RenderSystem; }
#include <components/settings/settings.hpp>
namespace Files { struct ConfigurationManager; }
@ -20,13 +18,12 @@ namespace Launcher
Q_OBJECT
public:
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent = 0);
void saveSettings();
bool loadSettings();
public slots:
void rendererChanged(const QString &renderer);
void screenChanged(int screen);
private slots:
@ -34,20 +31,12 @@ namespace Launcher
void slotStandardToggled(bool checked);
private:
OgreInit::OgreInit mOgreInit;
Ogre::Root *mOgre;
Ogre::RenderSystem *mSelectedRenderSystem;
Ogre::RenderSystem *mOpenGLRenderSystem;
Ogre::RenderSystem *mDirect3DRenderSystem;
Files::ConfigurationManager &mCfgMgr;
GraphicsSettings &mGraphicsSettings;
Settings::Manager &mEngineSettings;
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution();
bool setupOgre();
bool setupSDL();
};
}

@ -41,15 +41,6 @@ int main(int argc, char *argv[])
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());

@ -24,6 +24,15 @@
using namespace Process;
void cfgError(const QString& title, const QString& msg) {
QMessageBox msgBox;
msgBox.setWindowTitle(title);
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(msg);
msgBox.exec();
}
Launcher::MainDialog::MainDialog(QWidget *parent)
: QMainWindow(parent), mGameSettings (mCfgMgr)
{
@ -57,26 +66,6 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
// Remove what's this? button
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
// Add version information to bottom of the window
QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH);
versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!revision.isEmpty() && !tag.isEmpty())
{
if (revision == tag) {
versionLabel->setText(tr("OpenMW %1 release").arg(OPENMW_VERSION));
} else {
versionLabel->setText(tr("OpenMW development (%1)").arg(revision.left(10)));
}
// Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %1 %2").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
}
createIcons();
}
@ -125,7 +114,7 @@ void Launcher::MainDialog::createPages()
{
mPlayPage = new PlayPage(this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this);
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage
@ -186,11 +175,34 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure;
}
void Launcher::MainDialog::setVersionLabel()
{
// Add version information to bottom of the window
Version::Version v = Version::getOpenmwVersion(mGameSettings.value("resources").toUtf8().constData());
QString revision(QString::fromUtf8(v.mCommitHash.c_str()));
QString tag(QString::fromUtf8(v.mTagHash.c_str()));
versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!v.mVersion.empty() && (revision.isEmpty() || revision == tag))
versionLabel->setText(tr("OpenMW %1 release").arg(QString::fromUtf8(v.mVersion.c_str())));
else
versionLabel->setText(tr("OpenMW development (%1)").arg(revision.left(10)));
// Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %1 %2").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
}
bool Launcher::MainDialog::setup()
{
if (!setupGameSettings())
return false;
setVersionLabel();
mLauncherSettings.setContentList(mGameSettings);
if (!setupGraphicsSettings())
@ -199,7 +211,7 @@ bool Launcher::MainDialog::setup()
// Now create the pages as they need the settings
createPages();
// Call this so we can exit on Ogre/SDL errors before mainwindow is shown
// Call this so we can exit on SDL errors before mainwindow is shown
if (!mGraphicsPage->loadSettings())
return false;
@ -245,6 +257,8 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
bool Launcher::MainDialog::setupLauncherSettings()
{
mLauncherSettings.clear();
mLauncherSettings.setMultiValueEnabled(true);
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
@ -254,18 +268,14 @@ bool Launcher::MainDialog::setupLauncherSettings()
paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
qDebug() << "Loading config file:" << path.toUtf8().constData();
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
@ -281,6 +291,8 @@ bool Launcher::MainDialog::setupLauncherSettings()
bool Launcher::MainDialog::setupGameSettings()
{
mGameSettings.clear();
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
@ -289,18 +301,14 @@ bool Launcher::MainDialog::setupGameSettings()
QString path = userPath + QLatin1String("openmw.cfg");
QFile file(path);
qDebug() << "Loading config file:" << qPrintable(path);
qDebug() << "Loading config file:" << path.toUtf8().constData();
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
@ -309,26 +317,22 @@ bool Launcher::MainDialog::setupGameSettings()
mGameSettings.readUserFile(stream);
}
// Now the rest
// Now the rest - priority: user > local > global
QStringList paths;
paths.append(userPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg"));
paths.append(globalPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg"));
paths.append(userPath + QString("openmw.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
qDebug() << "Loading config file:" << path.toUtf8().constData();
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
@ -380,53 +384,54 @@ bool Launcher::MainDialog::setupGameSettings()
bool Launcher::MainDialog::setupGraphicsSettings()
{
mGraphicsSettings.setMultiValueEnabled(false);
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
QFile localDefault(QString("settings-default.cfg"));
QFile globalDefault(globalPath + QString("settings-default.cfg"));
if (!localDefault.exists() && !globalDefault.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error reading OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not find settings-default.cfg</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem."));
msgBox.exec();
// This method is almost a copy of OMW::Engine::loadSettings(). They should definitely
// remain consistent, and possibly be merged into a shared component. At the very least
// the filenames should be in the CfgMgr component.
// Ensure to clear previous settings in case we had already loaded settings.
mEngineSettings.clear();
// Create the settings manager and load default settings file
const std::string localDefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
const std::string globalDefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
std::string defaultPath;
// Prefer the settings-default.cfg in the current directory.
if (boost::filesystem::exists(localDefault))
defaultPath = localDefault;
else if (boost::filesystem::exists(globalDefault))
defaultPath = globalDefault;
// Something's very wrong if we can't find the file at all.
else {
cfgError(tr("Error reading OpenMW configuration file"),
tr("<br><b>Could not find settings-default.cfg</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem."));
return false;
}
// Load the default settings, report any parsing errors.
try {
mEngineSettings.loadDefault(defaultPath);
}
catch (std::exception& e) {
std::string msg = std::string("<br><b>Error reading settings-default.cfg</b><br><br>") + e.what();
cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str()));
return false;
}
QStringList paths;
paths.append(globalPath + QString("settings-default.cfg"));
paths.append(QString("settings-default.cfg"));
paths.append(userPath + QString("settings.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
// Load user settings if they exist
const std::string userPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
// User settings are not required to exist, so if they don't we're done.
if (!boost::filesystem::exists(userPath)) return true;
mGraphicsSettings.readFile(stream);
}
file.close();
try {
mEngineSettings.loadUser(userPath);
}
catch (std::exception& e) {
std::string msg = std::string("<br><b>Error reading settings.cfg</b><br><br>") + e.what();
cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str()));
return false;
}
return true;
@ -475,15 +480,11 @@ bool Launcher::MainDialog::writeSettings()
if (!dir.exists()) {
if (!dir.mkpath(userPath)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(userPath));
msgBox.exec();
return false;
cfgError(tr("Error creating OpenMW configuration directory"),
tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(userPath));
return false;
}
}
@ -492,15 +493,11 @@ bool Launcher::MainDialog::writeSettings()
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
cfgError(tr("Error writing OpenMW configuration file"),
tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
@ -508,44 +505,30 @@ bool Launcher::MainDialog::writeSettings()
file.close();
// Graphics settings
file.setFileName(userPath + QString("settings.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
const std::string settingsPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
try {
mEngineSettings.saveUser(settingsPath);
}
catch (std::exception& e) {
std::string msg = "<br><b>Error writing settings.cfg</b><br><br>" +
settingsPath + "<br><br>" + e.what();
cfgError(tr("Error writing user settings file"), tr(msg.c_str()));
return false;
}
QTextStream stream(&file);
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGraphicsSettings.writeFile(stream);
file.close();
// Launcher settings
file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing Launcher configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
cfgError(tr("Error writing Launcher configuration file"),
tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));

@ -13,7 +13,7 @@
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include "settings/graphicssettings.hpp"
#include <components/settings/settings.hpp>
#include "ui_mainwindow.h"
@ -72,6 +72,8 @@ namespace Launcher
bool setupGameSettings();
bool setupGraphicsSettings();
void setVersionLabel();
void loadSettings();
void saveSettings();
@ -91,7 +93,7 @@ namespace Launcher
Files::ConfigurationManager mCfgMgr;
Config::GameSettings mGameSettings;
GraphicsSettings mGraphicsSettings;
Settings::Manager mEngineSettings;
Config::LauncherSettings mLauncherSettings;
};

@ -2,20 +2,11 @@
#include <QListView>
#ifdef Q_OS_MAC
#include <QPlastiqueStyle>
#endif
Launcher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
{
setObjectName ("PlayPage");
setupUi(this);
// Hacks to get the stylesheet look properly
#ifdef Q_OS_MAC
QPlastiqueStyle *style = new QPlastiqueStyle;
profilesComboBox->setStyle(style);
#endif
profilesComboBox->setView(new QListView());
connect(profilesComboBox, SIGNAL(activated(int)), this, SIGNAL (signalProfileChanged(int)));

@ -1,44 +0,0 @@
#include "graphicssettings.hpp"
#include <QTextStream>
#include <QString>
#include <QRegExp>
#include <QMap>
Launcher::GraphicsSettings::GraphicsSettings()
{
}
Launcher::GraphicsSettings::~GraphicsSettings()
{
}
bool Launcher::GraphicsSettings::writeFile(QTextStream &stream)
{
QString sectionPrefix;
QRegExp sectionRe("([^/]+)/(.+)$");
QMap<QString, QString> settings = SettingsBase::getSettings();
QMapIterator<QString, QString> i(settings);
while (i.hasNext()) {
i.next();
QString prefix;
QString key;
if (sectionRe.exactMatch(i.key())) {
prefix = sectionRe.cap(1);
key = sectionRe.cap(2);
}
if (sectionPrefix != prefix) {
sectionPrefix = prefix;
stream << "\n[" << prefix << "]\n";
}
stream << key << " = " << i.value() << "\n";
}
return true;
}

@ -1,18 +0,0 @@
#ifndef GRAPHICSSETTINGS_HPP
#define GRAPHICSSETTINGS_HPP
#include <components/config/settingsbase.hpp>
namespace Launcher
{
class GraphicsSettings : public Config::SettingsBase<QMap<QString, QString> >
{
public:
GraphicsSettings();
~GraphicsSettings();
bool writeFile(QTextStream &stream);
};
}
#endif // GRAPHICSSETTINGS_HPP

@ -20,7 +20,6 @@ MwIniImporter::MwIniImporter()
{
const char *map[][2] =
{
{ "fps", "General:Show FPS" },
{ "no-sound", "General:Disable Audio" },
{ 0, 0 }
};
@ -639,6 +638,9 @@ MwIniImporter::MwIniImporter()
"Blood:Texture Name 1",
"Blood:Texture Name 2",
// werewolf (Bloodmoon)
"General:Werewolf FOV",
0
};
@ -847,7 +849,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
std::string filetype(entry->substr(entry->length()-3));
Misc::StringUtils::toLower(filetype);
Misc::StringUtils::lowerCaseInPlace(filetype);
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
boost::filesystem::path filepath(gameFilesDir);

@ -9,7 +9,7 @@ add_executable(niftest
)
target_link_libraries(niftest
${Boost_LIBRARIES}
${Boost_FILESYSTEM_LIBRARY}
components
)

@ -0,0 +1,165 @@
///Program to test .nif files both on the FileSystem and in BSA archives.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <components/nif/niffile.hpp>
#include <components/files/constrainedfilestream.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/bsaarchive.hpp>
#include <components/vfs/filesystemarchive.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
// Create local aliases for brevity
namespace bpo = boost::program_options;
namespace bfs = boost::filesystem;
///See if the file has the named extension
bool hasExtension(std::string filename, std::string extensionToFind)
{
std::string extension = filename.substr(filename.find_last_of(".")+1);
//Convert strings to lower case for comparison
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);
if(extension == extensionToFind)
return true;
else
return false;
}
///See if the file has the "nif" extension.
bool isNIF(std::string filename)
{
return hasExtension(filename,"nif");
}
///See if the file has the "bsa" extension.
bool isBSA(std::string filename)
{
return hasExtension(filename,"bsa");
}
/// Check all the nif files in a given VFS::Archive
/// \note Takes ownership!
/// \note Can not read a bsa file inside of a bsa file.
void readVFS(VFS::Archive* anArchive,std::string archivePath = "")
{
VFS::Manager myManager(true);
myManager.addArchive(anArchive);
myManager.buildIndex();
std::map<std::string, VFS::File*> files=myManager.getIndex();
for(std::map<std::string, VFS::File*>::const_iterator it=files.begin(); it!=files.end(); ++it)
{
std::string name = it->first;
try{
if(isNIF(name))
{
// std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(myManager.get(name),archivePath+name);
}
else if(isBSA(name))
{
if(!archivePath.empty() && !isBSA(archivePath))
{
// std::cout << "Reading BSA File: " << name << std::endl;
readVFS(new VFS::BsaArchive(archivePath+name),archivePath+name+"/");
// std::cout << "Done with BSA File: " << name << std::endl;
}
}
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
}
}
}
std::vector<std::string> parseOptions (int argc, char** argv)
{
bpo::options_description desc("Ensure that OpenMW can use the provided NIF and BSA files\n\n"
"Usages:\n"
" niftool <nif files, BSA files, or directories>\n"
" Scan the file or directories for nif errors.\n\n"
"Allowed options");
desc.add_options()
("help,h", "print help message.")
("input-file", bpo::value< std::vector<std::string> >(), "input file")
;
//Default option if none provided
bpo::positional_options_description p;
p.add("input-file", -1);
bpo::variables_map variables;
try
{
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).
options(desc).positional(p).run();
bpo::store(valid_opts, variables);
}
catch(std::exception &e)
{
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
<< desc << std::endl;
exit(1);
}
bpo::notify(variables);
if (variables.count ("help"))
{
std::cout << desc << std::endl;
exit(1);
}
if (variables.count("input-file"))
{
return variables["input-file"].as< std::vector<std::string> >();
}
std::cout << "No input files or directories specified!" << std::endl;
std::cout << desc << std::endl;
exit(1);
}
int main(int argc, char **argv)
{
std::vector<std::string> files = parseOptions (argc, argv);
// std::cout << "Reading Files" << std::endl;
for(std::vector<std::string>::const_iterator it=files.begin(); it!=files.end(); ++it)
{
std::string name = *it;
try{
if(isNIF(name))
{
//std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(Files::openConstrainedFileStream(name.c_str()),name);
}
else if(isBSA(name))
{
// std::cout << "Reading BSA File: " << name << std::endl;
readVFS(new VFS::BsaArchive(name));
}
else if(bfs::is_directory(bfs::path(name)))
{
// std::cout << "Reading All Files in: " << name << std::endl;
readVFS(new VFS::FileSystemArchive(name),name);
}
else
{
std::cerr << "ERROR: \"" << name << "\" is not a nif file, bsa file, or directory!" << std::endl;
}
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
}
}
return 0;
}

@ -23,10 +23,10 @@ opencs_units (model/world
opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection
universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager
idcompletionmanager metadata
)
opencs_hdrs_noqt (model/world
@ -35,13 +35,18 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools
tools reportmodel
tools reportmodel mergeoperation
)
opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages
)
opencs_hdrs_noqt (model/tools
mergestate
)
@ -62,30 +67,30 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview
cellcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
)
opencs_units_noqt (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator physicssystem idcompletiondelegate
colordelegate
scripthighlighter idvalidator dialoguecreator idcompletiondelegate
colordelegate dragdroputils
)
opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
scenetooltoggle2 completerpopup coloreditor colorpickerpopup
scenetooltoggle2 completerpopup coloreditor colorpickerpopup droplineedit
)
opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode
previewwidget editmode instancemode
)
opencs_units_noqt (view/render
navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight
lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem mousestate
lighting lightingday lightingnight
lightingbright object cell terrainstorage tagbase cellarrow
)
opencs_hdrs_noqt (view/render
@ -94,38 +99,23 @@ opencs_hdrs_noqt (view/render
opencs_units (view/tools
reportsubview reporttable searchsubview searchbox
reportsubview reporttable searchsubview searchbox merge
)
opencs_units_noqt (view/tools
subviews
)
opencs_units (view/settings
settingwindow
dialog
page
view
booleanview
textview
listview
rangeview
resizeablestackedwidget
spinbox
opencs_units (view/prefs
dialogue pagebase page
)
opencs_units_noqt (view/settings
frame
opencs_units (model/prefs
state setting intsetting doublesetting boolsetting enumsetting coloursetting
)
opencs_units (model/settings
usersettings
setting
connector
)
opencs_hdrs_noqt (model/settings
support
opencs_units_noqt (model/prefs
category
)
opencs_units_noqt (model/filter
@ -192,6 +182,7 @@ if(APPLE)
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION}
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/files/mac/openmw-cs-Info.plist.in"
)
set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES
@ -199,16 +190,16 @@ if(APPLE)
endif(APPLE)
target_link_libraries(openmw-cs
${OENGINE_LIBRARY}
${OGRE_LIBRARIES}
${OGRE_Overlay_LIBRARIES}
${OGRE_STATIC_PLUGINS}
${SHINY_LIBRARIES}
${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES}
${OSGUTIL_LIBRARIES}
${OSGVIEWER_LIBRARIES}
${OSGGA_LIBRARIES}
${OSGFX_LIBRARIES}
${OSGQT_LIBRARIES}
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_WAVE_LIBRARY}
${BULLET_LIBRARIES}
components
)
@ -216,14 +207,14 @@ if (DESIRED_QT_VERSION MATCHES 4)
target_link_libraries(openmw-cs
${QT_QTGUI_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTNETWORK_LIBRARY})
${QT_QTNETWORK_LIBRARY}
${QT_QTOPENGL_LIBRARY})
if (WIN32)
target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY})
endif()
else()
qt5_use_modules(openmw-cs Widgets Core Network)
qt5_use_modules(openmw-cs Widgets Core Network OpenGL)
if (WIN32)
target_link_libraries(Qt5::WinMain)
endif()

@ -1,54 +1,48 @@
#include "editor.hpp"
#include <openengine/bullet/BulletShapeLoader.h>
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include <QMessageBox>
#include <OgreRoot.h>
#include <OgreRenderWindow.h>
#include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp>
#include <components/ogreinit/ogreinit.hpp>
#include <components/nifogre/ogrenifloader.hpp>
#include <components/bsa/resources.hpp>
#include <components/nifosg/nifloader.hpp>
#include "model/doc/document.hpp"
#include "model/world/data.hpp"
CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
: mUserSettings (mCfgMgr), mOverlaySystem (0), mDocumentManager (mCfgMgr),
#ifdef _WIN32
#include <Windows.h>
#endif
CS::Editor::Editor ()
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""),
mLock(), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
setupDataFiles (config.first);
CSMSettings::UserSettings::instance().loadSettings ("opencs.ini");
mSettings.setModel (CSMSettings::UserSettings::instance());
NifOsg::Loader::setShowMarkers(true);
ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string());
mVFS.reset(new VFS::Manager(mFsStrict));
NifOgre::Loader::setShowMarkers(true);
VFS::registerArchives(mVFS.get(), Files::Collections(config.first, !mFsStrict), config.second, true);
mOverlaySystem.reset (new CSVRender::OverlaySystem);
Bsa::registerResources (Files::Collections (config.first, !mFsStrict), config.second, true,
mFsStrict);
mDocumentManager.listResources();
mDocumentManager.setVFS(mVFS.get());
mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal);
mMerge.setLocalData (mLocal);
connect (&mDocumentManager, SIGNAL (documentAdded (CSMDoc::Document *)),
this, SLOT (documentAdded (CSMDoc::Document *)));
connect (&mDocumentManager, SIGNAL (documentAboutToBeRemoved (CSMDoc::Document *)),
this, SLOT (documentAboutToBeRemoved (CSMDoc::Document *)));
connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()),
this, SLOT (lastDocumentDeleted()));
@ -56,6 +50,7 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));
connect (&mViewManager, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SLOT (mergeDocument (CSMDoc::Document *)));
connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ()));
connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ()));
@ -81,9 +76,6 @@ CS::Editor::~Editor ()
if(mServer && boost::filesystem::exists(mPid))
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();
}
void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs)
@ -364,120 +356,26 @@ int CS::Editor::run()
return QApplication::exec();
}
std::auto_ptr<sh::Factory> CS::Editor::setupGraphics()
void CS::Editor::documentAdded (CSMDoc::Document *document)
{
std::string renderer =
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
"Direct3D9 Rendering Subsystem";
#else
"OpenGL Rendering Subsystem";
#endif
std::string renderSystem = mUserSettings.setting("Video/render system", renderer.c_str()).toStdString();
Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName(renderSystem));
// Initialise Ogre::OverlaySystem after Ogre::Root but before initialisation
mOverlaySystem.get();
Ogre::Root::getSingleton().initialise(false);
// Create a hidden background window to keep resources
Ogre::NameValuePairList params;
params.insert(std::make_pair("title", ""));
std::string antialiasing = mUserSettings.settingValue("Video/antialiasing").toStdString();
if(antialiasing == "MSAA 16") antialiasing = "16";
else if(antialiasing == "MSAA 8") antialiasing = "8";
else if(antialiasing == "MSAA 4") antialiasing = "4";
else if(antialiasing == "MSAA 2") antialiasing = "2";
else antialiasing = "0";
params.insert(std::make_pair("FSAA", antialiasing));
params.insert(std::make_pair("vsync", "false"));
params.insert(std::make_pair("hidden", "true"));
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
params.insert(std::make_pair("macAPI", "cocoa"));
#endif
// NOTE: fullscreen mode not supported (doesn't really make sense for opencs)
Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, &params);
hiddenWindow->setActive(false);
sh::OgrePlatform* platform =
new sh::OgrePlatform ("General", (mResources / "materials").string());
// for font used in overlays
Ogre::Root::getSingleton().addResourceLocation ((mResources / "mygui").string(),
"FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
if (!boost::filesystem::exists (mCfgMgr.getCachePath()))
boost::filesystem::create_directories (mCfgMgr.getCachePath());
platform->setCacheFolder (mCfgMgr.getCachePath().string());
std::auto_ptr<sh::Factory> factory (new sh::Factory (platform));
QString shLang = mUserSettings.settingValue("General/shader mode");
QString rend = renderSystem.c_str();
bool openGL = rend.contains(QRegExp("^OpenGL", Qt::CaseInsensitive));
bool glES = rend.contains(QRegExp("^OpenGL ES", Qt::CaseInsensitive));
// force shader language based on render system
if(shLang == ""
|| (openGL && shLang == "hlsl")
|| (!openGL && shLang == "glsl")
|| (glES && shLang != "glsles"))
{
shLang = openGL ? (glES ? "glsles" : "glsl") : "hlsl";
//no group means "General" group in the "ini" file standard
mUserSettings.setDefinitions("shader mode", (QStringList() << shLang));
}
enum sh::Language lang;
if(shLang == "glsl") lang = sh::Language_GLSL;
else if(shLang == "glsles") lang = sh::Language_GLSLES;
else if(shLang == "hlsl") lang = sh::Language_HLSL;
else lang = sh::Language_CG;
factory->setCurrentLanguage (lang);
factory->setWriteSourceCache (true);
factory->setReadSourceCache (true);
factory->setReadMicrocodeCache (true);
factory->setWriteMicrocodeCache (true);
factory->loadAllFiles();
bool shaders = mUserSettings.setting("3d-render/shaders", QString("true")) == "true" ? true : false;
sh::Factory::getInstance ().setShadersEnabled (shaders);
std::string fog = mUserSettings.setting("Shader/fog", QString("true")).toStdString();
sh::Factory::getInstance().setGlobalSetting ("fog", fog);
std::string shadows = mUserSettings.setting("Shader/shadows", QString("false")).toStdString();
sh::Factory::getInstance().setGlobalSetting ("shadows", shadows);
std::string shadows_pssm = mUserSettings.setting("Shader/shadows_pssm", QString("false")).toStdString();
sh::Factory::getInstance().setGlobalSetting ("shadows_pssm", shadows_pssm);
std::string render_refraction = mUserSettings.setting("Shader/render_refraction", QString("false")).toStdString();
sh::Factory::getInstance ().setGlobalSetting ("render_refraction", render_refraction);
// internal setting - may be switched on or off by the use of shader configurations
sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false");
std::string num_lights = mUserSettings.setting("3d-render-adv/num_lights", QString("8")).toStdString();
sh::Factory::getInstance ().setGlobalSetting ("num_lights", num_lights);
/// \todo add more configurable shiny settings
return factory;
mViewManager.addView (document);
}
void CS::Editor::documentAdded (CSMDoc::Document *document)
void CS::Editor::documentAboutToBeRemoved (CSMDoc::Document *document)
{
mViewManager.addView (document);
if (mMerge.getDocument()==document)
mMerge.cancel();
}
void CS::Editor::lastDocumentDeleted()
{
QApplication::quit();
}
void CS::Editor::mergeDocument (CSMDoc::Document *document)
{
mMerge.configure (document);
mMerge.show();
mMerge.raise();
mMerge.activateWindow();
}

@ -11,30 +11,33 @@
#include <QLocalServer>
#include <QLocalSocket>
#include <extern/shiny/Main/Factory.hpp>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include <components/files/multidircollection.hpp>
#include <components/nifcache/nifcache.hpp>
#include "model/settings/usersettings.hpp"
#include "model/doc/documentmanager.hpp"
#include "model/prefs/state.hpp"
#include "view/doc/viewmanager.hpp"
#include "view/doc/startup.hpp"
#include "view/doc/filedialog.hpp"
#include "view/doc/newgame.hpp"
#include "view/settings/dialog.hpp"
#include "view/render/overlaysystem.hpp"
#include "view/prefs/dialogue.hpp"
namespace OgreInit
#include "view/tools/merge.hpp"
namespace VFS
{
class OgreInit;
class Manager;
}
namespace CSMDoc
{
class Document;
}
namespace CS
@ -43,15 +46,16 @@ namespace CS
{
Q_OBJECT
Nif::Cache mNifCache;
// FIXME: should be moved to document, so we can have different resources for each opened project
std::auto_ptr<VFS::Manager> mVFS;
Files::ConfigurationManager mCfgMgr;
CSMSettings::UserSettings mUserSettings;
std::auto_ptr<CSVRender::OverlaySystem> mOverlaySystem;
CSMPrefs::State mSettingsState;
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup;
CSVDoc::NewGameDialogue mNewGame;
CSVSettings::Dialog mSettings;
CSVPrefs::Dialogue mSettings;
CSVDoc::FileDialog mFileDialog;
boost::filesystem::path mLocal;
boost::filesystem::path mResources;
@ -59,6 +63,7 @@ namespace CS
boost::interprocess::file_lock mLock;
boost::filesystem::ofstream mPidFile;
bool mFsStrict;
CSVTools::Merge mMerge;
void setupDataFiles (const Files::PathContainer& dataDirs);
@ -71,7 +76,7 @@ namespace CS
public:
Editor (OgreInit::OgreInit& ogreInit);
Editor ();
~Editor ();
bool makeIPCServer();
@ -80,9 +85,6 @@ namespace CS
int run();
///< \return error status
std::auto_ptr<sh::Factory> setupGraphics();
///< The returned factory must persist at least as long as *this.
private slots:
void createGame();
@ -101,8 +103,12 @@ namespace CS
void documentAdded (CSMDoc::Document *document);
void documentAboutToBeRemoved (CSMDoc::Document *document);
void lastDocumentDeleted();
void mergeDocument (CSMDoc::Document *document);
private:
QString mIpcServerName;

@ -1,4 +1,3 @@
#include "editor.hpp"
#include <exception>
@ -9,10 +8,6 @@
#include <QIcon>
#include <QMetaType>
#include <extern/shiny/Main/Factory.hpp>
#include <components/ogreinit/ogreinit.hpp>
#include "model/doc/messages.hpp"
#include "model/world/universalid.hpp"
@ -48,18 +43,21 @@ class Application : public QApplication
int main(int argc, char *argv[])
{
#ifdef Q_OS_MAC
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
try
{
// To allow background thread drawing in OSG
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
OgreInit::OgreInit ogreInit;
std::auto_ptr<sh::Factory> shinyFactory;
Application application (argc, argv);
#ifdef Q_OS_MAC
@ -70,28 +68,17 @@ int main(int argc, char *argv[])
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);
CS::Editor editor;
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
shinyFactory = editor.setupGraphics();
return editor.run();
}
catch (std::exception& e)

@ -1,4 +1,3 @@
#include "blacklist.hpp"
#include <algorithm>

@ -2,6 +2,7 @@
#include <cassert>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
@ -9,8 +10,6 @@
#include <components/files/configurationmanager.hpp>
#endif
#include "../../view/world/physicssystem.hpp"
void CSMDoc::Document::addGmsts()
{
static const char *gmstFloats[] =
@ -799,9 +798,9 @@ void CSMDoc::Document::addGmsts()
"sBookSkillMessage",
"sBounty",
"sBreath",
"sBribe",
"sBribe",
"sBribe",
"sBribe 10 Gold",
"sBribe 100 Gold",
"sBribe 1000 Gold",
"sBribeFail",
"sBribeSuccess",
"sBuy",
@ -2245,20 +2244,19 @@ void CSMDoc::Document::createBase()
}
}
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mTools (*this),
: mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSavingOperation (*this, mProjectPath, encoding),
mSaving (&mSavingOperation),
mResDir(resDir),
mRunner (mProjectPath), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>()),
mIdCompletionManager(mData)
mRunner (mProjectPath), mDirty (false), mIdCompletionManager(mData)
{
if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence");
@ -2282,9 +2280,6 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
if (mNew)
{
mData.setDescription ("");
mData.setAuthor ("");
if (mContentFiles.size()==1)
createBase();
}
@ -2299,6 +2294,8 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SIGNAL (mergeDone (CSMDoc::Document*)));
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
@ -2314,6 +2311,11 @@ CSMDoc::Document::~Document()
{
}
const VFS::Manager *CSMDoc::Document::getVFS() const
{
return mVFS;
}
QUndoStack& CSMDoc::Document::getUndoStack()
{
return mUndoStack;
@ -2323,7 +2325,7 @@ int CSMDoc::Document::getState() const
{
int state = 0;
if (!mUndoStack.isClean())
if (!mUndoStack.isClean() || mDirty)
state |= State_Modified;
if (mSaving.isRunning())
@ -2369,9 +2371,9 @@ void CSMDoc::Document::save()
emit stateChanged (getState(), this);
}
CSMWorld::UniversalId CSMDoc::Document::verify()
CSMWorld::UniversalId CSMDoc::Document::verify (const CSMWorld::UniversalId& reportId)
{
CSMWorld::UniversalId id = mTools.runVerifier();
CSMWorld::UniversalId id = mTools.runVerifier (reportId);
emit stateChanged (getState(), this);
return id;
}
@ -2388,6 +2390,12 @@ void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const C
emit stateChanged (getState(), this);
}
void CSMDoc::Document::runMerge (std::auto_ptr<CSMDoc::Document> target)
{
mTools.runMerge (target);
emit stateChanged (getState(), this);
}
void CSMDoc::Document::abortOperation (int type)
{
if (type==State_Saving)
@ -2409,6 +2417,9 @@ void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)
void CSMDoc::Document::operationDone (int type, bool failed)
{
if (type==CSMDoc::State_Saving && !failed)
mDirty = false;
emit stateChanged (getState(), this);
}
@ -2481,15 +2492,12 @@ void CSMDoc::Document::progress (int current, int max, int type)
emit progress (current, max, type, 1, this);
}
boost::shared_ptr<CSVWorld::PhysicsSystem> CSMDoc::Document::getPhysics ()
CSMWorld::IdCompletionManager &CSMDoc::Document::getIdCompletionManager()
{
if(!mPhysics)
mPhysics = boost::shared_ptr<CSVWorld::PhysicsSystem> (new CSVWorld::PhysicsSystem());
return mPhysics;
return mIdCompletionManager;
}
CSMWorld::IdCompletionManager &CSMDoc::Document::getIdCompletionManager()
void CSMDoc::Document::flagAsDirty()
{
return mIdCompletionManager;
mDirty = true;
}

@ -25,6 +25,12 @@
class QAbstractItemModel;
namespace VFS
{
class Manager;
}
namespace ESM
{
struct GameSetting;
@ -42,11 +48,6 @@ namespace CSMWorld
class ResourcesManager;
}
namespace CSVWorld
{
class PhysicsSystem;
}
namespace CSMDoc
{
class Document : public QObject
@ -55,6 +56,7 @@ namespace CSMDoc
private:
const VFS::Manager* mVFS;
boost::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles;
bool mNew;
@ -66,7 +68,8 @@ namespace CSMDoc
boost::filesystem::path mResDir;
Blacklist mBlacklist;
Runner mRunner;
boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
bool mDirty;
CSMWorld::IdCompletionManager mIdCompletionManager;
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
@ -95,7 +98,7 @@ namespace CSMDoc
public:
Document (const Files::ConfigurationManager& configuration,
Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
@ -103,6 +106,8 @@ namespace CSMDoc
~Document();
const VFS::Manager* getVFS() const;
QUndoStack& getUndoStack();
int getState() const;
@ -120,12 +125,14 @@ namespace CSMDoc
void save();
CSMWorld::UniversalId verify();
CSMWorld::UniversalId verify (const CSMWorld::UniversalId& reportId = CSMWorld::UniversalId());
CSMWorld::UniversalId newSearch();
void runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search);
void runMerge (std::auto_ptr<CSMDoc::Document> target);
void abortOperation (int type);
const CSMWorld::Data& getData() const;
@ -144,16 +151,20 @@ namespace CSMDoc
QTextDocument *getRunLog();
boost::shared_ptr<CSVWorld::PhysicsSystem> getPhysics();
CSMWorld::IdCompletionManager &getIdCompletionManager();
void flagAsDirty();
signals:
void stateChanged (int state, CSMDoc::Document *document);
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
private slots:
void modificationStateChanged (bool clean);
@ -171,4 +182,3 @@ namespace CSMDoc
}
#endif

@ -1,4 +1,3 @@
#include "documentmanager.hpp"
#include <algorithm>
@ -13,7 +12,7 @@
#include "document.hpp"
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252)
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mVFS(NULL)
{
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
@ -57,10 +56,24 @@ bool CSMDoc::DocumentManager::isEmpty()
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_)
{
Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts);
Document *document = makeDocument (files, savePath, new_);
insertDocument (document);
}
CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_)
{
return new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts);
}
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
{
mDocuments.push_back (document);
connect (document, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SLOT (insertDocument (CSMDoc::Document*)));
emit loadRequest (document);
mLoader.hasThingsToDo().wakeAll();
@ -73,6 +86,8 @@ void CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document)
if (iter==mDocuments.end())
throw std::runtime_error ("removing invalid document");
emit documentAboutToBeRemoved (document);
mDocuments.erase (iter);
document->deleteLater();
@ -95,11 +110,6 @@ void CSMDoc::DocumentManager::setBlacklistedScripts (const std::vector<std::stri
mBlacklistedScripts = scriptIds;
}
void CSMDoc::DocumentManager::listResources()
{
mResourcesManager.listResources();
}
void CSMDoc::DocumentManager::documentLoaded (Document *document)
{
emit documentAdded (document);
@ -113,3 +123,9 @@ void CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::
if (error.empty()) // do not remove the document yet, if we have an error
removeDocument (document);
}
void CSMDoc::DocumentManager::setVFS(const VFS::Manager *vfs)
{
mResourcesManager.setVFS(vfs);
mVFS = vfs;
}

@ -15,6 +15,11 @@
#include "loader.hpp"
namespace VFS
{
class Manager;
}
namespace Files
{
struct ConfigurationManager;
@ -35,6 +40,7 @@ namespace CSMDoc
ToUTF8::FromType mEncoding;
CSMWorld::ResourcesManager mResourcesManager;
std::vector<std::string> mBlacklistedScripts;
const VFS::Manager* mVFS;
DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&);
@ -50,14 +56,22 @@ namespace CSMDoc
///< \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way.
/// Create a new document. The ownership of the created document is transferred to
/// the calling function. The DocumentManager does not manage it. Loading has not
/// taken place at the point when the document is returned.
///
/// \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way.
Document *makeDocument (const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_);
void setResourceDir (const boost::filesystem::path& parResDir);
void setEncoding (ToUTF8::FromType encoding);
void setBlacklistedScripts (const std::vector<std::string>& scriptIds);
/// Ask OGRE for a list of available resources.
void listResources();
void setVFS(const VFS::Manager* vfs);
bool isEmpty();
@ -79,10 +93,16 @@ namespace CSMDoc
void removeDocument (CSMDoc::Document *document);
///< Emits the lastDocumentDeleted signal, if applicable.
/// Hand over document to *this. The ownership is transferred. The DocumentManager
/// will initiate the load procedure, if necessary
void insertDocument (CSMDoc::Document *document);
signals:
void documentAdded (CSMDoc::Document *document);
void documentAboutToBeRemoved (CSMDoc::Document *document);
void loadRequest (CSMDoc::Document *document);
void lastDocumentDeleted();

@ -1,4 +1,3 @@
#include "loader.hpp"
#include <QTimer>

@ -1,4 +1,3 @@
#include "messages.hpp"
CSMDoc::Message::Message() {}
@ -8,6 +7,20 @@ CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& me
: mId (id), mMessage (message), mHint (hint), mSeverity (severity)
{}
std::string CSMDoc::Message::toString (Severity severity)
{
switch (severity)
{
case CSMDoc::Message::Severity_Info: return "Information";
case CSMDoc::Message::Severity_Warning: return "Warning";
case CSMDoc::Message::Severity_Error: return "Error";
case CSMDoc::Message::Severity_SeriousError: return "Serious Error";
case CSMDoc::Message::Severity_Default: break;
}
return "";
}
CSMDoc::Messages::Messages (Message::Severity default_)
: mDefault (default_)
@ -18,7 +31,7 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string&
{
if (severity==Message::Severity_Default)
severity = mDefault;
mMessages.push_back (Message (id, message, hint, severity));
}

@ -21,18 +21,20 @@ namespace CSMDoc
// reporting it correctly
Severity_Default = 4
};
CSMWorld::UniversalId mId;
std::string mMessage;
std::string mHint;
Severity mSeverity;
Message();
Message (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, Severity severity);
static std::string toString (Severity severity);
};
class Messages
{
public:

@ -1,4 +1,3 @@
#include "operation.hpp"
#include <string>
@ -7,7 +6,6 @@
#include <QTimer>
#include "../world/universalid.hpp"
#include "../settings/usersettings.hpp"
#include "state.hpp"
#include "stage.hpp"
@ -24,9 +22,6 @@ void CSMDoc::Operation::prepareStages()
{
iter->second = iter->first->setup();
mTotalSteps += iter->second;
for (std::map<QString, QStringList>::const_iterator iter2 (mSettings.begin()); iter2!=mSettings.end(); ++iter2)
iter->first->updateUserSetting (iter2->first, iter2->second);
}
}
@ -48,7 +43,7 @@ CSMDoc::Operation::~Operation()
void CSMDoc::Operation::run()
{
mTimer->stop();
if (!mConnected)
{
connect (mTimer, SIGNAL (timeout()), this, SLOT (executeStage()));
@ -65,14 +60,6 @@ void CSMDoc::Operation::appendStage (Stage *stage)
mStages.push_back (std::make_pair (stage, 0));
}
void CSMDoc::Operation::configureSettings (const std::vector<QString>& settings)
{
for (std::vector<QString>::const_iterator iter (settings.begin()); iter!=settings.end(); ++iter)
{
mSettings.insert (std::make_pair (*iter, CSMSettings::UserSettings::instance().definitions (*iter)));
}
}
void CSMDoc::Operation::setDefaultSeverity (Message::Severity severity)
{
mDefaultSeverity = severity;
@ -102,14 +89,6 @@ void CSMDoc::Operation::abort()
mCurrentStage = mStages.end();
}
void CSMDoc::Operation::updateUserSetting (const QString& name, const QStringList& value)
{
std::map<QString, QStringList>::iterator iter = mSettings.find (name);
if (iter!=mSettings.end())
iter->second = value;
}
void CSMDoc::Operation::executeStage()
{
if (!mPrepared)
@ -117,7 +96,7 @@ void CSMDoc::Operation::executeStage()
prepareStages();
mPrepared = true;
}
Messages messages (mDefaultSeverity);
while (mCurrentStage!=mStages.end())

@ -34,7 +34,6 @@ namespace CSMDoc
bool mError;
bool mConnected;
QTimer *mTimer;
std::map<QString, QStringList> mSettings;
bool mPrepared;
Message::Severity mDefaultSeverity;
@ -53,11 +52,6 @@ namespace CSMDoc
///
/// \attention Do no call this function while this Operation is running.
/// Specify settings to be passed on to stages.
///
/// \attention Do no call this function while this Operation is running.
void configureSettings (const std::vector<QString>& settings);
/// \attention Do no call this function while this Operation is running.
void setDefaultSeverity (Message::Severity severity);
@ -77,13 +71,13 @@ namespace CSMDoc
void run();
void updateUserSetting (const QString& name, const QStringList& value);
private slots:
void executeStage();
void operationDone();
protected slots:
virtual void operationDone();
};
}

@ -1,8 +1,5 @@
#include "operationholder.hpp"
#include "../settings/usersettings.hpp"
#include "operation.hpp"
CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false)
@ -31,9 +28,6 @@ void CSMDoc::OperationHolder::setOperation (Operation *operation)
connect (this, SIGNAL (abortSignal()), mOperation, SLOT (abort()));
connect (&mThread, SIGNAL (started()), mOperation, SLOT (run()));
connect (&CSMSettings::UserSettings::instance(), SIGNAL (userSettingUpdated (const QString&, const QStringList&)),
mOperation, SLOT (updateUserSetting (const QString&, const QStringList&)));
}
bool CSMDoc::OperationHolder::isRunning() const

@ -1,4 +1,3 @@
#include "runner.hpp"
#include <QApplication>

@ -1,4 +1,3 @@
#include "saving.hpp"
#include "../world/data.hpp"
@ -81,22 +80,25 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::StartScript> >
(mDocument.getData().getStartScripts(), mState));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, false));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, true));
appendStage (new WriteRefIdCollectionStage (mDocument, mState));
appendStage (new CollectionReferencesStage (mDocument, mState));
appendStage (new WriteCellCollectionStage (mDocument, mState));
appendStage (new WritePathgridCollectionStage (mDocument, mState));
// Dialogue can reference objects and cells so must be written after these records for vanilla-compatible files
appendStage (new WriteLandCollectionStage (mDocument, mState));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, false));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, true));
appendStage (new WritePathgridCollectionStage (mDocument, mState));
appendStage (new WriteLandTextureCollectionStage (mDocument, mState));
// references Land Textures
appendStage (new WriteLandCollectionStage (mDocument, mState));
// close file and clean up
appendStage (new CloseSaveStage (mState));

@ -1,4 +1,3 @@
#include "savingstages.hpp"
#include <fstream>
@ -53,18 +52,16 @@ void CSMDoc::WriteHeaderStage::perform (int stage, Messages& messages)
mState.getWriter().clearMaster();
mState.getWriter().setFormat (0);
if (mSimple)
{
mState.getWriter().setAuthor ("");
mState.getWriter().setDescription ("");
mState.getWriter().setRecordCount (0);
mState.getWriter().setFormat (ESM::Header::CurrentFormat);
}
else
{
mState.getWriter().setAuthor (mDocument.getData().getAuthor());
mState.getWriter().setDescription (mDocument.getData().getDescription());
mDocument.getData().getMetaData().save (mState.getWriter());
mState.getWriter().setRecordCount (
mDocument.getData().count (CSMWorld::RecordBase::State_Modified) +
mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) +
@ -102,84 +99,77 @@ int CSMDoc::WriteDialogueCollectionStage::setup()
void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& messages)
{
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<ESM::Dialogue>& topic = mTopics.getRecord (stage);
CSMWorld::RecordBase::State state = topic.mState;
if (state==CSMWorld::RecordBase::State_Deleted)
if (topic.mState == CSMWorld::RecordBase::State_Deleted)
{
// if the topic is deleted, we do not need to bother with INFO records.
/// \todo wrote record with delete flag
ESM::Dialogue dialogue = topic.get();
writer.startRecord(dialogue.sRecordId);
dialogue.save(writer, true);
writer.endRecord(dialogue.sRecordId);
return;
}
// Test, if we need to save anything associated info records.
bool infoModified = false;
CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId);
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{
CSMWorld::RecordBase::State state = iter->mState;
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
state==CSMWorld::RecordBase::State_Deleted)
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
{
infoModified = true;
break;
}
}
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
infoModified)
if (topic.isModified() || infoModified)
{
mState.getWriter().startRecord (topic.mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (topic.mModified.sRecordId);
if (infoModified && topic.mState != CSMWorld::RecordBase::State_Modified
&& topic.mState != CSMWorld::RecordBase::State_ModifiedOnly)
{
mState.getWriter().startRecord (topic.mBase.sRecordId);
topic.mBase.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);
mState.getWriter().endRecord (topic.mBase.sRecordId);
}
else
{
mState.getWriter().startRecord (topic.mModified.sRecordId);
topic.mModified.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);
mState.getWriter().endRecord (topic.mModified.sRecordId);
}
// write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
++iter)
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{
CSMWorld::RecordBase::State state = iter->mState;
if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo wrote record with delete flag
}
else if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
{
ESM::DialInfo info = iter->get();
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
info.mPrev = "";
if (iter!=range.first)
{
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
--prev;
info.mPrev =
prev->mModified.mId.substr (prev->mModified.mId.find_last_of ('#')+1);
info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1);
}
CSMWorld::InfoCollection::RecordConstIterator next = iter;
++next;
info.mNext = "";
if (next!=range.second)
{
info.mNext =
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1);
}
mState.getWriter().startRecord (info.sRecordId);
mState.getWriter().writeHNCString ("INAM", info.mId);
info.save (mState.getWriter());
mState.getWriter().endRecord (info.sRecordId);
writer.startRecord (info.sRecordId);
info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (info.sRecordId);
}
}
}
@ -227,9 +217,7 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
const CSMWorld::Record<CSMWorld::CellRef>& record =
mDocument.getData().getReferences().getRecord (i);
if (record.mState==CSMWorld::RecordBase::State_Deleted ||
record.mState==CSMWorld::RecordBase::State_Modified ||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted)
{
std::string cellId = record.get().mOriginalCell.empty() ?
record.get().mCell : record.get().mOriginalCell;
@ -271,36 +259,34 @@ int CSMDoc::WriteCellCollectionStage::setup()
void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::Cell>& cell =
mDocument.getData().getCells().getRecord (stage);
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Cell>& cell = mDocument.getData().getCells().getRecord (stage);
std::map<std::string, std::deque<int> >::const_iterator references =
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
if (cell.mState==CSMWorld::RecordBase::State_Modified ||
cell.mState==CSMWorld::RecordBase::State_ModifiedOnly ||
if (cell.isModified() ||
cell.mState == CSMWorld::RecordBase::State_Deleted ||
references!=mState.getSubRecords().end())
{
bool interior = cell.get().mId.substr (0, 1)!="#";
CSMWorld::Cell cellRecord = cell.get();
bool interior = cellRecord.mId.substr (0, 1)!="#";
// write cell data
mState.getWriter().startRecord (cell.mModified.sRecordId);
mState.getWriter().writeHNOCString ("NAME", cell.get().mName);
ESM::Cell cell2 = cell.get();
writer.startRecord (cellRecord.sRecordId);
if (interior)
cell2.mData.mFlags |= ESM::Cell::Interior;
cellRecord.mData.mFlags |= ESM::Cell::Interior;
else
{
cell2.mData.mFlags &= ~ESM::Cell::Interior;
cellRecord.mData.mFlags &= ~ESM::Cell::Interior;
std::istringstream stream (cell.get().mId.c_str());
std::istringstream stream (cellRecord.mId.c_str());
char ignore;
stream >> ignore >> cell2.mData.mX >> cell2.mData.mY;
stream >> ignore >> cellRecord.mData.mX >> cellRecord.mData.mY;
}
cell2.save (mState.getWriter());
cellRecord.save (writer, cell.mState == CSMWorld::RecordBase::State_Deleted);
// write references
if (references!=mState.getSubRecords().end())
@ -311,24 +297,25 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
const CSMWorld::Record<CSMWorld::CellRef>& ref =
mDocument.getData().getReferences().getRecord (*iter);
if (ref.mState==CSMWorld::RecordBase::State_Modified ||
ref.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::CellRef refRecord = ref.get();
// recalculate the ref's cell location
std::ostringstream stream;
if (!interior)
{
std::pair<int, int> index = ref.get().getCellIndex();
std::pair<int, int> index = refRecord.getCellIndex();
stream << "#" << index.first << " " << index.second;
}
// An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again.
if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell)
if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell)
!= stream.str() && !interior)
{
ESM::MovedCellRef moved;
moved.mRefNum = ref.get().mRefNum;
moved.mRefNum = refRecord.mRefNum;
// Need to fill mTarget with the ref's new position.
std::istringstream istream (stream.str().c_str());
@ -336,24 +323,16 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
char ignore;
istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1];
ref.get().mRefNum.save (mState.getWriter(), false, "MVRF");
mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8);
refRecord.mRefNum.save (writer, false, "MVRF");
writer.writeHNT ("CNDT", moved.mTarget, 8);
}
ref.get().save (mState.getWriter());
}
else if (ref.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted);
}
}
}
mState.getWriter().endRecord (cell.mModified.sRecordId);
}
else if (cell.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.endRecord (cellRecord.sRecordId);
}
}
@ -370,11 +349,11 @@ int CSMDoc::WritePathgridCollectionStage::setup()
void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
mDocument.getData().getPathgrids().getRecord (stage);
if (pathgrid.mState==CSMWorld::RecordBase::State_Modified ||
pathgrid.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::Pathgrid record = pathgrid.get();
@ -387,15 +366,9 @@ void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& message
else
record.mCell = record.mId;
mState.getWriter().startRecord (record.sRecordId);
record.save (mState.getWriter());
mState.getWriter().endRecord (record.sRecordId);
}
else if (pathgrid.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.startRecord (record.sRecordId);
record.save (writer, pathgrid.mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (record.sRecordId);
}
}
@ -412,25 +385,20 @@ int CSMDoc::WriteLandCollectionStage::setup()
void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& land =
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Land>& land =
mDocument.getData().getLand().getRecord (stage);
if (land.mState==CSMWorld::RecordBase::State_Modified ||
land.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::Land record = land.get();
writer.startRecord (record.sRecordId);
record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted);
mState.getWriter().startRecord (record.mLand->sRecordId);
if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes))
data->save (mState.getWriter());
record.mLand->save (mState.getWriter());
if(record.mLand->mLandData)
record.mLand->mLandData->save (mState.getWriter());
mState.getWriter().endRecord (record.mLand->sRecordId);
}
else if (land.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.endRecord (record.sRecordId);
}
}
@ -447,23 +415,16 @@ int CSMDoc::WriteLandTextureCollectionStage::setup()
void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
mDocument.getData().getLandTextures().getRecord (stage);
if (landTexture.mState==CSMWorld::RecordBase::State_Modified ||
landTexture.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::LandTexture record = landTexture.get();
mState.getWriter().startRecord (record.sRecordId);
record.save (mState.getWriter());
mState.getWriter().endRecord (record.sRecordId);
}
else if (landTexture.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.startRecord (record.sRecordId);
record.save (writer, landTexture.mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (record.sRecordId);
}
}

@ -100,26 +100,17 @@ namespace CSMDoc
if (CSMWorld::getScopeFromId (mCollection.getRecord (stage).get().mId)!=mScope)
return;
ESM::ESMWriter& writer = mState.getWriter();
CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;
typename CollectionT::ESXRecord record = mCollection.getRecord (stage).get();
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
if (state == CSMWorld::RecordBase::State_Modified ||
state == CSMWorld::RecordBase::State_ModifiedOnly ||
state == CSMWorld::RecordBase::State_Deleted)
{
// FIXME: A quick Workaround to support records which should not write
// NAME, including SKIL, MGEF and SCPT. If there are many more
// idcollection records that doesn't use NAME then a more generic
// solution may be required.
uint32_t name = mCollection.getRecord (stage).mModified.sRecordId;
mState.getWriter().startRecord (name);
if(name != ESM::REC_SKIL && name != ESM::REC_MGEF && name != ESM::REC_SCPT)
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
mCollection.getRecord (stage).mModified.save (mState.getWriter());
mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.startRecord (record.sRecordId);
record.save (writer, state == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (record.sRecordId);
}
}

@ -1,4 +1,3 @@
#include "savingstate.hpp"
#include "operation.hpp"

@ -1,6 +1,3 @@
#include "stage.hpp"
CSMDoc::Stage::~Stage() {}
void CSMDoc::Stage::updateUserSetting (const QString& name, const QStringList& value) {}

@ -23,11 +23,7 @@ namespace CSMDoc
virtual void perform (int stage, Messages& messages) = 0;
///< Messages resulting from this stage will be appended to \a messages.
/// Default-implementation: ignore
virtual void updateUserSetting (const QString& name, const QStringList& value);
};
}
#endif

@ -12,7 +12,7 @@ namespace CSMDoc
State_Saving = 16,
State_Verifying = 32,
State_Compiling = 64, // not implemented yet
State_Merging = 64,
State_Searching = 128,
State_Loading = 256 // pseudo-state; can not be encountered in a loaded document
};

@ -1,4 +1,3 @@
#include "andnode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "booleannode.hpp"
CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}

@ -1,4 +1,3 @@
#include "leafnode.hpp"
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const

@ -1,4 +1,3 @@
#include "narynode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "node.hpp"
CSMFilter::Node::Node() {}

@ -1,4 +1,3 @@
#include "notnode.hpp"
CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {}

@ -1,4 +1,3 @@
#include "ornode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "parser.hpp"
#include <cctype>

@ -1,4 +1,3 @@
#include "textnode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "unarynode.hpp"
CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr<Node> child, const std::string& name)

@ -1,4 +1,3 @@
#include "valuenode.hpp"
#include <sstream>

@ -0,0 +1,47 @@
#include "boolsetting.hpp"
#include <QCheckBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::BoolSetting::BoolSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, bool default_)
: Setting (parent, values, mutex, key, label), mDefault (default_)
{}
CSMPrefs::BoolSetting& CSMPrefs::BoolSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::BoolSetting::makeWidgets (QWidget *parent)
{
QCheckBox *widget = new QCheckBox (QString::fromUtf8 (getLabel().c_str()), parent);
widget->setCheckState (mDefault ? Qt::Checked : Qt::Unchecked);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (stateChanged (int)), this, SLOT (valueChanged (int)));
return std::make_pair (static_cast<QWidget *> (0), widget);
}
void CSMPrefs::BoolSetting::valueChanged (int value)
{
{
QMutexLocker lock (getMutex());
getValues().setBool (getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,31 @@
#ifndef CSM_PREFS_BOOLSETTING_H
#define CSM_PREFS_BOOLSETTING_H
#include "setting.hpp"
namespace CSMPrefs
{
class BoolSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
bool mDefault;
public:
BoolSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, bool default_);
BoolSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (int value);
};
}
#endif

@ -0,0 +1,51 @@
#include "category.hpp"
#include <stdexcept>
#include "setting.hpp"
#include "state.hpp"
CSMPrefs::Category::Category (State *parent, const std::string& key)
: mParent (parent), mKey (key)
{}
const std::string& CSMPrefs::Category::getKey() const
{
return mKey;
}
CSMPrefs::State *CSMPrefs::Category::getState() const
{
return mParent;
}
void CSMPrefs::Category::addSetting (Setting *setting)
{
mSettings.push_back (setting);
}
CSMPrefs::Category::Iterator CSMPrefs::Category::begin()
{
return mSettings.begin();
}
CSMPrefs::Category::Iterator CSMPrefs::Category::end()
{
return mSettings.end();
}
CSMPrefs::Setting& CSMPrefs::Category::operator[] (const std::string& key)
{
for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter)
if ((*iter)->getKey()==key)
return **iter;
throw std::logic_error ("Invalid user setting: " + key);
}
void CSMPrefs::Category::update()
{
for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter)
mParent->update (**iter);
}

@ -0,0 +1,45 @@
#ifndef CSM_PREFS_CATEGORY_H
#define CSM_PREFS_CATEGORY_H
#include <string>
#include <vector>
namespace CSMPrefs
{
class State;
class Setting;
class Category
{
public:
typedef std::vector<Setting *> Container;
typedef Container::iterator Iterator;
private:
State *mParent;
std::string mKey;
Container mSettings;
public:
Category (State *parent, const std::string& key);
const std::string& getKey() const;
State *getState() const;
void addSetting (Setting *setting);
Iterator begin();
Iterator end();
Setting& operator[] (const std::string& key);
void update();
};
}
#endif

@ -0,0 +1,52 @@
#include "coloursetting.hpp"
#include <QLabel>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "../../view/widget/coloreditor.hpp"
#include "category.hpp"
#include "state.hpp"
CSMPrefs::ColourSetting::ColourSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, QColor default_)
: Setting (parent, values, mutex, key, label), mDefault (default_)
{}
CSMPrefs::ColourSetting& CSMPrefs::ColourSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::ColourSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
CSVWidget::ColorEditor *widget = new CSVWidget::ColorEditor (mDefault, parent);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (pickingFinished()), this, SLOT (valueChanged()));
return std::make_pair (label, widget);
}
void CSMPrefs::ColourSetting::valueChanged()
{
CSVWidget::ColorEditor& widget = dynamic_cast<CSVWidget::ColorEditor&> (*sender());
{
QMutexLocker lock (getMutex());
getValues().setString (getKey(), getParent()->getKey(), widget.color().name().toUtf8().data());
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,34 @@
#ifndef CSM_PREFS_COLOURSETTING_H
#define CSM_PREFS_COLOURSETTING_H
#include "setting.hpp"
#include <QColor>
namespace CSMPrefs
{
class ColourSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
QColor mDefault;
public:
ColourSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label,
QColor default_);
ColourSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged();
};
}
#endif

@ -0,0 +1,75 @@
#include "doublesetting.hpp"
#include <limits>
#include <QLabel>
#include <QDoubleSpinBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, double default_)
: Setting (parent, values, mutex, key, label),
mMin (0), mMax (std::numeric_limits<double>::max()),
mDefault (default_)
{}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max)
{
mMin = min;
mMax = max;
return *this;
}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMin (double min)
{
mMin = min;
return *this;
}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMax (double max)
{
mMax = max;
return *this;
}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::DoubleSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
QDoubleSpinBox *widget = new QDoubleSpinBox (parent);
widget->setRange (mMin, mMax);
widget->setValue (mDefault);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (valueChanged (double)), this, SLOT (valueChanged (double)));
return std::make_pair (label, widget);
}
void CSMPrefs::DoubleSetting::valueChanged (double value)
{
{
QMutexLocker lock (getMutex());
getValues().setFloat (getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,41 @@
#ifndef CSM_PREFS_DOUBLESETTING_H
#define CSM_PREFS_DOUBLESETTING_H
#include "setting.hpp"
namespace CSMPrefs
{
class DoubleSetting : public Setting
{
Q_OBJECT
double mMin;
double mMax;
std::string mTooltip;
double mDefault;
public:
DoubleSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label,
double default_);
// defaults to [0, std::numeric_limits<double>::max()]
DoubleSetting& setRange (double min, double max);
DoubleSetting& setMin (double min);
DoubleSetting& setMax (double max);
DoubleSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (double value);
};
}
#endif

@ -0,0 +1,112 @@
#include "enumsetting.hpp"
#include <QLabel>
#include <QComboBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::EnumValue::EnumValue (const std::string& value, const std::string& tooltip)
: mValue (value), mTooltip (tooltip)
{}
CSMPrefs::EnumValue::EnumValue (const char *value)
: mValue (value)
{}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValues& values)
{
mValues.insert (mValues.end(), values.mValues.begin(), values.mValues.end());
return *this;
}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValue& value)
{
mValues.push_back (value);
return *this;
}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const std::string& value, const std::string& tooltip)
{
mValues.push_back (EnumValue (value, tooltip));
return *this;
}
CSMPrefs::EnumSetting::EnumSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, const EnumValue& default_)
: Setting (parent, values, mutex, key, label), mDefault (default_)
{}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValues (const EnumValues& values)
{
mValues.add (values);
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const EnumValue& value)
{
mValues.add (value);
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const std::string& value, const std::string& tooltip)
{
mValues.add (value, tooltip);
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::EnumSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
QComboBox *widget = new QComboBox (parent);
int index = 0;
for (int i=0; i<static_cast<int> (mValues.mValues.size()); ++i)
{
if (mDefault.mValue==mValues.mValues[i].mValue)
index = i;
widget->addItem (QString::fromUtf8 (mValues.mValues[i].mValue.c_str()));
if (!mValues.mValues[i].mTooltip.empty())
widget->setItemData (i, QString::fromUtf8 (mValues.mValues[i].mTooltip.c_str()),
Qt::ToolTipRole);
}
widget->setCurrentIndex (index);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
}
connect (widget, SIGNAL (currentIndexChanged (int)), this, SLOT (valueChanged (int)));
return std::make_pair (label, widget);
}
void CSMPrefs::EnumSetting::valueChanged (int value)
{
{
QMutexLocker lock (getMutex());
getValues().setString (getKey(), getParent()->getKey(), mValues.mValues.at (value).mValue);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,62 @@
#ifndef CSM_PREFS_ENUMSETTING_H
#define CSM_PREFS_ENUMSETTING_H
#include <vector>
#include "setting.hpp"
namespace CSMPrefs
{
struct EnumValue
{
std::string mValue;
std::string mTooltip;
EnumValue (const std::string& value, const std::string& tooltip = "");
EnumValue (const char *value);
};
struct EnumValues
{
std::vector<EnumValue> mValues;
EnumValues& add (const EnumValues& values);
EnumValues& add (const EnumValue& value);
EnumValues& add (const std::string& value, const std::string& tooltip);
};
class EnumSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
EnumValue mDefault;
EnumValues mValues;
public:
EnumSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label,
const EnumValue& default_);
EnumSetting& setTooltip (const std::string& tooltip);
EnumSetting& addValues (const EnumValues& values);
EnumSetting& addValue (const EnumValue& value);
EnumSetting& addValue (const std::string& value, const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (int value);
};
}
#endif

@ -0,0 +1,74 @@
#include "intsetting.hpp"
#include <limits>
#include <QLabel>
#include <QSpinBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::IntSetting::IntSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, int default_)
: Setting (parent, values, mutex, key, label), mMin (0), mMax (std::numeric_limits<int>::max()),
mDefault (default_)
{}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setRange (int min, int max)
{
mMin = min;
mMax = max;
return *this;
}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMin (int min)
{
mMin = min;
return *this;
}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMax (int max)
{
mMax = max;
return *this;
}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::IntSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
QSpinBox *widget = new QSpinBox (parent);
widget->setRange (mMin, mMax);
widget->setValue (mDefault);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));
return std::make_pair (label, widget);
}
void CSMPrefs::IntSetting::valueChanged (int value)
{
{
QMutexLocker lock (getMutex());
getValues().setInt (getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,40 @@
#ifndef CSM_PREFS_INTSETTING_H
#define CSM_PREFS_INTSETTING_H
#include "setting.hpp"
namespace CSMPrefs
{
class IntSetting : public Setting
{
Q_OBJECT
int mMin;
int mMax;
std::string mTooltip;
int mDefault;
public:
IntSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, int default_);
// defaults to [0, std::numeric_limits<int>::max()]
IntSetting& setRange (int min, int max);
IntSetting& setMin (int min);
IntSetting& setMax (int max);
IntSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (int value);
};
}
#endif

@ -0,0 +1,97 @@
#include "setting.hpp"
#include <QColor>
#include <QMutexLocker>
#include "category.hpp"
#include "state.hpp"
Settings::Manager& CSMPrefs::Setting::getValues()
{
return *mValues;
}
QMutex *CSMPrefs::Setting::getMutex()
{
return mMutex;
}
CSMPrefs::Setting::Setting (Category *parent, Settings::Manager *values, QMutex *mutex,
const std::string& key, const std::string& label)
: QObject (parent->getState()), mParent (parent), mValues (values), mMutex (mutex), mKey (key),
mLabel (label)
{}
CSMPrefs::Setting:: ~Setting() {}
std::pair<QWidget *, QWidget *> CSMPrefs::Setting::makeWidgets (QWidget *parent)
{
return std::pair<QWidget *, QWidget *> (0, 0);
}
const CSMPrefs::Category *CSMPrefs::Setting::getParent() const
{
return mParent;
}
const std::string& CSMPrefs::Setting::getKey() const
{
return mKey;
}
const std::string& CSMPrefs::Setting::getLabel() const
{
return mLabel;
}
int CSMPrefs::Setting::toInt() const
{
QMutexLocker lock (mMutex);
return mValues->getInt (mKey, mParent->getKey());
}
double CSMPrefs::Setting::toDouble() const
{
QMutexLocker lock (mMutex);
return mValues->getFloat (mKey, mParent->getKey());
}
std::string CSMPrefs::Setting::toString() const
{
QMutexLocker lock (mMutex);
return mValues->getString (mKey, mParent->getKey());
}
bool CSMPrefs::Setting::isTrue() const
{
QMutexLocker lock (mMutex);
return mValues->getBool (mKey, mParent->getKey());
}
QColor CSMPrefs::Setting::toColor() const
{
// toString() handles lock
return QColor (QString::fromUtf8 (toString().c_str()));
}
bool CSMPrefs::operator== (const Setting& setting, const std::string& key)
{
std::string fullKey = setting.getParent()->getKey() + "/" + setting.getKey();
return fullKey==key;
}
bool CSMPrefs::operator== (const std::string& key, const Setting& setting)
{
return setting==key;
}
bool CSMPrefs::operator!= (const Setting& setting, const std::string& key)
{
return !(setting==key);
}
bool CSMPrefs::operator!= (const std::string& key, const Setting& setting)
{
return !(key==setting);
}

@ -0,0 +1,74 @@
#ifndef CSM_PREFS_SETTING_H
#define CSM_PREFS_SETTING_H
#include <string>
#include <utility>
#include <QObject>
class QWidget;
class QColor;
class QMutex;
namespace Settings
{
class Manager;
}
namespace CSMPrefs
{
class Category;
class Setting : public QObject
{
Q_OBJECT
Category *mParent;
Settings::Manager *mValues;
QMutex *mMutex;
std::string mKey;
std::string mLabel;
protected:
Settings::Manager& getValues();
QMutex *getMutex();
public:
Setting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label);
virtual ~Setting();
/// Return label, input widget.
///
/// \note first can be a 0-pointer, which means that the label is part of the input
/// widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
const Category *getParent() const;
const std::string& getKey() const;
const std::string& getLabel() const;
int toInt() const;
double toDouble() const;
std::string toString() const;
bool isTrue() const;
QColor toColor() const;
};
// note: fullKeys have the format categoryKey/settingKey
bool operator== (const Setting& setting, const std::string& fullKey);
bool operator== (const std::string& fullKey, const Setting& setting);
bool operator!= (const Setting& setting, const std::string& fullKey);
bool operator!= (const std::string& fullKey, const Setting& setting);
}
#endif

@ -0,0 +1,392 @@
#include "state.hpp"
#include <stdexcept>
#include <algorithm>
#include <sstream>
#include "intsetting.hpp"
#include "doublesetting.hpp"
#include "boolsetting.hpp"
#include "coloursetting.hpp"
CSMPrefs::State *CSMPrefs::State::sThis = 0;
void CSMPrefs::State::load()
{
// default settings file
boost::filesystem::path local = mConfigurationManager.getLocalPath() / mConfigFile;
boost::filesystem::path global = mConfigurationManager.getGlobalPath() / mConfigFile;
if (boost::filesystem::exists (local))
mSettings.loadDefault (local.string());
else if (boost::filesystem::exists (global))
mSettings.loadDefault (global.string());
else
throw std::runtime_error ("No default settings file found! Make sure the file \"openmw-cs.cfg\" was properly installed.");
// user settings file
boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile;
if (boost::filesystem::exists (user))
mSettings.loadUser (user.string());
}
void CSMPrefs::State::declare()
{
declareCategory ("Windows");
declareInt ("default-width", "Default window width", 800).
setTooltip ("Newly opened top-level windows will open with this width.").
setMin (80);
declareInt ("default-height", "Default window height", 600).
setTooltip ("Newly opened top-level windows will open with this height.").
setMin (80);
declareBool ("show-statusbar", "Show Status Bar", true).
setTooltip ("If a newly open top level window is showing status bars or not. "
" Note that this does not affect existing windows.");
declareSeparator();
declareBool ("reuse", "Reuse Subviews", true).
setTooltip ("When a new subview is requested and a matching subview already "
" exist, do not open a new subview and use the existing one instead.");
declareInt ("max-subviews", "Maximum number of subviews per top-level window", 256).
setTooltip ("If the maximum number is reached and a new subview is opened "
"it will be placed into a new top-level window.").
setRange (1, 256);
declareBool ("hide-subview", "Hide single subview", false).
setTooltip ("When a view contains only a single subview, hide the subview title "
"bar and if this subview is closed also close the view (unless it is the last "
"view for this document)");
declareInt ("minimum-width", "Minimum subview width", 325).
setTooltip ("Minimum width of subviews.").
setRange (50, 10000);
declareSeparator();
EnumValue scrollbarOnly ("Scrollbar Only", "Simple addition of scrollbars, the view window "
"does not grow automatically.");
declareEnum ("mainwindow-scrollbar", "Horizontal scrollbar mode for main window.", scrollbarOnly).
addValue (scrollbarOnly).
addValue ("Grow Only", "The view window grows as subviews are added. No scrollbars.").
addValue ("Grow then Scroll", "The view window grows. The scrollbar appears once it cannot grow any further.");
declareBool ("grow-limit", "Grow Limit Screen", false).
setTooltip ("When \"Grow then Scroll\" option is selected, the window size grows to"
" the width of the virtual desktop. \nIf this option is selected the the window growth"
"is limited to the current screen.");
declareCategory ("Records");
EnumValue iconAndText ("Icon and Text");
EnumValues recordValues;
recordValues.add (iconAndText).add ("Icon Only").add ("Text Only");
declareEnum ("status-format", "Modification status display format", iconAndText).
addValues (recordValues);
declareEnum ("type-format", "ID type display format", iconAndText).
addValues (recordValues);
declareCategory ("ID Tables");
EnumValue inPlaceEdit ("Edit in Place", "Edit the clicked cell");
EnumValue editRecord ("Edit Record", "Open a dialogue subview for the clicked record");
EnumValue view ("View", "Open a scene subview for the clicked record (not available everywhere)");
EnumValue editRecordAndClose ("Edit Record and Close");
EnumValues doubleClickValues;
doubleClickValues.add (inPlaceEdit).add (editRecord).add (view).add ("Revert").
add ("Delete").add (editRecordAndClose).
add ("View and Close", "Open a scene subview for the clicked record and close the table subview");
declareEnum ("double", "Double Click", inPlaceEdit).addValues (doubleClickValues);
declareEnum ("double-s", "Shift Double Click", editRecord).addValues (doubleClickValues);
declareEnum ("double-c", "Control Double Click", view).addValues (doubleClickValues);
declareEnum ("double-sc", "Shift Control Double Click", editRecordAndClose).addValues (doubleClickValues);
declareSeparator();
EnumValue jumpAndSelect ("Jump and Select", "Scroll new record into view and make it the selection");
declareEnum ("jump-to-added", "Action on adding or cloning a record", jumpAndSelect).
addValue (jumpAndSelect).
addValue ("Jump Only", "Scroll new record into view").
addValue ("No Jump", "No special action");
declareBool ("extended-config",
"Manually specify affected record types for an extended delete/revert", false).
setTooltip ("Delete and revert commands have an extended form that also affects "
"associated records.\n\n"
"If this option is enabled, types of affected records are selected "
"manually before a command execution.\nOtherwise, all associated "
"records are deleted/reverted immediately.");
declareCategory ("ID Dialogues");
declareBool ("toolbar", "Show toolbar", true);
declareCategory ("Reports");
EnumValue actionNone ("None");
EnumValue actionEdit ("Edit", "Open a table or dialogue suitable for addressing the listed report");
EnumValue actionRemove ("Remove", "Remove the report from the report table");
EnumValue actionEditAndRemove ("Edit And Remove", "Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table");
EnumValues reportValues;
reportValues.add (actionNone).add (actionEdit).add (actionRemove).add (actionEditAndRemove);
declareEnum ("double", "Double Click", actionEdit).addValues (reportValues);
declareEnum ("double-s", "Shift Double Click", actionRemove).addValues (reportValues);
declareEnum ("double-c", "Control Double Click", actionEditAndRemove).addValues (reportValues);
declareEnum ("double-sc", "Shift Control Double Click", actionNone).addValues (reportValues);
declareCategory ("Search & Replace");
declareInt ("char-before", "Characters before search string", 10).
setTooltip ("Maximum number of character to display in search result before the searched text");
declareInt ("char-after", "Characters after search string", 10).
setTooltip ("Maximum number of character to display in search result after the searched text");
declareBool ("auto-delete", "Delete row from result table after a successful replace", true);
declareCategory ("Scripts");
declareBool ("show-linenum", "Show Line Numbers", true).
setTooltip ("Show line numbers to the left of the script editor window."
"The current row and column numbers of the text cursor are shown at the bottom.");
declareBool ("mono-font", "Use monospace font", true);
EnumValue warningsNormal ("Normal", "Report warnings as warning");
declareEnum ("warnings", "Warning Mode", warningsNormal).
addValue ("Ignore", "Do not report warning").
addValue (warningsNormal).
addValue ("Strcit", "Promote warning to an error");
declareBool ("toolbar", "Show toolbar", true);
declareInt ("compile-delay", "Delay between updating of source errors", 100).
setTooltip ("Delay in milliseconds").
setRange (0, 10000);
declareInt ("error-height", "Initial height of the error panel", 100).
setRange (100, 10000);
declareSeparator();
declareColour ("colour-int", "Highlight Colour: Integer Literals", QColor ("darkmagenta"));
declareColour ("colour-float", "Highlight Colour: Float Literals", QColor ("magenta"));
declareColour ("colour-name", "Highlight Colour: Names", QColor ("grey"));
declareColour ("colour-keyword", "Highlight Colour: Keywords", QColor ("red"));
declareColour ("colour-special", "Highlight Colour: Special Characters", QColor ("darkorange"));
declareColour ("colour-comment", "Highlight Colour: Comments", QColor ("green"));
declareColour ("colour-id", "Highlight Colour: IDs", QColor ("blue"));
declareCategory ("General Input");
declareBool ("cycle", "Cyclic next/previous", false).
setTooltip ("When using next/previous functions at the last/first item of a "
"list go to the first/last item");
declareCategory ("3D Scene Input");
EnumValue left ("Left Mouse-Button");
EnumValue cLeft ("Ctrl-Left Mouse-Button");
EnumValue right ("Right Mouse-Button");
EnumValue cRight ("Ctrl-Right Mouse-Button");
EnumValue middle ("Middle Mouse-Button");
EnumValue cMiddle ("Ctrl-Middle Mouse-Button");
EnumValues inputButtons;
inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle);
declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons);
declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons);
declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons);
declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons);
declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons);
declareEnum ("s-select", "Secondary Selection Button", cMiddle).addValues (inputButtons);
declareSeparator();
declareBool ("context-select", "Context Sensitive Selection", false);
declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0).
setRange (0.001, 100.0);
declareDouble ("drag-wheel-factor", "Mouse wheel sensitivity during drag operations", 1.0).
setRange (0.001, 100.0);
declareDouble ("drag-shift-factor",
"Shift-acceleration factor during drag operations", 4.0).
setTooltip ("Acceleration factor during drag operations while holding down shift").
setRange (0.001, 100.0);
declareCategory ("Tooltips");
declareBool ("scene", "Show Tooltips in 3D scenes", true);
declareBool ("scene-hide-basic", "Hide basic 3D scenes tooltips", false);
declareInt ("scene-delay", "Tooltip delay in milliseconds", 500).
setMin (1);
}
void CSMPrefs::State::declareCategory (const std::string& key)
{
std::map<std::string, Category>::iterator iter = mCategories.find (key);
if (iter!=mCategories.end())
{
mCurrentCategory = iter;
}
else
{
mCurrentCategory =
mCategories.insert (std::make_pair (key, Category (this, key))).first;
}
}
CSMPrefs::IntSetting& CSMPrefs::State::declareInt (const std::string& key,
const std::string& label, int default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
std::ostringstream stream;
stream << default_;
setDefault (key, stream.str());
default_ = mSettings.getInt (key, mCurrentCategory->second.getKey());
CSMPrefs::IntSetting *setting =
new CSMPrefs::IntSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble (const std::string& key,
const std::string& label, double default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
std::ostringstream stream;
stream << default_;
setDefault (key, stream.str());
default_ = mSettings.getFloat (key, mCurrentCategory->second.getKey());
CSMPrefs::DoubleSetting *setting =
new CSMPrefs::DoubleSetting (&mCurrentCategory->second, &mSettings, &mMutex,
key, label, default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::BoolSetting& CSMPrefs::State::declareBool (const std::string& key,
const std::string& label, bool default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_ ? "true" : "false");
default_ = mSettings.getBool (key, mCurrentCategory->second.getKey());
CSMPrefs::BoolSetting *setting =
new CSMPrefs::BoolSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::EnumSetting& CSMPrefs::State::declareEnum (const std::string& key,
const std::string& label, EnumValue default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_.mValue);
default_.mValue = mSettings.getString (key, mCurrentCategory->second.getKey());
CSMPrefs::EnumSetting *setting =
new CSMPrefs::EnumSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::ColourSetting& CSMPrefs::State::declareColour (const std::string& key,
const std::string& label, QColor default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_.name().toUtf8().data());
default_.setNamedColor (QString::fromUtf8 (mSettings.getString (key, mCurrentCategory->second.getKey()).c_str()));
CSMPrefs::ColourSetting *setting =
new CSMPrefs::ColourSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
void CSMPrefs::State::declareSeparator()
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
CSMPrefs::Setting *setting =
new CSMPrefs::Setting (&mCurrentCategory->second, &mSettings, &mMutex, "", "");
mCurrentCategory->second.addSetting (setting);
}
void CSMPrefs::State::setDefault (const std::string& key, const std::string& default_)
{
Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key);
Settings::CategorySettingValueMap::iterator iter =
mSettings.mDefaultSettings.find (fullKey);
if (iter==mSettings.mDefaultSettings.end())
mSettings.mDefaultSettings.insert (std::make_pair (fullKey, default_));
}
CSMPrefs::State::State (const Files::ConfigurationManager& configurationManager)
: mConfigFile ("openmw-cs.cfg"), mConfigurationManager (configurationManager),
mCurrentCategory (mCategories.end())
{
if (sThis)
throw std::logic_error ("An instance of CSMPRefs::State already exists");
load();
declare();
sThis = this;
}
CSMPrefs::State::~State()
{
sThis = 0;
}
void CSMPrefs::State::save()
{
boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile;
mSettings.saveUser (user.string());
}
CSMPrefs::State::Iterator CSMPrefs::State::begin()
{
return mCategories.begin();
}
CSMPrefs::State::Iterator CSMPrefs::State::end()
{
return mCategories.end();
}
CSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& key)
{
Iterator iter = mCategories.find (key);
if (iter==mCategories.end())
throw std::logic_error ("Invalid user settings category: " + key);
return iter->second;
}
void CSMPrefs::State::update (const Setting& setting)
{
emit (settingChanged (&setting));
}
CSMPrefs::State& CSMPrefs::State::get()
{
if (!sThis)
throw std::logic_error ("No instance of CSMPrefs::State");
return *sThis;
}
CSMPrefs::State& CSMPrefs::get()
{
return State::get();
}

@ -0,0 +1,105 @@
#ifndef CSV_PREFS_STATE_H
#define CSM_PREFS_STATE_H
#include <map>
#include <string>
#include <QObject>
#include <QMutex>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "setting.hpp"
#include "enumsetting.hpp"
class QColor;
namespace CSMPrefs
{
class IntSetting;
class DoubleSetting;
class BoolSetting;
class ColourSetting;
/// \brief User settings state
///
/// \note Access to the user settings is thread-safe once all declarations and loading has
/// been completed.
class State : public QObject
{
Q_OBJECT
static State *sThis;
public:
typedef std::map<std::string, Category> Collection;
typedef Collection::iterator Iterator;
private:
const std::string mConfigFile;
const Files::ConfigurationManager& mConfigurationManager;
Settings::Manager mSettings;
Collection mCategories;
Iterator mCurrentCategory;
QMutex mMutex;
// not implemented
State (const State&);
State& operator= (const State&);
private:
void load();
void declare();
void declareCategory (const std::string& key);
IntSetting& declareInt (const std::string& key, const std::string& label, int default_);
DoubleSetting& declareDouble (const std::string& key, const std::string& label, double default_);
BoolSetting& declareBool (const std::string& key, const std::string& label, bool default_);
EnumSetting& declareEnum (const std::string& key, const std::string& label, EnumValue default_);
ColourSetting& declareColour (const std::string& key, const std::string& label, QColor default_);
void declareSeparator();
void setDefault (const std::string& key, const std::string& default_);
public:
State (const Files::ConfigurationManager& configurationManager);
~State();
void save();
Iterator begin();
Iterator end();
Category& operator[](const std::string& key);
void update (const Setting& setting);
static State& get();
signals:
void settingChanged (const CSMPrefs::Setting *setting);
};
// convenience function
State& get();
}
#endif

@ -1,128 +0,0 @@
#include "connector.hpp"
#include "../../view/settings/view.hpp"
#include "../../view/settings/page.hpp"
CSMSettings::Connector::Connector(CSVSettings::View *master,
QObject *parent)
: QObject(parent), mMasterView (master)
{}
void CSMSettings::Connector::addSlaveView (CSVSettings::View *view,
QList <QStringList> &masterProxyValues)
{
mSlaveViews.append (view);
mProxyListMap[view->viewKey()].append (masterProxyValues);
}
QList <QStringList> CSMSettings::Connector::getSlaveViewValues() const
{
QList <QStringList> list;
foreach (const CSVSettings::View *view, mSlaveViews)
list.append (view->selectedValues());
return list;
}
bool CSMSettings::Connector::proxyListsMatch (
const QList <QStringList> &list1,
const QList <QStringList> &list2) const
{
bool success = true;
for (int i = 0; i < list1.size(); i++)
{
success = stringListsMatch (list1.at(i), list2.at(i));
if (!success)
break;
}
return success;
}
void CSMSettings::Connector::slotUpdateMaster() const
{
//list of the current values for each slave.
QList <QStringList> slaveValueList = getSlaveViewValues();
int masterColumn = -1;
/*
* A row in the master view is one of the values in the
* master view's data model. This corresponds directly to the number of
* values in a proxy list contained in the ProxyListMap member.
* Thus, we iterate each "column" in the master proxy list
* (one for each vlaue in the master. Each column represents
* one master value's corresponding list of slave values. We examine
* each master value's list, comparing it to the current slave value list,
* stopping when we find a match using proxyListsMatch().
*
* If no match is found, clear the master view's value
*/
for (int i = 0; i < mMasterView->rowCount(); i++)
{
QList <QStringList> proxyValueList;
foreach (const QString &settingKey, mProxyListMap.keys())
{
// append the proxy value list stored in the i'th column
// for each setting key. A setting key is the id of the setting
// in page.name format.
proxyValueList.append (mProxyListMap.value(settingKey).at(i));
}
if (proxyListsMatch (slaveValueList, proxyValueList))
{
masterColumn = i;
break;
}
}
QString masterValue = mMasterView->value (masterColumn);
mMasterView->setSelectedValue (masterValue);
}
void CSMSettings::Connector::slotUpdateSlaves() const
{
int row = mMasterView->currentIndex();
if (row == -1)
return;
//iterate the proxy lists for the chosen master index
//and pass the list to each slave for updating
for (int i = 0; i < mSlaveViews.size(); i++)
{
QList <QStringList> proxyList =
mProxyListMap.value(mSlaveViews.at(i)->viewKey());
mSlaveViews.at(i)->setSelectedValues (proxyList.at(row));
}
}
bool CSMSettings::Connector::stringListsMatch (
const QStringList &list1,
const QStringList &list2) const
{
//returns a "sloppy" match, verifying that each list contains all the same
//items, though not necessarily in the same order.
if (list1.size() != list2.size())
return false;
QStringList tempList(list2);
//iterate each value in the list, removing one occurrence of the value in
//the other list. If no corresponding value is found, test fails
foreach (const QString &value, list1)
{
if (!tempList.contains(value))
return false;
tempList.removeOne(value);
}
return true;
}

@ -1,67 +0,0 @@
#ifndef CSMSETTINGS_CONNECTOR_HPP
#define CSMSETTINGS_CONNECTOR_HPP
#include <QObject>
#include <QList>
#include <QMap>
#include <QStringList>
#include "support.hpp"
namespace CSVSettings {
class View;
}
namespace CSMSettings {
class Connector : public QObject
{
Q_OBJECT
CSVSettings::View *mMasterView;
///map using the view pointer as a key to it's index value
QList <CSVSettings::View *> mSlaveViews;
///list of proxy values for each master value.
///value list order is indexed to the master value index.
QMap < QString, QList <QStringList> > mProxyListMap;
public:
explicit Connector(CSVSettings::View *master,
QObject *parent = 0);
///Set the view which acts as a proxy for other setting views
void setMasterView (CSVSettings::View *view);
///Add a view to be updated / update to the master
void addSlaveView (CSVSettings::View *view,
QList <QStringList> &masterProxyValues);
private:
///loosely matches lists of proxy values across registered slaves
///against a proxy value list for a given master value
bool proxyListsMatch (const QList <QStringList> &list1,
const QList <QStringList> &list2) const;
///loosely matches two string lists
bool stringListsMatch (const QStringList &list1,
const QStringList &list2) const;
///retrieves current values of registered slave views
QList <QStringList> getSlaveViewValues() const;
public slots:
///updates slave views with proxy values associated with current
///master value
void slotUpdateSlaves() const;
///updates master value associated with the currently selected
///slave values, if applicable.
void slotUpdateMaster() const;
};
}
#endif // CSMSETTINGS_CONNECTOR_HPP

@ -1,414 +0,0 @@
#include "setting.hpp"
#include "support.hpp"
CSMSettings::Setting::Setting(SettingType typ, const QString &settingName,
const QString &pageName, const QString& label)
: mIsEditorSetting (true)
{
buildDefaultSetting();
int settingType = static_cast <int> (typ);
//even-numbered setting types are multi-valued
if ((settingType % 2) == 0)
setProperty (Property_IsMultiValue, QVariant(true).toString());
//view type is related to setting type by an order of magnitude
setProperty (Property_SettingType, QVariant (settingType).toString());
setProperty (Property_Page, pageName);
setProperty (Property_Name, settingName);
setProperty (Property_Label, label.isEmpty() ? settingName : label);
}
void CSMSettings::Setting::buildDefaultSetting()
{
int arrLen = sizeof(sPropertyDefaults) / sizeof (*sPropertyDefaults);
for (int i = 0; i < arrLen; i++)
{
QStringList propertyList;
if (i <Property_DefaultValues)
propertyList.append (sPropertyDefaults[i]);
mProperties.append (propertyList);
}
}
void CSMSettings::Setting::addProxy (const Setting *setting,
const QStringList &vals)
{
if (serializable())
setSerializable (false);
QList <QStringList> list;
foreach (const QString &val, vals)
list << (QStringList() << val);
mProxies [setting->page() + '/' + setting->name()] = list;
}
void CSMSettings::Setting::addProxy (const Setting *setting,
const QList <QStringList> &list)
{
if (serializable())
setProperty (Property_Serializable, false);
mProxies [setting->page() + '/' + setting->name()] = list;
}
void CSMSettings::Setting::setColumnSpan (int value)
{
setProperty (Property_ColumnSpan, value);
}
int CSMSettings::Setting::columnSpan() const
{
return property (Property_ColumnSpan).at(0).toInt();
}
void CSMSettings::Setting::setDeclaredValues (QStringList list)
{
setProperty (Property_DeclaredValues, list);
}
QStringList CSMSettings::Setting::declaredValues() const
{
return property (Property_DeclaredValues);
}
QStringList CSMSettings::Setting::property (SettingProperty prop) const
{
if (prop >= mProperties.size())
return QStringList();
return mProperties.at(prop);
}
void CSMSettings::Setting::setDefaultValue (int value)
{
setDefaultValues (QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setDefaultValue (double value)
{
setDefaultValues (QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setDefaultValue (const QString &value)
{
setDefaultValues (QStringList() << value);
}
void CSMSettings::Setting::setDefaultValues (const QStringList &values)
{
setProperty (Property_DefaultValues, values);
}
QStringList CSMSettings::Setting::defaultValues() const
{
return property (Property_DefaultValues);
}
void CSMSettings::Setting::setDelimiter (const QString &value)
{
setProperty (Property_Delimiter, value);
}
QString CSMSettings::Setting::delimiter() const
{
return property (Property_Delimiter).at(0);
}
void CSMSettings::Setting::setEditorSetting(bool state)
{
mIsEditorSetting = true;
}
bool CSMSettings::Setting::isEditorSetting() const
{
return mIsEditorSetting;
}
void CSMSettings::Setting::setIsMultiLine (bool state)
{
setProperty (Property_IsMultiLine, state);
}
bool CSMSettings::Setting::isMultiLine() const
{
return (property (Property_IsMultiLine).at(0) == "true");
}
void CSMSettings::Setting::setIsMultiValue (bool state)
{
setProperty (Property_IsMultiValue, state);
}
bool CSMSettings::Setting::isMultiValue() const
{
return (property (Property_IsMultiValue).at(0) == "true");
}
const CSMSettings::ProxyValueMap &CSMSettings::Setting::proxyLists() const
{
return mProxies;
}
void CSMSettings::Setting::setSerializable (bool state)
{
setProperty (Property_Serializable, state);
}
bool CSMSettings::Setting::serializable() const
{
return (property (Property_Serializable).at(0) == "true");
}
void CSMSettings::Setting::setSpecialValueText(const QString &text)
{
setProperty (Property_SpecialValueText, text);
}
QString CSMSettings::Setting::specialValueText() const
{
return property (Property_SpecialValueText).at(0);
}
void CSMSettings::Setting::setName (const QString &value)
{
setProperty (Property_Name, value);
}
QString CSMSettings::Setting::name() const
{
return property (Property_Name).at(0);
}
void CSMSettings::Setting::setPage (const QString &value)
{
setProperty (Property_Page, value);
}
QString CSMSettings::Setting::page() const
{
return property (Property_Page).at(0);
}
void CSMSettings::Setting::setStyleSheet (const QString &value)
{
setProperty (Property_StyleSheet, value);
}
QString CSMSettings::Setting::styleSheet() const
{
return property (Property_StyleSheet).at(0);
}
void CSMSettings::Setting::setPrefix (const QString &value)
{
setProperty (Property_Prefix, value);
}
QString CSMSettings::Setting::prefix() const
{
return property (Property_Prefix).at(0);
}
void CSMSettings::Setting::setRowSpan (const int value)
{
setProperty (Property_RowSpan, value);
}
int CSMSettings::Setting::rowSpan () const
{
return property (Property_RowSpan).at(0).toInt();
}
void CSMSettings::Setting::setSingleStep (int value)
{
setProperty (Property_SingleStep, value);
}
void CSMSettings::Setting::setSingleStep (double value)
{
setProperty (Property_SingleStep, value);
}
QString CSMSettings::Setting::singleStep() const
{
return property (Property_SingleStep).at(0);
}
void CSMSettings::Setting::setSuffix (const QString &value)
{
setProperty (Property_Suffix, value);
}
QString CSMSettings::Setting::suffix() const
{
return property (Property_Suffix).at(0);
}
void CSMSettings::Setting::setTickInterval (int value)
{
setProperty (Property_TickInterval, value);
}
int CSMSettings::Setting::tickInterval () const
{
return property (Property_TickInterval).at(0).toInt();
}
void CSMSettings::Setting::setTicksAbove (bool state)
{
setProperty (Property_TicksAbove, state);
}
bool CSMSettings::Setting::ticksAbove() const
{
return (property (Property_TicksAbove).at(0) == "true");
}
void CSMSettings::Setting::setTicksBelow (bool state)
{
setProperty (Property_TicksBelow, state);
}
bool CSMSettings::Setting::ticksBelow() const
{
return (property (Property_TicksBelow).at(0) == "true");
}
void CSMSettings::Setting::setType (int settingType)
{
setProperty (Property_SettingType, settingType);
}
CSMSettings::SettingType CSMSettings::Setting::type() const
{
return static_cast <CSMSettings::SettingType> ( property (
Property_SettingType).at(0).toInt());
}
void CSMSettings::Setting::setRange (int min, int max)
{
setProperty (Property_Minimum, min);
setProperty (Property_Maximum, max);
}
void CSMSettings::Setting::setRange (double min, double max)
{
setProperty (Property_Minimum, min);
setProperty (Property_Maximum, max);
}
QString CSMSettings::Setting::maximum() const
{
return property (Property_Maximum).at(0);
}
QString CSMSettings::Setting::minimum() const
{
return property (Property_Minimum).at(0);
}
CSVSettings::ViewType CSMSettings::Setting::viewType() const
{
return static_cast <CSVSettings::ViewType> ( property (
Property_SettingType).at(0).toInt() / 10);
}
void CSMSettings::Setting::setViewColumn (int value)
{
setProperty (Property_ViewColumn, value);
}
int CSMSettings::Setting::viewColumn() const
{
return property (Property_ViewColumn).at(0).toInt();
}
void CSMSettings::Setting::setViewLocation (int row, int column)
{
setViewRow (row);
setViewColumn (column);
}
void CSMSettings::Setting::setViewRow (int value)
{
setProperty (Property_ViewRow, value);
}
int CSMSettings::Setting::viewRow() const
{
return property (Property_ViewRow).at(0).toInt();
}
void CSMSettings::Setting::setWidgetWidth (int value)
{
setProperty (Property_WidgetWidth, value);
}
int CSMSettings::Setting::widgetWidth() const
{
return property (Property_WidgetWidth).at(0).toInt();
}
void CSMSettings::Setting::setWrapping (bool state)
{
setProperty (Property_Wrapping, state);
}
bool CSMSettings::Setting::wrapping() const
{
return (property (Property_Wrapping).at(0) == "true");
}
void CSMSettings::Setting::setLabel (const QString& label)
{
setProperty (Property_Label, label);
}
QString CSMSettings::Setting::getLabel() const
{
return property (Property_Label).at (0);
}
void CSMSettings::Setting::setToolTip (const QString& toolTip)
{
setProperty (Property_ToolTip, toolTip);
}
QString CSMSettings::Setting::getToolTip() const
{
return property (Property_ToolTip).at (0);
}
void CSMSettings::Setting::setProperty (SettingProperty prop, bool value)
{
setProperty (prop, QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setProperty (SettingProperty prop, int value)
{
setProperty (prop, QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setProperty (SettingProperty prop, double value)
{
setProperty (prop, QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setProperty (SettingProperty prop,
const QString &value)
{
setProperty (prop, QStringList() << value);
}
void CSMSettings::Setting::setProperty (SettingProperty prop,
const QStringList &value)
{
if (prop < mProperties.size())
mProperties.replace (prop, value);
}

@ -1,159 +0,0 @@
#ifndef CSMSETTINGS_SETTING_HPP
#define CSMSETTINGS_SETTING_HPP
#include <QStringList>
#include <QMap>
#include "support.hpp"
namespace CSMSettings
{
//QString is the setting id in the form of "page/name"
//QList is a list of stringlists of proxy values.
//Order is important! Proxy stringlists are matched against
//master values by their position in the QList.
typedef QMap <QString, QList <QStringList> > ProxyValueMap;
///Setting class is the interface for the User Settings. It contains
///a great deal of boiler plate to provide the core API functions, as
///well as the property() functions which use enumeration to be iterable.
///This makes the Setting class capable of being manipulated by script.
///See CSMSettings::support.hpp for enumerations / string values.
class Setting
{
QList <QStringList> mProperties;
QStringList mDefaults;
bool mIsEditorSetting;
ProxyValueMap mProxies;
public:
Setting(SettingType typ, const QString &settingName,
const QString &pageName, const QString& label = "");
void addProxy (const Setting *setting, const QStringList &vals);
void addProxy (const Setting *setting, const QList <QStringList> &list);
const QList <QStringList> &properties() const { return mProperties; }
const ProxyValueMap &proxies() const { return mProxies; }
void setColumnSpan (int value);
int columnSpan() const;
void setDeclaredValues (QStringList list);
QStringList declaredValues() const;
void setDefaultValue (int value);
void setDefaultValue (double value);
void setDefaultValue (const QString &value);
void setDefaultValues (const QStringList &values);
QStringList defaultValues() const;
void setDelimiter (const QString &value);
QString delimiter() const;
void setEditorSetting (bool state);
bool isEditorSetting() const;
void setIsMultiLine (bool state);
bool isMultiLine() const;
void setIsMultiValue (bool state);
bool isMultiValue() const;
void setMask (const QString &value);
QString mask() const;
void setRange (int min, int max);
void setRange (double min, double max);
QString maximum() const;
QString minimum() const;
void setName (const QString &value);
QString name() const;
void setPage (const QString &value);
QString page() const;
void setStyleSheet (const QString &value);
QString styleSheet() const;
void setPrefix (const QString &value);
QString prefix() const;
void setRowSpan (const int value);
int rowSpan() const;
const ProxyValueMap &proxyLists() const;
void setSerializable (bool state);
bool serializable() const;
void setSpecialValueText (const QString &text);
QString specialValueText() const;
void setSingleStep (int value);
void setSingleStep (double value);
QString singleStep() const;
void setSuffix (const QString &value);
QString suffix() const;
void setTickInterval (int value);
int tickInterval() const;
void setTicksAbove (bool state);
bool ticksAbove() const;
void setTicksBelow (bool state);
bool ticksBelow() const;
void setViewColumn (int value);
int viewColumn() const;
void setViewLocation (int row = -1, int column = -1);
void setViewRow (int value);
int viewRow() const;
void setType (int settingType);
CSMSettings::SettingType type() const;
CSVSettings::ViewType viewType() const;
void setWrapping (bool state);
bool wrapping() const;
void setWidgetWidth (int value);
int widgetWidth() const;
/// This is the text the user gets to see.
void setLabel (const QString& label);
QString getLabel() const;
void setToolTip (const QString& toolTip);
QString getToolTip() const;
///returns the specified property value
QStringList property (SettingProperty prop) const;
///boilerplate code to convert setting values of common types
void setProperty (SettingProperty prop, bool value);
void setProperty (SettingProperty prop, int value);
void setProperty (SettingProperty prop, double value);
void setProperty (SettingProperty prop, const QString &value);
void setProperty (SettingProperty prop, const QStringList &value);
void addProxy (Setting* setting,
QMap <QString, QStringList> &proxyMap);
protected:
void buildDefaultSetting();
};
}
#endif // CSMSETTINGS_SETTING_HPP

@ -1,149 +0,0 @@
#ifndef SETTING_SUPPORT_HPP
#define SETTING_SUPPORT_HPP
#include <Qt>
#include <QPair>
#include <QList>
#include <QVariant>
#include <QStringList>
//Enums
namespace CSMSettings
{
///Enumerated properties for scripting
enum SettingProperty
{
Property_Name = 0,
Property_Page = 1,
Property_SettingType = 2,
Property_IsMultiValue = 3,
Property_IsMultiLine = 4,
Property_WidgetWidth = 5,
Property_ViewRow = 6,
Property_ViewColumn = 7,
Property_Delimiter = 8,
Property_Serializable = 9,
Property_ColumnSpan = 10,
Property_RowSpan = 11,
Property_Minimum = 12,
Property_Maximum = 13,
Property_SpecialValueText = 14,
Property_Prefix = 15,
Property_Suffix = 16,
Property_SingleStep = 17,
Property_Wrapping = 18,
Property_TickInterval = 19,
Property_TicksAbove = 20,
Property_TicksBelow = 21,
Property_StyleSheet = 22,
Property_Label = 23,
Property_ToolTip = 24,
//Stringlists should always be the last items
Property_DefaultValues = 25,
Property_DeclaredValues = 26,
Property_DefinedValues = 27,
Property_Proxies = 28
};
///Basic setting widget types.
enum SettingType
{
/*
* 0 - 9 - Boolean widgets
* 10-19 - List widgets
* 21-29 - Range widgets
* 31-39 - Text widgets
*
* Each range corresponds to a View_Type enum by a factor of 10.
*
* Even-numbered values are single-value widgets
* Odd-numbered values are multi-valued widgets
*/
Type_CheckBox = 0,
Type_RadioButton = 1,
Type_ListView = 10,
Type_ComboBox = 11,
Type_SpinBox = 21,
Type_DoubleSpinBox = 23,
Type_Slider = 25,
Type_Dial = 27,
Type_TextArea = 30,
Type_LineEdit = 31,
Type_Undefined = 40
};
}
namespace CSVSettings
{
///Categorical view types which encompass the setting widget types
enum ViewType
{
ViewType_Boolean = 0,
ViewType_List = 1,
ViewType_Range = 2,
ViewType_Text = 3,
ViewType_Undefined = 4
};
}
namespace CSMSettings
{
///used to construct default settings in the Setting class
struct PropertyDefaultValues
{
int id;
QString name;
QVariant value;
};
///strings which correspond to setting values. These strings represent
///the script language keywords which would be used to declare setting
///views for 3rd party addons
const QString sPropertyNames[] =
{
"name", "page", "setting_type", "is_multi_value",
"is_multi_line", "widget_width", "view_row", "view_column", "delimiter",
"is_serializable","column_span", "row_span", "minimum", "maximum",
"special_value_text", "prefix", "suffix", "single_step", "wrapping",
"tick_interval", "ticks_above", "ticks_below", "stylesheet",
"defaults", "declarations", "definitions", "proxies"
};
///Default values for a setting. Used in setting creation.
const QString sPropertyDefaults[] =
{
"", //name
"", //page
"40", //setting type
"false", //multivalue
"false", //multiline
"7", //widget width
"-1", //view row
"-1", //view column
",", //delimiter
"true", //serialized
"1", //column span
"1", //row span
"0", //value range
"0", //value minimum
"0", //value maximum
"", //special text
"", //prefix
"", //suffix
"false", //wrapping
"1", //tick interval
"false", //ticks above
"true", //ticks below
"", //StyleSheet
"", //default values
"", //declared values
"", //defined values
"" //proxy values
};
}
#endif // VIEW_SUPPORT_HPP

@ -1,744 +0,0 @@
#include "usersettings.hpp"
#include <QSettings>
#include <QFile>
#include <components/files/configurationmanager.hpp>
#include <components/settings/settings.hpp>
#include <boost/version.hpp>
#include "setting.hpp"
#include "support.hpp"
#include <QTextCodec>
#include <QDebug>
#include <extern/shiny/Main/Factory.hpp>
/**
* Workaround for problems with whitespaces in paths in older versions of Boost library
*/
#if (BOOST_VERSION <= 104600)
namespace boost
{
template<>
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
{
return boost::filesystem::path(arg);
}
} /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */
CSMSettings::UserSettings *CSMSettings::UserSettings::sUserSettingsInstance = 0;
CSMSettings::UserSettings::UserSettings (const Files::ConfigurationManager& configurationManager)
: mCfgMgr (configurationManager)
, mSettingDefinitions(NULL)
{
assert(!sUserSettingsInstance);
sUserSettingsInstance = this;
buildSettingModelDefaults();
}
void CSMSettings::UserSettings::buildSettingModelDefaults()
{
QString section;
declareSection ("3d-render", "3D Rendering");
{
Setting *shaders = createSetting (Type_CheckBox, "shaders", "Enable Shaders");
shaders->setDefaultValue ("true");
Setting *farClipDist = createSetting (Type_DoubleSpinBox, "far-clip-distance", "Far clipping distance");
farClipDist->setDefaultValue (300000);
farClipDist->setRange (0, 1000000);
farClipDist->setToolTip ("The maximum distance objects are still rendered at.");
QString defaultValue = "None";
Setting *antialiasing = createSetting (Type_ComboBox, "antialiasing", "Antialiasing");
antialiasing->setDeclaredValues (QStringList()
<< defaultValue << "MSAA 2" << "MSAA 4" << "MSAA 8" << "MSAA 16");
antialiasing->setDefaultValue (defaultValue);
}
declareSection ("3d-render-adv", "3D Rendering (Advanced)");
{
Setting *numLights = createSetting (Type_SpinBox, "num_lights",
"Number of lights per pass");
numLights->setDefaultValue (8);
numLights->setRange (1, 100);
}
declareSection ("scene-input", "Scene Input");
{
Setting *timer = createSetting (Type_SpinBox, "timer", "Input responsiveness");
timer->setDefaultValue (20);
timer->setRange (1, 100);
timer->setToolTip ("The time between two checks for user input in milliseconds.<p>"
"Lower value result in higher responsiveness.");
Setting *fastFactor = createSetting (Type_SpinBox, "fast-factor",
"Fast movement factor");
fastFactor->setDefaultValue (4);
fastFactor->setRange (1, 100);
fastFactor->setToolTip (
"Factor by which movement is speed up while the shift key is held down.");
}
declareSection ("window", "Window");
{
Setting *preDefined = createSetting (Type_ComboBox, "pre-defined",
"Default window size");
preDefined->setEditorSetting (false);
preDefined->setDeclaredValues (
QStringList() << "640 x 480" << "800 x 600" << "1024 x 768" << "1440 x 900");
preDefined->setViewLocation (1, 1);
preDefined->setColumnSpan (2);
preDefined->setToolTip ("Newly opened top-level windows will open with this size "
"(picked from a list of pre-defined values)");
Setting *width = createSetting (Type_LineEdit, "default-width",
"Default window width");
width->setDefaultValues (QStringList() << "1024");
width->setViewLocation (2, 1);
width->setColumnSpan (1);
width->setToolTip ("Newly opened top-level windows will open with this width.");
preDefined->addProxy (width, QStringList() << "640" << "800" << "1024" << "1440");
Setting *height = createSetting (Type_LineEdit, "default-height",
"Default window height");
height->setDefaultValues (QStringList() << "768");
height->setViewLocation (2, 2);
height->setColumnSpan (1);
height->setToolTip ("Newly opened top-level windows will open with this height.");
preDefined->addProxy (height, QStringList() << "480" << "600" << "768" << "900");
Setting *reuse = createSetting (Type_CheckBox, "reuse", "Reuse Subviews");
reuse->setDefaultValue ("true");
reuse->setToolTip ("When a new subview is requested and a matching subview already "
" exist, do not open a new subview and use the existing one instead.");
Setting *statusBar = createSetting (Type_CheckBox, "show-statusbar", "Show Status Bar");
statusBar->setDefaultValue ("true");
statusBar->setToolTip ("If a newly open top level window is showing status bars or not. "
" Note that this does not affect existing windows.");
Setting *maxSubView = createSetting (Type_SpinBox, "max-subviews",
"Maximum number of subviews per top-level window");
maxSubView->setDefaultValue (256);
maxSubView->setRange (1, 256);
maxSubView->setToolTip ("If the maximum number is reached and a new subview is opened "
"it will be placed into a new top-level window.");
Setting *hide = createSetting (Type_CheckBox, "hide-subview", "Hide single subview");
hide->setDefaultValue ("false");
hide->setToolTip ("When a view contains only a single subview, hide the subview title "
"bar and if this subview is closed also close the view (unless it is the last "
"view for this document)");
Setting *minWidth = createSetting (Type_SpinBox, "minimum-width",
"Minimum subview width");
minWidth->setDefaultValue (325);
minWidth->setRange (50, 10000);
minWidth->setToolTip ("Minimum width of subviews.");
QString defaultScroll = "Scrollbar Only";
QStringList scrollValues = QStringList() << defaultScroll << "Grow Only" << "Grow then Scroll";
Setting *mainwinScroll = createSetting (Type_RadioButton, "mainwindow-scrollbar",
"Add a horizontal scrollbar to the main view window.");
mainwinScroll->setDefaultValue (defaultScroll);
mainwinScroll->setDeclaredValues (scrollValues);
mainwinScroll->setToolTip ("Scrollbar Only: Simple addition of scrollbars, the view window does not grow"
" automatically.\n"
"Grow Only: Original Editor behaviour. The view window grows as subviews are added. No scrollbars.\n"
"Grow then Scroll: The view window grows. The scrollbar appears once it cannot grow any further.");
Setting *grow = createSetting (Type_CheckBox, "grow-limit", "Grow Limit Screen");
grow->setDefaultValue ("false");
grow->setToolTip ("When \"Grow then Scroll\" option is selected, the window size grows to"
" the width of the virtual desktop. \nIf this option is selected the the window growth"
"is limited to the current screen.");
}
declareSection ("records", "Records");
{
QString defaultValue = "Icon and Text";
QStringList values = QStringList() << defaultValue << "Icon Only" << "Text Only";
Setting *rsd = createSetting (Type_RadioButton, "status-format",
"Modification status display format");
rsd->setDefaultValue (defaultValue);
rsd->setDeclaredValues (values);
Setting *ritd = createSetting (Type_RadioButton, "type-format",
"ID type display format");
ritd->setDefaultValue (defaultValue);
ritd->setDeclaredValues (values);
}
declareSection ("table-input", "Table Input");
{
QString inPlaceEdit ("Edit in Place");
QString editRecord ("Edit Record");
QString view ("View");
QString editRecordAndClose ("Edit Record and Close");
QStringList values;
values
<< "None" << inPlaceEdit << editRecord << view << "Revert" << "Delete"
<< editRecordAndClose << "View and Close";
QString toolTip = "<ul>"
"<li>None</li>"
"<li>Edit in Place: Edit the clicked cell</li>"
"<li>Edit Record: Open a dialogue subview for the clicked record</li>"
"<li>View: Open a scene subview for the clicked record (not available everywhere)</li>"
"<li>Revert: Revert record</li>"
"<li>Delete: Delete recordy</li>"
"<li>Edit Record and Close: Open a dialogue subview for the clicked record and close the table subview</li>"
"<li>View And Close: Open a scene subview for the clicked record and close the table subview</li>"
"</ul>";
Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click");
doubleClick->setDeclaredValues (values);
doubleClick->setDefaultValue (inPlaceEdit);
doubleClick->setToolTip ("Action on double click in table:<p>" + toolTip);
Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s",
"Shift Double Click");
shiftDoubleClick->setDeclaredValues (values);
shiftDoubleClick->setDefaultValue (editRecord);
shiftDoubleClick->setToolTip ("Action on shift double click in table:<p>" + toolTip);
Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c",
"Control Double Click");
ctrlDoubleClick->setDeclaredValues (values);
ctrlDoubleClick->setDefaultValue (view);
ctrlDoubleClick->setToolTip ("Action on control double click in table:<p>" + toolTip);
Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc",
"Shift Control Double Click");
shiftCtrlDoubleClick->setDeclaredValues (values);
shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose);
shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:<p>" + toolTip);
QString defaultValue = "Jump and Select";
QStringList jumpValues = QStringList() << defaultValue << "Jump Only" << "No Jump";
Setting *jumpToAdded = createSetting (Type_RadioButton, "jump-to-added",
"Jump to the added or cloned record.");
jumpToAdded->setDefaultValue (defaultValue);
jumpToAdded->setDeclaredValues (jumpValues);
}
declareSection ("report-input", "Report Input");
{
QString none ("None");
QString edit ("Edit");
QString remove ("Remove");
QString editAndRemove ("Edit And Remove");
QStringList values;
values << none << edit << remove << editAndRemove;
QString toolTip = "<ul>"
"<li>None</li>"
"<li>Edit: Open a table or dialogue suitable for addressing the listed report</li>"
"<li>Remove: Remove the report from the report table</li>"
"<li>Edit and Remove: Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table</li>"
"</ul>";
Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click");
doubleClick->setDeclaredValues (values);
doubleClick->setDefaultValue (edit);
doubleClick->setToolTip ("Action on double click in report table:<p>" + toolTip);
Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s",
"Shift Double Click");
shiftDoubleClick->setDeclaredValues (values);
shiftDoubleClick->setDefaultValue (remove);
shiftDoubleClick->setToolTip ("Action on shift double click in report table:<p>" + toolTip);
Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c",
"Control Double Click");
ctrlDoubleClick->setDeclaredValues (values);
ctrlDoubleClick->setDefaultValue (editAndRemove);
ctrlDoubleClick->setToolTip ("Action on control double click in report table:<p>" + toolTip);
Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc",
"Shift Control Double Click");
shiftCtrlDoubleClick->setDeclaredValues (values);
shiftCtrlDoubleClick->setDefaultValue (none);
shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in report table:<p>" + toolTip);
}
declareSection ("search", "Search & Replace");
{
Setting *before = createSetting (Type_SpinBox, "char-before",
"Characters before search string");
before->setDefaultValue (10);
before->setRange (0, 1000);
before->setToolTip ("Maximum number of character to display in search result before the searched text");
Setting *after = createSetting (Type_SpinBox, "char-after",
"Characters after search string");
after->setDefaultValue (10);
after->setRange (0, 1000);
after->setToolTip ("Maximum number of character to display in search result after the searched text");
Setting *autoDelete = createSetting (Type_CheckBox, "auto-delete", "Delete row from result table after a successful replace");
autoDelete->setDefaultValue ("true");
}
declareSection ("script-editor", "Scripts");
{
Setting *lineNum = createSetting (Type_CheckBox, "show-linenum", "Show Line Numbers");
lineNum->setDefaultValue ("true");
lineNum->setToolTip ("Show line numbers to the left of the script editor window."
"The current row and column numbers of the text cursor are shown at the bottom.");
Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font");
monoFont->setDefaultValue ("true");
monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview.");
QString tooltip =
"\n#RGB (each of R, G, and B is a single hex digit)"
"\n#RRGGBB"
"\n#RRRGGGBBB"
"\n#RRRRGGGGBBBB"
"\nA name from the list of colors defined in the list of SVG color keyword names."
"\nX11 color names may also work.";
QString modeNormal ("Normal");
QStringList modes;
modes << "Ignore" << modeNormal << "Strict";
Setting *warnings = createSetting (Type_ComboBox, "warnings",
"Warning Mode");
warnings->setDeclaredValues (modes);
warnings->setDefaultValue (modeNormal);
warnings->setToolTip ("<ul>How to handle warning messages during compilation:<p>"
"<li>Ignore: Do not report warning</li>"
"<li>Normal: Report warning as a warning</li>"
"<li>Strict: Promote warning to an error</li>"
"</ul>");
Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int");
formatInt->setDefaultValues (QStringList() << "Dark magenta");
formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);
Setting *formatFloat = createSetting (Type_LineEdit, "colour-float", "Highlight Colour: Float");
formatFloat->setDefaultValues (QStringList() << "Magenta");
formatFloat->setToolTip ("(Default: Magenta) Use one of the following formats:" + tooltip);
Setting *formatName = createSetting (Type_LineEdit, "colour-name", "Highlight Colour: Name");
formatName->setDefaultValues (QStringList() << "Gray");
formatName->setToolTip ("(Default: Gray) Use one of the following formats:" + tooltip);
Setting *formatKeyword = createSetting (Type_LineEdit, "colour-keyword", "Highlight Colour: Keyword");
formatKeyword->setDefaultValues (QStringList() << "Red");
formatKeyword->setToolTip ("(Default: Red) Use one of the following formats:" + tooltip);
Setting *formatSpecial = createSetting (Type_LineEdit, "colour-special", "Highlight Colour: Special");
formatSpecial->setDefaultValues (QStringList() << "Dark yellow");
formatSpecial->setToolTip ("(Default: Dark yellow) Use one of the following formats:" + tooltip);
Setting *formatComment = createSetting (Type_LineEdit, "colour-comment", "Highlight Colour: Comment");
formatComment->setDefaultValues (QStringList() << "Green");
formatComment->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);
Setting *formatId = createSetting (Type_LineEdit, "colour-id", "Highlight Colour: Id");
formatId->setDefaultValues (QStringList() << "Blue");
formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip);
}
{
/******************************************************************
* There are three types of values:
*
* Declared values
*
* Pre-determined values, typically for
* combobox drop downs and boolean (radiobutton / checkbox) labels.
* These values represent the total possible list of values that
* may define a setting. No other values are allowed.
*
* Defined values
*
* Values which represent the actual, current value of
* a setting. For settings with declared values, this must be one
* or several declared values, as appropriate.
*
* Proxy values
* Values the proxy master updates the proxy slave when
* it's own definition is set / changed. These are definitions for
* proxy slave settings, but must match any declared values the
* proxy slave has, if any.
*******************************************************************/
/*
//create setting objects, specifying the basic widget type,
//the page name, and the view name
Setting *masterBoolean = createSetting (Type_RadioButton, section,
"Master Proxy");
Setting *slaveBoolean = createSetting (Type_CheckBox, section,
"Proxy Checkboxes");
Setting *slaveSingleText = createSetting (Type_LineEdit, section,
"Proxy TextBox 1");
Setting *slaveMultiText = createSetting (Type_LineEdit, section,
"ProxyTextBox 2");
Setting *slaveAlphaSpinbox = createSetting (Type_SpinBox, section,
"Alpha Spinbox");
Setting *slaveIntegerSpinbox = createSetting (Type_SpinBox, section,
"Int Spinbox");
Setting *slaveDoubleSpinbox = createSetting (Type_DoubleSpinBox,
section, "Double Spinbox");
Setting *slaveSlider = createSetting (Type_Slider, section, "Slider");
Setting *slaveDial = createSetting (Type_Dial, section, "Dial");
//set declared values for selected views
masterBoolean->setDeclaredValues (QStringList()
<< "Profile One" << "Profile Two"
<< "Profile Three" << "Profile Four");
slaveBoolean->setDeclaredValues (QStringList()
<< "One" << "Two" << "Three" << "Four" << "Five");
slaveAlphaSpinbox->setDeclaredValues (QStringList()
<< "One" << "Two" << "Three" << "Four");
masterBoolean->addProxy (slaveBoolean, QList <QStringList>()
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three" << "Five")
<< (QStringList() << "Two" << "Four")
);
masterBoolean->addProxy (slaveSingleText, QList <QStringList>()
<< (QStringList() << "Text A")
<< (QStringList() << "Text B")
<< (QStringList() << "Text A")
<< (QStringList() << "Text C")
);
masterBoolean->addProxy (slaveMultiText, QList <QStringList>()
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three" << "Five")
<< (QStringList() << "Two" << "Four")
);
masterBoolean->addProxy (slaveAlphaSpinbox, QList <QStringList>()
<< (QStringList() << "Four")
<< (QStringList() << "Three")
<< (QStringList() << "Two")
<< (QStringList() << "One"));
masterBoolean->addProxy (slaveIntegerSpinbox, QList <QStringList> ()
<< (QStringList() << "0")
<< (QStringList() << "7")
<< (QStringList() << "14")
<< (QStringList() << "21"));
masterBoolean->addProxy (slaveDoubleSpinbox, QList <QStringList> ()
<< (QStringList() << "0.17")
<< (QStringList() << "0.34")
<< (QStringList() << "0.51")
<< (QStringList() << "0.68"));
masterBoolean->addProxy (slaveSlider, QList <QStringList> ()
<< (QStringList() << "25")
<< (QStringList() << "50")
<< (QStringList() << "75")
<< (QStringList() << "100")
);
masterBoolean->addProxy (slaveDial, QList <QStringList> ()
<< (QStringList() << "25")
<< (QStringList() << "50")
<< (QStringList() << "75")
<< (QStringList() << "100")
);
//settings with proxies are not serialized by default
//other settings non-serialized for demo purposes
slaveBoolean->setSerializable (false);
slaveSingleText->setSerializable (false);
slaveMultiText->setSerializable (false);
slaveAlphaSpinbox->setSerializable (false);
slaveIntegerSpinbox->setSerializable (false);
slaveDoubleSpinbox->setSerializable (false);
slaveSlider->setSerializable (false);
slaveDial->setSerializable (false);
slaveBoolean->setDefaultValues (QStringList()
<< "One" << "Three" << "Five");
slaveSingleText->setDefaultValue ("Text A");
slaveMultiText->setDefaultValues (QStringList()
<< "One" << "Three" << "Five");
slaveSingleText->setWidgetWidth (24);
slaveMultiText->setWidgetWidth (24);
slaveAlphaSpinbox->setDefaultValue ("Two");
slaveAlphaSpinbox->setWidgetWidth (20);
//slaveAlphaSpinbox->setPrefix ("No. ");
//slaveAlphaSpinbox->setSuffix ("!");
slaveAlphaSpinbox->setWrapping (true);
slaveIntegerSpinbox->setDefaultValue (14);
slaveIntegerSpinbox->setMinimum (0);
slaveIntegerSpinbox->setMaximum (58);
slaveIntegerSpinbox->setPrefix ("$");
slaveIntegerSpinbox->setSuffix (".00");
slaveIntegerSpinbox->setWidgetWidth (10);
slaveIntegerSpinbox->setSpecialValueText ("Nothing!");
slaveDoubleSpinbox->setDefaultValue (0.51);
slaveDoubleSpinbox->setSingleStep(0.17);
slaveDoubleSpinbox->setMaximum(4.0);
slaveSlider->setMinimum (0);
slaveSlider->setMaximum (100);
slaveSlider->setDefaultValue (75);
slaveSlider->setWidgetWidth (100);
slaveSlider->setTicksAbove (true);
slaveSlider->setTickInterval (25);
slaveDial->setMinimum (0);
slaveDial->setMaximum (100);
slaveDial->setSingleStep (5);
slaveDial->setDefaultValue (75);
slaveDial->setTickInterval (25);
*/
}
}
CSMSettings::UserSettings::~UserSettings()
{
sUserSettingsInstance = 0;
}
void CSMSettings::UserSettings::loadSettings (const QString &fileName)
{
QString userFilePath = QString::fromUtf8
(mCfgMgr.getUserConfigPath().string().c_str());
QString globalFilePath = QString::fromUtf8
(mCfgMgr.getGlobalPath().string().c_str());
QString otherFilePath = globalFilePath;
//test for local only if global fails (uninstalled copy)
if (!QFile (globalFilePath + fileName).exists())
{
//if global is invalid, use the local path
otherFilePath = QString::fromUtf8
(mCfgMgr.getLocalPath().string().c_str());
}
QSettings::setPath
(QSettings::IniFormat, QSettings::UserScope, userFilePath);
QSettings::setPath
(QSettings::IniFormat, QSettings::SystemScope, otherFilePath);
mSettingDefinitions = new QSettings
(QSettings::IniFormat, QSettings::UserScope, "opencs", QString(), this);
}
// if the key is not found create one with a default value
QString CSMSettings::UserSettings::setting(const QString &viewKey, const QString &value)
{
if(mSettingDefinitions->contains(viewKey))
return settingValue(viewKey);
else if(value != QString())
{
mSettingDefinitions->setValue (viewKey, QStringList() << value);
return value;
}
return QString();
}
bool CSMSettings::UserSettings::hasSettingDefinitions (const QString &viewKey) const
{
return (mSettingDefinitions->contains (viewKey));
}
void CSMSettings::UserSettings::setDefinitions
(const QString &key, const QStringList &list)
{
mSettingDefinitions->setValue (key, list);
}
void CSMSettings::UserSettings::saveDefinitions() const
{
mSettingDefinitions->sync();
}
QString CSMSettings::UserSettings::settingValue (const QString &settingKey)
{
QStringList defs;
if (!mSettingDefinitions->contains (settingKey))
return QString();
defs = mSettingDefinitions->value (settingKey).toStringList();
if (defs.isEmpty())
return QString();
return defs.at(0);
}
CSMSettings::UserSettings& CSMSettings::UserSettings::instance()
{
assert(sUserSettingsInstance);
return *sUserSettingsInstance;
}
void CSMSettings::UserSettings::updateUserSetting(const QString &settingKey,
const QStringList &list)
{
mSettingDefinitions->setValue (settingKey ,list);
if(settingKey == "3d-render-adv/num_lights" && !list.empty())
{
sh::Factory::getInstance ().setGlobalSetting ("num_lights", list.at(0).toStdString());
}
else if(settingKey == "3d-render/shaders" && !list.empty())
{
sh::Factory::getInstance ().setShadersEnabled (list.at(0).toStdString() == "true" ? true : false);
}
emit userSettingUpdated (settingKey, list);
}
CSMSettings::Setting *CSMSettings::UserSettings::findSetting
(const QString &pageName, const QString &settingName)
{
foreach (Setting *setting, mSettings)
{
if (setting->name() == settingName)
{
if (setting->page() == pageName)
return setting;
}
}
return 0;
}
void CSMSettings::UserSettings::removeSetting
(const QString &pageName, const QString &settingName)
{
if (mSettings.isEmpty())
return;
QList <Setting *>::iterator removeIterator = mSettings.begin();
while (removeIterator != mSettings.end())
{
if ((*removeIterator)->name() == settingName)
{
if ((*removeIterator)->page() == pageName)
{
mSettings.erase (removeIterator);
break;
}
}
removeIterator++;
}
}
CSMSettings::SettingPageMap CSMSettings::UserSettings::settingPageMap() const
{
SettingPageMap pageMap;
foreach (Setting *setting, mSettings)
{
SettingPageMap::iterator iter = pageMap.find (setting->page());
if (iter==pageMap.end())
{
QPair<QString, QList <Setting *> > value;
std::map<QString, QString>::const_iterator iter2 =
mSectionLabels.find (setting->page());
value.first = iter2!=mSectionLabels.end() ? iter2->second : "";
iter = pageMap.insert (setting->page(), value);
}
iter->second.append (setting);
}
return pageMap;
}
CSMSettings::Setting *CSMSettings::UserSettings::createSetting
(CSMSettings::SettingType type, const QString &name, const QString& label)
{
Setting *setting = new Setting (type, name, mSection, label);
// set useful defaults
int row = 1;
if (!mSettings.empty())
row = mSettings.back()->viewRow()+1;
setting->setViewLocation (row, 1);
setting->setColumnSpan (3);
int width = 10;
if (type==Type_CheckBox)
width = 40;
setting->setWidgetWidth (width);
if (type==Type_CheckBox)
setting->setStyleSheet ("QGroupBox { border: 0px; }");
if (type==Type_CheckBox)
setting->setDeclaredValues(QStringList() << "true" << "false");
if (type==Type_CheckBox)
setting->setSpecialValueText (setting->getLabel());
//add declaration to the model
mSettings.append (setting);
return setting;
}
void CSMSettings::UserSettings::declareSection (const QString& page, const QString& label)
{
mSection = page;
mSectionLabels[page] = label;
}
QStringList CSMSettings::UserSettings::definitions (const QString &viewKey) const
{
if (mSettingDefinitions->contains (viewKey))
return mSettingDefinitions->value (viewKey).toStringList();
return QStringList();
}

@ -1,107 +0,0 @@
#ifndef USERSETTINGS_HPP
#define USERSETTINGS_HPP
#include <map>
#include <QList>
#include <QStringList>
#include <QString>
#include <QMap>
#include <QPair>
#include <boost/filesystem/path.hpp>
#include "support.hpp"
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
struct ConfigurationManager;}
class QFile;
class QSettings;
namespace CSMSettings {
class Setting;
typedef QMap <QString, QPair<QString, QList <Setting *> > > SettingPageMap;
class UserSettings: public QObject
{
Q_OBJECT
static UserSettings *sUserSettingsInstance;
const Files::ConfigurationManager& mCfgMgr;
QSettings *mSettingDefinitions;
QList <Setting *> mSettings;
QString mSection;
std::map<QString, QString> mSectionLabels;
public:
/// Singleton implementation
static UserSettings& instance();
UserSettings (const Files::ConfigurationManager& configurationManager);
~UserSettings();
UserSettings (UserSettings const &); //not implemented
UserSettings& operator= (UserSettings const &); //not implemented
/// Retrieves the settings file at all three levels (global, local and user).
void loadSettings (const QString &fileName);
/// Updates QSettings and syncs with the ini file
void setDefinitions (const QString &key, const QStringList &defs);
QString settingValue (const QString &settingKey);
///retrieve a setting object from a given page and setting name
Setting *findSetting
(const QString &pageName, const QString &settingName = QString());
///remove a setting from the list
void removeSetting
(const QString &pageName, const QString &settingName);
///Retrieve a map of the settings, keyed by page name
SettingPageMap settingPageMap() const;
///Returns a string list of defined vlaues for the specified setting
///in "page/name" format.
QStringList definitions (const QString &viewKey) const;
///Test to indicate whether or not a setting has any definitions
bool hasSettingDefinitions (const QString &viewKey) const;
///Save any unsaved changes in the QSettings object
void saveDefinitions() const;
QString setting(const QString &viewKey, const QString &value = QString());
private:
void buildSettingModelDefaults();
///add a new setting to the model and return it
Setting *createSetting (CSMSettings::SettingType type, const QString &name,
const QString& label);
/// Set the section for createSetting calls.
///
/// Sections can be declared multiple times.
void declareSection (const QString& page, const QString& label);
signals:
void userSettingUpdated (const QString &, const QStringList &);
public slots:
void updateUserSetting (const QString &, const QStringList &);
};
}
#endif // USERSETTINGS_HPP

@ -1,4 +1,3 @@
#include "birthsigncheck.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "classcheck.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "factioncheck.hpp"
#include <sstream>

@ -0,0 +1,133 @@
#include "magiceffectcheck.hpp"
#include <components/misc/resourcehelpers.hpp>
#include "../world/resources.hpp"
#include "../world/data.hpp"
namespace
{
void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string text)
{
if (!text.empty())
{
messages.push_back(std::make_pair(id, text));
}
}
}
bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const
{
const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures;
bool exists = false;
if (textures.searchId(texture) != -1)
{
exists = true;
}
else
{
std::string ddsTexture = texture;
if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1)
{
exists = true;
}
}
return exists;
}
std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id,
const CSMWorld::UniversalId &type,
const std::string &column) const
{
std::string error;
if (!id.empty())
{
CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id);
if (index.first == -1)
{
error = "No such " + column + " '" + id + "'";
}
else if (index.second != type.getType())
{
error = column + " is not of type " + type.getTypeName();
}
}
return error;
}
std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const
{
std::string error;
if (!id.empty() && mSounds.searchId(id) == -1)
{
error = "No such " + column + " '" + id + "'";
}
return error;
}
CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables,
const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures)
: mMagicEffects(effects),
mSounds(sounds),
mReferenceables(referenceables),
mIcons(icons),
mTextures(textures)
{}
int CSMTools::MagicEffectCheckStage::setup()
{
return mMagicEffects.getSize();
}
void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
ESM::MagicEffect effect = mMagicEffects.getRecord(stage).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
if (effect.mData.mBaseCost < 0.0f)
{
messages.push_back(std::make_pair(id, "Base Cost is negative"));
}
if (effect.mIcon.empty())
{
messages.push_back(std::make_pair(id, "Icon is not specified"));
}
else if (!isTextureExists(effect.mIcon, true))
{
messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'"));
}
if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false))
{
messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'"));
}
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound"));
if (effect.mDescription.empty())
{
messages.push_back(std::make_pair(id, "Description is empty"));
}
}

@ -0,0 +1,50 @@
#ifndef CSM_TOOLS_MAGICEFFECTCHECK_HPP
#define CSM_TOOLS_MAGICEFFECTCHECK_HPP
#include <components/esm/loadmgef.hpp>
#include <components/esm/loadsoun.hpp>
#include "../world/idcollection.hpp"
#include "../world/refidcollection.hpp"
#include "../doc/stage.hpp"
namespace CSMWorld
{
class Resources;
}
namespace CSMTools
{
/// \brief VerifyStage: make sure that magic effect records are internally consistent
class MagicEffectCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::MagicEffect> &mMagicEffects;
const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables;
const CSMWorld::Resources &mIcons;
const CSMWorld::Resources &mTextures;
private:
bool isTextureExists(const std::string &texture, bool isIcon) const;
std::string checkReferenceable(const std::string &id,
const CSMWorld::UniversalId &type,
const std::string &column) const;
std::string checkSound(const std::string &id, const std::string &column) const;
public:
MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables,
const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages &messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

@ -1,4 +1,3 @@
#include "mandatoryid.hpp"
#include "../world/collectionbase.hpp"

@ -0,0 +1,59 @@
#include "mergeoperation.hpp"
#include "../doc/state.hpp"
#include "../doc/document.hpp"
#include "mergestages.hpp"
CSMTools::MergeOperation::MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding)
: CSMDoc::Operation (CSMDoc::State_Merging, true), mState (document)
{
appendStage (new StartMergeStage (mState));
appendStage (new MergeIdCollectionStage<ESM::Global> (mState, &CSMWorld::Data::getGlobals));
appendStage (new MergeIdCollectionStage<ESM::GameSetting> (mState, &CSMWorld::Data::getGmsts));
appendStage (new MergeIdCollectionStage<ESM::Skill> (mState, &CSMWorld::Data::getSkills));
appendStage (new MergeIdCollectionStage<ESM::Class> (mState, &CSMWorld::Data::getClasses));
appendStage (new MergeIdCollectionStage<ESM::Faction> (mState, &CSMWorld::Data::getFactions));
appendStage (new MergeIdCollectionStage<ESM::Race> (mState, &CSMWorld::Data::getRaces));
appendStage (new MergeIdCollectionStage<ESM::Sound> (mState, &CSMWorld::Data::getSounds));
appendStage (new MergeIdCollectionStage<ESM::Script> (mState, &CSMWorld::Data::getScripts));
appendStage (new MergeIdCollectionStage<ESM::Region> (mState, &CSMWorld::Data::getRegions));
appendStage (new MergeIdCollectionStage<ESM::BirthSign> (mState, &CSMWorld::Data::getBirthsigns));
appendStage (new MergeIdCollectionStage<ESM::Spell> (mState, &CSMWorld::Data::getSpells));
appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getTopics));
appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getJournals));
appendStage (new MergeIdCollectionStage<CSMWorld::Cell> (mState, &CSMWorld::Data::getCells));
appendStage (new MergeIdCollectionStage<ESM::Filter> (mState, &CSMWorld::Data::getFilters));
appendStage (new MergeIdCollectionStage<ESM::Enchantment> (mState, &CSMWorld::Data::getEnchantments));
appendStage (new MergeIdCollectionStage<ESM::BodyPart> (mState, &CSMWorld::Data::getBodyParts));
appendStage (new MergeIdCollectionStage<ESM::DebugProfile> (mState, &CSMWorld::Data::getDebugProfiles));
appendStage (new MergeIdCollectionStage<ESM::SoundGenerator> (mState, &CSMWorld::Data::getSoundGens));
appendStage (new MergeIdCollectionStage<ESM::MagicEffect> (mState, &CSMWorld::Data::getMagicEffects));
appendStage (new MergeIdCollectionStage<ESM::StartScript> (mState, &CSMWorld::Data::getStartScripts));
appendStage (new MergeIdCollectionStage<CSMWorld::Pathgrid, CSMWorld::SubCellCollection<CSMWorld::Pathgrid> > (mState, &CSMWorld::Data::getPathgrids));
appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getTopicInfos));
appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getJournalInfos));
appendStage (new MergeRefIdsStage (mState));
appendStage (new MergeReferencesStage (mState));
appendStage (new MergeReferencesStage (mState));
appendStage (new ListLandTexturesMergeStage (mState));
appendStage (new MergeLandTexturesStage (mState));
appendStage (new MergeLandStage (mState));
appendStage (new FinishMergedDocumentStage (mState, encoding));
}
void CSMTools::MergeOperation::setTarget (std::auto_ptr<CSMDoc::Document> document)
{
mState.mTarget = document;
}
void CSMTools::MergeOperation::operationDone()
{
CSMDoc::Operation::operationDone();
if (mState.mCompleted)
emit mergeDone (mState.mTarget.release());
}

@ -0,0 +1,45 @@
#ifndef CSM_TOOLS_MERGEOPERATION_H
#define CSM_TOOLS_MERGEOPERATION_H
#include <memory>
#include <components/to_utf8/to_utf8.hpp>
#include "../doc/operation.hpp"
#include "mergestate.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSMTools
{
class MergeOperation : public CSMDoc::Operation
{
Q_OBJECT
MergeState mState;
public:
MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding);
/// \attention Do not call this function while a merge is running.
void setTarget (std::auto_ptr<CSMDoc::Document> document);
protected slots:
virtual void operationDone();
signals:
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
};
}
#endif

@ -0,0 +1,258 @@
#include "mergestages.hpp"
#include <sstream>
#include <components/misc/stringops.hpp>
#include "mergestate.hpp"
#include "../doc/document.hpp"
#include "../world/data.hpp"
CSMTools::StartMergeStage::StartMergeStage (MergeState& state)
: mState (state)
{}
int CSMTools::StartMergeStage::setup()
{
return 1;
}
void CSMTools::StartMergeStage::perform (int stage, CSMDoc::Messages& messages)
{
mState.mCompleted = false;
mState.mTextureIndices.clear();
}
CSMTools::FinishMergedDocumentStage::FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding)
: mState (state), mEncoder (encoding)
{}
int CSMTools::FinishMergedDocumentStage::setup()
{
return 1;
}
void CSMTools::FinishMergedDocumentStage::perform (int stage, CSMDoc::Messages& messages)
{
// We know that the content file list contains at least two entries and that the first one
// does exist on disc (otherwise it would have been impossible to initiate a merge on that
// document).
boost::filesystem::path path = mState.mSource.getContentFiles()[0];
ESM::ESMReader reader;
reader.setEncoder (&mEncoder);
reader.open (path.string());
CSMWorld::MetaData source;
source.mId = "sys::meta";
source.load (reader);
CSMWorld::MetaData target = mState.mTarget->getData().getMetaData();
target.mAuthor = source.mAuthor;
target.mDescription = source.mDescription;
mState.mTarget->getData().setMetaData (target);
mState.mCompleted = true;
}
CSMTools::MergeRefIdsStage::MergeRefIdsStage (MergeState& state) : mState (state) {}
int CSMTools::MergeRefIdsStage::setup()
{
return mState.mSource.getData().getReferenceables().getSize();
}
void CSMTools::MergeRefIdsStage::perform (int stage, CSMDoc::Messages& messages)
{
mState.mSource.getData().getReferenceables().copyTo (
stage, mState.mTarget->getData().getReferenceables());
}
CSMTools::MergeReferencesStage::MergeReferencesStage (MergeState& state)
: mState (state)
{}
int CSMTools::MergeReferencesStage::setup()
{
mIndex.clear();
return mState.mSource.getData().getReferences().getSize();
}
void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::CellRef>& record =
mState.mSource.getData().getReferences().getRecord (stage);
if (!record.isDeleted())
{
CSMWorld::CellRef ref = record.get();
ref.mOriginalCell = ref.mCell;
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
ref.mRefNum.mContentFile = 0;
CSMWorld::Record<CSMWorld::CellRef> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
mState.mTarget->getData().getReferences().appendRecord (newRecord);
}
}
CSMTools::ListLandTexturesMergeStage::ListLandTexturesMergeStage (MergeState& state)
: mState (state)
{}
int CSMTools::ListLandTexturesMergeStage::setup()
{
return mState.mSource.getData().getLand().getSize();
}
void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& record =
mState.mSource.getData().getLand().getRecord (stage);
if (!record.isDeleted())
{
const CSMWorld::Land& land = record.get();
// make sure record is loaded
land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);
if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX))
{
// list texture indices
std::pair<uint16_t, int> key;
key.second = land.mPlugin;
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i)
{
key.first = data->mTextures[i];
mState.mTextureIndices[key] = -1;
}
}
}
}
CSMTools::MergeLandTexturesStage::MergeLandTexturesStage (MergeState& state)
: mState (state), mNext (mState.mTextureIndices.end())
{}
int CSMTools::MergeLandTexturesStage::setup()
{
// Should use the size of mState.mTextureIndices instead, but that is not available at this
// point. Unless there are any errors in the land and land texture records this will not
// make a difference.
return mState.mSource.getData().getLandTextures().getSize();
}
void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& messages)
{
if (stage==0)
mNext = mState.mTextureIndices.begin();
bool found = false;
do
{
if (mNext==mState.mTextureIndices.end())
return;
mNext->second = stage+1;
std::ostringstream stream;
stream << mNext->first.first-1 << "_" << mNext->first.second;
int index = mState.mSource.getData().getLandTextures().searchId (stream.str());
if (index!=-1)
{
CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get();
std::ostringstream stream;
stream << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1;
texture.mId = stream.str();
CSMWorld::Record<CSMWorld::LandTexture> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture);
mState.mTarget->getData().getLandTextures().appendRecord (newRecord);
found = true;
}
++mNext;
}
while (!found);
}
CSMTools::MergeLandStage::MergeLandStage (MergeState& state) : mState (state) {}
int CSMTools::MergeLandStage::setup()
{
return mState.mSource.getData().getLand().getSize();
}
void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& record =
mState.mSource.getData().getLand().getRecord (stage);
if (!record.isDeleted())
{
const CSMWorld::Land& land = record.get();
land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);
CSMWorld::Land newLand (land);
newLand.mEsm = 0; // avoid potential dangling pointer (ESMReader isn't needed anyway,
// because record is already fully loaded)
newLand.mPlugin = 0;
if (land.mDataTypes & ESM::Land::DATA_VTEX)
{
// adjust land texture references
if (ESM::Land::LandData *data = newLand.getLandData())
{
std::pair<uint16_t, int> key;
key.second = land.mPlugin;
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i)
{
key.first = data->mTextures[i];
std::map<std::pair<uint16_t, int>, int>::const_iterator iter =
mState.mTextureIndices.find (key);
if (iter!=mState.mTextureIndices.end())
data->mTextures[i] = iter->second;
else
data->mTextures[i] = 0;
}
}
}
CSMWorld::Record<CSMWorld::Land> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand);
mState.mTarget->getData().getLand().appendRecord (newRecord);
}
}

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

Loading…
Cancel
Save