mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-29 13:27:59 +03:00
Imported Upstream version 0.26.0
This commit is contained in:
commit
9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions
39
.travis.yml
Normal file
39
.travis.yml
Normal file
|
@ -0,0 +1,39 @@
|
|||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- next
|
||||
before_install:
|
||||
- pwd
|
||||
- git submodule update --init --recursive
|
||||
- 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/deps
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock libzzip-dev
|
||||
- sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev
|
||||
- sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev
|
||||
- sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev
|
||||
- sudo apt-get install -qq libbullet-dev libogre-static-dev libmygui-static-dev libsdl2-static-dev libunshield-dev
|
||||
- sudo mkdir /usr/src/gtest/build
|
||||
- cd /usr/src/gtest/build
|
||||
- sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||
- sudo make -j4
|
||||
- sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
||||
- sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
||||
before_script:
|
||||
- cd -
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DOGRE_STATIC=1 -DMYGUI_STATIC=1 -DBOOST_STATIC=1 -DSDL2_STATIC=1 -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1
|
||||
script:
|
||||
- make -j4
|
||||
after_script:
|
||||
- ./openmw_test_suite
|
||||
notifications:
|
||||
recipients:
|
||||
- lgromanowski+travis.ci@gmail.com
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
722
CMakeLists.txt
Normal file
722
CMakeLists.txt
Normal file
|
@ -0,0 +1,722 @@
|
|||
project(OpenMW)
|
||||
|
||||
if (APPLE)
|
||||
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
|
||||
|
||||
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-F /Library/Frameworks")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-F /Library/Frameworks")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "-F /Library/Frameworks")
|
||||
endif (APPLE)
|
||||
|
||||
# Macros
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
||||
|
||||
include (OpenMWMacros)
|
||||
|
||||
# Version
|
||||
|
||||
set (OPENMW_VERSION_MAJOR 0)
|
||||
set (OPENMW_VERSION_MINOR 26)
|
||||
set (OPENMW_VERSION_RELEASE 0)
|
||||
|
||||
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
||||
|
||||
# doxygen main page
|
||||
|
||||
configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_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)
|
||||
|
||||
# Apps and tools
|
||||
option(BUILD_BSATOOL "build BSA extractor" OFF)
|
||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||
option(BUILD_LAUNCHER "build Launcher" ON)
|
||||
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
||||
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
|
||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF)
|
||||
|
||||
# Sound source selection
|
||||
option(USE_FFMPEG "use ffmpeg for sound" ON)
|
||||
option(USE_AUDIERE "use audiere for sound" ON)
|
||||
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
|
||||
|
||||
# OS X deployment
|
||||
option(OPENMW_OSX_DEPLOYMENT OFF)
|
||||
|
||||
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
||||
|
||||
# Location of morrowind data files
|
||||
if (APPLE)
|
||||
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files")
|
||||
elseif(UNIX)
|
||||
set(MORROWIND_DATA_FILES "/usr/share/games/openmw/data/" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
|
||||
else()
|
||||
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
|
||||
endif(APPLE)
|
||||
|
||||
if (WIN32)
|
||||
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
|
||||
endif()
|
||||
|
||||
# We probably support older versions than this.
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
# source directory: libs
|
||||
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
|
||||
|
||||
set(OENGINE_OGRE
|
||||
${LIBDIR}/openengine/ogre/renderer.cpp
|
||||
${LIBDIR}/openengine/ogre/fader.cpp
|
||||
${LIBDIR}/openengine/ogre/lights.cpp
|
||||
${LIBDIR}/openengine/ogre/particles.cpp
|
||||
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
|
||||
${LIBDIR}/openengine/ogre/imagerotate.cpp
|
||||
)
|
||||
|
||||
set(OENGINE_GUI
|
||||
${LIBDIR}/openengine/gui/manager.cpp
|
||||
)
|
||||
|
||||
set(OENGINE_BULLET
|
||||
${LIBDIR}/openengine/bullet/btKinematicCharacterController.cpp
|
||||
${LIBDIR}/openengine/bullet/btKinematicCharacterController.h
|
||||
${LIBDIR}/openengine/bullet/BtOgre.cpp
|
||||
${LIBDIR}/openengine/bullet/BtOgreExtras.h
|
||||
${LIBDIR}/openengine/bullet/BtOgreGP.h
|
||||
${LIBDIR}/openengine/bullet/BtOgrePG.h
|
||||
${LIBDIR}/openengine/bullet/CMotionState.cpp
|
||||
${LIBDIR}/openengine/bullet/CMotionState.h
|
||||
${LIBDIR}/openengine/bullet/physic.cpp
|
||||
${LIBDIR}/openengine/bullet/physic.hpp
|
||||
${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
|
||||
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
|
||||
${LIBDIR}/openengine/bullet/trace.cpp
|
||||
${LIBDIR}/openengine/bullet/trace.h
|
||||
|
||||
)
|
||||
|
||||
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_BULLET})
|
||||
source_group(libs\\openengine FILES ${OENGINE_ALL})
|
||||
|
||||
set(OPENMW_LIBS ${OENGINE_ALL})
|
||||
set(OPENMW_LIBS_HEADER)
|
||||
|
||||
# Sound setup
|
||||
set(GOT_SOUND_INPUT 0)
|
||||
set(SOUND_INPUT_INCLUDES "")
|
||||
set(SOUND_INPUT_LIBRARY "")
|
||||
set(SOUND_DEFINE "")
|
||||
if (USE_FFMPEG)
|
||||
set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE)
|
||||
find_package(FFmpeg)
|
||||
if (FFMPEG_FOUND)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG)
|
||||
set(GOT_SOUND_INPUT 1)
|
||||
endif (FFMPEG_FOUND)
|
||||
endif (USE_FFMPEG)
|
||||
|
||||
if (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
|
||||
find_package(Audiere)
|
||||
if (AUDIERE_FOUND)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE)
|
||||
set(GOT_SOUND_INPUT 1)
|
||||
endif (AUDIERE_FOUND)
|
||||
endif (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
|
||||
|
||||
if (USE_MPG123 AND NOT GOT_SOUND_INPUT)
|
||||
find_package(MPG123 REQUIRED)
|
||||
find_package(SNDFILE REQUIRED)
|
||||
if (MPG123_FOUND AND SNDFILE_FOUND)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
|
||||
set(GOT_SOUND_INPUT 1)
|
||||
endif (MPG123_FOUND AND SNDFILE_FOUND)
|
||||
endif (USE_MPG123 AND NOT GOT_SOUND_INPUT)
|
||||
|
||||
if (NOT GOT_SOUND_INPUT)
|
||||
message(WARNING "--------------------")
|
||||
message(WARNING "Failed to find any sound input packages")
|
||||
message(WARNING "--------------------")
|
||||
endif (NOT GOT_SOUND_INPUT)
|
||||
|
||||
if (NOT FFMPEG_FOUND)
|
||||
message(WARNING "--------------------")
|
||||
message(WARNING "FFmpeg not found, video playback will be disabled")
|
||||
message(WARNING "--------------------")
|
||||
endif (NOT FFMPEG_FOUND)
|
||||
|
||||
|
||||
# Platform specific
|
||||
if (WIN32)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(PLATFORM_INCLUDE_DIR "platform")
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
else (WIN32)
|
||||
set(PLATFORM_INCLUDE_DIR "")
|
||||
find_path (UUID_INCLUDE_DIR uuid/uuid.h)
|
||||
include_directories(${UUID_INCLUDE_DIR})
|
||||
endif (WIN32)
|
||||
if (MSVC10)
|
||||
set(PLATFORM_INCLUDE_DIR "")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif (APPLE)
|
||||
|
||||
# Dependencies
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package (Threads)
|
||||
endif()
|
||||
|
||||
include (CheckIncludeFileCXX)
|
||||
check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP)
|
||||
if (HAVE_UNORDERED_MAP)
|
||||
add_definitions(-DHAVE_UNORDERED_MAP)
|
||||
endif ()
|
||||
|
||||
|
||||
set(BOOST_COMPONENTS system filesystem program_options thread date_time wave)
|
||||
|
||||
IF(BOOST_STATIC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif()
|
||||
|
||||
find_package(OGRE REQUIRED)
|
||||
find_package(MyGUI REQUIRED)
|
||||
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(Bullet REQUIRED)
|
||||
IF(OGRE_STATIC)
|
||||
find_package(Cg)
|
||||
IF(WIN32)
|
||||
set(OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_Plugin_CgProgramManager_INCLUDE_DIRS} ${OGRE_Plugin_OctreeSceneManager_INCLUDE_DIRS} ${OGRE_Plugin_ParticleFX_INCLUDE_DIRS} ${OGRE_RenderSystem_Direct3D9_INCLUDE_DIRS} ${OGRE_RenderSystem_GL_INCLUDE_DIRS})
|
||||
ELSE(WIN32)
|
||||
set(OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_Plugin_CgProgramManager_INCLUDE_DIRS} ${OGRE_Plugin_OctreeSceneManager_INCLUDE_DIRS} ${OGRE_Plugin_ParticleFX_INCLUDE_DIRS} ${OGRE_RenderSystem_GL_INCLUDE_DIRS})
|
||||
ENDIF(WIN32)
|
||||
ENDIF(OGRE_STATIC)
|
||||
include_directories("."
|
||||
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
|
||||
${SDL2_INCLUDE_DIR}
|
||||
${Boost_INCLUDE_DIR}
|
||||
${PLATFORM_INCLUDE_DIR}
|
||||
${MYGUI_INCLUDE_DIRS}
|
||||
${MYGUI_PLATFORM_INCLUDE_DIRS}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${UUID_INCLUDE_DIR}
|
||||
${LIBDIR}
|
||||
)
|
||||
|
||||
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MYGUI_LIB_DIR})
|
||||
|
||||
if (APPLE)
|
||||
# List used Ogre plugins
|
||||
SET(USED_OGRE_PLUGINS ${OGRE_RenderSystem_GL_LIBRARY_REL}
|
||||
${OGRE_Plugin_OctreeSceneManager_LIBRARY_REL}
|
||||
${OGRE_Plugin_CgProgramManager_LIBRARY_REL}
|
||||
${OGRE_Plugin_ParticleFX_LIBRARY_REL})
|
||||
|
||||
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 ()
|
||||
|
||||
#set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist
|
||||
"${APP_BUNDLE_DIR}/Contents/Info.plist")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns
|
||||
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
|
||||
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()
|
||||
|
||||
add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}")
|
||||
add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}")
|
||||
if (APPLE AND OPENMW_OSX_DEPLOYMENT)
|
||||
add_definitions(-DOGRE_PLUGIN_DIR="${APP_BUNDLE_NAME}/Contents/Plugins")
|
||||
else()
|
||||
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")
|
||||
else (APPLE)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
|
||||
endif (APPLE)
|
||||
|
||||
# Other files
|
||||
|
||||
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.cfg
|
||||
"${OpenMW_BINARY_DIR}/opencs.cfg")
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
||||
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop
|
||||
"${OpenMW_BINARY_DIR}/opencs.desktop")
|
||||
endif()
|
||||
|
||||
# Compiler settings
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
SET(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# Silence warnings in OGRE headers. Remove once OGRE got fixed!
|
||||
SET(CMAKE_CXX_FLAGS "-Wno-ignored-qualifiers ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||
OUTPUT_VARIABLE GCC_VERSION)
|
||||
if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||
SET(CMAKE_CXX_FLAGS "-Wno-unused-but-set-parameter ${CMAKE_CXX_FLAGS}")
|
||||
endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||
endif (CMAKE_COMPILER_IS_GNUCC)
|
||||
|
||||
IF(NOT WIN32 AND NOT APPLE)
|
||||
## Debian and non debian Linux building
|
||||
# Paths
|
||||
IF (DPKG_PROGRAM)
|
||||
## Debian specific
|
||||
SET(CMAKE_INSTALL_PREFIX "/usr")
|
||||
SET(DATAROOTDIR "share" CACHE PATH "Sets the root of data directories to a non-default location")
|
||||
SET(DATADIR "share/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
|
||||
SET(ICONDIR "share/pixmaps" CACHE PATH "Set icon dir")
|
||||
SET(SYSCONFDIR "../etc/openmw" CACHE PATH "Set config dir")
|
||||
ELSE ()
|
||||
## Non debian specific
|
||||
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
|
||||
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
|
||||
SET(DATADIR "${DATAROOTDIR}/games/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.")
|
||||
SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir")
|
||||
|
||||
# Install binaries
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
|
||||
IF(BUILD_LAUNCHER)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_LAUNCHER)
|
||||
IF(BUILD_BSATOOL)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_BSATOOL)
|
||||
IF(BUILD_ESMTOOL)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_ESMTOOL)
|
||||
IF(BUILD_MWINIIMPORTER)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_MWINIIMPORTER)
|
||||
IF(BUILD_OPENCS)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_OPENCS)
|
||||
|
||||
# Install licenses
|
||||
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "Daedric Font License.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
|
||||
ENDIF (DPKG_PROGRAM)
|
||||
|
||||
# Install icon and desktop file
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
IF(BUILD_OPENCS)
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
|
||||
ENDIF(BUILD_OPENCS)
|
||||
|
||||
# Install global configuration files
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
IF(BUILD_OPENCS)
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
|
||||
ENDIF(BUILD_OPENCS)
|
||||
|
||||
# Install resources
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
|
||||
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
|
||||
|
||||
IF (DPKG_PROGRAM)
|
||||
## Debian Specific
|
||||
IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION )
|
||||
STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}")
|
||||
EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME )
|
||||
EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
|
||||
SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
|
||||
ELSE()
|
||||
SET(VERSION_STRING "${OPENMW_VERSION}")
|
||||
SET(PACKAGE_MAINTAINER "unknown")
|
||||
ENDIF()
|
||||
|
||||
SET(CPACK_GENERATOR "DEB")
|
||||
SET(CPACK_PACKAGE_NAME "openmw")
|
||||
SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org")
|
||||
SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}")
|
||||
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind
|
||||
OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind.
|
||||
Data files from the original game is required to run it.")
|
||||
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
|
||||
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
|
||||
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
|
||||
|
||||
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
|
||||
|
||||
STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${DPKG_PROGRAM} --print-architecture
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
|
||||
INCLUDE(CPack)
|
||||
ENDIF(DPKG_PROGRAM)
|
||||
ENDIF(NOT WIN32 AND NOT APPLE)
|
||||
|
||||
if(WIN32)
|
||||
FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll")
|
||||
INSTALL(FILES ${dll_files} DESTINATION ".")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
|
||||
INSTALL(FILES
|
||||
"${OpenMW_SOURCE_DIR}/readme.txt"
|
||||
"${OpenMW_SOURCE_DIR}/GPL3.txt"
|
||||
"${OpenMW_SOURCE_DIR}/OFL.txt"
|
||||
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt"
|
||||
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
|
||||
"${OpenMW_BINARY_DIR}/settings-default.cfg"
|
||||
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
|
||||
"${OpenMW_BINARY_DIR}/Release/mwiniimport.exe"
|
||||
"${OpenMW_BINARY_DIR}/Release/omwlauncher.exe"
|
||||
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
|
||||
DESTINATION ".")
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
|
||||
|
||||
SET(CPACK_GENERATOR "NSIS")
|
||||
SET(CPACK_PACKAGE_NAME "OpenMW")
|
||||
SET(CPACK_PACKAGE_VENDOR "OpenMW.org")
|
||||
SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
||||
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher")
|
||||
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
|
||||
SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\"
|
||||
")
|
||||
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt")
|
||||
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
|
||||
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
|
||||
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
|
||||
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
|
||||
SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe")
|
||||
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.ico")
|
||||
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.ico")
|
||||
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
|
||||
|
||||
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
|
||||
if(EXISTS ${VCREDIST32})
|
||||
INSTALL(FILES ${VCREDIST32} DESTINATION "redist")
|
||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" )
|
||||
endif(EXISTS ${VCREDIST32})
|
||||
|
||||
SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe")
|
||||
if(EXISTS ${VCREDIST64})
|
||||
INSTALL(FILES ${VCREDIST64} DESTINATION "redist")
|
||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" )
|
||||
endif(EXISTS ${VCREDIST64})
|
||||
|
||||
SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe")
|
||||
if(EXISTS ${OALREDIST})
|
||||
INSTALL(FILES ${OALREDIST} DESTINATION "redist")
|
||||
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
|
||||
ExecWait '\\\"$INSTDIR\\\\redist\\\\oalinst.exe\\\" /s'" )
|
||||
endif(EXISTS ${OALREDIST})
|
||||
|
||||
if(CMAKE_CL_64)
|
||||
SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
endif(WIN32)
|
||||
|
||||
# Extern
|
||||
add_subdirectory (extern/shiny)
|
||||
add_subdirectory (extern/oics)
|
||||
add_subdirectory (extern/sdl4ogre)
|
||||
|
||||
# Components
|
||||
add_subdirectory (components)
|
||||
|
||||
# Apps and tools
|
||||
add_subdirectory( apps/openmw )
|
||||
|
||||
if (BUILD_BSATOOL)
|
||||
add_subdirectory( apps/bsatool )
|
||||
endif()
|
||||
|
||||
if (BUILD_ESMTOOL)
|
||||
add_subdirectory( apps/esmtool )
|
||||
endif()
|
||||
|
||||
if (BUILD_LAUNCHER)
|
||||
if(NOT WIN32)
|
||||
find_package(LIBUNSHIELD REQUIRED)
|
||||
if(NOT LIBUNSHIELD_FOUND)
|
||||
message(SEND_ERROR "Failed to find libunshield")
|
||||
endif(NOT LIBUNSHIELD_FOUND)
|
||||
endif(NOT WIN32)
|
||||
|
||||
add_subdirectory( apps/launcher )
|
||||
endif()
|
||||
|
||||
if (BUILD_MWINIIMPORTER)
|
||||
add_subdirectory( apps/mwiniimporter )
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENCS)
|
||||
add_subdirectory (apps/opencs)
|
||||
endif()
|
||||
|
||||
# UnitTests
|
||||
if (BUILD_UNITTESTS)
|
||||
add_subdirectory( apps/openmw_test_suite )
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (MSVC)
|
||||
if (USE_DEBUG_CONSOLE)
|
||||
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()
|
||||
# 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")
|
||||
|
||||
# Play a bit with the warning levels
|
||||
|
||||
set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them
|
||||
|
||||
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
|
||||
|
||||
# Warnings that are thrown on standard libraries and not OpenMW
|
||||
4347 # Non-template function with same name and parameter count as template function
|
||||
4365 # Variable signed/unsigned mismatch
|
||||
4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base
|
||||
4706 # Assignment in conditional expression
|
||||
4738 # Storing 32-bit float result in memory, possible loss of performance
|
||||
4986 # Undocumented warning that occurs in the crtdbg.h file
|
||||
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'
|
||||
|
||||
# OpenMW specific warnings
|
||||
4099 # Type mismatch, declared class or struct is defined with other type
|
||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||
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)
|
||||
4305 # Truncating value (double to float, for example)
|
||||
4309 # Variable overflow, trying to store 128 in a signed char for example
|
||||
4355 # Using 'this' in member initialization list
|
||||
4701 # Potentially uninitialized local variable used
|
||||
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
|
||||
)
|
||||
|
||||
foreach(d ${WARNINGS_DISABLE})
|
||||
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||
endforeach(d)
|
||||
|
||||
set_target_properties(shiny PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
if (BUILD_LAUNCHER)
|
||||
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_LAUNCHER)
|
||||
set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
if (BUILD_BSATOOL)
|
||||
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_BSATOOL)
|
||||
if (BUILD_ESMTOOL)
|
||||
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_ESMTOOL)
|
||||
endif(MSVC)
|
||||
|
||||
# Same for MinGW
|
||||
if (MINGW)
|
||||
if (USE_DEBUG_CONSOLE)
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "-Wl,-subsystem,console")
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "-Wl,-subsystem,console")
|
||||
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
|
||||
else(USE_DEBUG_CONSOLE)
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "-Wl,-subsystem,windows")
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "-Wl,-subsystem,windows")
|
||||
endif(USE_DEBUG_CONSOLE)
|
||||
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "-Wl,-subsystem,console")
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "-Wl,-subsystem,console")
|
||||
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
|
||||
endif(MINGW)
|
||||
|
||||
# TODO: At some point release builds should not use the console but rather write to a log file
|
||||
#set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||
#set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
# Apple bundling
|
||||
if (APPLE)
|
||||
set(INSTALL_SUBDIR OpenMW)
|
||||
|
||||
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||
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}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||
install(FILES "${OpenMW_BINARY_DIR}/opencs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
|
||||
set(PLUGINS "")
|
||||
set(ABSOLUTE_PLUGINS "")
|
||||
|
||||
foreach (PLUGIN ${USED_OGRE_PLUGINS})
|
||||
get_filename_component(PLUGIN_ABS ${PLUGIN} REALPATH)
|
||||
set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})
|
||||
endforeach ()
|
||||
|
||||
set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins")
|
||||
install(FILES ${ABSOLUTE_PLUGINS} DESTINATION "${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins" COMPONENT Runtime)
|
||||
foreach (PLUGIN ${ABSOLUTE_PLUGINS})
|
||||
get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME)
|
||||
set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}")
|
||||
endforeach ()
|
||||
|
||||
#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 "
|
||||
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()
|
||||
endif()
|
||||
endfunction(gp_resolve_item_override)
|
||||
|
||||
cmake_policy(SET CMP0009 OLD)
|
||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(\"${APPS}\" \"${PLUGINS}\" \"${DIRS}\")
|
||||
" COMPONENT Runtime)
|
||||
include(CPack)
|
||||
endif (APPLE)
|
||||
|
10
Daedric Font License.txt
Normal file
10
Daedric Font License.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
Dongle's Oblivion Daedric font set
|
||||
http://www.uesp.net/wiki/Lore:Daedric_Alphabet#Daedric_Font
|
||||
|
||||
---------------------------------------------------
|
||||
|
||||
This was done entirely as a personal project. Bethesda Softworks graciously granted me the permission for it. I am not connected with them in any way.
|
||||
You may freely use these fonts to create anything you'd like. You may re-distribute the fonts freely, over the Internet, or by any other means. Always keep the .zip file intact, and this read me included.
|
||||
Please do not modify and redistribute the fonts without my permission.
|
||||
You may NOT sell any of these fonts under any circumstances. This includes putting them on compilation font CDs for sale, putting them in a "members only" pay-area of a website, or any other means of financial gain connected in ANY way with the redistribution of any of these fonts.
|
||||
You have my permission to create and sell any artwork made with these fonts, however you may need to contact Bethesda Softworks before doing so.
|
99
DejaVu Font License.txt
Normal file
99
DejaVu Font License.txt
Normal file
|
@ -0,0 +1,99 @@
|
|||
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
||||
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
|
||||
|
||||
Bitstream Vera Fonts Copyright
|
||||
------------------------------
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
|
||||
a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute the
|
||||
Font Software, including without limitation the rights to use, copy, merge,
|
||||
publish, distribute, and/or sell copies of the Font Software, and to permit
|
||||
persons to whom the Font Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice shall
|
||||
be included in all copies of one or more of the Font Software typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in particular
|
||||
the designs of glyphs or characters in the Fonts may be modified and
|
||||
additional glyphs or characters may be added to the Fonts, only if the fonts
|
||||
are renamed to names not containing either the words "Bitstream" or the word
|
||||
"Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts or Font
|
||||
Software that has been modified and is distributed under the "Bitstream
|
||||
Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but no
|
||||
copy of one or more of the Font Software typefaces may be sold by itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
||||
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
||||
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
|
||||
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
|
||||
FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font Software
|
||||
without prior written authorization from the Gnome Foundation or Bitstream
|
||||
Inc., respectively. For further information, contact: fonts at gnome dot
|
||||
org.
|
||||
|
||||
Arev Fonts Copyright
|
||||
------------------------------
|
||||
|
||||
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and
|
||||
associated documentation files (the "Font Software"), to reproduce
|
||||
and distribute the modifications to the Bitstream Vera Font Software,
|
||||
including without limitation the rights to use, copy, merge, publish,
|
||||
distribute, and/or sell copies of the Font Software, and to permit
|
||||
persons to whom the Font Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Tavmjong Bah" or the word "Arev".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Tavmjong Bah Arev" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Tavmjong Bah shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other
|
||||
dealings in this Font Software without prior written authorization
|
||||
from Tavmjong Bah. For further information, contact: tavmjong @ free
|
||||
. fr.
|
||||
|
||||
$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
|
1543
Docs/Doxyfile
Normal file
1543
Docs/Doxyfile
Normal file
File diff suppressed because it is too large
Load diff
1543
Docs/DoxyfilePages
Normal file
1543
Docs/DoxyfilePages
Normal file
File diff suppressed because it is too large
Load diff
5
Docs/mainpage.hpp.cmake
Normal file
5
Docs/mainpage.hpp.cmake
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// \mainpage
|
||||
///
|
||||
/// This is the source documentation for:
|
||||
///
|
||||
/// OpenMW @OPENMW_VERSION@
|
674
GPL3.txt
Normal file
674
GPL3.txt
Normal file
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
93
OFL.txt
Normal file
93
OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
160
README_Mac.md
Normal file
160
README_Mac.md
Normal file
|
@ -0,0 +1,160 @@
|
|||
#Getting OpenMW Working on OS X
|
||||
|
||||
## Initial setup
|
||||
First of all, clone OpenMW repo.
|
||||
|
||||
$ git clone github.com/zinnschlag/openmw
|
||||
|
||||
Or use your github url if you forked.
|
||||
|
||||
About dependencies: I prefer not to install them globally (i. e. in /usr/local/), so I'm installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs.
|
||||
|
||||
It's useful to create env var for lib install prefix:
|
||||
|
||||
$ export OMW_LIB_PREFIX=$HOME/path/libs/root`
|
||||
|
||||
Most of libs can be installed from [Homebrew][homebrew]. Only mpg123 needs to be installed from source (due to lack of universal compilation support). I think that some of libs can be installed from MacPorts or Fink too.
|
||||
|
||||
As OpenMW currently only supports i386 architecture on OS X, denendencies also should support it. Set some env vars in current terminal:
|
||||
|
||||
$ export CFLAGS="-arch i386"
|
||||
$ export CXXFLAGS="-arch i386"
|
||||
$ export LDFLAGS="-arch i386"
|
||||
|
||||
If you close your terminal, you should set env vars again before pcoceeding to next steps!
|
||||
|
||||
## Boost
|
||||
Download [boost][boost] and install it with the following command:
|
||||
|
||||
$ cd /path/to/boost/source
|
||||
$ ./bootstrap.sh --prefix=$OMW_LIB_PREFIX
|
||||
$ ./bjam --build-dir=build --layout=versioned \
|
||||
--toolset=darwin architecture=x86 address-model=32 \
|
||||
--link-shared,static --prefix=$OMW_LIB_PREFIX install
|
||||
|
||||
|
||||
Alternatively you can install boost with homebrew:
|
||||
|
||||
$ brew install boost --universal
|
||||
|
||||
I think MacPorts also should support universal build for boost.
|
||||
|
||||
## Ogre
|
||||
Download [Ogre][] SDK (tested with 1.7.3), unpack it somewhere and move
|
||||
`lib/Release/Ogre.framework` into `/Library/Frameworks`.
|
||||
|
||||
## OIS
|
||||
Download patched [OIS][] and use the XCode project provided. Be sure to set your build architecture to
|
||||
`i386`. Once it built, locate built OIS.framework with Xcode and move it to `/Library/Frameworks`.
|
||||
|
||||
## mpg123
|
||||
Download [MPG 123][mpg123] and build it:
|
||||
|
||||
$ cd /path/to/mpg123/source
|
||||
$ ./configure --prefix=$OMW_LIB_PREFIX --disable-debug \
|
||||
--disable-dependency-tracking \
|
||||
--with-optimization=4 \
|
||||
--with-audio=dummy \
|
||||
--with-default-audio=dummy \
|
||||
--with-cpu=sse_alone \
|
||||
$ make install
|
||||
|
||||
## libsndfile
|
||||
Download [libsndfile][] and build it:
|
||||
|
||||
$ cd /path/to/libsndfile/source
|
||||
$ ./configure --prefix=$OMW_LIB_PREFIX \
|
||||
--disable-dependency-tracking
|
||||
$ make install
|
||||
|
||||
or install with homebrew:
|
||||
|
||||
$ brew install libsndfile --universal
|
||||
|
||||
## Bullet
|
||||
Download [Bullet][] and build it:
|
||||
|
||||
$ cd /path/to/bullet/source
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$OMW_LIB_PREFIX \
|
||||
-DBUILD_EXTRAS=OFF \
|
||||
-DBUILD_DEMOS=OFF \
|
||||
-DCMAKE_OSX_ARCHITECTURES=i386 \
|
||||
-DCMAKE_INSTALL_NAME_DIR=$OMW_LIB_RPEFIX/lib \
|
||||
-G"Unix Makefiles" ../
|
||||
$ make install
|
||||
|
||||
or install with homebrew:
|
||||
|
||||
$ brew install bullet --HEAD --universal
|
||||
|
||||
I prefer head because 2.79 has some issue which causes OpenMW to lag. Also you can edit formula and install 2.77, which is stable and haven't mentioned issue.
|
||||
|
||||
## Qt
|
||||
Install [Qt][qt]. Qt SDK distributed by Nokia is not an option because it's 64 bit only, and OpenMW currently doesn't build for 64 bit on OS X. I'm installing it from Homebrew:
|
||||
|
||||
$ brew install qt --universal
|
||||
|
||||
## Run CMake
|
||||
Generate the Makefile for OpenMW as follows and build OpenMW:
|
||||
|
||||
$ mkdir /path/to/openmw/build/dir
|
||||
$ cd /path/to/open/build/dir
|
||||
$ cmake \
|
||||
-D CMAKE_OSX_ARCHITECTURES=i386 \
|
||||
-D OGRE_SDK=/path/to/ogre/sdk \
|
||||
-D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \
|
||||
-D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \
|
||||
-D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
|
||||
-D SNDFILE_LIBRARY=$OMW_LIB_PREFIX/lib/libsndfile.a \
|
||||
-D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \
|
||||
-D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
|
||||
-D BULLET_DYNAMICS_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletDynamics.a \
|
||||
-D BULLET_COLLISION_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletCollision.a \
|
||||
-D BULLET_MATH_LIBRARY=$OMW_LIB_PREFIX/lib/libLinearMath.a \
|
||||
-D BULLET_SOFTBODY_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletSoftBody.a \
|
||||
-D BULLET_INCLUDE_DIR=$OMW_LIB_PREFIX/include/bullet/ \
|
||||
-G "Unix Makefiles" /path/to/openmw/source/dir
|
||||
$ make
|
||||
|
||||
You can use `-G"Xcode"` if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles"
|
||||
if you prefer Eclipse. You also can specify `-D CMAKE_BUILD_TYPE=Debug` for debug
|
||||
build. As for CMake 2.8.7 and Xcode 4.3, Xcode generator is broken. Sadly Eclipse CDT also cannot import generated project at least on my machine.
|
||||
|
||||
If all libs installed via homebrew (excluding mpg123), then command would be even simplier:
|
||||
|
||||
$ cmake \
|
||||
-D CMAKE_OSX_ARCHITECTURES="i386" \
|
||||
-D OGRE_SDK=/path/to/ogre/sdk \
|
||||
-D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \
|
||||
-D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
|
||||
-G "Unix Makefiles" /path/to/openmw/source/dir
|
||||
$ make
|
||||
|
||||
Note for users with recent Xcode versions: you must explicitly specify what set of compilers do you use! If not, gcc will be used for C and Clang for C++. Just add this two -D's to command: `-D CMAKE_C_COMPILER=/usr/bin/clang` and `-D CMAKE_CXX_COMPILER=/usr/bin/clang`
|
||||
|
||||
Note for Xcode 4.3 users: you should specify full path to used SDK, because current CMake (2.8.7) couldn't find SDKs inside Xcode app bundle:
|
||||
|
||||
-D CMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk"
|
||||
|
||||
# Run
|
||||
From your build directory run:
|
||||
|
||||
$ OpenMW.app/Contents/MacOS/openmw
|
||||
or:
|
||||
|
||||
$ open OpenMW.app
|
||||
Enjoy!
|
||||
|
||||
[homebrew]: https://github.com/mxcl/homebrew
|
||||
[boost]: http://www.boost.org
|
||||
[Ogre]: http://www.ogre3d.org
|
||||
[Bullet]: http://bulletphysics.org
|
||||
[OIS]: https://github.com/corristo/ois-fork
|
||||
[mpg123]: http://www.mpg123.de
|
||||
[libsndfile]: http://www.mega-nerd.com/libsndfile
|
||||
[official website]: http://openmw.com
|
||||
[Will Thimbleby's Ogre Framework]: http://www.thimbleby.net/ogre/
|
||||
[qt]: http://qt.nokia.com/
|
19
apps/bsatool/CMakeLists.txt
Normal file
19
apps/bsatool/CMakeLists.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
set(BSATOOL
|
||||
bsatool.cpp
|
||||
)
|
||||
source_group(apps\\bsatool FILES ${BSATOOL})
|
||||
|
||||
# Main executable
|
||||
add_executable(bsatool
|
||||
${BSATOOL}
|
||||
)
|
||||
|
||||
target_link_libraries(bsatool
|
||||
${Boost_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(bsatool gcov)
|
||||
endif()
|
288
apps/bsatool/bsatool.cpp
Normal file
288
apps/bsatool/bsatool.cpp
Normal file
|
@ -0,0 +1,288 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <components/bsa/bsa_file.hpp>
|
||||
|
||||
#define BSATOOL_VERSION 1.1
|
||||
|
||||
// Create local aliases for brevity
|
||||
namespace bpo = boost::program_options;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
struct Arguments
|
||||
{
|
||||
std::string mode;
|
||||
std::string filename;
|
||||
std::string extractfile;
|
||||
std::string outdir;
|
||||
|
||||
bool longformat;
|
||||
bool fullpath;
|
||||
};
|
||||
|
||||
void replaceAll(std::string& str, const std::string& needle, const std::string& substitute)
|
||||
{
|
||||
int pos = str.find(needle);
|
||||
while(pos != -1)
|
||||
{
|
||||
str.replace(pos, needle.size(), substitute);
|
||||
pos = str.find(needle);
|
||||
}
|
||||
}
|
||||
|
||||
bool parseOptions (int argc, char** argv, Arguments &info)
|
||||
{
|
||||
bpo::options_description desc("Inspect and extract files from Bethesda BSA archives\n\n"
|
||||
"Usages:\n"
|
||||
" bsatool list [-l] archivefile\n"
|
||||
" List the files presents in the input archive.\n\n"
|
||||
" bsatool extract [-f] archivefile [file_to_extract] [output_directory]\n"
|
||||
" Extract a file from the input archive.\n\n"
|
||||
" bsatool extractall archivefile [output_directory]\n"
|
||||
" Extract all files from the input archive.\n\n"
|
||||
"Allowed options");
|
||||
|
||||
desc.add_options()
|
||||
("help,h", "print help message.")
|
||||
("version,v", "print version information and quit.")
|
||||
("long,l", "Include extra information in archive listing.")
|
||||
("full-path,f", "Create diretory hierarchy on file extraction "
|
||||
"(always true for extractall).")
|
||||
;
|
||||
|
||||
// input-file is hidden and used as a positional argument
|
||||
bpo::options_description hidden("Hidden Options");
|
||||
|
||||
hidden.add_options()
|
||||
( "mode,m", bpo::value<std::string>(), "bsatool mode")
|
||||
( "input-file,i", bpo::value< std::vector<std::string> >(), "input file")
|
||||
;
|
||||
|
||||
bpo::positional_options_description p;
|
||||
p.add("mode", 1).add("input-file", 3);
|
||||
|
||||
// there might be a better way to do this
|
||||
bpo::options_description all;
|
||||
all.add(desc).add(hidden);
|
||||
|
||||
bpo::variables_map variables;
|
||||
try
|
||||
{
|
||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
|
||||
.options(all).positional(p).run();
|
||||
bpo::store(valid_opts, variables);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bpo::notify(variables);
|
||||
|
||||
if (variables.count ("help"))
|
||||
{
|
||||
std::cout << desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (variables.count ("version"))
|
||||
{
|
||||
std::cout << "BSATool version " << BSATOOL_VERSION << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!variables.count("mode"))
|
||||
{
|
||||
std::cout << "ERROR: no mode specified!\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
info.mode = variables["mode"].as<std::string>();
|
||||
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall"))
|
||||
{
|
||||
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!variables.count("input-file"))
|
||||
{
|
||||
std::cout << "\nERROR: missing BSA archive\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
info.filename = variables["input-file"].as< std::vector<std::string> >()[0];
|
||||
|
||||
// Default output to the working directory
|
||||
info.outdir = ".";
|
||||
|
||||
if (info.mode == "extract")
|
||||
{
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() < 2)
|
||||
{
|
||||
std::cout << "\nERROR: file to extract unspecified\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.extractfile = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() > 2)
|
||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2];
|
||||
}
|
||||
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
|
||||
info.longformat = variables.count("long");
|
||||
info.fullpath = variables.count("full-path");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int list(Bsa::BSAFile& bsa, Arguments& info);
|
||||
int extract(Bsa::BSAFile& bsa, Arguments& info);
|
||||
int extractAll(Bsa::BSAFile& bsa, Arguments& info);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Arguments info;
|
||||
if(!parseOptions (argc, argv, info))
|
||||
return 1;
|
||||
|
||||
// Open file
|
||||
Bsa::BSAFile bsa;
|
||||
try
|
||||
{
|
||||
bsa.open(info.filename);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << "ERROR reading BSA archive '" << info.filename
|
||||
<< "'\nDetails:\n" << e.what() << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (info.mode == "list")
|
||||
return list(bsa, info);
|
||||
else if (info.mode == "extract")
|
||||
return extract(bsa, info);
|
||||
else if (info.mode == "extractall")
|
||||
return extractAll(bsa, info);
|
||||
else
|
||||
{
|
||||
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int list(Bsa::BSAFile& bsa, Arguments& info)
|
||||
{
|
||||
// List all files
|
||||
const Bsa::BSAFile::FileList &files = bsa.getList();
|
||||
for(int i=0; i<files.size(); i++)
|
||||
{
|
||||
if(info.longformat)
|
||||
{
|
||||
// Long format
|
||||
std::cout << std::setw(50) << std::left << files[i].name;
|
||||
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize;
|
||||
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl;
|
||||
}
|
||||
else
|
||||
std::cout << files[i].name << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extract(Bsa::BSAFile& bsa, Arguments& info)
|
||||
{
|
||||
std::string archivePath = info.extractfile;
|
||||
replaceAll(archivePath, "/", "\\");
|
||||
|
||||
std::string extractPath = info.extractfile;
|
||||
replaceAll(extractPath, "\\", "/");
|
||||
|
||||
if (!bsa.exists(archivePath.c_str()))
|
||||
{
|
||||
std::cout << "ERROR: file '" << archivePath << "' not found\n";
|
||||
std::cout << "In archive: " << info.filename << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get the target path (the path the file will be extracted to)
|
||||
bfs::path relPath (extractPath);
|
||||
bfs::path outdir (info.outdir);
|
||||
|
||||
bfs::path target;
|
||||
if (info.fullpath)
|
||||
target = outdir / relPath;
|
||||
else
|
||||
target = outdir / relPath.filename();
|
||||
|
||||
// Create the directory hierarchy
|
||||
bfs::create_directories(target.parent_path());
|
||||
|
||||
bfs::file_status s = bfs::status(target.parent_path());
|
||||
if (!bfs::is_directory(s))
|
||||
{
|
||||
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get a stream for the file to extract
|
||||
Ogre::DataStreamPtr data = 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.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extractAll(Bsa::BSAFile& bsa, Arguments& info)
|
||||
{
|
||||
// Get the list of files present in the archive
|
||||
Bsa::BSAFile::FileList list = bsa.getList();
|
||||
|
||||
// Iter on the list
|
||||
for(Bsa::BSAFile::FileList::iterator it = list.begin(); it != list.end(); ++it) {
|
||||
const char* archivePath = it->name;
|
||||
|
||||
std::string extractPath (archivePath);
|
||||
replaceAll(extractPath, "\\", "/");
|
||||
|
||||
// Get the target path (the path the file will be extracted to)
|
||||
bfs::path target (info.outdir);
|
||||
target /= extractPath;
|
||||
|
||||
// Create the directory hierarchy
|
||||
bfs::create_directories(target.parent_path());
|
||||
|
||||
bfs::file_status s = bfs::status(target.parent_path());
|
||||
if (!bfs::is_directory(s))
|
||||
{
|
||||
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get a stream for the file to extract
|
||||
// (inefficient because getFile iter on the list again)
|
||||
Ogre::DataStreamPtr 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.close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
3
apps/doc.hpp
Normal file
3
apps/doc.hpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Note: This is not a regular source file.
|
||||
|
||||
/// \defgroup apps Applications
|
23
apps/esmtool/CMakeLists.txt
Normal file
23
apps/esmtool/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
set(ESMTOOL
|
||||
esmtool.cpp
|
||||
labels.hpp
|
||||
labels.cpp
|
||||
record.hpp
|
||||
record.cpp
|
||||
)
|
||||
source_group(apps\\esmtool FILES ${ESMTOOL})
|
||||
|
||||
# Main executable
|
||||
add_executable(esmtool
|
||||
${ESMTOOL}
|
||||
)
|
||||
|
||||
target_link_libraries(esmtool
|
||||
${Boost_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(esmtool gcov)
|
||||
endif()
|
556
apps/esmtool/esmtool.cpp
Normal file
556
apps/esmtool/esmtool.cpp
Normal file
|
@ -0,0 +1,556 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/records.hpp>
|
||||
|
||||
#include "record.hpp"
|
||||
|
||||
#define ESMTOOL_VERSION 1.2
|
||||
|
||||
// Create a local alias for brevity
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
struct ESMData
|
||||
{
|
||||
std::string author;
|
||||
std::string description;
|
||||
int version;
|
||||
std::vector<ESM::Header::MasterData> masters;
|
||||
|
||||
std::deque<EsmTool::RecordBase *> mRecords;
|
||||
std::map<ESM::Cell *, std::deque<ESM::CellRef> > mCellRefs;
|
||||
std::map<int, int> mRecordStats;
|
||||
|
||||
static const std::set<int> sLabeledRec;
|
||||
};
|
||||
|
||||
static const int sLabeledRecIds[] = {
|
||||
ESM::REC_GLOB, ESM::REC_CLAS, ESM::REC_FACT, ESM::REC_RACE, ESM::REC_SOUN,
|
||||
ESM::REC_REGN, ESM::REC_BSGN, ESM::REC_LTEX, ESM::REC_STAT, ESM::REC_DOOR,
|
||||
ESM::REC_MISC, ESM::REC_WEAP, ESM::REC_CONT, ESM::REC_SPEL, ESM::REC_CREA,
|
||||
ESM::REC_BODY, ESM::REC_LIGH, ESM::REC_ENCH, ESM::REC_NPC_, ESM::REC_ARMO,
|
||||
ESM::REC_CLOT, ESM::REC_REPA, ESM::REC_ACTI, ESM::REC_APPA, ESM::REC_LOCK,
|
||||
ESM::REC_PROB, ESM::REC_INGR, ESM::REC_BOOK, ESM::REC_ALCH, ESM::REC_LEVI,
|
||||
ESM::REC_LEVC, ESM::REC_SNDG, ESM::REC_CELL, ESM::REC_DIAL
|
||||
};
|
||||
|
||||
const std::set<int> ESMData::sLabeledRec =
|
||||
std::set<int>(sLabeledRecIds, sLabeledRecIds + 34);
|
||||
|
||||
// Based on the legacy struct
|
||||
struct Arguments
|
||||
{
|
||||
unsigned int raw_given;
|
||||
unsigned int quiet_given;
|
||||
unsigned int loadcells_given;
|
||||
bool plain_given;
|
||||
|
||||
std::string mode;
|
||||
std::string encoding;
|
||||
std::string filename;
|
||||
std::string outname;
|
||||
|
||||
std::vector<std::string> types;
|
||||
|
||||
ESMData data;
|
||||
ESM::ESMReader reader;
|
||||
ESM::ESMWriter writer;
|
||||
};
|
||||
|
||||
bool parseOptions (int argc, char** argv, Arguments &info)
|
||||
{
|
||||
bpo::options_description desc("Inspect and extract from Morrowind ES files (ESM, ESP, ESS)\nSyntax: esmtool [options] mode infile [outfile]\nAllowed modes:\n dump\t Dumps all readable data from the input file.\n clone\t Clones the input file to the output file.\n comp\t Compares the given files.\n\nAllowed options");
|
||||
|
||||
desc.add_options()
|
||||
("help,h", "print help message.")
|
||||
("version,v", "print version information and quit.")
|
||||
("raw,r", "Show an unformatted list of all records and subrecords.")
|
||||
// The intention is that this option would interact better
|
||||
// with other modes including clone, dump, and raw.
|
||||
("type,t", bpo::value< std::vector<std::string> >(),
|
||||
"Show only records of this type (four character record code). May "
|
||||
"be specified multiple times. Only affects dump mode.")
|
||||
("plain,p", "Print contents of dialogs, books and scripts. "
|
||||
"(skipped by default)"
|
||||
"Only affects dump mode.")
|
||||
("quiet,q", "Supress all record information. Useful for speed tests.")
|
||||
("loadcells,C", "Browse through contents of all cells.")
|
||||
|
||||
( "encoding,e", bpo::value<std::string>(&(info.encoding))->
|
||||
default_value("win1252"),
|
||||
"Character encoding used in ESMTool:\n"
|
||||
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||
;
|
||||
|
||||
std::string finalText = "\nIf no option is given, the default action is to parse all records in the archive\nand display diagnostic information.";
|
||||
|
||||
// input-file is hidden and used as a positional argument
|
||||
bpo::options_description hidden("Hidden Options");
|
||||
|
||||
hidden.add_options()
|
||||
( "mode,m", bpo::value<std::string>(), "esmtool mode")
|
||||
( "input-file,i", bpo::value< std::vector<std::string> >(), "input file")
|
||||
;
|
||||
|
||||
bpo::positional_options_description p;
|
||||
p.add("mode", 1).add("input-file", 2);
|
||||
|
||||
// there might be a better way to do this
|
||||
bpo::options_description all;
|
||||
all.add(desc).add(hidden);
|
||||
bpo::variables_map variables;
|
||||
|
||||
try
|
||||
{
|
||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
|
||||
.options(all).positional(p).run();
|
||||
|
||||
bpo::store(valid_opts, variables);
|
||||
}
|
||||
catch(boost::program_options::unknown_option & x)
|
||||
{
|
||||
std::cerr << "ERROR: " << x.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
catch(boost::program_options::invalid_command_line_syntax & x)
|
||||
{
|
||||
std::cerr << "ERROR: " << x.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bpo::notify(variables);
|
||||
|
||||
if (variables.count ("help"))
|
||||
{
|
||||
std::cout << desc << finalText << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (variables.count ("version"))
|
||||
{
|
||||
std::cout << "ESMTool version " << ESMTOOL_VERSION << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!variables.count("mode"))
|
||||
{
|
||||
std::cout << "No mode specified!" << std::endl << std::endl
|
||||
<< desc << finalText << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (variables.count("type") > 0)
|
||||
info.types = variables["type"].as< std::vector<std::string> >();
|
||||
|
||||
info.mode = variables["mode"].as<std::string>();
|
||||
if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp"))
|
||||
{
|
||||
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"" << std::endl << std::endl
|
||||
<< desc << finalText << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !variables.count("input-file") )
|
||||
{
|
||||
std::cout << "\nERROR: missing ES file\n\n";
|
||||
std::cout << desc << finalText << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// handling gracefully the user adding multiple files
|
||||
/* if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
{
|
||||
std::cout << "\nERROR: more than one ES file specified\n\n";
|
||||
std::cout << desc << finalText << std::endl;
|
||||
return false;
|
||||
}*/
|
||||
|
||||
info.filename = variables["input-file"].as< std::vector<std::string> >()[0];
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.outname = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
|
||||
info.raw_given = variables.count ("raw");
|
||||
info.quiet_given = variables.count ("quiet");
|
||||
info.loadcells_given = variables.count ("loadcells");
|
||||
info.plain_given = (variables.count("plain") > 0);
|
||||
|
||||
// Font encoding settings
|
||||
info.encoding = variables["encoding"].as<std::string>();
|
||||
if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252")
|
||||
{
|
||||
std::cout << info.encoding << " is not a valid encoding option." << std::endl;
|
||||
info.encoding = "win1252";
|
||||
}
|
||||
std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void printRaw(ESM::ESMReader &esm);
|
||||
void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info);
|
||||
|
||||
int load(Arguments& info);
|
||||
int clone(Arguments& info);
|
||||
int comp(Arguments& info);
|
||||
|
||||
int main(int argc, char**argv)
|
||||
{
|
||||
Arguments info;
|
||||
if(!parseOptions (argc, argv, info))
|
||||
return 1;
|
||||
|
||||
if (info.mode == "dump")
|
||||
return load(info);
|
||||
else if (info.mode == "clone")
|
||||
return clone(info);
|
||||
else if (info.mode == "comp")
|
||||
return comp(info);
|
||||
else
|
||||
{
|
||||
std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
||||
{
|
||||
bool quiet = (info.quiet_given || info.mode == "clone");
|
||||
bool save = (info.mode == "clone");
|
||||
|
||||
// Skip back to the beginning of the reference list
|
||||
// FIXME: Changes to the references backend required to support multiple plugins have
|
||||
// almost certainly broken this following line. I'll leave it as is for now, so that
|
||||
// the compiler does not complain.
|
||||
cell.restore(esm, 0);
|
||||
|
||||
// Loop through all the references
|
||||
ESM::CellRef ref;
|
||||
if(!quiet) std::cout << " References:\n";
|
||||
while(cell.getNextRef(esm, ref))
|
||||
{
|
||||
if (save) {
|
||||
info.data.mCellRefs[&cell].push_back(ref);
|
||||
}
|
||||
|
||||
if(quiet) continue;
|
||||
|
||||
std::cout << " Refnum: " << ref.mRefnum << std::endl;
|
||||
std::cout << " ID: '" << ref.mRefID << "'\n";
|
||||
std::cout << " Owner: '" << ref.mOwner << "'\n";
|
||||
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
|
||||
std::cout << " Uses/health: '" << ref.mCharge << "'\n";
|
||||
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
|
||||
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void printRaw(ESM::ESMReader &esm)
|
||||
{
|
||||
while(esm.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = esm.getRecName();
|
||||
std::cout << "Record: " << n.toString() << std::endl;
|
||||
esm.getRecHeader();
|
||||
while(esm.hasMoreSubs())
|
||||
{
|
||||
uint64_t offs = esm.getOffset();
|
||||
esm.getSubName();
|
||||
esm.skipHSub();
|
||||
n = esm.retSubName();
|
||||
std::cout << " " << n.toString() << " - " << esm.getSubSize()
|
||||
<< " bytes @ 0x" << std::hex << offs << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int load(Arguments& info)
|
||||
{
|
||||
ESM::ESMReader& esm = info.reader;
|
||||
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
|
||||
esm.setEncoder(&encoder);
|
||||
|
||||
std::string filename = info.filename;
|
||||
std::cout << "Loading file: " << filename << std::endl;
|
||||
|
||||
std::list<int> skipped;
|
||||
|
||||
try {
|
||||
|
||||
if(info.raw_given && info.mode == "dump")
|
||||
{
|
||||
std::cout << "RAW file listing:\n";
|
||||
|
||||
esm.openRaw(filename);
|
||||
|
||||
printRaw(esm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool quiet = (info.quiet_given || info.mode == "clone");
|
||||
bool loadCells = (info.loadcells_given || info.mode == "clone");
|
||||
bool save = (info.mode == "clone");
|
||||
|
||||
esm.open(filename);
|
||||
|
||||
info.data.author = esm.getAuthor();
|
||||
info.data.description = esm.getDesc();
|
||||
info.data.masters = esm.getMasters();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
std::cout << "Author: " << esm.getAuthor() << std::endl
|
||||
<< "Description: " << esm.getDesc() << std::endl
|
||||
<< "File format version: " << esm.getFVer() << std::endl;
|
||||
std::vector<ESM::Header::MasterData> m = esm.getMasters();
|
||||
if (!m.empty())
|
||||
{
|
||||
std::cout << "Masters:" << std::endl;
|
||||
for(unsigned int i=0;i<m.size();i++)
|
||||
std::cout << " " << m[i].name << ", " << m[i].size << " bytes" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through all records
|
||||
while(esm.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = esm.getRecName();
|
||||
uint32_t flags;
|
||||
esm.getRecHeader(flags);
|
||||
|
||||
// Is the user interested in this record type?
|
||||
bool interested = true;
|
||||
if (info.types.size() > 0)
|
||||
{
|
||||
std::vector<std::string>::iterator match;
|
||||
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(!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);
|
||||
}
|
||||
|
||||
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) {
|
||||
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
|
||||
}
|
||||
|
||||
if (save) {
|
||||
info.data.mRecords.push_back(record);
|
||||
} else {
|
||||
delete record;
|
||||
}
|
||||
++info.data.mRecordStats[n.val];
|
||||
}
|
||||
}
|
||||
|
||||
} catch(std::exception &e) {
|
||||
std::cout << "\nERROR:\n\n " << e.what() << std::endl;
|
||||
|
||||
typedef std::deque<EsmTool::RecordBase *> RecStore;
|
||||
RecStore &store = info.data.mRecords;
|
||||
for (RecStore::iterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
store.clear();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
int clone(Arguments& info)
|
||||
{
|
||||
if (info.outname.empty())
|
||||
{
|
||||
std::cout << "You need to specify an output name" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (load(info) != 0)
|
||||
{
|
||||
std::cout << "Failed to load, aborting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int recordCount = info.data.mRecords.size();
|
||||
|
||||
int digitCount = 1; // For a nicer output
|
||||
if (recordCount > 9) ++digitCount;
|
||||
if (recordCount > 99) ++digitCount;
|
||||
if (recordCount > 999) ++digitCount;
|
||||
if (recordCount > 9999) ++digitCount;
|
||||
if (recordCount > 99999) ++digitCount;
|
||||
if (recordCount > 999999) ++digitCount;
|
||||
|
||||
std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl;
|
||||
|
||||
ESM::NAME name;
|
||||
|
||||
int i = 0;
|
||||
typedef std::map<int, int> Stats;
|
||||
Stats &stats = info.data.mRecordStats;
|
||||
for (Stats::iterator it = stats.begin(); it != stats.end(); ++it)
|
||||
{
|
||||
name.val = it->first;
|
||||
float amount = it->second;
|
||||
std::cout << std::setw(digitCount) << amount << " " << name.toString() << " ";
|
||||
|
||||
if (++i % 3 == 0)
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
if (i % 3 != 0)
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl;
|
||||
|
||||
ESM::ESMWriter& esm = info.writer;
|
||||
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
|
||||
esm.setEncoder(&encoder);
|
||||
esm.setAuthor(info.data.author);
|
||||
esm.setDescription(info.data.description);
|
||||
esm.setVersion(info.data.version);
|
||||
esm.setRecordCount (recordCount);
|
||||
|
||||
for (std::vector<ESM::Header::MasterData>::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it)
|
||||
esm.addMaster(it->name, it->size);
|
||||
|
||||
std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary);
|
||||
esm.save(save);
|
||||
|
||||
int saved = 0;
|
||||
typedef std::deque<EsmTool::RecordBase *> Records;
|
||||
Records &records = info.data.mRecords;
|
||||
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;
|
||||
RefList &refs = info.data.mCellRefs[ptr];
|
||||
for (RefList::iterator it = refs.begin(); it != refs.end(); ++it)
|
||||
{
|
||||
it->save(esm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esm.endRecord(name.toString());
|
||||
|
||||
saved++;
|
||||
int perc = (saved / (float)recordCount)*100;
|
||||
if (perc % 10 == 0)
|
||||
{
|
||||
std::cerr << "\r" << perc << "%";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\rDone!" << std::endl;
|
||||
|
||||
esm.close();
|
||||
save.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int comp(Arguments& info)
|
||||
{
|
||||
if (info.filename.empty() || info.outname.empty())
|
||||
{
|
||||
std::cout << "You need to specify two input files" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Arguments fileOne;
|
||||
Arguments fileTwo;
|
||||
|
||||
fileOne.raw_given = 0;
|
||||
fileTwo.raw_given = 0;
|
||||
|
||||
fileOne.mode = "clone";
|
||||
fileTwo.mode = "clone";
|
||||
|
||||
fileOne.encoding = info.encoding;
|
||||
fileTwo.encoding = info.encoding;
|
||||
|
||||
fileOne.filename = info.filename;
|
||||
fileTwo.filename = info.outname;
|
||||
|
||||
if (load(fileOne) != 0)
|
||||
{
|
||||
std::cout << "Failed to load " << info.filename << ", aborting comparison." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (load(fileTwo) != 0)
|
||||
{
|
||||
std::cout << "Failed to load " << info.outname << ", aborting comparison." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fileOne.data.mRecords.size() != fileTwo.data.mRecords.size())
|
||||
{
|
||||
std::cout << "Not equal, different amount of records." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
880
apps/esmtool/labels.cpp
Normal file
880
apps/esmtool/labels.cpp
Normal file
|
@ -0,0 +1,880 @@
|
|||
#include "labels.hpp"
|
||||
|
||||
#include <components/esm/loadbody.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/esm/loadcont.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/loadlevlist.hpp>
|
||||
#include <components/esm/loadligh.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/loadrace.hpp>
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/aipackage.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
std::string bodyPartLabel(int idx)
|
||||
{
|
||||
const char *bodyPartLabels[] = {
|
||||
"Head",
|
||||
"Hair",
|
||||
"Neck",
|
||||
"Cuirass",
|
||||
"Groin",
|
||||
"Skirt",
|
||||
"Right Hand",
|
||||
"Left Hand",
|
||||
"Right Wrist",
|
||||
"Left Wrist",
|
||||
"Shield",
|
||||
"Right Forearm",
|
||||
"Left Forearm",
|
||||
"Right Upperarm",
|
||||
"Left Upperarm",
|
||||
"Right Foot",
|
||||
"Left Foot",
|
||||
"Right Ankle",
|
||||
"Left Ankle",
|
||||
"Right Knee",
|
||||
"Left Knee",
|
||||
"Right Leg",
|
||||
"Left Leg",
|
||||
"Right Shoulder",
|
||||
"Left Shoulder",
|
||||
"Weapon",
|
||||
"Tail"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 26)
|
||||
return bodyPartLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string meshPartLabel(int idx)
|
||||
{
|
||||
const char *meshPartLabels[] = {
|
||||
"Head",
|
||||
"Hair",
|
||||
"Neck",
|
||||
"Chest",
|
||||
"Groin",
|
||||
"Hand",
|
||||
"Wrist",
|
||||
"Forearm",
|
||||
"Upperarm",
|
||||
"Foot",
|
||||
"Ankle",
|
||||
"Knee",
|
||||
"Upper Leg",
|
||||
"Clavicle",
|
||||
"Tail"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= ESM::BodyPart::MP_Tail)
|
||||
return meshPartLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string meshTypeLabel(int idx)
|
||||
{
|
||||
const char *meshTypeLabels[] = {
|
||||
"Skin",
|
||||
"Clothing",
|
||||
"Armor"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= ESM::BodyPart::MT_Armor)
|
||||
return meshTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string clothingTypeLabel(int idx)
|
||||
{
|
||||
const char *clothingTypeLabels[] = {
|
||||
"Pants",
|
||||
"Shoes",
|
||||
"Shirt",
|
||||
"Belt",
|
||||
"Robe",
|
||||
"Right Glove",
|
||||
"Left Glove",
|
||||
"Skirt",
|
||||
"Ring",
|
||||
"Amulet"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 9)
|
||||
return clothingTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string armorTypeLabel(int idx)
|
||||
{
|
||||
const char *armorTypeLabels[] = {
|
||||
"Helmet",
|
||||
"Cuirass",
|
||||
"Left Pauldron",
|
||||
"Right Pauldron",
|
||||
"Greaves",
|
||||
"Boots",
|
||||
"Left Gauntlet",
|
||||
"Right Gauntlet",
|
||||
"Shield",
|
||||
"Left Bracer",
|
||||
"Right Bracer"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 10)
|
||||
return armorTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string dialogTypeLabel(int idx)
|
||||
{
|
||||
const char *dialogTypeLabels[] = {
|
||||
"Topic",
|
||||
"Voice",
|
||||
"Greeting",
|
||||
"Persuasion",
|
||||
"Journal"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 4)
|
||||
return dialogTypeLabels[idx];
|
||||
else if (idx == -1)
|
||||
return "Deleted";
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string questStatusLabel(int idx)
|
||||
{
|
||||
const char *questStatusLabels[] = {
|
||||
"None",
|
||||
"Name",
|
||||
"Finished",
|
||||
"Restart",
|
||||
"Deleted"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 4)
|
||||
return questStatusLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string creatureTypeLabel(int idx)
|
||||
{
|
||||
const char *creatureTypeLabels[] = {
|
||||
"Creature",
|
||||
"Daedra",
|
||||
"Undead",
|
||||
"Humanoid",
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 3)
|
||||
return creatureTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string soundTypeLabel(int idx)
|
||||
{
|
||||
const char *soundTypeLabels[] = {
|
||||
"Left Foot",
|
||||
"Right Foot",
|
||||
"Swim Left",
|
||||
"Swim Right",
|
||||
"Moan",
|
||||
"Roar",
|
||||
"Scream",
|
||||
"Land"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 7)
|
||||
return soundTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string weaponTypeLabel(int idx)
|
||||
{
|
||||
const char *weaponTypeLabels[] = {
|
||||
"Short Blade One Hand",
|
||||
"Long Blade One Hand",
|
||||
"Long Blade Two Hand",
|
||||
"Blunt One Hand",
|
||||
"Blunt Two Close",
|
||||
"Blunt Two Wide",
|
||||
"Spear Two Wide",
|
||||
"Axe One Hand",
|
||||
"Axe Two Hand",
|
||||
"Marksman Bow",
|
||||
"Marksman Crossbow",
|
||||
"Marksman Thrown",
|
||||
"Arrow",
|
||||
"Bolt"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx <= 13)
|
||||
return weaponTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string aiTypeLabel(int type)
|
||||
{
|
||||
if (type == ESM::AI_Wander) return "Wander";
|
||||
else if (type == ESM::AI_Travel) return "Travel";
|
||||
else if (type == ESM::AI_Follow) return "Follow";
|
||||
else if (type == ESM::AI_Escort) return "Escort";
|
||||
else if (type == ESM::AI_Activate) return "Activate";
|
||||
else return "Invalid";
|
||||
}
|
||||
|
||||
std::string magicEffectLabel(int idx)
|
||||
{
|
||||
const char* magicEffectLabels [] = {
|
||||
"Water Breathing",
|
||||
"Swift Swim",
|
||||
"Water Walking",
|
||||
"Shield",
|
||||
"Fire Shield",
|
||||
"Lightning Shield",
|
||||
"Frost Shield",
|
||||
"Burden",
|
||||
"Feather",
|
||||
"Jump",
|
||||
"Levitate",
|
||||
"SlowFall",
|
||||
"Lock",
|
||||
"Open",
|
||||
"Fire Damage",
|
||||
"Shock Damage",
|
||||
"Frost Damage",
|
||||
"Drain Attribute",
|
||||
"Drain Health",
|
||||
"Drain Magicka",
|
||||
"Drain Fatigue",
|
||||
"Drain Skill",
|
||||
"Damage Attribute",
|
||||
"Damage Health",
|
||||
"Damage Magicka",
|
||||
"Damage Fatigue",
|
||||
"Damage Skill",
|
||||
"Poison",
|
||||
"Weakness to Fire",
|
||||
"Weakness to Frost",
|
||||
"Weakness to Shock",
|
||||
"Weakness to Magicka",
|
||||
"Weakness to Common Disease",
|
||||
"Weakness to Blight Disease",
|
||||
"Weakness to Corprus Disease",
|
||||
"Weakness to Poison",
|
||||
"Weakness to Normal Weapons",
|
||||
"Disintegrate Weapon",
|
||||
"Disintegrate Armor",
|
||||
"Invisibility",
|
||||
"Chameleon",
|
||||
"Light",
|
||||
"Sanctuary",
|
||||
"Night Eye",
|
||||
"Charm",
|
||||
"Paralyze",
|
||||
"Silence",
|
||||
"Blind",
|
||||
"Sound",
|
||||
"Calm Humanoid",
|
||||
"Calm Creature",
|
||||
"Frenzy Humanoid",
|
||||
"Frenzy Creature",
|
||||
"Demoralize Humanoid",
|
||||
"Demoralize Creature",
|
||||
"Rally Humanoid",
|
||||
"Rally Creature",
|
||||
"Dispel",
|
||||
"Soultrap",
|
||||
"Telekinesis",
|
||||
"Mark",
|
||||
"Recall",
|
||||
"Divine Intervention",
|
||||
"Almsivi Intervention",
|
||||
"Detect Animal",
|
||||
"Detect Enchantment",
|
||||
"Detect Key",
|
||||
"Spell Absorption",
|
||||
"Reflect",
|
||||
"Cure Common Disease",
|
||||
"Cure Blight Disease",
|
||||
"Cure Corprus Disease",
|
||||
"Cure Poison",
|
||||
"Cure Paralyzation",
|
||||
"Restore Attribute",
|
||||
"Restore Health",
|
||||
"Restore Magicka",
|
||||
"Restore Fatigue",
|
||||
"Restore Skill",
|
||||
"Fortify Attribute",
|
||||
"Fortify Health",
|
||||
"Fortify Magicka",
|
||||
"Fortify Fatigue",
|
||||
"Fortify Skill",
|
||||
"Fortify Maximum Magicka",
|
||||
"Absorb Attribute",
|
||||
"Absorb Health",
|
||||
"Absorb Magicka",
|
||||
"Absorb Fatigue",
|
||||
"Absorb Skill",
|
||||
"Resist Fire",
|
||||
"Resist Frost",
|
||||
"Resist Shock",
|
||||
"Resist Magicka",
|
||||
"Resist Common Disease",
|
||||
"Resist Blight Disease",
|
||||
"Resist Corprus Disease",
|
||||
"Resist Poison",
|
||||
"Resist Normal Weapons",
|
||||
"Resist Paralysis",
|
||||
"Remove Curse",
|
||||
"Turn Undead",
|
||||
"Summon Scamp",
|
||||
"Summon Clannfear",
|
||||
"Summon Daedroth",
|
||||
"Summon Dremora",
|
||||
"Summon Ancestral Ghost",
|
||||
"Summon Skeletal Minion",
|
||||
"Summon Bonewalker",
|
||||
"Summon Greater Bonewalker",
|
||||
"Summon Bonelord",
|
||||
"Summon Winged Twilight",
|
||||
"Summon Hunger",
|
||||
"Summon Golden Saint",
|
||||
"Summon Flame Atronach",
|
||||
"Summon Frost Atronach",
|
||||
"Summon Storm Atronach",
|
||||
"Fortify Attack",
|
||||
"Command Creature",
|
||||
"Command Humanoid",
|
||||
"Bound Dagger",
|
||||
"Bound Longsword",
|
||||
"Bound Mace",
|
||||
"Bound Battle Axe",
|
||||
"Bound Spear",
|
||||
"Bound Longbow",
|
||||
"EXTRA SPELL",
|
||||
"Bound Cuirass",
|
||||
"Bound Helm",
|
||||
"Bound Boots",
|
||||
"Bound Shield",
|
||||
"Bound Gloves",
|
||||
"Corprus",
|
||||
"Vampirism",
|
||||
"Summon Centurion Sphere",
|
||||
"Sun Damage",
|
||||
"Stunted Magicka",
|
||||
"Summon Fabricant",
|
||||
"sEffectSummonCreature01",
|
||||
"sEffectSummonCreature02",
|
||||
"sEffectSummonCreature03",
|
||||
"sEffectSummonCreature04",
|
||||
"sEffectSummonCreature05"
|
||||
};
|
||||
if (idx >= 0 && idx <= 143)
|
||||
return magicEffectLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string attributeLabel(int idx)
|
||||
{
|
||||
const char* attributeLabels [] = {
|
||||
"Strength",
|
||||
"Intelligence",
|
||||
"Willpower",
|
||||
"Agility",
|
||||
"Speed",
|
||||
"Endurance",
|
||||
"Personality",
|
||||
"Luck"
|
||||
};
|
||||
if (idx >= 0 && idx <= 7)
|
||||
return attributeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string spellTypeLabel(int idx)
|
||||
{
|
||||
const char* spellTypeLabels [] = {
|
||||
"Spells",
|
||||
"Abilities",
|
||||
"Blight Disease",
|
||||
"Disease",
|
||||
"Curse",
|
||||
"Powers"
|
||||
};
|
||||
if (idx >= 0 && idx <= 5)
|
||||
return spellTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string specializationLabel(int idx)
|
||||
{
|
||||
const char* specializationLabels [] = {
|
||||
"Combat",
|
||||
"Magic",
|
||||
"Stealth"
|
||||
};
|
||||
if (idx >= 0 && idx <= 2)
|
||||
return specializationLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string skillLabel(int idx)
|
||||
{
|
||||
const char* skillLabels [] = {
|
||||
"Block",
|
||||
"Armorer",
|
||||
"Medium Armor",
|
||||
"Heavy Armor",
|
||||
"Blunt Weapon",
|
||||
"Long Blade",
|
||||
"Axe",
|
||||
"Spear",
|
||||
"Athletics",
|
||||
"Enchant",
|
||||
"Destruction",
|
||||
"Alteration",
|
||||
"Illusion",
|
||||
"Conjuration",
|
||||
"Mysticism",
|
||||
"Restoration",
|
||||
"Alchemy",
|
||||
"Unarmored",
|
||||
"Security",
|
||||
"Sneak",
|
||||
"Acrobatics",
|
||||
"Light Armor",
|
||||
"Short Blade",
|
||||
"Marksman",
|
||||
"Mercantile",
|
||||
"Speechcraft",
|
||||
"Hand-to-hand"
|
||||
};
|
||||
if (idx >= 0 && idx <= 27)
|
||||
return skillLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string apparatusTypeLabel(int idx)
|
||||
{
|
||||
const char* apparatusTypeLabels [] = {
|
||||
"Mortar",
|
||||
"Alembic",
|
||||
"Calcinator",
|
||||
"Retort",
|
||||
};
|
||||
if (idx >= 0 && idx <= 3)
|
||||
return apparatusTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string rangeTypeLabel(int idx)
|
||||
{
|
||||
const char* rangeTypeLabels [] = {
|
||||
"Self",
|
||||
"Touch",
|
||||
"Target"
|
||||
};
|
||||
if (idx >= 0 && idx <= 3)
|
||||
return rangeTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string schoolLabel(int idx)
|
||||
{
|
||||
const char* schoolLabels [] = {
|
||||
"Alteration",
|
||||
"Conjuration",
|
||||
"Destruction",
|
||||
"Illusion",
|
||||
"Mysticism",
|
||||
"Restoration"
|
||||
};
|
||||
if (idx >= 0 && idx <= 5)
|
||||
return schoolLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string enchantTypeLabel(int idx)
|
||||
{
|
||||
const char* enchantTypeLabels [] = {
|
||||
"Cast Once",
|
||||
"Cast When Strikes",
|
||||
"Cast When Used",
|
||||
"Constant Effect"
|
||||
};
|
||||
if (idx >= 0 && idx <= 3)
|
||||
return enchantTypeLabels[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
std::string ruleFunction(int idx)
|
||||
{
|
||||
std::string ruleFunctions[] = {
|
||||
"Reaction Low",
|
||||
"Reaction High",
|
||||
"Rank Requirement",
|
||||
"NPC? Reputation",
|
||||
"Health Percent",
|
||||
"Player Reputation",
|
||||
"NPC Level",
|
||||
"Player Health Percent",
|
||||
"Player Magicka",
|
||||
"Player Fatigue",
|
||||
"Player Attribute Strength",
|
||||
"Player Skill Block",
|
||||
"Player Skill Armorer",
|
||||
"Player Skill Medium Armor",
|
||||
"Player Skill Heavy Armor",
|
||||
"Player Skill Blunt Weapon",
|
||||
"Player Skill Long Blade",
|
||||
"Player Skill Axe",
|
||||
"Player Skill Spear",
|
||||
"Player Skill Athletics",
|
||||
"Player Skill Enchant",
|
||||
"Player Skill Destruction",
|
||||
"Player Skill Alteration",
|
||||
"Player Skill Illusion",
|
||||
"Player Skill Conjuration",
|
||||
"Player Skill Mysticism",
|
||||
"Player SKill Restoration",
|
||||
"Player Skill Alchemy",
|
||||
"Player Skill Unarmored",
|
||||
"Player Skill Security",
|
||||
"Player Skill Sneak",
|
||||
"Player Skill Acrobatics",
|
||||
"Player Skill Light Armor",
|
||||
"Player Skill Short Blade",
|
||||
"Player Skill Marksman",
|
||||
"Player Skill Mercantile",
|
||||
"Player Skill Speechcraft",
|
||||
"Player Skill Hand to Hand",
|
||||
"Player Gender",
|
||||
"Player Expelled from Faction",
|
||||
"Player Diseased (Common)",
|
||||
"Player Diseased (Blight)",
|
||||
"Player Clothing Modifier",
|
||||
"Player Crime Level",
|
||||
"Player Same Sex",
|
||||
"Player Same Race",
|
||||
"Player Same Faction",
|
||||
"Faction Rank Difference",
|
||||
"Player Detected",
|
||||
"Alarmed",
|
||||
"Choice Selected",
|
||||
"Player Attribute Intelligence",
|
||||
"Player Attribute Willpower",
|
||||
"Player Attribute Agility",
|
||||
"Player Attribute Speed",
|
||||
"Player Attribute Endurance",
|
||||
"Player Attribute Personality",
|
||||
"Player Attribute Luck",
|
||||
"Player Diseased (Corprus)",
|
||||
"Weather",
|
||||
"Player is a Vampire",
|
||||
"Player Level",
|
||||
"Attacked",
|
||||
"NPC Talked to Player",
|
||||
"Player Health",
|
||||
"Creature Target",
|
||||
"Friend Hit",
|
||||
"Fight",
|
||||
"Hello",
|
||||
"Alarm",
|
||||
"Flee",
|
||||
"Should Attack",
|
||||
"Werewolf"
|
||||
};
|
||||
if (idx >= 0 && idx <= 72)
|
||||
return ruleFunctions[idx];
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
// The "unused flag bits" should probably be defined alongside the
|
||||
// defined bits in the ESM component. The names of the flag bits are
|
||||
// very inconsistent.
|
||||
|
||||
std::string bodyPartFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::BodyPart::BPF_Female) properties += "Female ";
|
||||
if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::BodyPart::BPF_Female|
|
||||
ESM::BodyPart::BPF_NotPlayable));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string cellFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::Cell::HasWater) properties += "HasWater ";
|
||||
if (flags & ESM::Cell::Interior) properties += "Interior ";
|
||||
if (flags & ESM::Cell::NoSleep) properties += "NoSleep ";
|
||||
if (flags & ESM::Cell::QuasiEx) properties += "QuasiEx ";
|
||||
// This used value is not in the ESM component.
|
||||
if (flags & 0x00000040) properties += "Unknown ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::Cell::HasWater|
|
||||
ESM::Cell::Interior|
|
||||
ESM::Cell::NoSleep|
|
||||
ESM::Cell::QuasiEx|
|
||||
0x00000040));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string containerFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::Container::Unknown) properties += "Unknown ";
|
||||
if (flags & ESM::Container::Organic) properties += "Organic ";
|
||||
if (flags & ESM::Container::Respawn) properties += "Respawn ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::Container::Unknown|
|
||||
ESM::Container::Organic|
|
||||
ESM::Container::Respawn));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string creatureFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::Creature::None) properties += "All ";
|
||||
if (flags & ESM::Creature::Walks) properties += "Walks ";
|
||||
if (flags & ESM::Creature::Swims) properties += "Swims ";
|
||||
if (flags & ESM::Creature::Flies) properties += "Flies ";
|
||||
if (flags & ESM::Creature::Biped) properties += "Biped ";
|
||||
if (flags & ESM::Creature::Respawn) properties += "Respawn ";
|
||||
if (flags & ESM::Creature::Weapon) properties += "Weapon ";
|
||||
if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
|
||||
if (flags & ESM::Creature::Metal) properties += "Metal ";
|
||||
if (flags & ESM::Creature::Essential) properties += "Essential ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::Creature::None|
|
||||
ESM::Creature::Walks|
|
||||
ESM::Creature::Swims|
|
||||
ESM::Creature::Flies|
|
||||
ESM::Creature::Biped|
|
||||
ESM::Creature::Respawn|
|
||||
ESM::Creature::Weapon|
|
||||
ESM::Creature::Skeleton|
|
||||
ESM::Creature::Metal|
|
||||
ESM::Creature::Essential));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string landFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
// The ESM component says that this first four bits are used, but
|
||||
// only the first three bits are used as far as I can tell.
|
||||
// There's also no enumeration of the bit in the ESM component.
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & 0x00000001) properties += "Unknown1 ";
|
||||
if (flags & 0x00000004) properties += "Unknown3 ";
|
||||
if (flags & 0x00000002) properties += "Unknown2 ";
|
||||
if (flags & 0xFFFFFFF8) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string leveledListFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::LeveledListBase::AllLevels) properties += "AllLevels ";
|
||||
// This flag apparently not present on creature lists...
|
||||
if (flags & ESM::LeveledListBase::Each) properties += "Each ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::LeveledListBase::AllLevels|
|
||||
ESM::LeveledListBase::Each));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string lightFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::Light::Dynamic) properties += "Dynamic ";
|
||||
if (flags & ESM::Light::Fire) properties += "Fire ";
|
||||
if (flags & ESM::Light::Carry) properties += "Carry ";
|
||||
if (flags & ESM::Light::Flicker) properties += "Flicker ";
|
||||
if (flags & ESM::Light::FlickerSlow) properties += "FlickerSlow ";
|
||||
if (flags & ESM::Light::Pulse) properties += "Pulse ";
|
||||
if (flags & ESM::Light::PulseSlow) properties += "PulseSlow ";
|
||||
if (flags & ESM::Light::Negative) properties += "Negative ";
|
||||
if (flags & ESM::Light::OffDefault) properties += "OffDefault ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::Light::Dynamic|
|
||||
ESM::Light::Fire|
|
||||
ESM::Light::Carry|
|
||||
ESM::Light::Flicker|
|
||||
ESM::Light::FlickerSlow|
|
||||
ESM::Light::Pulse|
|
||||
ESM::Light::PulseSlow|
|
||||
ESM::Light::Negative|
|
||||
ESM::Light::OffDefault));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string magicEffectFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
// Enchanting & SpellMaking occur on the same list of effects.
|
||||
// "EXTRA SPELL" appears in the construction set under both the
|
||||
// spell making and enchanting tabs as an allowed effect. Since
|
||||
// most of the effects without this flags are defective in various
|
||||
// ways, it's still very unclear what these flag bits are.
|
||||
if (flags & ESM::MagicEffect::SpellMaking) properties += "SpellMaking ";
|
||||
if (flags & ESM::MagicEffect::Enchanting) properties += "Enchanting ";
|
||||
if (flags & 0x00000040) properties += "RangeNoSelf ";
|
||||
if (flags & 0x00000080) properties += "RangeTouch ";
|
||||
if (flags & 0x00000100) properties += "RangeTarget ";
|
||||
if (flags & 0x00001000) properties += "Unknown2 ";
|
||||
if (flags & 0x00000001) properties += "AffectSkill ";
|
||||
if (flags & 0x00000002) properties += "AffectAttribute ";
|
||||
if (flags & ESM::MagicEffect::NoDuration) properties += "NoDuration ";
|
||||
if (flags & 0x00000008) properties += "NoMagnitude ";
|
||||
if (flags & 0x00000010) properties += "Negative ";
|
||||
if (flags & 0x00000020) properties += "Unknown1 ";
|
||||
// ESM componet says 0x800 is negative, but none of the magic
|
||||
// effects have this flags set.
|
||||
if (flags & ESM::MagicEffect::Negative) properties += "Unused ";
|
||||
// Since only Chameleon has this flag it could be anything
|
||||
// that uniquely distinguishes Chameleon.
|
||||
if (flags & 0x00002000) properties += "Chameleon ";
|
||||
if (flags & 0x00004000) properties += "Bound ";
|
||||
if (flags & 0x00008000) properties += "Summon ";
|
||||
// Calm, Demoralize, Frenzy, Lock, Open, Rally, Soultrap, Turn Unded
|
||||
if (flags & 0x00010000) properties += "Unknown3 ";
|
||||
if (flags & 0x00020000) properties += "Absorb ";
|
||||
if (flags & 0xFFFC0000) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string npcFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
// Mythicmods and the ESM component differ. Mythicmods says
|
||||
// 0x8=None and 0x10=AutoCalc, while our code defines 0x8 as
|
||||
// AutoCalc. The former seems to be correct. All Bethesda
|
||||
// records have bit 0x8 set. A suspiciously large portion of
|
||||
// females have autocalc turned off.
|
||||
if (flags & ESM::NPC::Autocalc) properties += "Unknown ";
|
||||
if (flags & 0x00000010) properties += "Autocalc ";
|
||||
if (flags & ESM::NPC::Female) properties += "Female ";
|
||||
if (flags & ESM::NPC::Respawn) properties += "Respawn ";
|
||||
if (flags & ESM::NPC::Essential) properties += "Essential ";
|
||||
// These two flags do not appear on any NPCs and may have been
|
||||
// confused with the flags for creatures.
|
||||
if (flags & ESM::NPC::Skeleton) properties += "Skeleton ";
|
||||
if (flags & ESM::NPC::Metal) properties += "Metal ";
|
||||
// Whether corpses persist is a bit that is unaccounted for,
|
||||
// however the only unknown bit occurs on ALL records, and
|
||||
// relatively few NPCs have this bit set.
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::NPC::Autocalc|
|
||||
0x00000010|
|
||||
ESM::NPC::Female|
|
||||
ESM::NPC::Respawn|
|
||||
ESM::NPC::Essential|
|
||||
ESM::NPC::Skeleton|
|
||||
ESM::NPC::Metal));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string raceFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
// All races have the playable flag set in Bethesda files.
|
||||
if (flags & ESM::Race::Playable) properties += "Playable ";
|
||||
if (flags & ESM::Race::Beast) properties += "Beast ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::Race::Playable|
|
||||
ESM::Race::Beast));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string spellFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::Spell::F_Autocalc) properties += "Autocalc ";
|
||||
if (flags & ESM::Spell::F_PCStart) properties += "PCStart ";
|
||||
if (flags & ESM::Spell::F_Always) properties += "Always ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::Spell::F_Autocalc|
|
||||
ESM::Spell::F_PCStart|
|
||||
ESM::Spell::F_Always));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string weaponFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
// The interpretation of the flags are still unclear to me.
|
||||
// Apparently you can't be Silver without being Magical? Many of
|
||||
// the "Magical" weapons don't have enchantments of any sort.
|
||||
if (flags & ESM::Weapon::Magical) properties += "Magical ";
|
||||
if (flags & ESM::Weapon::Silver) properties += "Silver ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::Weapon::Magical|
|
||||
ESM::Weapon::Silver));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
64
apps/esmtool/labels.hpp
Normal file
64
apps/esmtool/labels.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#ifndef OPENMW_ESMTOOL_LABELS_H
|
||||
#define OPENMW_ESMTOOL_LABELS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string bodyPartLabel(int idx);
|
||||
std::string meshPartLabel(int idx);
|
||||
std::string meshTypeLabel(int idx);
|
||||
std::string clothingTypeLabel(int idx);
|
||||
std::string armorTypeLabel(int idx);
|
||||
std::string dialogTypeLabel(int idx);
|
||||
std::string questStatusLabel(int idx);
|
||||
std::string creatureTypeLabel(int idx);
|
||||
std::string soundTypeLabel(int idx);
|
||||
std::string weaponTypeLabel(int idx);
|
||||
|
||||
// This function's a bit different because the types are record types,
|
||||
// not consecutive values.
|
||||
std::string aiTypeLabel(int type);
|
||||
|
||||
// This one's also a bit different, because it enumerates dialog
|
||||
// select rule functions, not types. Structurally, it still converts
|
||||
// indexes to strings for display.
|
||||
std::string ruleFunction(int idx);
|
||||
|
||||
// The labels below here can all be loaded from GMSTs, but are not
|
||||
// currently because among other things, that requires loading the
|
||||
// GMSTs before dumping any of the records.
|
||||
|
||||
// If the data format supported ordered lists of GMSTs (post 1.0), the
|
||||
// lists could define the valid values, their localization strings,
|
||||
// and the indexes for referencing the types in other records in the
|
||||
// database. Then a single label function could work for all types.
|
||||
|
||||
std::string magicEffectLabel(int idx);
|
||||
std::string attributeLabel(int idx);
|
||||
std::string spellTypeLabel(int idx);
|
||||
std::string specializationLabel(int idx);
|
||||
std::string skillLabel(int idx);
|
||||
std::string apparatusTypeLabel(int idx);
|
||||
std::string rangeTypeLabel(int idx);
|
||||
std::string schoolLabel(int idx);
|
||||
std::string enchantTypeLabel(int idx);
|
||||
|
||||
// The are the flag functions that convert a bitmask into a list of
|
||||
// human readble strings representing the set bits.
|
||||
|
||||
std::string bodyPartFlags(int flags);
|
||||
std::string cellFlags(int flags);
|
||||
std::string containerFlags(int flags);
|
||||
std::string creatureFlags(int flags);
|
||||
std::string landFlags(int flags);
|
||||
std::string leveledListFlags(int flags);
|
||||
std::string lightFlags(int flags);
|
||||
std::string magicEffectFlags(int flags);
|
||||
std::string npcFlags(int flags);
|
||||
std::string raceFlags(int flags);
|
||||
std::string spellFlags(int flags);
|
||||
std::string weaponFlags(int flags);
|
||||
|
||||
// Missing flags functions:
|
||||
// aiServicesFlags, possibly more
|
||||
|
||||
#endif
|
1300
apps/esmtool/record.cpp
Normal file
1300
apps/esmtool/record.cpp
Normal file
File diff suppressed because it is too large
Load diff
136
apps/esmtool/record.hpp
Normal file
136
apps/esmtool/record.hpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
#ifndef OPENMW_ESMTOOL_RECORD_H
|
||||
#define OPENMW_ESMTOOL_RECORD_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/records.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
}
|
||||
|
||||
namespace EsmTool
|
||||
{
|
||||
template <class T> class Record;
|
||||
|
||||
class RecordBase
|
||||
{
|
||||
protected:
|
||||
std::string mId;
|
||||
int mFlags;
|
||||
ESM::NAME mType;
|
||||
bool mPrintPlain;
|
||||
|
||||
public:
|
||||
RecordBase () { mPrintPlain = false; }
|
||||
virtual ~RecordBase() {}
|
||||
|
||||
const std::string &getId() const {
|
||||
return mId;
|
||||
}
|
||||
|
||||
void setId(const std::string &id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
int getFlags() const {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
void setFlags(int flags) {
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
ESM::NAME getType() const {
|
||||
return mType;
|
||||
}
|
||||
|
||||
bool getPrintPlain() const {
|
||||
return mPrintPlain;
|
||||
}
|
||||
|
||||
void setPrintPlain(bool plain) {
|
||||
mPrintPlain = plain;
|
||||
}
|
||||
|
||||
virtual void load(ESM::ESMReader &esm) = 0;
|
||||
virtual void save(ESM::ESMWriter &esm) = 0;
|
||||
virtual void print() = 0;
|
||||
|
||||
static RecordBase *create(ESM::NAME type);
|
||||
|
||||
// just make it a bit shorter
|
||||
template <class T>
|
||||
Record<T> *cast() {
|
||||
return static_cast<Record<T> *>(this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Record : public RecordBase
|
||||
{
|
||||
T mData;
|
||||
|
||||
public:
|
||||
T &get() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
void save(ESM::ESMWriter &esm) {
|
||||
mData.save(esm);
|
||||
}
|
||||
|
||||
void load(ESM::ESMReader &esm) {
|
||||
mData.load(esm);
|
||||
}
|
||||
|
||||
void print();
|
||||
};
|
||||
|
||||
template<> void Record<ESM::Activator>::print();
|
||||
template<> void Record<ESM::Potion>::print();
|
||||
template<> void Record<ESM::Armor>::print();
|
||||
template<> void Record<ESM::Apparatus>::print();
|
||||
template<> void Record<ESM::BodyPart>::print();
|
||||
template<> void Record<ESM::Book>::print();
|
||||
template<> void Record<ESM::BirthSign>::print();
|
||||
template<> void Record<ESM::Cell>::print();
|
||||
template<> void Record<ESM::Class>::print();
|
||||
template<> void Record<ESM::Clothing>::print();
|
||||
template<> void Record<ESM::Container>::print();
|
||||
template<> void Record<ESM::Creature>::print();
|
||||
template<> void Record<ESM::Dialogue>::print();
|
||||
template<> void Record<ESM::Door>::print();
|
||||
template<> void Record<ESM::Enchantment>::print();
|
||||
template<> void Record<ESM::Faction>::print();
|
||||
template<> void Record<ESM::Global>::print();
|
||||
template<> void Record<ESM::GameSetting>::print();
|
||||
template<> void Record<ESM::DialInfo>::print();
|
||||
template<> void Record<ESM::Ingredient>::print();
|
||||
template<> void Record<ESM::Land>::print();
|
||||
template<> void Record<ESM::CreatureLevList>::print();
|
||||
template<> void Record<ESM::ItemLevList>::print();
|
||||
template<> void Record<ESM::Light>::print();
|
||||
template<> void Record<ESM::Lockpick>::print();
|
||||
template<> void Record<ESM::Probe>::print();
|
||||
template<> void Record<ESM::Repair>::print();
|
||||
template<> void Record<ESM::LandTexture>::print();
|
||||
template<> void Record<ESM::MagicEffect>::print();
|
||||
template<> void Record<ESM::Miscellaneous>::print();
|
||||
template<> void Record<ESM::NPC>::print();
|
||||
template<> void Record<ESM::Pathgrid>::print();
|
||||
template<> void Record<ESM::Race>::print();
|
||||
template<> void Record<ESM::Region>::print();
|
||||
template<> void Record<ESM::Script>::print();
|
||||
template<> void Record<ESM::Skill>::print();
|
||||
template<> void Record<ESM::SoundGenerator>::print();
|
||||
template<> void Record<ESM::Sound>::print();
|
||||
template<> void Record<ESM::Spell>::print();
|
||||
template<> void Record<ESM::StartScript>::print();
|
||||
template<> void Record<ESM::Static>::print();
|
||||
template<> void Record<ESM::Weapon>::print();
|
||||
}
|
||||
|
||||
#endif
|
139
apps/launcher/CMakeLists.txt
Normal file
139
apps/launcher/CMakeLists.txt
Normal file
|
@ -0,0 +1,139 @@
|
|||
set(LAUNCHER
|
||||
datafilespage.cpp
|
||||
graphicspage.cpp
|
||||
main.cpp
|
||||
maindialog.cpp
|
||||
playpage.cpp
|
||||
textslotmsgbox.cpp
|
||||
|
||||
settings/gamesettings.cpp
|
||||
settings/graphicssettings.cpp
|
||||
settings/launchersettings.cpp
|
||||
|
||||
utils/checkablemessagebox.cpp
|
||||
utils/textinputdialog.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc
|
||||
)
|
||||
if(NOT WIN32)
|
||||
LIST(APPEND LAUNCHER unshieldthread.cpp)
|
||||
endif(NOT WIN32)
|
||||
|
||||
set(LAUNCHER_HEADER
|
||||
datafilespage.hpp
|
||||
graphicspage.hpp
|
||||
maindialog.hpp
|
||||
playpage.hpp
|
||||
unshieldthread.hpp
|
||||
textslotmsgbox.hpp
|
||||
|
||||
settings/gamesettings.hpp
|
||||
settings/graphicssettings.hpp
|
||||
settings/launchersettings.hpp
|
||||
settings/settingsbase.hpp
|
||||
|
||||
utils/checkablemessagebox.hpp
|
||||
utils/textinputdialog.hpp
|
||||
|
||||
)
|
||||
if(NOT WIN32)
|
||||
LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp)
|
||||
endif(NOT WIN32)
|
||||
|
||||
|
||||
# Headers that must be pre-processed
|
||||
set(LAUNCHER_HEADER_MOC
|
||||
datafilespage.hpp
|
||||
graphicspage.hpp
|
||||
maindialog.hpp
|
||||
playpage.hpp
|
||||
unshieldthread.hpp
|
||||
textslotmsgbox.hpp
|
||||
|
||||
utils/checkablemessagebox.hpp
|
||||
utils/textinputdialog.hpp
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
LIST(APPEND LAUNCHER_HEADER_MOC unshieldthread.hpp)
|
||||
endif(NOT WIN32)
|
||||
|
||||
|
||||
set(LAUNCHER_UI
|
||||
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
|
||||
)
|
||||
|
||||
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
|
||||
|
||||
find_package(Qt4 REQUIRED)
|
||||
set(QT_USE_QTGUI 1)
|
||||
|
||||
# Set some platform specific settings
|
||||
if(WIN32)
|
||||
set(GUI_TYPE WIN32)
|
||||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
|
||||
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
|
||||
|
||||
|
||||
include(${QT_USE_FILE})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
if(NOT WIN32)
|
||||
include_directories(${LIBUNSHIELD_INCLUDE})
|
||||
endif(NOT WIN32)
|
||||
|
||||
# Main executable
|
||||
IF(OGRE_STATIC)
|
||||
IF(WIN32)
|
||||
ADD_DEFINITIONS(-DENABLE_PLUGIN_Direct3D9 -DENABLE_PLUGIN_GL)
|
||||
set(OGRE_STATIC_PLUGINS ${OGRE_RenderSystem_Direct3D9_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES})
|
||||
ELSE(WIN32)
|
||||
ADD_DEFINITIONS(-DENABLE_PLUGIN_GL)
|
||||
set(OGRE_STATIC_PLUGINS ${OGRE_RenderSystem_GL_LIBRARIES})
|
||||
ENDIF(WIN32)
|
||||
ENDIF(OGRE_STATIC)
|
||||
add_executable(omwlauncher
|
||||
${GUI_TYPE}
|
||||
${LAUNCHER}
|
||||
${LAUNCHER_HEADER}
|
||||
${RCC_SRCS}
|
||||
${MOC_SRCS}
|
||||
${UI_HDRS}
|
||||
)
|
||||
|
||||
target_link_libraries(omwlauncher
|
||||
${Boost_LIBRARIES}
|
||||
${OGRE_LIBRARIES}
|
||||
${OGRE_STATIC_PLUGINS}
|
||||
${SDL2_LIBRARY}
|
||||
${QT_LIBRARIES}
|
||||
components
|
||||
)
|
||||
if(NOT WIN32)
|
||||
target_link_libraries(omwlauncher
|
||||
${LIBUNSHIELD_LIBRARY}
|
||||
)
|
||||
endif(NOT WIN32)
|
||||
|
||||
|
||||
|
||||
if(DPKG_PROGRAM)
|
||||
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)
|
||||
endif()
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(omwlauncher gcov)
|
||||
endif()
|
||||
|
||||
# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(omwlauncher dl Xt)
|
||||
endif()
|
||||
|
551
apps/launcher/datafilespage.cpp
Normal file
551
apps/launcher/datafilespage.cpp
Normal file
|
@ -0,0 +1,551 @@
|
|||
#include "datafilespage.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QCheckBox>
|
||||
#include <QMenu>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <components/fileorderlist/model/datafilesmodel.hpp>
|
||||
#include <components/fileorderlist/model/pluginsproxymodel.hpp>
|
||||
#include <components/fileorderlist/model/esm/esmfile.hpp>
|
||||
|
||||
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||
#include <components/fileorderlist/utils/naturalsort.hpp>
|
||||
#include <components/fileorderlist/utils/profilescombobox.hpp>
|
||||
|
||||
#include "settings/gamesettings.hpp"
|
||||
#include "settings/launchersettings.hpp"
|
||||
|
||||
#include "utils/textinputdialog.hpp"
|
||||
|
||||
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent)
|
||||
: mCfgMgr(cfg)
|
||||
, mGameSettings(gameSettings)
|
||||
, mLauncherSettings(launcherSettings)
|
||||
, QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
// Models
|
||||
mDataFilesModel = new DataFilesModel(this);
|
||||
|
||||
mMastersProxyModel = new QSortFilterProxyModel();
|
||||
mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm"));
|
||||
mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mMastersProxyModel->setSourceModel(mDataFilesModel);
|
||||
|
||||
mPluginsProxyModel = new PluginsProxyModel();
|
||||
mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp"));
|
||||
mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mPluginsProxyModel->setSourceModel(mDataFilesModel);
|
||||
|
||||
mFilterProxyModel = new QSortFilterProxyModel();
|
||||
mFilterProxyModel->setDynamicSortFilter(true);
|
||||
mFilterProxyModel->setSourceModel(mPluginsProxyModel);
|
||||
|
||||
QCheckBox checkBox;
|
||||
unsigned int height = checkBox.sizeHint().height() + 4;
|
||||
|
||||
mastersTable->setModel(mMastersProxyModel);
|
||||
mastersTable->setObjectName("MastersTable");
|
||||
mastersTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
mastersTable->setSortingEnabled(false);
|
||||
mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
mastersTable->setAlternatingRowColors(true);
|
||||
mastersTable->horizontalHeader()->setStretchLastSection(true);
|
||||
mastersTable->horizontalHeader()->hide();
|
||||
|
||||
// Set the row height to the size of the checkboxes
|
||||
mastersTable->verticalHeader()->setDefaultSectionSize(height);
|
||||
mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||
mastersTable->verticalHeader()->hide();
|
||||
|
||||
pluginsTable->setModel(mFilterProxyModel);
|
||||
pluginsTable->setObjectName("PluginsTable");
|
||||
pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
pluginsTable->setSortingEnabled(false);
|
||||
pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
pluginsTable->setAlternatingRowColors(true);
|
||||
pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||||
pluginsTable->horizontalHeader()->setStretchLastSection(true);
|
||||
pluginsTable->horizontalHeader()->hide();
|
||||
|
||||
pluginsTable->verticalHeader()->setDefaultSectionSize(height);
|
||||
pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||
|
||||
// Adjust the tableview widths inside the splitter
|
||||
QList<int> sizeList;
|
||||
sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt();
|
||||
sizeList << mLauncherSettings.value(QString("General/PluginTable/width"), QString("340")).toInt();
|
||||
|
||||
splitter->setSizes(sizeList);
|
||||
|
||||
// Create a dialog for the new profile name input
|
||||
mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
|
||||
|
||||
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
|
||||
|
||||
connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString)));
|
||||
|
||||
connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||
connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||
|
||||
connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
|
||||
connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
|
||||
|
||||
connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews()));
|
||||
|
||||
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
|
||||
|
||||
connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter()));
|
||||
|
||||
createActions();
|
||||
setupDataFiles();
|
||||
}
|
||||
|
||||
void DataFilesPage::createActions()
|
||||
{
|
||||
|
||||
// Add the actions to the toolbuttons
|
||||
newProfileButton->setDefaultAction(newProfileAction);
|
||||
deleteProfileButton->setDefaultAction(deleteProfileAction);
|
||||
|
||||
// Context menu actions
|
||||
mContextMenu = new QMenu(this);
|
||||
mContextMenu->addAction(checkAction);
|
||||
mContextMenu->addAction(uncheckAction);
|
||||
}
|
||||
|
||||
void DataFilesPage::setupDataFiles()
|
||||
{
|
||||
// Set the encoding to the one found in openmw.cfg or the default
|
||||
mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252")));
|
||||
|
||||
QStringList paths = mGameSettings.getDataDirs();
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
mDataFilesModel->addFiles(path);
|
||||
}
|
||||
|
||||
QString dataLocal = mGameSettings.getDataLocal();
|
||||
if (!dataLocal.isEmpty())
|
||||
mDataFilesModel->addFiles(dataLocal);
|
||||
|
||||
// Sort by date accessed for now
|
||||
mDataFilesModel->sort(3);
|
||||
|
||||
QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/"));
|
||||
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
|
||||
|
||||
if (!profiles.isEmpty())
|
||||
profilesComboBox->addItems(profiles);
|
||||
|
||||
// Add the current profile if empty
|
||||
if (profilesComboBox->findText(profile) == -1 && !profile.isEmpty())
|
||||
profilesComboBox->addItem(profile);
|
||||
|
||||
if (profilesComboBox->findText(QString("Default")) == -1)
|
||||
profilesComboBox->addItem(QString("Default"));
|
||||
|
||||
if (profile.isEmpty() || profile == QLatin1String("Default")) {
|
||||
deleteProfileAction->setEnabled(false);
|
||||
profilesComboBox->setEditEnabled(false);
|
||||
profilesComboBox->setCurrentIndex(profilesComboBox->findText(QString("Default")));
|
||||
} else {
|
||||
profilesComboBox->setEditEnabled(true);
|
||||
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
|
||||
}
|
||||
|
||||
// We do this here to prevent deletion of profiles when initializing the combobox
|
||||
connect(profilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
|
||||
connect(profilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
|
||||
|
||||
loadSettings();
|
||||
|
||||
}
|
||||
|
||||
void DataFilesPage::loadSettings()
|
||||
{
|
||||
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
|
||||
|
||||
if (profile.isEmpty())
|
||||
return;
|
||||
|
||||
mDataFilesModel->uncheckAll();
|
||||
|
||||
QStringList masters = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly);
|
||||
QStringList plugins = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly);
|
||||
|
||||
foreach (const QString &master, masters) {
|
||||
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master));
|
||||
if (index.isValid())
|
||||
mDataFilesModel->setCheckState(index, Qt::Checked);
|
||||
}
|
||||
|
||||
foreach (const QString &plugin, plugins) {
|
||||
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin));
|
||||
if (index.isValid())
|
||||
mDataFilesModel->setCheckState(index, Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::saveSettings()
|
||||
{
|
||||
if (mDataFilesModel->rowCount() < 1)
|
||||
return;
|
||||
|
||||
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
|
||||
|
||||
if (profile.isEmpty()) {
|
||||
profile = profilesComboBox->currentText();
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile);
|
||||
}
|
||||
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
|
||||
|
||||
mGameSettings.remove(QString("master"));
|
||||
mGameSettings.remove(QString("plugin"));
|
||||
|
||||
QStringList items = mDataFilesModel->checkedItems();
|
||||
|
||||
foreach(const QString &item, items) {
|
||||
|
||||
if (item.endsWith(QString(".esm"), Qt::CaseInsensitive)) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item);
|
||||
mGameSettings.setMultiValue(QString("master"), item);
|
||||
|
||||
} else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item);
|
||||
mGameSettings.setMultiValue(QString("plugin"), item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DataFilesPage::updateOkButton(const QString &text)
|
||||
{
|
||||
// We do this here because we need the profiles combobox text
|
||||
if (text.isEmpty()) {
|
||||
mNewProfileDialog->setOkButtonEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
(profilesComboBox->findText(text) == -1)
|
||||
? mNewProfileDialog->setOkButtonEnabled(true)
|
||||
: mNewProfileDialog->setOkButtonEnabled(false);
|
||||
}
|
||||
|
||||
void DataFilesPage::updateSplitter()
|
||||
{
|
||||
// Sigh, update the saved splitter size in settings only when moved
|
||||
// Since getting mSplitter->sizes() if page is hidden returns invalid values
|
||||
QList<int> sizes = splitter->sizes();
|
||||
|
||||
mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0)));
|
||||
mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1)));
|
||||
}
|
||||
|
||||
void DataFilesPage::updateViews()
|
||||
{
|
||||
// Ensure the columns are hidden because sort() re-enables them
|
||||
mastersTable->setColumnHidden(1, true);
|
||||
mastersTable->setColumnHidden(2, true);
|
||||
mastersTable->setColumnHidden(3, true);
|
||||
mastersTable->setColumnHidden(4, true);
|
||||
mastersTable->setColumnHidden(5, true);
|
||||
mastersTable->setColumnHidden(6, true);
|
||||
mastersTable->setColumnHidden(7, true);
|
||||
mastersTable->setColumnHidden(8, true);
|
||||
|
||||
pluginsTable->setColumnHidden(1, true);
|
||||
pluginsTable->setColumnHidden(2, true);
|
||||
pluginsTable->setColumnHidden(3, true);
|
||||
pluginsTable->setColumnHidden(4, true);
|
||||
pluginsTable->setColumnHidden(5, true);
|
||||
pluginsTable->setColumnHidden(6, true);
|
||||
pluginsTable->setColumnHidden(7, true);
|
||||
pluginsTable->setColumnHidden(8, true);
|
||||
}
|
||||
|
||||
void DataFilesPage::setProfilesComboBoxIndex(int index)
|
||||
{
|
||||
profilesComboBox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void DataFilesPage::slotCurrentIndexChanged(int index)
|
||||
{
|
||||
emit profileChanged(index);
|
||||
}
|
||||
|
||||
QAbstractItemModel* DataFilesPage::profilesComboBoxModel()
|
||||
{
|
||||
return profilesComboBox->model();
|
||||
}
|
||||
|
||||
int DataFilesPage::profilesComboBoxIndex()
|
||||
{
|
||||
return profilesComboBox->currentIndex();
|
||||
}
|
||||
|
||||
void DataFilesPage::on_newProfileAction_triggered()
|
||||
{
|
||||
if (mNewProfileDialog->exec() == QDialog::Accepted) {
|
||||
QString profile = mNewProfileDialog->lineEdit()->text();
|
||||
profilesComboBox->addItem(profile);
|
||||
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::on_deleteProfileAction_triggered()
|
||||
{
|
||||
QString profile = profilesComboBox->currentText();
|
||||
|
||||
if (profile.isEmpty())
|
||||
return;
|
||||
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setWindowTitle(tr("Delete Profile"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(tr("Are you sure you want to delete <b>%0</b>?").arg(profile));
|
||||
|
||||
QAbstractButton *deleteButton =
|
||||
msgBox.addButton(tr("Delete"), QMessageBox::ActionRole);
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
if (msgBox.clickedButton() == deleteButton) {
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
|
||||
|
||||
// Remove the profile from the combobox
|
||||
profilesComboBox->removeItem(profilesComboBox->findText(profile));
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::on_checkAction_triggered()
|
||||
{
|
||||
if (pluginsTable->hasFocus())
|
||||
setPluginsCheckstates(Qt::Checked);
|
||||
|
||||
if (mastersTable->hasFocus())
|
||||
setMastersCheckstates(Qt::Checked);
|
||||
|
||||
}
|
||||
|
||||
void DataFilesPage::on_uncheckAction_triggered()
|
||||
{
|
||||
if (pluginsTable->hasFocus())
|
||||
setPluginsCheckstates(Qt::Unchecked);
|
||||
|
||||
if (mastersTable->hasFocus())
|
||||
setMastersCheckstates(Qt::Unchecked);
|
||||
}
|
||||
|
||||
void DataFilesPage::setMastersCheckstates(Qt::CheckState state)
|
||||
{
|
||||
if (!mastersTable->selectionModel()->hasSelection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
mDataFilesModel->setCheckState(sourceIndex, state);
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::setPluginsCheckstates(Qt::CheckState state)
|
||||
{
|
||||
if (!pluginsTable->selectionModel()->hasSelection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
|
||||
mFilterProxyModel->mapToSource(index));
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
mDataFilesModel->setCheckState(sourceIndex, state);
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::setCheckState(QModelIndex index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QObject *object = QObject::sender();
|
||||
|
||||
// Not a signal-slot call
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
|
||||
if (object->objectName() == QLatin1String("PluginsTable")) {
|
||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
|
||||
mFilterProxyModel->mapToSource(index));
|
||||
|
||||
if (sourceIndex.isValid()) {
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
|
||||
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
if (object->objectName() == QLatin1String("MastersTable")) {
|
||||
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
|
||||
|
||||
if (sourceIndex.isValid()) {
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
|
||||
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void DataFilesPage::filterChanged(const QString filter)
|
||||
{
|
||||
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
||||
mFilterProxyModel->setFilterRegExp(regExp);
|
||||
}
|
||||
|
||||
void DataFilesPage::profileChanged(const QString &previous, const QString ¤t)
|
||||
{
|
||||
// Prevent the deletion of the default profile
|
||||
if (current == QLatin1String("Default")) {
|
||||
deleteProfileAction->setEnabled(false);
|
||||
profilesComboBox->setEditEnabled(false);
|
||||
} else {
|
||||
deleteProfileAction->setEnabled(true);
|
||||
profilesComboBox->setEditEnabled(true);
|
||||
}
|
||||
|
||||
if (previous.isEmpty())
|
||||
return;
|
||||
|
||||
if (profilesComboBox->findText(previous) == -1)
|
||||
return; // Profile was deleted
|
||||
|
||||
// Store the previous profile
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous);
|
||||
saveSettings();
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
void DataFilesPage::profileRenamed(const QString &previous, const QString ¤t)
|
||||
{
|
||||
if (previous.isEmpty())
|
||||
return;
|
||||
|
||||
// Save the new profile name
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
|
||||
saveSettings();
|
||||
|
||||
// Remove the old one
|
||||
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin"));
|
||||
|
||||
// Remove the profile from the combobox
|
||||
profilesComboBox->removeItem(profilesComboBox->findText(previous));
|
||||
|
||||
loadSettings();
|
||||
|
||||
}
|
||||
|
||||
void DataFilesPage::showContextMenu(const QPoint &point)
|
||||
{
|
||||
QObject *object = QObject::sender();
|
||||
|
||||
// Not a signal-slot call
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
if (object->objectName() == QLatin1String("PluginsTable")) {
|
||||
if (!pluginsTable->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QPoint globalPos = pluginsTable->mapToGlobal(point);
|
||||
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
|
||||
|
||||
// Show the check/uncheck actions depending on the state of the selected items
|
||||
uncheckAction->setEnabled(false);
|
||||
checkAction->setEnabled(false);
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
|
||||
mFilterProxyModel->mapToSource(index));
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? uncheckAction->setEnabled(true)
|
||||
: checkAction->setEnabled(true);
|
||||
}
|
||||
|
||||
// Show menu
|
||||
mContextMenu->exec(globalPos);
|
||||
}
|
||||
|
||||
if (object->objectName() == QLatin1String("MastersTable")) {
|
||||
if (!mastersTable->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QPoint globalPos = mastersTable->mapToGlobal(point);
|
||||
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
|
||||
|
||||
// Show the check/uncheck actions depending on the state of the selected items
|
||||
uncheckAction->setEnabled(false);
|
||||
checkAction->setEnabled(false);
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? uncheckAction->setEnabled(true)
|
||||
: checkAction->setEnabled(true);
|
||||
}
|
||||
|
||||
mContextMenu->exec(globalPos);
|
||||
}
|
||||
}
|
88
apps/launcher/datafilespage.hpp
Normal file
88
apps/launcher/datafilespage.hpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#ifndef DATAFILESPAGE_H
|
||||
#define DATAFILESPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QModelIndex>
|
||||
|
||||
#include "ui_datafilespage.h"
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
class QAbstractItemModel;
|
||||
class QAction;
|
||||
class QMenu;
|
||||
|
||||
class DataFilesModel;
|
||||
class TextInputDialog;
|
||||
class GameSettings;
|
||||
class LauncherSettings;
|
||||
class PluginsProxyModel;
|
||||
|
||||
namespace Files { struct ConfigurationManager; }
|
||||
|
||||
class DataFilesPage : public QWidget, private Ui::DataFilesPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0);
|
||||
|
||||
QAbstractItemModel* profilesComboBoxModel();
|
||||
int profilesComboBoxIndex();
|
||||
|
||||
void writeConfig(QString profile = QString());
|
||||
void saveSettings();
|
||||
|
||||
signals:
|
||||
void profileChanged(int index);
|
||||
|
||||
public slots:
|
||||
void setCheckState(QModelIndex index);
|
||||
void setProfilesComboBoxIndex(int index);
|
||||
|
||||
void filterChanged(const QString filter);
|
||||
void showContextMenu(const QPoint &point);
|
||||
void profileChanged(const QString &previous, const QString ¤t);
|
||||
void profileRenamed(const QString &previous, const QString ¤t);
|
||||
void updateOkButton(const QString &text);
|
||||
void updateSplitter();
|
||||
void updateViews();
|
||||
|
||||
// Action slots
|
||||
void on_newProfileAction_triggered();
|
||||
void on_deleteProfileAction_triggered();
|
||||
void on_checkAction_triggered();
|
||||
void on_uncheckAction_triggered();
|
||||
|
||||
private slots:
|
||||
void slotCurrentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
DataFilesModel *mDataFilesModel;
|
||||
|
||||
PluginsProxyModel *mPluginsProxyModel;
|
||||
QSortFilterProxyModel *mMastersProxyModel;
|
||||
|
||||
QSortFilterProxyModel *mFilterProxyModel;
|
||||
|
||||
QMenu *mContextMenu;
|
||||
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
GameSettings &mGameSettings;
|
||||
LauncherSettings &mLauncherSettings;
|
||||
|
||||
TextInputDialog *mNewProfileDialog;
|
||||
|
||||
void setMastersCheckstates(Qt::CheckState state);
|
||||
void setPluginsCheckstates(Qt::CheckState state);
|
||||
|
||||
void createActions();
|
||||
void setupDataFiles();
|
||||
void setupConfig();
|
||||
void readConfig();
|
||||
|
||||
void loadSettings();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
385
apps/launcher/graphicspage.cpp
Normal file
385
apps/launcher/graphicspage.cpp
Normal file
|
@ -0,0 +1,385 @@
|
|||
#include "graphicspage.hpp"
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
|
||||
#ifdef __APPLE__
|
||||
// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154
|
||||
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
||||
#endif
|
||||
#include <SDL.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <boost/math/common_factor.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/files/ogreplugin.hpp>
|
||||
|
||||
#include <components/fileorderlist/utils/naturalsort.hpp>
|
||||
|
||||
#include "settings/graphicssettings.hpp"
|
||||
|
||||
QString getAspect(int x, int y)
|
||||
{
|
||||
int gcd = boost::math::gcd (x, y);
|
||||
int xaspect = x / gcd;
|
||||
int yaspect = y / gcd;
|
||||
// special case: 8 : 5 is usually referred to as 16:10
|
||||
if (xaspect == 8 && yaspect == 5)
|
||||
return QString("16:10");
|
||||
|
||||
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
|
||||
}
|
||||
|
||||
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
|
||||
: mCfgMgr(cfg)
|
||||
, mGraphicsSettings(graphicsSetting)
|
||||
, QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
// Set the maximum res we can set in windowed mode
|
||||
QRect res = getMaximumResolution();
|
||||
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 GraphicsPage::setupOgre()
|
||||
{
|
||||
// Create a log manager so we can surpress debug text to stdout/stderr
|
||||
Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager;
|
||||
logMgr->createLog((mCfgMgr.getLogPath().string() + "/launcherOgre.log"), true, false, false);
|
||||
|
||||
try
|
||||
{
|
||||
mOgre = new Ogre::Root("", "", "./launcherOgre.log");
|
||||
}
|
||||
catch(Ogre::Exception &ex)
|
||||
{
|
||||
QString ogreError = QString::fromStdString(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;
|
||||
}
|
||||
|
||||
|
||||
std::string pluginDir;
|
||||
const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR");
|
||||
if (pluginEnv)
|
||||
pluginDir = pluginEnv;
|
||||
else
|
||||
{
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
|
||||
pluginDir = ".\\";
|
||||
#endif
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||
pluginDir = OGRE_PLUGIN_DIR;
|
||||
#endif
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
|
||||
pluginDir = OGRE_PLUGIN_DIR_REL;
|
||||
#endif
|
||||
}
|
||||
|
||||
QDir dir(QString::fromStdString(pluginDir));
|
||||
pluginDir = dir.absolutePath().toStdString();
|
||||
|
||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre);
|
||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre);
|
||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre);
|
||||
|
||||
#ifdef ENABLE_PLUGIN_GL
|
||||
mGLPlugin = new Ogre::GLPlugin();
|
||||
mOgre->installPlugin(mGLPlugin);
|
||||
#endif
|
||||
#ifdef ENABLE_PLUGIN_Direct3D9
|
||||
mD3D9Plugin = new Ogre::D3D9Plugin();
|
||||
mOgre->installPlugin(mD3D9Plugin);
|
||||
#endif
|
||||
|
||||
// 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 the plugins.cfg file exists and contains a valid rendering plugin.<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 GraphicsPage::setupSDL()
|
||||
{
|
||||
int displays = SDL_GetNumVideoDisplays();
|
||||
|
||||
if (displays < 0)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error receiving number of screens"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>");
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < displays; i++)
|
||||
{
|
||||
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphicsPage::loadSettings()
|
||||
{
|
||||
if (!setupSDL())
|
||||
return false;
|
||||
if (!setupOgre())
|
||||
return false;
|
||||
|
||||
if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))
|
||||
vSyncCheckBox->setCheckState(Qt::Checked);
|
||||
|
||||
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
|
||||
fullScreenCheckBox->setCheckState(Qt::Checked);
|
||||
|
||||
int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing")));
|
||||
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 resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
|
||||
|
||||
if (resIndex != -1) {
|
||||
standardRadioButton->toggle();
|
||||
resolutionComboBox->setCurrentIndex(resIndex);
|
||||
} else {
|
||||
customRadioButton->toggle();
|
||||
customWidthSpinBox->setValue(width.toInt());
|
||||
customHeightSpinBox->setValue(height.toInt());
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void 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"));
|
||||
|
||||
mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText());
|
||||
mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText());
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
} 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 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::fromStdString((*opt_it).c_str()).simplified();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort ascending
|
||||
qSort(result.begin(), result.end(), naturalSortLessThanCI);
|
||||
|
||||
// Replace the zero option with Off
|
||||
int index = result.indexOf("MSAA 0");
|
||||
|
||||
if (index != -1)
|
||||
result.replace(index, tr("Off"));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList GraphicsPage::getAvailableResolutions(int screen)
|
||||
{
|
||||
QStringList result;
|
||||
SDL_DisplayMode mode;
|
||||
int modeIndex, modes = SDL_GetNumDisplayModes(screen);
|
||||
|
||||
if (modes < 0)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error receiving resolutions"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>");
|
||||
msgBox.exec();
|
||||
return result;
|
||||
}
|
||||
|
||||
for (modeIndex = 0; modeIndex < modes; modeIndex++)
|
||||
{
|
||||
if (SDL_GetDisplayMode(screen, modeIndex, &mode) < 0)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error receiving resolutions"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>SDL_GetDisplayMode failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>");
|
||||
msgBox.exec();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString aspect = getAspect(mode.w, mode.h);
|
||||
QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h);
|
||||
|
||||
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
|
||||
resolution.append(tr("\t(Wide ") + aspect + ")");
|
||||
|
||||
} else if (aspect == QLatin1String("4:3")) {
|
||||
resolution.append(tr("\t(Standard 4:3)"));
|
||||
}
|
||||
|
||||
result.append(resolution);
|
||||
}
|
||||
|
||||
result.removeDuplicates();
|
||||
return result;
|
||||
}
|
||||
|
||||
QRect GraphicsPage::getMaximumResolution()
|
||||
{
|
||||
QRect max;
|
||||
int screens = QApplication::desktop()->screenCount();
|
||||
for (int i = 0; i < screens; ++i)
|
||||
{
|
||||
QRect res = QApplication::desktop()->screenGeometry(i);
|
||||
if (res.width() > max.width())
|
||||
max.setWidth(res.width());
|
||||
if (res.height() > max.height())
|
||||
max.setHeight(res.height());
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void GraphicsPage::rendererChanged(const QString &renderer)
|
||||
{
|
||||
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
|
||||
|
||||
antiAliasingComboBox->clear();
|
||||
|
||||
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
|
||||
}
|
||||
|
||||
void GraphicsPage::screenChanged(int screen)
|
||||
{
|
||||
if (screen >= 0) {
|
||||
resolutionComboBox->clear();
|
||||
resolutionComboBox->addItems(getAvailableResolutions(screen));
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsPage::slotFullScreenChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked) {
|
||||
standardRadioButton->toggle();
|
||||
customRadioButton->setEnabled(false);
|
||||
customWidthSpinBox->setEnabled(false);
|
||||
customHeightSpinBox->setEnabled(false);
|
||||
} else {
|
||||
customRadioButton->setEnabled(true);
|
||||
customWidthSpinBox->setEnabled(true);
|
||||
customHeightSpinBox->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsPage::slotStandardToggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
resolutionComboBox->setEnabled(true);
|
||||
customWidthSpinBox->setEnabled(false);
|
||||
customHeightSpinBox->setEnabled(false);
|
||||
} else {
|
||||
resolutionComboBox->setEnabled(false);
|
||||
customWidthSpinBox->setEnabled(true);
|
||||
customHeightSpinBox->setEnabled(true);
|
||||
}
|
||||
}
|
66
apps/launcher/graphicspage.hpp
Normal file
66
apps/launcher/graphicspage.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#ifndef GRAPHICSPAGE_H
|
||||
#define GRAPHICSPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreRenderSystem.h>
|
||||
//#include <OgreConfigFile.h>
|
||||
//#include <OgreConfigDialog.h>
|
||||
|
||||
// Static plugin headers
|
||||
#ifdef ENABLE_PLUGIN_GL
|
||||
# include "OgreGLPlugin.h"
|
||||
#endif
|
||||
#ifdef ENABLE_PLUGIN_Direct3D9
|
||||
# include "OgreD3D9Plugin.h"
|
||||
#endif
|
||||
|
||||
#include "ui_graphicspage.h"
|
||||
|
||||
class GraphicsSettings;
|
||||
|
||||
namespace Files { struct ConfigurationManager; }
|
||||
|
||||
class GraphicsPage : public QWidget, private Ui::GraphicsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
|
||||
|
||||
void saveSettings();
|
||||
bool loadSettings();
|
||||
|
||||
public slots:
|
||||
void rendererChanged(const QString &renderer);
|
||||
void screenChanged(int screen);
|
||||
|
||||
private slots:
|
||||
void slotFullScreenChanged(int state);
|
||||
void slotStandardToggled(bool checked);
|
||||
|
||||
private:
|
||||
Ogre::Root *mOgre;
|
||||
Ogre::RenderSystem *mSelectedRenderSystem;
|
||||
Ogre::RenderSystem *mOpenGLRenderSystem;
|
||||
Ogre::RenderSystem *mDirect3DRenderSystem;
|
||||
#ifdef ENABLE_PLUGIN_GL
|
||||
Ogre::GLPlugin* mGLPlugin;
|
||||
#endif
|
||||
#ifdef ENABLE_PLUGIN_Direct3D9
|
||||
Ogre::D3D9Plugin* mD3D9Plugin;
|
||||
#endif
|
||||
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
GraphicsSettings &mGraphicsSettings;
|
||||
|
||||
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
|
||||
QStringList getAvailableResolutions(int screen);
|
||||
QRect getMaximumResolution();
|
||||
|
||||
bool setupOgre();
|
||||
bool setupSDL();
|
||||
};
|
||||
|
||||
#endif
|
64
apps/launcher/main.cpp
Normal file
64
apps/launcher/main.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include <QApplication>
|
||||
#include <QTextCodec>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef __APPLE__
|
||||
// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154
|
||||
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
||||
#endif
|
||||
#include <SDL.h>
|
||||
|
||||
#include "maindialog.hpp"
|
||||
// SDL workaround
|
||||
#include "graphicspage.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0)
|
||||
{
|
||||
qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Now we make sure the current dir is set to application path
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
if (dir.dirName() == "MacOS") {
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
}
|
||||
|
||||
// force Qt to load only LOCAL plugins, don't touch system Qt installation
|
||||
QDir pluginsPath(QCoreApplication::applicationDirPath());
|
||||
pluginsPath.cdUp();
|
||||
pluginsPath.cd("Plugins");
|
||||
|
||||
QStringList libraryPaths;
|
||||
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
|
||||
app.setLibraryPaths(libraryPaths);
|
||||
#endif
|
||||
|
||||
QDir::setCurrent(dir.absolutePath());
|
||||
|
||||
// Support non-latin characters
|
||||
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
MainDialog mainWin;
|
||||
|
||||
if (mainWin.setup()) {
|
||||
mainWin.show();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int returnValue = app.exec();
|
||||
SDL_Quit();
|
||||
return returnValue;
|
||||
}
|
||||
|
854
apps/launcher/maindialog.cpp
Normal file
854
apps/launcher/maindialog.cpp
Normal file
|
@ -0,0 +1,854 @@
|
|||
#include "maindialog.hpp"
|
||||
|
||||
#include <QFontDatabase>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QCloseEvent>
|
||||
#include <QTextCodec>
|
||||
#include <QProcess>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#ifndef WIN32
|
||||
#include "unshieldthread.hpp"
|
||||
#endif
|
||||
|
||||
#include "textslotmsgbox.hpp"
|
||||
|
||||
#include "utils/checkablemessagebox.hpp"
|
||||
|
||||
#include "playpage.hpp"
|
||||
#include "graphicspage.hpp"
|
||||
#include "datafilespage.hpp"
|
||||
|
||||
MainDialog::MainDialog()
|
||||
: mGameSettings(mCfgMgr)
|
||||
{
|
||||
// Install the stylesheet font
|
||||
QFile file;
|
||||
QFontDatabase fontDatabase;
|
||||
|
||||
const QStringList fonts = fontDatabase.families();
|
||||
|
||||
// Check if the font is installed
|
||||
if (!fonts.contains("EB Garamond")) {
|
||||
|
||||
QString font = QString::fromStdString(mCfgMgr.getGlobalDataPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
|
||||
file.setFileName(font);
|
||||
|
||||
if (!file.exists()) {
|
||||
font = QString::fromStdString(mCfgMgr.getLocalPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
|
||||
}
|
||||
|
||||
fontDatabase.addApplicationFont(font);
|
||||
}
|
||||
|
||||
setupUi(this);
|
||||
|
||||
iconWidget->setViewMode(QListView::IconMode);
|
||||
iconWidget->setWrapping(false);
|
||||
iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
|
||||
iconWidget->setIconSize(QSize(48, 48));
|
||||
iconWidget->setMovement(QListView::Static);
|
||||
|
||||
iconWidget->setSpacing(4);
|
||||
iconWidget->setCurrentRow(0);
|
||||
iconWidget->setFlow(QListView::LeftToRight);
|
||||
|
||||
QPushButton *playButton = new QPushButton(tr("Play"));
|
||||
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
|
||||
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
|
||||
|
||||
// Remove what's this? button
|
||||
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
createIcons();
|
||||
}
|
||||
|
||||
void MainDialog::createIcons()
|
||||
{
|
||||
if (!QIcon::hasThemeIcon("document-new"))
|
||||
QIcon::setThemeName("tango");
|
||||
|
||||
// We create a fallback icon because the default fallback doesn't work
|
||||
QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png");
|
||||
|
||||
QListWidgetItem *playButton = new QListWidgetItem(iconWidget);
|
||||
playButton->setIcon(QIcon(":/images/openmw.png"));
|
||||
playButton->setText(tr("Play"));
|
||||
playButton->setTextAlignment(Qt::AlignCenter);
|
||||
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
||||
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
|
||||
graphicsButton->setText(tr("Graphics"));
|
||||
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
|
||||
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||
dataFilesButton->setText(tr("Data Files"));
|
||||
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
connect(iconWidget,
|
||||
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
||||
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
|
||||
|
||||
}
|
||||
|
||||
void MainDialog::createPages()
|
||||
{
|
||||
mPlayPage = new PlayPage(this);
|
||||
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
|
||||
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
||||
|
||||
// Set the combobox of the play page to imitate the combobox on the datafilespage
|
||||
mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel());
|
||||
mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex());
|
||||
|
||||
// Add the pages to the stacked widget
|
||||
pagesWidget->addWidget(mPlayPage);
|
||||
pagesWidget->addWidget(mGraphicsPage);
|
||||
pagesWidget->addWidget(mDataFilesPage);
|
||||
|
||||
// Select the first page
|
||||
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
|
||||
|
||||
connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play()));
|
||||
|
||||
connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int)));
|
||||
connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int)));
|
||||
|
||||
}
|
||||
|
||||
bool MainDialog::showFirstRunDialog()
|
||||
{
|
||||
QStringList iniPaths;
|
||||
|
||||
foreach (const QString &path, mGameSettings.getDataDirs()) {
|
||||
QDir dir(path);
|
||||
dir.setPath(dir.canonicalPath()); // Resolve symlinks
|
||||
|
||||
if (dir.exists(QString("Morrowind.ini")))
|
||||
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
|
||||
else
|
||||
{
|
||||
if (!dir.cdUp())
|
||||
continue; // Cannot move from Data Files
|
||||
|
||||
if (dir.exists(QString("Morrowind.ini")))
|
||||
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
|
||||
}
|
||||
}
|
||||
|
||||
// Ask the user where the Morrowind.ini is
|
||||
if (iniPaths.empty()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error detecting Morrowind configuration"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(QObject::tr("<br><b>Could not find Morrowind.ini</b><br><br> \
|
||||
OpenMW needs to import settings from this file.<br><br> \
|
||||
Press \"Browse...\" to specify the location manually.<br>"));
|
||||
|
||||
QAbstractButton *dirSelectButton =
|
||||
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole);
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
QString iniFile;
|
||||
if (msgBox.clickedButton() == dirSelectButton) {
|
||||
iniFile = QFileDialog::getOpenFileName(
|
||||
NULL,
|
||||
QObject::tr("Select configuration file"),
|
||||
QDir::currentPath(),
|
||||
QString(tr("Morrowind configuration file (*.ini)")));
|
||||
}
|
||||
|
||||
if (iniFile.isEmpty())
|
||||
return false; // Cancel was clicked;
|
||||
|
||||
QFileInfo info(iniFile);
|
||||
iniPaths.clear();
|
||||
iniPaths.append(info.absoluteFilePath());
|
||||
}
|
||||
|
||||
CheckableMessageBox msgBox(this);
|
||||
msgBox.setWindowTitle(tr("Morrowind installation detected"));
|
||||
|
||||
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion);
|
||||
int size = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize);
|
||||
msgBox.setIconPixmap(icon.pixmap(size, size));
|
||||
|
||||
QAbstractButton *importerButton =
|
||||
msgBox.addButton(tr("Import"), QDialogButtonBox::AcceptRole); // ActionRole doesn't work?!
|
||||
QAbstractButton *skipButton =
|
||||
msgBox.addButton(tr("Skip"), QDialogButtonBox::RejectRole);
|
||||
|
||||
Q_UNUSED(skipButton); // Surpress compiler unused warning
|
||||
|
||||
msgBox.setStandardButtons(QDialogButtonBox::NoButton);
|
||||
msgBox.setText(tr("<br><b>An existing Morrowind configuration was detected</b><br> \
|
||||
<br>Would you like to import settings from Morrowind.ini?<br> \
|
||||
<br><b>Warning: In most cases OpenMW needs these settings to run properly</b><br>"));
|
||||
msgBox.setCheckBoxText(tr("Include selected masters and plugins (creates a new profile)"));
|
||||
msgBox.exec();
|
||||
|
||||
|
||||
if (msgBox.clickedButton() == importerButton) {
|
||||
|
||||
if (iniPaths.count() > 1) {
|
||||
// Multiple Morrowind.ini files found
|
||||
bool ok;
|
||||
QString path = QInputDialog::getItem(this, tr("Multiple configurations found"),
|
||||
tr("<br><b>There are multiple Morrowind.ini files found.</b><br><br> \
|
||||
Please select the one you wish to import from:"), iniPaths, 0, false, &ok);
|
||||
if (ok && !path.isEmpty()) {
|
||||
iniPaths.clear();
|
||||
iniPaths.append(path);
|
||||
} else {
|
||||
// Cancel was clicked
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the file if it doesn't already exist, else the importer will fail
|
||||
QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg");
|
||||
QFile file(path);
|
||||
|
||||
if (!file.exists()) {
|
||||
if (!file.open(QIODevice::ReadWrite)) {
|
||||
// File cannot be 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;
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Construct the arguments to run the importer
|
||||
QStringList arguments;
|
||||
|
||||
if (msgBox.isChecked())
|
||||
arguments.append(QString("--game-files"));
|
||||
|
||||
arguments.append(QString("--encoding"));
|
||||
arguments.append(mGameSettings.value(QString("encoding"), QString("win1252")));
|
||||
arguments.append(QString("--ini"));
|
||||
arguments.append(iniPaths.first());
|
||||
arguments.append(QString("--cfg"));
|
||||
arguments.append(path);
|
||||
|
||||
if (!startProgram(QString("mwiniimport"), arguments, false))
|
||||
return false;
|
||||
|
||||
// Re-read the game settings
|
||||
if (!setupGameSettings())
|
||||
return false;
|
||||
|
||||
// Add a new profile
|
||||
if (msgBox.isChecked()) {
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported"));
|
||||
|
||||
mLauncherSettings.remove(QString("Profiles/Imported/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/Imported/plugin"));
|
||||
|
||||
QStringList masters = mGameSettings.values(QString("master"));
|
||||
QStringList plugins = mGameSettings.values(QString("plugin"));
|
||||
|
||||
foreach (const QString &master, masters) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/Imported/master"), master);
|
||||
}
|
||||
|
||||
foreach (const QString &plugin, plugins) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/Imported/plugin"), plugin);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainDialog::setup()
|
||||
{
|
||||
if (!setupLauncherSettings())
|
||||
return false;
|
||||
|
||||
if (!setupGameSettings())
|
||||
return false;
|
||||
|
||||
if (!setupGraphicsSettings())
|
||||
return false;
|
||||
|
||||
// Check if we need to show the importer
|
||||
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
|
||||
{
|
||||
if (!showFirstRunDialog())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now create the pages as they need the settings
|
||||
createPages();
|
||||
|
||||
// Call this so we can exit on Ogre/SDL errors before mainwindow is shown
|
||||
if (!mGraphicsPage->loadSettings())
|
||||
return false;
|
||||
|
||||
loadSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
|
||||
{
|
||||
if (!current)
|
||||
current = previous;
|
||||
|
||||
pagesWidget->setCurrentIndex(iconWidget->row(current));
|
||||
}
|
||||
|
||||
bool MainDialog::setupLauncherSettings()
|
||||
{
|
||||
mLauncherSettings.setMultiValueEnabled(true);
|
||||
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
|
||||
QStringList paths;
|
||||
paths.append(QString("launcher.cfg"));
|
||||
paths.append(userPath + QString("launcher.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(QObject::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"));
|
||||
|
||||
mLauncherSettings.readFile(stream);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
bool expansions(UnshieldThread& cd)
|
||||
{
|
||||
if(cd.BloodmoonDone())
|
||||
{
|
||||
cd.Done();
|
||||
return false;
|
||||
}
|
||||
|
||||
QMessageBox expansionsBox;
|
||||
expansionsBox.setText(QObject::tr("<br>Would you like to install expansions now ? (make sure you have the disc)<br> \
|
||||
If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.<br>"));
|
||||
|
||||
QAbstractButton* tribunalButton = NULL;
|
||||
if(!cd.TribunalDone())
|
||||
tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole);
|
||||
|
||||
QAbstractButton* bloodmoonButton = expansionsBox.addButton(QObject::tr("&Bloodmoon"), QMessageBox::ActionRole);
|
||||
QAbstractButton* noneButton = expansionsBox.addButton(QObject::tr("&None"), QMessageBox::ActionRole);
|
||||
|
||||
expansionsBox.exec();
|
||||
|
||||
if(expansionsBox.clickedButton() == noneButton)
|
||||
{
|
||||
cd.Done();
|
||||
return false;
|
||||
}
|
||||
else if(expansionsBox.clickedButton() == tribunalButton)
|
||||
{
|
||||
|
||||
TextSlotMsgBox cdbox;
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
|
||||
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
|
||||
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
|
||||
|
||||
cd.SetTribunalPath(
|
||||
QFileDialog::getOpenFileName(
|
||||
NULL,
|
||||
QObject::tr("Select data1.hdr from Tribunal Installation CD (Tribunal/data1.hdr on GOTY CDs)"),
|
||||
QDir::currentPath(),
|
||||
QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
|
||||
|
||||
cd.start();
|
||||
cdbox.exec();
|
||||
}
|
||||
else if(expansionsBox.clickedButton() == bloodmoonButton)
|
||||
{
|
||||
|
||||
TextSlotMsgBox cdbox;
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
|
||||
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
|
||||
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
|
||||
|
||||
cd.SetBloodmoonPath(
|
||||
QFileDialog::getOpenFileName(
|
||||
NULL,
|
||||
QObject::tr("Select data1.hdr from Bloodmoon Installation CD (Bloodmoon/data1.hdr on GOTY CDs)"),
|
||||
QDir::currentPath(),
|
||||
QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
|
||||
|
||||
cd.start();
|
||||
cdbox.exec();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // WIN32
|
||||
|
||||
bool MainDialog::setupGameSettings()
|
||||
{
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
|
||||
|
||||
QStringList paths;
|
||||
paths.append(userPath + QString("openmw.cfg"));
|
||||
paths.append(QString("openmw.cfg"));
|
||||
paths.append(globalPath + QString("openmw.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(QObject::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"));
|
||||
|
||||
mGameSettings.readFile(stream);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
QStringList dataDirs;
|
||||
|
||||
// Check if the paths actually contain data files
|
||||
foreach (const QString path, mGameSettings.getDataDirs()) {
|
||||
QDir dir(path);
|
||||
QStringList filters;
|
||||
filters << "*.esp" << "*.esm";
|
||||
|
||||
if (!dir.entryList(filters).isEmpty())
|
||||
dataDirs.append(path);
|
||||
}
|
||||
|
||||
if (dataDirs.isEmpty())
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error detecting Morrowind installation"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(QObject::tr("<br><b>Could not find the Data Files location</b><br><br> \
|
||||
The directory containing the data files was not found.<br><br> \
|
||||
Press \"Browse...\" to specify the location manually.<br>"));
|
||||
|
||||
QAbstractButton *dirSelectButton =
|
||||
msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole);
|
||||
|
||||
#ifndef WIN32
|
||||
QAbstractButton *cdSelectButton =
|
||||
msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole);
|
||||
#endif
|
||||
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
QString selectedFile;
|
||||
if (msgBox.clickedButton() == dirSelectButton) {
|
||||
selectedFile = QFileDialog::getOpenFileName(
|
||||
NULL,
|
||||
QObject::tr("Select master file"),
|
||||
QDir::currentPath(),
|
||||
QString(tr("Morrowind master file (*.esm)")));
|
||||
}
|
||||
#ifndef WIN32
|
||||
else if(msgBox.clickedButton() == cdSelectButton) {
|
||||
UnshieldThread cd;
|
||||
|
||||
{
|
||||
TextSlotMsgBox cdbox;
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
|
||||
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
|
||||
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
|
||||
|
||||
cd.SetMorrowindPath(
|
||||
QFileDialog::getOpenFileName(
|
||||
NULL,
|
||||
QObject::tr("Select data1.hdr from Morrowind Installation CD"),
|
||||
QDir::currentPath(),
|
||||
QString(tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
|
||||
|
||||
cd.SetOutputPath(
|
||||
QFileDialog::getExistingDirectory(
|
||||
NULL,
|
||||
QObject::tr("Select where to extract files to"),
|
||||
QDir::currentPath(),
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData());
|
||||
|
||||
cd.start();
|
||||
cdbox.exec();
|
||||
}
|
||||
|
||||
while(expansions(cd));
|
||||
|
||||
selectedFile = QString::fromStdString(cd.GetMWEsmPath());
|
||||
}
|
||||
#endif // WIN32
|
||||
|
||||
if (selectedFile.isEmpty())
|
||||
return false; // Cancel was clicked;
|
||||
|
||||
QFileInfo info(selectedFile);
|
||||
|
||||
// Add the new dir to the settings file and to the data dir container
|
||||
mGameSettings.setMultiValue(QString("data"), info.absolutePath());
|
||||
mGameSettings.addDataDir(info.absolutePath());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainDialog::setupGraphicsSettings()
|
||||
{
|
||||
mGraphicsSettings.setMultiValueEnabled(false);
|
||||
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
|
||||
|
||||
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(QObject::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();
|
||||
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(QObject::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"));
|
||||
|
||||
mGraphicsSettings.readFile(stream);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainDialog::loadSettings()
|
||||
{
|
||||
int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt();
|
||||
int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt();
|
||||
|
||||
int posX = mLauncherSettings.value(QString("General/MainWindow/posx")).toInt();
|
||||
int posY = mLauncherSettings.value(QString("General/MainWindow/posy")).toInt();
|
||||
|
||||
resize(width, height);
|
||||
move(posX, posY);
|
||||
}
|
||||
|
||||
void MainDialog::saveSettings()
|
||||
{
|
||||
QString width = QString::number(this->width());
|
||||
QString height = QString::number(this->height());
|
||||
|
||||
mLauncherSettings.setValue(QString("General/MainWindow/width"), width);
|
||||
mLauncherSettings.setValue(QString("General/MainWindow/height"), height);
|
||||
|
||||
QString posX = QString::number(this->pos().x());
|
||||
QString posY = QString::number(this->pos().y());
|
||||
|
||||
mLauncherSettings.setValue(QString("General/MainWindow/posx"), posX);
|
||||
mLauncherSettings.setValue(QString("General/MainWindow/posy"), posY);
|
||||
|
||||
mLauncherSettings.setValue(QString("General/firstrun"), QString("false"));
|
||||
|
||||
}
|
||||
|
||||
bool MainDialog::writeSettings()
|
||||
{
|
||||
// Now write all config files
|
||||
saveSettings();
|
||||
mGraphicsPage->saveSettings();
|
||||
mDataFilesPage->saveSettings();
|
||||
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
QDir dir(userPath);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Game settings
|
||||
QFile file(userPath + QString("openmw.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;
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGameSettings.writeFile(stream);
|
||||
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;
|
||||
}
|
||||
|
||||
stream.setDevice(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGraphicsSettings.writeFile(stream);
|
||||
file.close();
|
||||
|
||||
// Launcher settings
|
||||
file.setFileName(userPath + QString("launcher.cfg"));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
stream.setDevice(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mLauncherSettings.writeFile(stream);
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
writeSettings();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void MainDialog::play()
|
||||
{
|
||||
if (!writeSettings()) {
|
||||
qApp->quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mGameSettings.hasMaster()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("No master file selected"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>You do not have any master files selected.</b><br><br> \
|
||||
OpenMW will not start without a master file selected.<br>"));
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
// Launch the game detached
|
||||
startProgram(QString("openmw"), true);
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
|
||||
{
|
||||
QString path = name;
|
||||
#ifdef Q_OS_WIN
|
||||
path.append(QString(".exe"));
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
path = dir.absoluteFilePath(name);
|
||||
#else
|
||||
path.prepend(QString("./"));
|
||||
#endif
|
||||
|
||||
QFile file(path);
|
||||
|
||||
QProcess process;
|
||||
QFileInfo info(file);
|
||||
|
||||
if (!file.exists()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>Could not find %1</b><br><br> \
|
||||
The application is not found.<br> \
|
||||
Please make sure OpenMW is installed correctly and try again.<br>").arg(info.fileName()));
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info.isExecutable()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
|
||||
The application is not executable.<br> \
|
||||
Please make sure you have the right permissions and try again.<br>").arg(info.fileName()));
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start the executable
|
||||
if (detached) {
|
||||
if (!process.startDetached(path, arguments)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
|
||||
An error occurred while starting %1.<br><br> \
|
||||
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
|
||||
msgBox.setDetailedText(process.errorString());
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
process.start(path, arguments);
|
||||
if (!process.waitForFinished()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
|
||||
An error occurred while starting %1.<br><br> \
|
||||
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
|
||||
msgBox.setDetailedText(process.errorString());
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (process.exitCode() != 0 || process.exitStatus() == QProcess::CrashExit) {
|
||||
QString error(process.readAllStandardError());
|
||||
error.append(tr("\nArguments:\n"));
|
||||
error.append(arguments.join(" "));
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error running executable"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>Executable %1 returned an error</b><br><br> \
|
||||
An error occurred while running %1.<br><br> \
|
||||
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
|
||||
msgBox.setDetailedText(error);
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
67
apps/launcher/maindialog.hpp
Normal file
67
apps/launcher/maindialog.hpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#ifndef MAINDIALOG_H
|
||||
#define MAINDIALOG_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
#include "settings/gamesettings.hpp"
|
||||
#include "settings/graphicssettings.hpp"
|
||||
#include "settings/launchersettings.hpp"
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QStackedWidget;
|
||||
class QStringList;
|
||||
class QStringListModel;
|
||||
class QString;
|
||||
|
||||
class PlayPage;
|
||||
class GraphicsPage;
|
||||
class DataFilesPage;
|
||||
|
||||
class MainDialog : public QMainWindow, private Ui::MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainDialog();
|
||||
bool setup();
|
||||
bool showFirstRunDialog();
|
||||
|
||||
public slots:
|
||||
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
|
||||
void play();
|
||||
|
||||
private:
|
||||
void createIcons();
|
||||
void createPages();
|
||||
|
||||
bool setupLauncherSettings();
|
||||
bool setupGameSettings();
|
||||
bool setupGraphicsSettings();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
bool writeSettings();
|
||||
|
||||
inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); }
|
||||
bool startProgram(const QString &name, const QStringList &arguments, bool detached = false);
|
||||
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
PlayPage *mPlayPage;
|
||||
GraphicsPage *mGraphicsPage;
|
||||
DataFilesPage *mDataFilesPage;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
|
||||
GameSettings mGameSettings;
|
||||
GraphicsSettings mGraphicsSettings;
|
||||
LauncherSettings mLauncherSettings;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
43
apps/launcher/playpage.cpp
Normal file
43
apps/launcher/playpage.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "playpage.hpp"
|
||||
|
||||
#include <QListView>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <QPlastiqueStyle>
|
||||
#endif
|
||||
|
||||
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
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(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
|
||||
connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked()));
|
||||
|
||||
}
|
||||
|
||||
void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model)
|
||||
{
|
||||
profilesComboBox->setModel(model);
|
||||
}
|
||||
|
||||
void PlayPage::setProfilesComboBoxIndex(int index)
|
||||
{
|
||||
profilesComboBox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void PlayPage::slotCurrentIndexChanged(int index)
|
||||
{
|
||||
emit profileChanged(index);
|
||||
}
|
||||
|
||||
void PlayPage::slotPlayClicked()
|
||||
{
|
||||
emit playButtonClicked();
|
||||
}
|
35
apps/launcher/playpage.hpp
Normal file
35
apps/launcher/playpage.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef PLAYPAGE_H
|
||||
#define PLAYPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_playpage.h"
|
||||
|
||||
class QComboBox;
|
||||
class QPushButton;
|
||||
class QAbstractItemModel;
|
||||
|
||||
class PlayPage : public QWidget, private Ui::PlayPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlayPage(QWidget *parent = 0);
|
||||
void setProfilesComboBoxModel(QAbstractItemModel *model);
|
||||
|
||||
signals:
|
||||
void profileChanged(int index);
|
||||
void playButtonClicked();
|
||||
|
||||
public slots:
|
||||
void setProfilesComboBoxIndex(int index);
|
||||
|
||||
private slots:
|
||||
void slotCurrentIndexChanged(int index);
|
||||
void slotPlayClicked();
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
175
apps/launcher/settings/gamesettings.cpp
Normal file
175
apps/launcher/settings/gamesettings.cpp
Normal file
|
@ -0,0 +1,175 @@
|
|||
#include "gamesettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <boost/version.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) */
|
||||
|
||||
|
||||
GameSettings::GameSettings(Files::ConfigurationManager &cfg)
|
||||
: mCfgMgr(cfg)
|
||||
{
|
||||
}
|
||||
|
||||
GameSettings::~GameSettings()
|
||||
{
|
||||
}
|
||||
|
||||
void GameSettings::validatePaths()
|
||||
{
|
||||
if (mSettings.isEmpty() || !mDataDirs.isEmpty())
|
||||
return; // Don't re-validate paths if they are already parsed
|
||||
|
||||
QStringList paths = mSettings.values(QString("data"));
|
||||
Files::PathContainer dataDirs;
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
dataDirs.push_back(Files::PathContainer::value_type(path.toStdString()));
|
||||
}
|
||||
|
||||
// Parse the data dirs to convert the tokenized paths
|
||||
mCfgMgr.processPaths(dataDirs);
|
||||
mDataDirs.clear();
|
||||
|
||||
for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) {
|
||||
QString path = QString::fromStdString(it->string());
|
||||
path.remove(QChar('\"'));
|
||||
|
||||
QDir dir(path);
|
||||
if (dir.exists())
|
||||
mDataDirs.append(path);
|
||||
}
|
||||
|
||||
// Do the same for data-local
|
||||
QString local = mSettings.value(QString("data-local"));
|
||||
|
||||
if (local.isEmpty())
|
||||
return;
|
||||
|
||||
dataDirs.clear();
|
||||
dataDirs.push_back(Files::PathContainer::value_type(local.toStdString()));
|
||||
|
||||
mCfgMgr.processPaths(dataDirs);
|
||||
|
||||
if (!dataDirs.empty()) {
|
||||
QString path = QString::fromStdString(dataDirs.front().string());
|
||||
path.remove(QChar('\"'));
|
||||
|
||||
QDir dir(path);
|
||||
if (dir.exists())
|
||||
mDataLocal = path;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GameSettings::values(const QString &key, const QStringList &defaultValues)
|
||||
{
|
||||
if (!mSettings.values(key).isEmpty())
|
||||
return mSettings.values(key);
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
bool GameSettings::readFile(QTextStream &stream)
|
||||
{
|
||||
QMap<QString, QString> cache;
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).trimmed();
|
||||
QString value = keyRe.cap(2).trimmed();
|
||||
|
||||
// Don't remove existing data entries
|
||||
if (key != QLatin1String("data"))
|
||||
mSettings.remove(key);
|
||||
|
||||
QStringList values = cache.values(key);
|
||||
values.append(mSettings.values(key));
|
||||
|
||||
if (!values.contains(value)) {
|
||||
cache.insertMulti(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mSettings.isEmpty()) {
|
||||
mSettings = cache; // This is the first time we read a file
|
||||
validatePaths();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the changed keys with those which didn't
|
||||
mSettings.unite(cache);
|
||||
validatePaths();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
// Iterate in reverse order to preserve insertion order
|
||||
QMapIterator<QString, QString> i(mSettings);
|
||||
i.toBack();
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
i.previous();
|
||||
|
||||
if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin"))
|
||||
continue;
|
||||
|
||||
// Quote paths with spaces
|
||||
if (i.key() == QLatin1String("data")
|
||||
|| i.key() == QLatin1String("data-local")
|
||||
|| i.key() == QLatin1String("resources"))
|
||||
{
|
||||
if (i.value().contains(QChar(' ')))
|
||||
{
|
||||
QString stripped = i.value();
|
||||
stripped.remove(QChar('\"')); // Remove quotes
|
||||
|
||||
stream << i.key() << "=\"" << stripped << "\"\n";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
stream << i.key() << "=" << i.value() << "\n";
|
||||
|
||||
}
|
||||
|
||||
QStringList masters = mSettings.values(QString("master"));
|
||||
for (int i = masters.count(); i--;) {
|
||||
stream << "master=" << masters.at(i) << "\n";
|
||||
}
|
||||
|
||||
QStringList plugins = mSettings.values(QString("plugin"));
|
||||
for (int i = plugins.count(); i--;) {
|
||||
stream << "plugin=" << plugins.at(i) << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
62
apps/launcher/settings/gamesettings.hpp
Normal file
62
apps/launcher/settings/gamesettings.hpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#ifndef GAMESETTINGS_HPP
|
||||
#define GAMESETTINGS_HPP
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
|
||||
struct ConfigurationManager;}
|
||||
|
||||
class GameSettings
|
||||
{
|
||||
public:
|
||||
GameSettings(Files::ConfigurationManager &cfg);
|
||||
~GameSettings();
|
||||
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
|
||||
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
}
|
||||
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
|
||||
inline QStringList getDataDirs() { return mDataDirs; }
|
||||
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
|
||||
inline QString getDataLocal() {return mDataLocal; }
|
||||
inline bool hasMaster() { return mSettings.count(QString("master")) > 0; }
|
||||
|
||||
QStringList values(const QString &key, const QStringList &defaultValues = QStringList());
|
||||
bool readFile(QTextStream &stream);
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
void validatePaths();
|
||||
QMap<QString, QString> mSettings;
|
||||
|
||||
QStringList mDataDirs;
|
||||
QString mDataLocal;
|
||||
};
|
||||
|
||||
#endif // GAMESETTINGS_HPP
|
44
apps/launcher/settings/graphicssettings.cpp
Normal file
44
apps/launcher/settings/graphicssettings.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "graphicssettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
GraphicsSettings::GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
GraphicsSettings::~GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
bool 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;
|
||||
|
||||
}
|
16
apps/launcher/settings/graphicssettings.hpp
Normal file
16
apps/launcher/settings/graphicssettings.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef GRAPHICSSETTINGS_HPP
|
||||
#define GRAPHICSSETTINGS_HPP
|
||||
|
||||
#include "settingsbase.hpp"
|
||||
|
||||
class GraphicsSettings : public SettingsBase<QMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
GraphicsSettings();
|
||||
~GraphicsSettings();
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
|
||||
#endif // GRAPHICSSETTINGS_HPP
|
101
apps/launcher/settings/launchersettings.cpp
Normal file
101
apps/launcher/settings/launchersettings.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "launchersettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
LauncherSettings::LauncherSettings()
|
||||
{
|
||||
}
|
||||
|
||||
LauncherSettings::~LauncherSettings()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
|
||||
{
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
if (flags == Qt::MatchExactly)
|
||||
return settings.values(key);
|
||||
|
||||
QStringList result;
|
||||
|
||||
if (flags == Qt::MatchStartsWith) {
|
||||
QStringList keys = settings.keys();
|
||||
|
||||
foreach (const QString ¤tKey, keys) {
|
||||
if (currentKey.startsWith(key))
|
||||
result.append(settings.value(currentKey));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList LauncherSettings::subKeys(const QString &key)
|
||||
{
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
QStringList keys = settings.uniqueKeys();
|
||||
|
||||
QRegExp keyRe("(.+)/");
|
||||
|
||||
QStringList result;
|
||||
|
||||
foreach (const QString ¤tKey, keys) {
|
||||
|
||||
if (keyRe.indexIn(currentKey) != -1) {
|
||||
|
||||
QString prefixedKey = keyRe.cap(1);
|
||||
if(prefixedKey.startsWith(key)) {
|
||||
|
||||
QString subKey = prefixedKey.remove(key);
|
||||
if (!subKey.isEmpty())
|
||||
result.append(subKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.removeDuplicates();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LauncherSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
QMapIterator<QString, QString> i(settings);
|
||||
i.toBack();
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
i.previous();
|
||||
|
||||
QString prefix;
|
||||
QString key;
|
||||
|
||||
if (sectionRe.exactMatch(i.key())) {
|
||||
prefix = sectionRe.cap(1);
|
||||
key = sectionRe.cap(2);
|
||||
}
|
||||
|
||||
// Get rid of legacy settings
|
||||
if (key.contains(QChar('\\')))
|
||||
continue;
|
||||
|
||||
if (key == QLatin1String("CurrentProfile"))
|
||||
continue;
|
||||
|
||||
if (sectionPrefix != prefix) {
|
||||
sectionPrefix = prefix;
|
||||
stream << "\n[" << prefix << "]\n";
|
||||
}
|
||||
|
||||
stream << key << "=" << i.value() << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
19
apps/launcher/settings/launchersettings.hpp
Normal file
19
apps/launcher/settings/launchersettings.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef LAUNCHERSETTINGS_HPP
|
||||
#define LAUNCHERSETTINGS_HPP
|
||||
|
||||
#include "settingsbase.hpp"
|
||||
|
||||
class LauncherSettings : public SettingsBase<QMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
LauncherSettings();
|
||||
~LauncherSettings();
|
||||
|
||||
QStringList subKeys(const QString &key);
|
||||
QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly);
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
|
||||
#endif // LAUNCHERSETTINGS_HPP
|
109
apps/launcher/settings/settingsbase.hpp
Normal file
109
apps/launcher/settings/settingsbase.hpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
#ifndef SETTINGSBASE_HPP
|
||||
#define SETTINGSBASE_HPP
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
template <class Map>
|
||||
class SettingsBase
|
||||
{
|
||||
|
||||
public:
|
||||
SettingsBase() { mMultiValue = false; }
|
||||
~SettingsBase() {}
|
||||
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
}
|
||||
|
||||
inline void setMultiValueEnabled(bool enable)
|
||||
{
|
||||
mMultiValue = enable;
|
||||
}
|
||||
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
|
||||
Map getSettings() {return mSettings;}
|
||||
|
||||
bool readFile(QTextStream &stream)
|
||||
{
|
||||
mCache.clear();
|
||||
|
||||
QString sectionPrefix;
|
||||
|
||||
QRegExp sectionRe("^\\[([^]]+)\\]");
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
if (sectionRe.exactMatch(line)) {
|
||||
sectionPrefix = sectionRe.cap(1);
|
||||
sectionPrefix.append("/");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).trimmed();
|
||||
QString value = keyRe.cap(2).trimmed();
|
||||
|
||||
if (!sectionPrefix.isEmpty())
|
||||
key.prepend(sectionPrefix);
|
||||
|
||||
mSettings.remove(key);
|
||||
|
||||
QStringList values = mCache.values(key);
|
||||
|
||||
if (!values.contains(value)) {
|
||||
if (mMultiValue) {
|
||||
mCache.insertMulti(key, value);
|
||||
} else {
|
||||
mCache.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mSettings.isEmpty()) {
|
||||
mSettings = mCache; // This is the first time we read a file
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the changed keys with those which didn't
|
||||
mSettings.unite(mCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Map mSettings;
|
||||
Map mCache;
|
||||
|
||||
bool mMultiValue;
|
||||
};
|
||||
|
||||
#endif // SETTINGSBASE_HPP
|
6
apps/launcher/textslotmsgbox.cpp
Normal file
6
apps/launcher/textslotmsgbox.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "textslotmsgbox.hpp"
|
||||
|
||||
void TextSlotMsgBox::setTextSlot(const QString& string)
|
||||
{
|
||||
setText(string);
|
||||
}
|
13
apps/launcher/textslotmsgbox.hpp
Normal file
13
apps/launcher/textslotmsgbox.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef TEXT_SLOT_MSG_BOX
|
||||
#define TEXT_SLOT_MSG_BOX
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
class TextSlotMsgBox : public QMessageBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void setTextSlot(const QString& string);
|
||||
};
|
||||
|
||||
#endif
|
487
apps/launcher/unshieldthread.cpp
Normal file
487
apps/launcher/unshieldthread.cpp
Normal file
|
@ -0,0 +1,487 @@
|
|||
#include "unshieldthread.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
static bool make_sure_directory_exists(bfs::path directory)
|
||||
{
|
||||
|
||||
if(!bfs::exists(directory))
|
||||
{
|
||||
bfs::create_directories(directory);
|
||||
}
|
||||
|
||||
return bfs::exists(directory);
|
||||
}
|
||||
|
||||
void fill_path(bfs::path& path, const std::string& name)
|
||||
{
|
||||
size_t start = 0;
|
||||
|
||||
size_t i;
|
||||
for(i = 0; i < name.length(); i++)
|
||||
{
|
||||
switch(name[i])
|
||||
{
|
||||
case '\\':
|
||||
path /= name.substr(start, i-start);
|
||||
start = i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
path /= name.substr(start, i-start);
|
||||
}
|
||||
|
||||
std::string get_setting(const std::string& category, const std::string& setting, const std::string& inx)
|
||||
{
|
||||
size_t start = inx.find(category);
|
||||
start = inx.find(setting, start) + setting.length() + 3;
|
||||
|
||||
size_t end = inx.find("!", start);
|
||||
|
||||
return inx.substr(start, end-start);
|
||||
}
|
||||
|
||||
std::string read_to_string(const bfs::path& path)
|
||||
{
|
||||
std::ifstream strstream(path.c_str(), std::ios::in | std::ios::binary);
|
||||
std::string str;
|
||||
|
||||
strstream.seekg(0, std::ios::end);
|
||||
str.resize(strstream.tellg());
|
||||
strstream.seekg(0, std::ios::beg);
|
||||
strstream.read(&str[0], str.size());
|
||||
strstream.close();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void add_setting(const std::string& category, const std::string& setting, const std::string& val, std::string& ini)
|
||||
{
|
||||
size_t loc;
|
||||
loc = ini.find("[" + category + "]");
|
||||
|
||||
// If category is not found, create it
|
||||
if(loc == std::string::npos)
|
||||
{
|
||||
loc = ini.size() + 2;
|
||||
ini += ("\r\n[" + category + "]\r\n");
|
||||
}
|
||||
|
||||
loc += category.length() +2 +2;
|
||||
ini.insert(loc, setting + "=" + val + "\r\n");
|
||||
}
|
||||
|
||||
void bloodmoon_fix_ini(std::string& ini, const bfs::path inxPath)
|
||||
{
|
||||
std::string inx = read_to_string(inxPath);
|
||||
|
||||
// Remove this one setting (the only one actually changed by bloodmoon, as opposed to just adding new ones)
|
||||
size_t start = ini.find("[Weather Blight]");
|
||||
start = ini.find("Ambient Loop Sound ID", start);
|
||||
size_t end = ini.find("\r\n", start) +2;
|
||||
ini.erase(start, end-start);
|
||||
|
||||
std::string category;
|
||||
std::string setting;
|
||||
|
||||
category = "General";
|
||||
{
|
||||
setting = "Werewolf FOV"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Moons";
|
||||
{
|
||||
setting = "Script Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather";
|
||||
{
|
||||
setting = "Snow Ripples"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripple Radius"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripples Per Flake"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripple Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripple Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Gravity Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow High Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Low Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather Blight";
|
||||
{
|
||||
setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather Snow";
|
||||
{
|
||||
setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Diameter"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Height Min"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Height Max"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Entrance Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Max Snowflakes"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather Blizzard";
|
||||
{
|
||||
setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Storm Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fix_ini(const bfs::path& output_dir, bfs::path cdPath, bool tribunal, bool bloodmoon)
|
||||
{
|
||||
bfs::path ini_path = output_dir;
|
||||
ini_path /= "Morrowind.ini";
|
||||
|
||||
std::string ini = read_to_string(ini_path.string());
|
||||
|
||||
if(tribunal)
|
||||
{
|
||||
add_setting("Game Files", "GameFile1", "Tribunal.esm", ini);
|
||||
add_setting("Archives", "Archive 0", "Tribunal.bsa", ini);
|
||||
}
|
||||
if(bloodmoon)
|
||||
{
|
||||
bloodmoon_fix_ini(ini, cdPath / "setup.inx");
|
||||
add_setting("Game Files", "GameFile2", "Bloodmoon.esm", ini);
|
||||
add_setting("Archives", "Archive 1", "Bloodmoon.bsa", ini);
|
||||
}
|
||||
|
||||
std::ofstream inistream(ini_path.c_str());
|
||||
inistream << ini;
|
||||
inistream.close();
|
||||
}
|
||||
|
||||
void installToPath(const bfs::path& from, const bfs::path& to, bool copy = false)
|
||||
{
|
||||
make_sure_directory_exists(to);
|
||||
|
||||
for ( bfs::directory_iterator end, dir(from); dir != end; ++dir )
|
||||
{
|
||||
if(bfs::is_directory(dir->path()))
|
||||
installToPath(dir->path(), to / dir->path().filename(), copy);
|
||||
else
|
||||
{
|
||||
if(copy)
|
||||
bfs::copy_file(dir->path(), to / dir->path().filename());
|
||||
else
|
||||
bfs::rename(dir->path(), to / dir->path().filename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bfs::path findFile(const bfs::path& in, std::string filename, bool recursive = true)
|
||||
{
|
||||
if(recursive)
|
||||
{
|
||||
for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( bfs::directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool contains(const bfs::path& in, std::string filename)
|
||||
{
|
||||
for(bfs::directory_iterator end, dir(in); dir != end; ++dir)
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t getTime(const char* time)
|
||||
{
|
||||
struct tm tms;
|
||||
memset(&tms, 0, sizeof(struct tm));
|
||||
strptime(time, "%d %B %Y", &tms);
|
||||
return mktime(&tms);
|
||||
}
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetMorrowindPath(const std::string& path)
|
||||
{
|
||||
mMorrowindPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetTribunalPath(const std::string& path)
|
||||
{
|
||||
mTribunalPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetBloodmoonPath(const std::string& path)
|
||||
{
|
||||
mBloodmoonPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnshieldThread::SetOutputPath(const std::string& path)
|
||||
{
|
||||
mOutputPath = path;
|
||||
}
|
||||
|
||||
bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index)
|
||||
{
|
||||
bool success;
|
||||
bfs::path dirname;
|
||||
bfs::path filename;
|
||||
int directory = unshield_file_directory(unshield, index);
|
||||
|
||||
dirname = output_dir;
|
||||
|
||||
if (prefix && prefix[0])
|
||||
dirname /= prefix;
|
||||
|
||||
if (directory >= 0)
|
||||
{
|
||||
const char* tmp = unshield_directory_name(unshield, directory);
|
||||
if (tmp && tmp[0])
|
||||
fill_path(dirname, tmp);
|
||||
}
|
||||
|
||||
make_sure_directory_exists(dirname);
|
||||
|
||||
filename = dirname;
|
||||
filename /= unshield_file_name(unshield, index);
|
||||
|
||||
emit signalGUI(QString("Extracting: ") + QString(filename.c_str()));
|
||||
|
||||
success = unshield_file_save(unshield, index, filename.c_str());
|
||||
|
||||
if (!success)
|
||||
bfs::remove(filename);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini)
|
||||
{
|
||||
Unshield * unshield;
|
||||
unshield = unshield_open(cab.c_str());
|
||||
|
||||
int i;
|
||||
for (i = 0; i < unshield_file_group_count(unshield); i++)
|
||||
{
|
||||
UnshieldFileGroup* file_group = unshield_file_group_get(unshield, i);
|
||||
|
||||
for (size_t j = file_group->first_file; j <= file_group->last_file; j++)
|
||||
{
|
||||
if (unshield_file_is_valid(unshield, j))
|
||||
extract_file(unshield, output_dir, file_group->name, j);
|
||||
}
|
||||
}
|
||||
unshield_close(unshield);
|
||||
}
|
||||
|
||||
|
||||
bool UnshieldThread::extract()
|
||||
{
|
||||
bfs::path outputDataFilesDir = mOutputPath;
|
||||
outputDataFilesDir /= "Data Files";
|
||||
bfs::path extractPath = mOutputPath;
|
||||
extractPath /= "extract-temp";
|
||||
|
||||
if(!mMorrowindDone && mMorrowindPath.string().length() > 0)
|
||||
{
|
||||
mMorrowindDone = true;
|
||||
|
||||
bfs::path mwExtractPath = extractPath / "morrowind";
|
||||
extract_cab(mMorrowindPath, mwExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(mwExtractPath, "morrowind.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
// Videos are often kept uncompressed on the cd
|
||||
bfs::path videosPath = findFile(mMorrowindPath.parent_path(), "video", false);
|
||||
if(videosPath.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Videos..."));
|
||||
installToPath(videosPath, outputDataFilesDir / "Video", true);
|
||||
}
|
||||
|
||||
bfs::path cdDFiles = findFile(mMorrowindPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
|
||||
bfs::rename(findFile(mwExtractPath, "morrowind.ini"), outputDataFilesDir / "Morrowind.ini");
|
||||
|
||||
mTribunalDone = contains(outputDataFilesDir, "tribunal.esm");
|
||||
mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm");
|
||||
|
||||
}
|
||||
|
||||
else if(!mTribunalDone && mTribunalPath.string().length() > 0)
|
||||
{
|
||||
mTribunalDone = true;
|
||||
|
||||
bfs::path tbExtractPath = extractPath / "tribunal";
|
||||
extract_cab(mTribunalPath, tbExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(tbExtractPath, "tribunal.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
// Mt GOTY CD has Sounds in a seperate folder from the rest of the data files
|
||||
bfs::path soundsPath = findFile(tbExtractPath, "sounds", false);
|
||||
if(soundsPath.string() != "")
|
||||
installToPath(soundsPath, outputDataFilesDir / "Sounds");
|
||||
|
||||
bfs::path cdDFiles = findFile(mTribunalPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm");
|
||||
|
||||
fix_ini(outputDataFilesDir, bfs::path(mTribunalPath).parent_path(), mTribunalDone, mBloodmoonDone);
|
||||
}
|
||||
|
||||
else if(!mBloodmoonDone && mBloodmoonPath.string().length() > 0)
|
||||
{
|
||||
mBloodmoonDone = true;
|
||||
|
||||
bfs::path bmExtractPath = extractPath / "bloodmoon";
|
||||
extract_cab(mBloodmoonPath, bmExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(bmExtractPath, "bloodmoon.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
// My GOTY CD contains a folder within cab files called Tribunal patch,
|
||||
// which contains Tribunal.esm
|
||||
bfs::path tbPatchPath = findFile(bmExtractPath, "tribunal.esm");
|
||||
if(tbPatchPath.string() != "")
|
||||
bfs::rename(tbPatchPath, outputDataFilesDir / "Tribunal.esm");
|
||||
|
||||
bfs::path cdDFiles = findFile(mBloodmoonPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
fix_ini(outputDataFilesDir, bfs::path(mBloodmoonPath).parent_path(), false, mBloodmoonDone);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnshieldThread::Done()
|
||||
{
|
||||
// Get rid of unnecessary files
|
||||
bfs::remove_all(mOutputPath / "extract-temp");
|
||||
|
||||
// Set modified time to release dates, to preserve load order
|
||||
if(mMorrowindDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "morrowind.esm"), getTime("1 May 2002"));
|
||||
|
||||
if(mTribunalDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "tribunal.esm"), getTime("6 November 2002"));
|
||||
|
||||
if(mBloodmoonDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003"));
|
||||
}
|
||||
|
||||
std::string UnshieldThread::GetMWEsmPath()
|
||||
{
|
||||
return findFile(mOutputPath / "Data Files", "morrowind.esm").string();
|
||||
}
|
||||
|
||||
bool UnshieldThread::TribunalDone()
|
||||
{
|
||||
return mTribunalDone;
|
||||
}
|
||||
|
||||
bool UnshieldThread::BloodmoonDone()
|
||||
{
|
||||
return mBloodmoonDone;
|
||||
}
|
||||
|
||||
void UnshieldThread::run()
|
||||
{
|
||||
extract();
|
||||
emit close();
|
||||
}
|
||||
|
||||
UnshieldThread::UnshieldThread()
|
||||
{
|
||||
mMorrowindDone = false;
|
||||
mTribunalDone = false;
|
||||
mBloodmoonDone = false;
|
||||
}
|
57
apps/launcher/unshieldthread.hpp
Normal file
57
apps/launcher/unshieldthread.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#ifndef UNSHIELD_THREAD_H
|
||||
#define UNSHIELD_THREAD_H
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <libunshield.h>
|
||||
|
||||
|
||||
class UnshieldThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool SetMorrowindPath(const std::string& path);
|
||||
bool SetTribunalPath(const std::string& path);
|
||||
bool SetBloodmoonPath(const std::string& path);
|
||||
|
||||
void SetOutputPath(const std::string& path);
|
||||
|
||||
bool extract();
|
||||
|
||||
bool TribunalDone();
|
||||
bool BloodmoonDone();
|
||||
|
||||
void Done();
|
||||
|
||||
std::string GetMWEsmPath();
|
||||
|
||||
UnshieldThread();
|
||||
|
||||
private:
|
||||
|
||||
void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false);
|
||||
bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index);
|
||||
|
||||
boost::filesystem::path mMorrowindPath;
|
||||
boost::filesystem::path mTribunalPath;
|
||||
boost::filesystem::path mBloodmoonPath;
|
||||
|
||||
bool mMorrowindDone;
|
||||
bool mTribunalDone;
|
||||
bool mBloodmoonDone;
|
||||
|
||||
boost::filesystem::path mOutputPath;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void signalGUI(QString);
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
269
apps/launcher/utils/checkablemessagebox.cpp
Normal file
269
apps/launcher/utils/checkablemessagebox.cpp
Normal file
|
@ -0,0 +1,269 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "checkablemessagebox.hpp"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
||||
/*!
|
||||
\class Utils::CheckableMessageBox
|
||||
|
||||
\brief A messagebox suitable for questions with a
|
||||
"Do not ask me again" checkbox.
|
||||
|
||||
Emulates the QMessageBox API with
|
||||
static conveniences. The message label can open external URLs.
|
||||
*/
|
||||
|
||||
class CheckableMessageBoxPrivate
|
||||
{
|
||||
public:
|
||||
CheckableMessageBoxPrivate(QDialog *q)
|
||||
: clickedButton(0)
|
||||
{
|
||||
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
|
||||
pixmapLabel = new QLabel(q);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
|
||||
pixmapLabel->setSizePolicy(sizePolicy);
|
||||
pixmapLabel->setVisible(false);
|
||||
|
||||
QSpacerItem *pixmapSpacer =
|
||||
new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
|
||||
|
||||
messageLabel = new QLabel(q);
|
||||
messageLabel->setMinimumSize(QSize(300, 0));
|
||||
messageLabel->setWordWrap(true);
|
||||
messageLabel->setOpenExternalLinks(true);
|
||||
messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
|
||||
|
||||
QSpacerItem *checkBoxRightSpacer =
|
||||
new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
QSpacerItem *buttonSpacer =
|
||||
new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
checkBox = new QCheckBox(q);
|
||||
checkBox->setText(CheckableMessageBox::tr("Do not ask again"));
|
||||
|
||||
buttonBox = new QDialogButtonBox(q);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
|
||||
QVBoxLayout *verticalLayout = new QVBoxLayout();
|
||||
verticalLayout->addWidget(pixmapLabel);
|
||||
verticalLayout->addItem(pixmapSpacer);
|
||||
|
||||
QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
|
||||
horizontalLayout_2->addLayout(verticalLayout);
|
||||
horizontalLayout_2->addWidget(messageLabel);
|
||||
|
||||
QHBoxLayout *horizontalLayout = new QHBoxLayout();
|
||||
horizontalLayout->addWidget(checkBox);
|
||||
horizontalLayout->addItem(checkBoxRightSpacer);
|
||||
|
||||
QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
|
||||
verticalLayout_2->addLayout(horizontalLayout_2);
|
||||
verticalLayout_2->addLayout(horizontalLayout);
|
||||
verticalLayout_2->addItem(buttonSpacer);
|
||||
verticalLayout_2->addWidget(buttonBox);
|
||||
}
|
||||
|
||||
QLabel *pixmapLabel;
|
||||
QLabel *messageLabel;
|
||||
QCheckBox *checkBox;
|
||||
QDialogButtonBox *buttonBox;
|
||||
QAbstractButton *clickedButton;
|
||||
};
|
||||
|
||||
CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
d(new CheckableMessageBoxPrivate(this))
|
||||
{
|
||||
setModal(true);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
|
||||
connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
|
||||
SLOT(slotClicked(QAbstractButton*)));
|
||||
}
|
||||
|
||||
CheckableMessageBox::~CheckableMessageBox()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void CheckableMessageBox::slotClicked(QAbstractButton *b)
|
||||
{
|
||||
d->clickedButton = b;
|
||||
}
|
||||
|
||||
QAbstractButton *CheckableMessageBox::clickedButton() const
|
||||
{
|
||||
return d->clickedButton;
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const
|
||||
{
|
||||
if (d->clickedButton)
|
||||
return d->buttonBox->standardButton(d->clickedButton);
|
||||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
QString CheckableMessageBox::text() const
|
||||
{
|
||||
return d->messageLabel->text();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setText(const QString &t)
|
||||
{
|
||||
d->messageLabel->setText(t);
|
||||
}
|
||||
|
||||
QPixmap CheckableMessageBox::iconPixmap() const
|
||||
{
|
||||
if (const QPixmap *p = d->pixmapLabel->pixmap())
|
||||
return QPixmap(*p);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setIconPixmap(const QPixmap &p)
|
||||
{
|
||||
d->pixmapLabel->setPixmap(p);
|
||||
d->pixmapLabel->setVisible(!p.isNull());
|
||||
}
|
||||
|
||||
bool CheckableMessageBox::isChecked() const
|
||||
{
|
||||
return d->checkBox->isChecked();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setChecked(bool s)
|
||||
{
|
||||
d->checkBox->setChecked(s);
|
||||
}
|
||||
|
||||
QString CheckableMessageBox::checkBoxText() const
|
||||
{
|
||||
return d->checkBox->text();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setCheckBoxText(const QString &t)
|
||||
{
|
||||
d->checkBox->setText(t);
|
||||
}
|
||||
|
||||
bool CheckableMessageBox::isCheckBoxVisible() const
|
||||
{
|
||||
return d->checkBox->isVisible();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setCheckBoxVisible(bool v)
|
||||
{
|
||||
d->checkBox->setVisible(v);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const
|
||||
{
|
||||
return d->buttonBox->standardButtons();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
|
||||
{
|
||||
d->buttonBox->setStandardButtons(s);
|
||||
}
|
||||
|
||||
QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
|
||||
{
|
||||
return d->buttonBox->button(b);
|
||||
}
|
||||
|
||||
QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
|
||||
{
|
||||
return d->buttonBox->addButton(text, role);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const
|
||||
{
|
||||
foreach (QAbstractButton *b, d->buttonBox->buttons())
|
||||
if (QPushButton *pb = qobject_cast<QPushButton *>(b))
|
||||
if (pb->isDefault())
|
||||
return d->buttonBox->standardButton(pb);
|
||||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
|
||||
{
|
||||
if (QPushButton *b = d->buttonBox->button(s)) {
|
||||
b->setDefault(true);
|
||||
b->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton
|
||||
CheckableMessageBox::question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons,
|
||||
QDialogButtonBox::StandardButton defaultButton)
|
||||
{
|
||||
CheckableMessageBox mb(parent);
|
||||
mb.setWindowTitle(title);
|
||||
mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question));
|
||||
mb.setText(question);
|
||||
mb.setCheckBoxText(checkBoxText);
|
||||
mb.setChecked(*checkBoxSetting);
|
||||
mb.setStandardButtons(buttons);
|
||||
mb.setDefaultButton(defaultButton);
|
||||
mb.exec();
|
||||
*checkBoxSetting = mb.isChecked();
|
||||
return mb.clickedStandardButton();
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
|
||||
{
|
||||
return static_cast<QMessageBox::StandardButton>(int(db));
|
||||
}
|
100
apps/launcher/utils/checkablemessagebox.hpp
Normal file
100
apps/launcher/utils/checkablemessagebox.hpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CHECKABLEMESSAGEBOX_HPP
|
||||
#define CHECKABLEMESSAGEBOX_HPP
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
#include <QDialog>
|
||||
|
||||
class CheckableMessageBoxPrivate;
|
||||
|
||||
class CheckableMessageBox : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText)
|
||||
Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
|
||||
Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
|
||||
Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
|
||||
|
||||
public:
|
||||
explicit CheckableMessageBox(QWidget *parent);
|
||||
virtual ~CheckableMessageBox();
|
||||
|
||||
static QDialogButtonBox::StandardButton
|
||||
question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
|
||||
QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &);
|
||||
|
||||
bool isChecked() const;
|
||||
void setChecked(bool s);
|
||||
|
||||
QString checkBoxText() const;
|
||||
void setCheckBoxText(const QString &);
|
||||
|
||||
bool isCheckBoxVisible() const;
|
||||
void setCheckBoxVisible(bool);
|
||||
|
||||
QDialogButtonBox::StandardButtons standardButtons() const;
|
||||
void setStandardButtons(QDialogButtonBox::StandardButtons s);
|
||||
QPushButton *button(QDialogButtonBox::StandardButton b) const;
|
||||
QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
|
||||
|
||||
QDialogButtonBox::StandardButton defaultButton() const;
|
||||
void setDefaultButton(QDialogButtonBox::StandardButton s);
|
||||
|
||||
// See static QMessageBox::standardPixmap()
|
||||
QPixmap iconPixmap() const;
|
||||
void setIconPixmap (const QPixmap &p);
|
||||
|
||||
// Query the result
|
||||
QAbstractButton *clickedButton() const;
|
||||
QDialogButtonBox::StandardButton clickedStandardButton() const;
|
||||
|
||||
// Conversion convenience
|
||||
static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
|
||||
|
||||
private slots:
|
||||
void slotClicked(QAbstractButton *b);
|
||||
|
||||
private:
|
||||
CheckableMessageBoxPrivate *d;
|
||||
};
|
||||
|
||||
#endif // CHECKABLEMESSAGEBOX_HPP
|
71
apps/launcher/utils/textinputdialog.cpp
Normal file
71
apps/launcher/utils/textinputdialog.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "textinputdialog.hpp"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QApplication>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QValidator>
|
||||
#include <QLabel>
|
||||
|
||||
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||
|
||||
TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
mButtonBox = new QDialogButtonBox(this);
|
||||
mButtonBox->addButton(QDialogButtonBox::Ok);
|
||||
mButtonBox->addButton(QDialogButtonBox::Cancel);
|
||||
|
||||
// Line edit
|
||||
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
||||
mLineEdit = new LineEdit(this);
|
||||
mLineEdit->setValidator(validator);
|
||||
mLineEdit->setCompleter(0);
|
||||
|
||||
QLabel *label = new QLabel(this);
|
||||
label->setText(text);
|
||||
|
||||
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
|
||||
dialogLayout->addWidget(label);
|
||||
dialogLayout->addWidget(mLineEdit);
|
||||
dialogLayout->addWidget(mButtonBox);
|
||||
|
||||
// Messageboxes on mac have no title
|
||||
#ifndef Q_OS_MAC
|
||||
setWindowTitle(title);
|
||||
#else
|
||||
Q_UNUSED(title);
|
||||
#endif
|
||||
|
||||
setOkButtonEnabled(false);
|
||||
setModal(true);
|
||||
|
||||
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
|
||||
}
|
||||
|
||||
int TextInputDialog::exec()
|
||||
{
|
||||
mLineEdit->clear();
|
||||
mLineEdit->setFocus();
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
void TextInputDialog::setOkButtonEnabled(bool enabled)
|
||||
{
|
||||
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
|
||||
okButton->setEnabled(enabled);
|
||||
|
||||
QPalette *palette = new QPalette();
|
||||
palette->setColor(QPalette::Text,Qt::red);
|
||||
|
||||
if (enabled) {
|
||||
mLineEdit->setPalette(QApplication::palette());
|
||||
} else {
|
||||
// Existing profile name, make the text red
|
||||
mLineEdit->setPalette(*palette);
|
||||
}
|
||||
|
||||
}
|
28
apps/launcher/utils/textinputdialog.hpp
Normal file
28
apps/launcher/utils/textinputdialog.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef TEXTINPUTDIALOG_HPP
|
||||
#define TEXTINPUTDIALOG_HPP
|
||||
|
||||
#include <QDialog>
|
||||
//#include "lineedit.hpp"
|
||||
|
||||
class QDialogButtonBox;
|
||||
class LineEdit;
|
||||
|
||||
class TextInputDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0);
|
||||
inline LineEdit *lineEdit() { return mLineEdit; }
|
||||
void setOkButtonEnabled(bool enabled);
|
||||
|
||||
LineEdit *mLineEdit;
|
||||
|
||||
int exec();
|
||||
|
||||
private:
|
||||
QDialogButtonBox *mButtonBox;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // TEXTINPUTDIALOG_HPP
|
29
apps/mwiniimporter/CMakeLists.txt
Normal file
29
apps/mwiniimporter/CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
set(MWINIIMPORT
|
||||
main.cpp
|
||||
importer.cpp
|
||||
)
|
||||
|
||||
set(MWINIIMPORT_HEADER
|
||||
importer.hpp
|
||||
)
|
||||
|
||||
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
|
||||
|
||||
add_executable(mwiniimport
|
||||
${MWINIIMPORT}
|
||||
)
|
||||
|
||||
target_link_libraries(mwiniimport
|
||||
${Boost_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(mwiniimport gcov)
|
||||
endif()
|
||||
|
||||
if(DPKG_PROGRAM)
|
||||
INSTALL(TARGETS mwiniimport RUNTIME DESTINATION games COMPONENT mwiniimport)
|
||||
endif()
|
||||
|
873
apps/mwiniimporter/importer.cpp
Normal file
873
apps/mwiniimporter/importer.cpp
Normal file
|
@ -0,0 +1,873 @@
|
|||
#include "importer.hpp"
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
|
||||
MwIniImporter::MwIniImporter()
|
||||
: mVerbose(false)
|
||||
{
|
||||
const char *map[][2] =
|
||||
{
|
||||
{ "fps", "General:Show FPS" },
|
||||
{ "nosound", "General:Disable Audio" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
const char *fallback[] = {
|
||||
|
||||
// light
|
||||
"LightAttenuation:UseConstant",
|
||||
"LightAttenuation:ConstantValue",
|
||||
"LightAttenuation:UseLinear",
|
||||
"LightAttenuation:LinearMethod",
|
||||
"LightAttenuation:LinearValue",
|
||||
"LightAttenuation:LinearRadiusMult",
|
||||
"LightAttenuation:UseQuadratic",
|
||||
"LightAttenuation:QuadraticMethod",
|
||||
"LightAttenuation:QuadraticValue",
|
||||
"LightAttenuation:QuadraticRadiusMult",
|
||||
"LightAttenuation:OutQuadInLin",
|
||||
|
||||
// inventory
|
||||
"Inventory:DirectionalDiffuseR",
|
||||
"Inventory:DirectionalDiffuseG",
|
||||
"Inventory:DirectionalDiffuseB",
|
||||
"Inventory:DirectionalAmbientR",
|
||||
"Inventory:DirectionalAmbientG",
|
||||
"Inventory:DirectionalAmbientB",
|
||||
"Inventory:DirectionalRotationX",
|
||||
"Inventory:DirectionalRotationY",
|
||||
"Inventory:UniformScaling",
|
||||
|
||||
// map
|
||||
"Map:Travel Siltstrider Red",
|
||||
"Map:Travel Siltstrider Green",
|
||||
"Map:Travel Siltstrider Blue",
|
||||
"Map:Travel Boat Red",
|
||||
"Map:Travel Boat Green",
|
||||
"Map:Travel Boat Blue",
|
||||
"Map:Travel Magic Red",
|
||||
"Map:Travel Magic Green",
|
||||
"Map:Travel Magic Blue",
|
||||
"Map:Show Travel Lines",
|
||||
|
||||
// water
|
||||
"Water:Map Alpha",
|
||||
"Water:World Alpha",
|
||||
"Water:SurfaceTextureSize",
|
||||
"Water:SurfaceTileCount",
|
||||
"Water:SurfaceFPS",
|
||||
"Water:SurfaceTexture",
|
||||
"Water:SurfaceFrameCount",
|
||||
"Water:TileTextureDivisor",
|
||||
"Water:RippleTexture",
|
||||
"Water:RippleFrameCount",
|
||||
"Water:RippleLifetime",
|
||||
"Water:MaxNumberRipples",
|
||||
"Water:RippleScale",
|
||||
"Water:RippleRotSpeed",
|
||||
"Water:RippleAlphas",
|
||||
"Water:PSWaterReflectTerrain",
|
||||
"Water:PSWaterReflectUpdate",
|
||||
"Water:NearWaterRadius",
|
||||
"Water:NearWaterPoints",
|
||||
"Water:NearWaterUnderwaterFreq",
|
||||
"Water:NearWaterUnderwaterVolume",
|
||||
"Water:NearWaterIndoorTolerance",
|
||||
"Water:NearWaterOutdoorTolerance",
|
||||
"Water:NearWaterIndoorID",
|
||||
"Water:NearWaterOutdoorID",
|
||||
"Water:UnderwaterSunriseFog",
|
||||
"Water:UnderwaterDayFog",
|
||||
"Water:UnderwaterSunsetFog",
|
||||
"Water:UnderwaterNightFog",
|
||||
"Water:UnderwaterIndoorFog",
|
||||
"Water:UnderwaterColor",
|
||||
"Water:UnderwaterColorWeight",
|
||||
|
||||
// pixelwater
|
||||
"PixelWater:SurfaceFPS",
|
||||
"PixelWater:TileCount",
|
||||
"PixelWater:Resolution",
|
||||
|
||||
// fonts
|
||||
"Fonts:Font 0",
|
||||
"Fonts:Font 1",
|
||||
"Fonts:Font 2",
|
||||
|
||||
// UI colors
|
||||
"FontColor:color_normal",
|
||||
"FontColor:color_normal_over",
|
||||
"FontColor:color_normal_pressed",
|
||||
"FontColor:color_active",
|
||||
"FontColor:color_active_over",
|
||||
"FontColor:color_active_pressed",
|
||||
"FontColor:color_disabled",
|
||||
"FontColor:color_disabled_over",
|
||||
"FontColor:color_disabled_pressed",
|
||||
"FontColor:color_link",
|
||||
"FontColor:color_link_over",
|
||||
"FontColor:color_link_pressed",
|
||||
"FontColor:color_journal_link",
|
||||
"FontColor:color_journal_link_over",
|
||||
"FontColor:color_journal_link_pressed",
|
||||
"FontColor:color_journal_topic",
|
||||
"FontColor:color_journal_topic_over",
|
||||
"FontColor:color_journal_topic_pressed",
|
||||
"FontColor:color_answer",
|
||||
"FontColor:color_answer_over",
|
||||
"FontColor:color_answer_pressed",
|
||||
"FontColor:color_header",
|
||||
"FontColor:color_notify",
|
||||
"FontColor:color_big_normal",
|
||||
"FontColor:color_big_normal_over",
|
||||
"FontColor:color_big_normal_pressed",
|
||||
"FontColor:color_big_link",
|
||||
"FontColor:color_big_link_over",
|
||||
"FontColor:color_big_link_pressed",
|
||||
"FontColor:color_big_answer",
|
||||
"FontColor:color_big_answer_over",
|
||||
"FontColor:color_big_answer_pressed",
|
||||
"FontColor:color_big_header",
|
||||
"FontColor:color_big_notify",
|
||||
"FontColor:color_background",
|
||||
"FontColor:color_focus",
|
||||
"FontColor:color_health",
|
||||
"FontColor:color_magic",
|
||||
"FontColor:color_fatigue",
|
||||
"FontColor:color_misc",
|
||||
"FontColor:color_weapon_fill",
|
||||
"FontColor:color_magic_fill",
|
||||
"FontColor:color_positive",
|
||||
"FontColor:color_negative",
|
||||
"FontColor:color_count",
|
||||
|
||||
// level up messages
|
||||
"Level Up:Level2",
|
||||
"Level Up:Level3",
|
||||
"Level Up:Level4",
|
||||
"Level Up:Level5",
|
||||
"Level Up:Level6",
|
||||
"Level Up:Level7",
|
||||
"Level Up:Level8",
|
||||
"Level Up:Level9",
|
||||
"Level Up:Level10",
|
||||
"Level Up:Level11",
|
||||
"Level Up:Level12",
|
||||
"Level Up:Level13",
|
||||
"Level Up:Level14",
|
||||
"Level Up:Level15",
|
||||
"Level Up:Level16",
|
||||
"Level Up:Level17",
|
||||
"Level Up:Level18",
|
||||
"Level Up:Level19",
|
||||
"Level Up:Level20",
|
||||
"Level Up:Default",
|
||||
|
||||
// character creation multiple choice test
|
||||
"Question 1:Question",
|
||||
"Question 1:AnswerOne",
|
||||
"Question 1:AnswerTwo",
|
||||
"Question 1:AnswerThree",
|
||||
"Question 1:Sound",
|
||||
"Question 2:Question",
|
||||
"Question 2:AnswerOne",
|
||||
"Question 2:AnswerTwo",
|
||||
"Question 2:AnswerThree",
|
||||
"Question 2:Sound",
|
||||
"Question 3:Question",
|
||||
"Question 3:AnswerOne",
|
||||
"Question 3:AnswerTwo",
|
||||
"Question 3:AnswerThree",
|
||||
"Question 3:Sound",
|
||||
"Question 4:Question",
|
||||
"Question 4:AnswerOne",
|
||||
"Question 4:AnswerTwo",
|
||||
"Question 4:AnswerThree",
|
||||
"Question 4:Sound",
|
||||
"Question 5:Question",
|
||||
"Question 5:AnswerOne",
|
||||
"Question 5:AnswerTwo",
|
||||
"Question 5:AnswerThree",
|
||||
"Question 5:Sound",
|
||||
"Question 6:Question",
|
||||
"Question 6:AnswerOne",
|
||||
"Question 6:AnswerTwo",
|
||||
"Question 6:AnswerThree",
|
||||
"Question 6:Sound",
|
||||
"Question 7:Question",
|
||||
"Question 7:AnswerOne",
|
||||
"Question 7:AnswerTwo",
|
||||
"Question 7:AnswerThree",
|
||||
"Question 7:Sound",
|
||||
"Question 8:Question",
|
||||
"Question 8:AnswerOne",
|
||||
"Question 8:AnswerTwo",
|
||||
"Question 8:AnswerThree",
|
||||
"Question 8:Sound",
|
||||
"Question 9:Question",
|
||||
"Question 9:AnswerOne",
|
||||
"Question 9:AnswerTwo",
|
||||
"Question 9:AnswerThree",
|
||||
"Question 9:Sound",
|
||||
"Question 10:Question",
|
||||
"Question 10:AnswerOne",
|
||||
"Question 10:AnswerTwo",
|
||||
"Question 10:AnswerThree",
|
||||
"Question 10:Sound",
|
||||
|
||||
// blood textures and models
|
||||
"Blood:Model 0",
|
||||
"Blood:Model 1",
|
||||
"Blood:Model 2",
|
||||
"Blood:Texture 0",
|
||||
"Blood:Texture 1",
|
||||
"Blood:Texture 2",
|
||||
"Blood:Texture Name 0",
|
||||
"Blood:Texture Name 1",
|
||||
"Blood:Texture Name 2",
|
||||
|
||||
// movies
|
||||
"Movies:Company Logo",
|
||||
"Movies:Morrowind Logo",
|
||||
"Movies:New Game",
|
||||
"Movies:Loading",
|
||||
"Movies:Options Menu",
|
||||
|
||||
// weather related values
|
||||
|
||||
"Weather Thunderstorm:Thunder Sound ID 0",
|
||||
"Weather Thunderstorm:Thunder Sound ID 1",
|
||||
"Weather Thunderstorm:Thunder Sound ID 2",
|
||||
"Weather Thunderstorm:Thunder Sound ID 3",
|
||||
"Weather:Sunrise Time",
|
||||
"Weather:Sunset Time",
|
||||
"Weather:Sunrise Duration",
|
||||
"Weather:Sunset Duration",
|
||||
"Weather:Hours Between Weather Changes", // AKA weather update time
|
||||
"Weather Thunderstorm:Thunder Frequency",
|
||||
"Weather Thunderstorm:Thunder Threshold",
|
||||
|
||||
"Weather:EnvReduceColor",
|
||||
"Weather:LerpCloseColor",
|
||||
"Weather:BumpFadeColor",
|
||||
"Weather:AlphaReduce",
|
||||
"Weather:Minimum Time Between Environmental Sounds",
|
||||
"Weather:Maximum Time Between Environmental Sounds",
|
||||
"Weather:Sun Glare Fader Max",
|
||||
"Weather:Sun Glare Fader Angle Max",
|
||||
"Weather:Sun Glare Fader Color",
|
||||
"Weather:Timescale Clouds",
|
||||
"Weather:Precip Gravity",
|
||||
"Weather:Rain Ripples",
|
||||
"Weather:Rain Ripple Radius",
|
||||
"Weather:Rain Ripples Per Drop",
|
||||
"Weather:Rain Ripple Scale",
|
||||
"Weather:Rain Ripple Speed",
|
||||
"Weather:Fog Depth Change Speed",
|
||||
"Weather:Sky Pre-Sunrise Time",
|
||||
"Weather:Sky Post-Sunrise Time",
|
||||
"Weather:Sky Pre-Sunset Time",
|
||||
"Weather:Sky Post-Sunset Time",
|
||||
"Weather:Ambient Pre-Sunrise Time",
|
||||
"Weather:Ambient Post-Sunrise Time",
|
||||
"Weather:Ambient Pre-Sunset Time",
|
||||
"Weather:Ambient Post-Sunset Time",
|
||||
"Weather:Fog Pre-Sunrise Time",
|
||||
"Weather:Fog Post-Sunrise Time",
|
||||
"Weather:Fog Pre-Sunset Time",
|
||||
"Weather:Fog Post-Sunset Time",
|
||||
"Weather:Sun Pre-Sunrise Time",
|
||||
"Weather:Sun Post-Sunrise Time",
|
||||
"Weather:Sun Pre-Sunset Time",
|
||||
"Weather:Sun Post-Sunset Time",
|
||||
"Weather:Stars Post-Sunset Start",
|
||||
"Weather:Stars Pre-Sunrise Finish",
|
||||
"Weather:Stars Fading Duration",
|
||||
"Weather:Snow Ripples",
|
||||
"Weather:Snow Ripple Radius",
|
||||
"Weather:Snow Ripples Per Flake",
|
||||
"Weather:Snow Ripple Scale",
|
||||
"Weather:Snow Ripple Speed",
|
||||
"Weather:Snow Gravity Scale",
|
||||
"Weather:Snow High Kill",
|
||||
"Weather:Snow Low Kill",
|
||||
|
||||
"Weather Clear:Cloud Texture",
|
||||
"Weather Clear:Clouds Maximum Percent",
|
||||
"Weather Clear:Transition Delta",
|
||||
"Weather Clear:Sky Sunrise Color",
|
||||
"Weather Clear:Sky Day Color",
|
||||
"Weather Clear:Sky Sunset Color",
|
||||
"Weather Clear:Sky Night Color",
|
||||
"Weather Clear:Fog Sunrise Color",
|
||||
"Weather Clear:Fog Day Color",
|
||||
"Weather Clear:Fog Sunset Color",
|
||||
"Weather Clear:Fog Night Color",
|
||||
"Weather Clear:Ambient Sunrise Color",
|
||||
"Weather Clear:Ambient Day Color",
|
||||
"Weather Clear:Ambient Sunset Color",
|
||||
"Weather Clear:Ambient Night Color",
|
||||
"Weather Clear:Sun Sunrise Color",
|
||||
"Weather Clear:Sun Day Color",
|
||||
"Weather Clear:Sun Sunset Color",
|
||||
"Weather Clear:Sun Night Color",
|
||||
"Weather Clear:Sun Disc Sunset Color",
|
||||
"Weather Clear:Land Fog Day Depth",
|
||||
"Weather Clear:Land Fog Night Depth",
|
||||
"Weather Clear:Wind Speed",
|
||||
"Weather Clear:Cloud Speed",
|
||||
"Weather Clear:Glare View",
|
||||
"Weather Clear:Ambient Loop Sound ID",
|
||||
|
||||
"Weather Cloudy:Cloud Texture",
|
||||
"Weather Cloudy:Clouds Maximum Percent",
|
||||
"Weather Cloudy:Transition Delta",
|
||||
"Weather Cloudy:Sky Sunrise Color",
|
||||
"Weather Cloudy:Sky Day Color",
|
||||
"Weather Cloudy:Sky Sunset Color",
|
||||
"Weather Cloudy:Sky Night Color",
|
||||
"Weather Cloudy:Fog Sunrise Color",
|
||||
"Weather Cloudy:Fog Day Color",
|
||||
"Weather Cloudy:Fog Sunset Color",
|
||||
"Weather Cloudy:Fog Night Color",
|
||||
"Weather Cloudy:Ambient Sunrise Color",
|
||||
"Weather Cloudy:Ambient Day Color",
|
||||
"Weather Cloudy:Ambient Sunset Color",
|
||||
"Weather Cloudy:Ambient Night Color",
|
||||
"Weather Cloudy:Sun Sunrise Color",
|
||||
"Weather Cloudy:Sun Day Color",
|
||||
"Weather Cloudy:Sun Sunset Color",
|
||||
"Weather Cloudy:Sun Night Color",
|
||||
"Weather Cloudy:Sun Disc Sunset Color",
|
||||
"Weather Cloudy:Land Fog Day Depth",
|
||||
"Weather Cloudy:Land Fog Night Depth",
|
||||
"Weather Cloudy:Wind Speed",
|
||||
"Weather Cloudy:Cloud Speed",
|
||||
"Weather Cloudy:Glare View",
|
||||
"Weather Cloudy:Ambient Loop Sound ID",
|
||||
|
||||
"Weather Foggy:Cloud Texture",
|
||||
"Weather Foggy:Clouds Maximum Percent",
|
||||
"Weather Foggy:Transition Delta",
|
||||
"Weather Foggy:Sky Sunrise Color",
|
||||
"Weather Foggy:Sky Day Color",
|
||||
"Weather Foggy:Sky Sunset Color",
|
||||
"Weather Foggy:Sky Night Color",
|
||||
"Weather Foggy:Fog Sunrise Color",
|
||||
"Weather Foggy:Fog Day Color",
|
||||
"Weather Foggy:Fog Sunset Color",
|
||||
"Weather Foggy:Fog Night Color",
|
||||
"Weather Foggy:Ambient Sunrise Color",
|
||||
"Weather Foggy:Ambient Day Color",
|
||||
"Weather Foggy:Ambient Sunset Color",
|
||||
"Weather Foggy:Ambient Night Color",
|
||||
"Weather Foggy:Sun Sunrise Color",
|
||||
"Weather Foggy:Sun Day Color",
|
||||
"Weather Foggy:Sun Sunset Color",
|
||||
"Weather Foggy:Sun Night Color",
|
||||
"Weather Foggy:Sun Disc Sunset Color",
|
||||
"Weather Foggy:Land Fog Day Depth",
|
||||
"Weather Foggy:Land Fog Night Depth",
|
||||
"Weather Foggy:Wind Speed",
|
||||
"Weather Foggy:Cloud Speed",
|
||||
"Weather Foggy:Glare View",
|
||||
"Weather Foggy:Ambient Loop Sound ID",
|
||||
|
||||
"Weather Thunderstorm:Cloud Texture",
|
||||
"Weather Thunderstorm:Clouds Maximum Percent",
|
||||
"Weather Thunderstorm:Transition Delta",
|
||||
"Weather Thunderstorm:Sky Sunrise Color",
|
||||
"Weather Thunderstorm:Sky Day Color",
|
||||
"Weather Thunderstorm:Sky Sunset Color",
|
||||
"Weather Thunderstorm:Sky Night Color",
|
||||
"Weather Thunderstorm:Fog Sunrise Color",
|
||||
"Weather Thunderstorm:Fog Day Color",
|
||||
"Weather Thunderstorm:Fog Sunset Color",
|
||||
"Weather Thunderstorm:Fog Night Color",
|
||||
"Weather Thunderstorm:Ambient Sunrise Color",
|
||||
"Weather Thunderstorm:Ambient Day Color",
|
||||
"Weather Thunderstorm:Ambient Sunset Color",
|
||||
"Weather Thunderstorm:Ambient Night Color",
|
||||
"Weather Thunderstorm:Sun Sunrise Color",
|
||||
"Weather Thunderstorm:Sun Day Color",
|
||||
"Weather Thunderstorm:Sun Sunset Color",
|
||||
"Weather Thunderstorm:Sun Night Color",
|
||||
"Weather Thunderstorm:Sun Disc Sunset Color",
|
||||
"Weather Thunderstorm:Land Fog Day Depth",
|
||||
"Weather Thunderstorm:Land Fog Night Depth",
|
||||
"Weather Thunderstorm:Wind Speed",
|
||||
"Weather Thunderstorm:Cloud Speed",
|
||||
"Weather Thunderstorm:Glare View",
|
||||
"Weather Thunderstorm:Rain Loop Sound ID",
|
||||
"Weather Thunderstorm:Using Precip",
|
||||
"Weather Thunderstorm:Rain Diameter",
|
||||
"Weather Thunderstorm:Rain Height Min",
|
||||
"Weather Thunderstorm:Rain Height Max",
|
||||
"Weather Thunderstorm:Rain Threshold",
|
||||
"Weather Thunderstorm:Max Raindrops",
|
||||
"Weather Thunderstorm:Rain Entrance Speed",
|
||||
"Weather Thunderstorm:Ambient Loop Sound ID",
|
||||
"Weather Thunderstorm:Flash Decrement",
|
||||
|
||||
"Weather Rain:Cloud Texture",
|
||||
"Weather Rain:Clouds Maximum Percent",
|
||||
"Weather Rain:Transition Delta",
|
||||
"Weather Rain:Sky Sunrise Color",
|
||||
"Weather Rain:Sky Day Color",
|
||||
"Weather Rain:Sky Sunset Color",
|
||||
"Weather Rain:Sky Night Color",
|
||||
"Weather Rain:Fog Sunrise Color",
|
||||
"Weather Rain:Fog Day Color",
|
||||
"Weather Rain:Fog Sunset Color",
|
||||
"Weather Rain:Fog Night Color",
|
||||
"Weather Rain:Ambient Sunrise Color",
|
||||
"Weather Rain:Ambient Day Color",
|
||||
"Weather Rain:Ambient Sunset Color",
|
||||
"Weather Rain:Ambient Night Color",
|
||||
"Weather Rain:Sun Sunrise Color",
|
||||
"Weather Rain:Sun Day Color",
|
||||
"Weather Rain:Sun Sunset Color",
|
||||
"Weather Rain:Sun Night Color",
|
||||
"Weather Rain:Sun Disc Sunset Color",
|
||||
"Weather Rain:Land Fog Day Depth",
|
||||
"Weather Rain:Land Fog Night Depth",
|
||||
"Weather Rain:Wind Speed",
|
||||
"Weather Rain:Cloud Speed",
|
||||
"Weather Rain:Glare View",
|
||||
"Weather Rain:Rain Loop Sound ID",
|
||||
"Weather Rain:Using Precip",
|
||||
"Weather Rain:Rain Diameter",
|
||||
"Weather Rain:Rain Height Min",
|
||||
"Weather Rain:Rain Height Max",
|
||||
"Weather Rain:Rain Threshold",
|
||||
"Weather Rain:Rain Entrance Speed",
|
||||
"Weather Rain:Ambient Loop Sound ID",
|
||||
"Weather Rain:Max Raindrops",
|
||||
|
||||
"Weather Overcast:Cloud Texture",
|
||||
"Weather Overcast:Clouds Maximum Percent",
|
||||
"Weather Overcast:Transition Delta",
|
||||
"Weather Overcast:Sky Sunrise Color",
|
||||
"Weather Overcast:Sky Day Color",
|
||||
"Weather Overcast:Sky Sunset Color",
|
||||
"Weather Overcast:Sky Night Color",
|
||||
"Weather Overcast:Fog Sunrise Color",
|
||||
"Weather Overcast:Fog Day Color",
|
||||
"Weather Overcast:Fog Sunset Color",
|
||||
"Weather Overcast:Fog Night Color",
|
||||
"Weather Overcast:Ambient Sunrise Color",
|
||||
"Weather Overcast:Ambient Day Color",
|
||||
"Weather Overcast:Ambient Sunset Color",
|
||||
"Weather Overcast:Ambient Night Color",
|
||||
"Weather Overcast:Sun Sunrise Color",
|
||||
"Weather Overcast:Sun Day Color",
|
||||
"Weather Overcast:Sun Sunset Color",
|
||||
"Weather Overcast:Sun Night Color",
|
||||
"Weather Overcast:Sun Disc Sunset Color",
|
||||
"Weather Overcast:Land Fog Day Depth",
|
||||
"Weather Overcast:Land Fog Night Depth",
|
||||
"Weather Overcast:Wind Speed",
|
||||
"Weather Overcast:Cloud Speed",
|
||||
"Weather Overcast:Glare View",
|
||||
"Weather Overcast:Ambient Loop Sound ID",
|
||||
|
||||
"Weather Ashstorm:Cloud Texture",
|
||||
"Weather Ashstorm:Clouds Maximum Percent",
|
||||
"Weather Ashstorm:Transition Delta",
|
||||
"Weather Ashstorm:Sky Sunrise Color",
|
||||
"Weather Ashstorm:Sky Day Color",
|
||||
"Weather Ashstorm:Sky Sunset Color",
|
||||
"Weather Ashstorm:Sky Night Color",
|
||||
"Weather Ashstorm:Fog Sunrise Color",
|
||||
"Weather Ashstorm:Fog Day Color",
|
||||
"Weather Ashstorm:Fog Sunset Color",
|
||||
"Weather Ashstorm:Fog Night Color",
|
||||
"Weather Ashstorm:Ambient Sunrise Color",
|
||||
"Weather Ashstorm:Ambient Day Color",
|
||||
"Weather Ashstorm:Ambient Sunset Color",
|
||||
"Weather Ashstorm:Ambient Night Color",
|
||||
"Weather Ashstorm:Sun Sunrise Color",
|
||||
"Weather Ashstorm:Sun Day Color",
|
||||
"Weather Ashstorm:Sun Sunset Color",
|
||||
"Weather Ashstorm:Sun Night Color",
|
||||
"Weather Ashstorm:Sun Disc Sunset Color",
|
||||
"Weather Ashstorm:Land Fog Day Depth",
|
||||
"Weather Ashstorm:Land Fog Night Depth",
|
||||
"Weather Ashstorm:Wind Speed",
|
||||
"Weather Ashstorm:Cloud Speed",
|
||||
"Weather Ashstorm:Glare View",
|
||||
"Weather Ashstorm:Ambient Loop Sound ID",
|
||||
"Weather Ashstorm:Storm Threshold",
|
||||
|
||||
"Weather Blight:Cloud Texture",
|
||||
"Weather Blight:Clouds Maximum Percent",
|
||||
"Weather Blight:Transition Delta",
|
||||
"Weather Blight:Sky Sunrise Color",
|
||||
"Weather Blight:Sky Day Color",
|
||||
"Weather Blight:Sky Sunset Color",
|
||||
"Weather Blight:Sky Night Color",
|
||||
"Weather Blight:Fog Sunrise Color",
|
||||
"Weather Blight:Fog Day Color",
|
||||
"Weather Blight:Fog Sunset Color",
|
||||
"Weather Blight:Fog Night Color",
|
||||
"Weather Blight:Ambient Sunrise Color",
|
||||
"Weather Blight:Ambient Day Color",
|
||||
"Weather Blight:Ambient Sunset Color",
|
||||
"Weather Blight:Ambient Night Color",
|
||||
"Weather Blight:Sun Sunrise Color",
|
||||
"Weather Blight:Sun Day Color",
|
||||
"Weather Blight:Sun Sunset Color",
|
||||
"Weather Blight:Sun Night Color",
|
||||
"Weather Blight:Sun Disc Sunset Color",
|
||||
"Weather Blight:Land Fog Day Depth",
|
||||
"Weather Blight:Land Fog Night Depth",
|
||||
"Weather Blight:Wind Speed",
|
||||
"Weather Blight:Cloud Speed",
|
||||
"Weather Blight:Glare View",
|
||||
"Weather Blight:Ambient Loop Sound ID",
|
||||
"Weather Blight:Storm Threshold",
|
||||
"Weather Blight:Disease Chance",
|
||||
|
||||
// for Bloodmoon
|
||||
"Weather Snow:Cloud Texture",
|
||||
"Weather Snow:Clouds Maximum Percent",
|
||||
"Weather Snow:Transition Delta",
|
||||
"Weather Snow:Sky Sunrise Color",
|
||||
"Weather Snow:Sky Day Color",
|
||||
"Weather Snow:Sky Sunset Color",
|
||||
"Weather Snow:Sky Night Color",
|
||||
"Weather Snow:Fog Sunrise Color",
|
||||
"Weather Snow:Fog Day Color",
|
||||
"Weather Snow:Fog Sunset Color",
|
||||
"Weather Snow:Fog Night Color",
|
||||
"Weather Snow:Ambient Sunrise Color",
|
||||
"Weather Snow:Ambient Day Color",
|
||||
"Weather Snow:Ambient Sunset Color",
|
||||
"Weather Snow:Ambient Night Color",
|
||||
"Weather Snow:Sun Sunrise Color",
|
||||
"Weather Snow:Sun Day Color",
|
||||
"Weather Snow:Sun Sunset Color",
|
||||
"Weather Snow:Sun Night Color",
|
||||
"Weather Snow:Sun Disc Sunset Color",
|
||||
"Weather Snow:Land Fog Day Depth",
|
||||
"Weather Snow:Land Fog Night Depth",
|
||||
"Weather Snow:Wind Speed",
|
||||
"Weather Snow:Cloud Speed",
|
||||
"Weather Snow:Glare View",
|
||||
"Weather Snow:Snow Diameter",
|
||||
"Weather Snow:Snow Height Min",
|
||||
"Weather Snow:Snow Height Max",
|
||||
"Weather Snow:Snow Entrance Speed",
|
||||
"Weather Snow:Max Snowflakes",
|
||||
"Weather Snow:Ambient Loop Sound ID",
|
||||
"Weather Snow:Snow Threshold",
|
||||
|
||||
// for Bloodmoon
|
||||
"Weather Blizzard:Cloud Texture",
|
||||
"Weather Blizzard:Clouds Maximum Percent",
|
||||
"Weather Blizzard:Transition Delta",
|
||||
"Weather Blizzard:Sky Sunrise Color",
|
||||
"Weather Blizzard:Sky Day Color",
|
||||
"Weather Blizzard:Sky Sunset Color",
|
||||
"Weather Blizzard:Sky Night Color",
|
||||
"Weather Blizzard:Fog Sunrise Color",
|
||||
"Weather Blizzard:Fog Day Color",
|
||||
"Weather Blizzard:Fog Sunset Color",
|
||||
"Weather Blizzard:Fog Night Color",
|
||||
"Weather Blizzard:Ambient Sunrise Color",
|
||||
"Weather Blizzard:Ambient Day Color",
|
||||
"Weather Blizzard:Ambient Sunset Color",
|
||||
"Weather Blizzard:Ambient Night Color",
|
||||
"Weather Blizzard:Sun Sunrise Color",
|
||||
"Weather Blizzard:Sun Day Color",
|
||||
"Weather Blizzard:Sun Sunset Color",
|
||||
"Weather Blizzard:Sun Night Color",
|
||||
"Weather Blizzard:Sun Disc Sunset Color",
|
||||
"Weather Blizzard:Land Fog Day Depth",
|
||||
"Weather Blizzard:Land Fog Night Depth",
|
||||
"Weather Blizzard:Wind Speed",
|
||||
"Weather Blizzard:Cloud Speed",
|
||||
"Weather Blizzard:Glare View",
|
||||
"Weather Blizzard:Ambient Loop Sound ID",
|
||||
"Weather Blizzard:Storm Threshold",
|
||||
|
||||
// moons
|
||||
"Moons:Secunda Size",
|
||||
"Moons:Secunda Axis Offset",
|
||||
"Moons:Secunda Speed",
|
||||
"Moons:Secunda Daily Increment",
|
||||
"Moons:Secunda Moon Shadow Early Fade Angle",
|
||||
"Moons:Secunda Fade Start Angle",
|
||||
"Moons:Secunda Fade End Angle",
|
||||
"Moons:Secunda Fade In Start",
|
||||
"Moons:Secunda Fade In Finish",
|
||||
"Moons:Secunda Fade Out Start",
|
||||
"Moons:Secunda Fade Out Finish",
|
||||
"Moons:Masser Size",
|
||||
"Moons:Masser Axis Offset",
|
||||
"Moons:Masser Speed",
|
||||
"Moons:Masser Daily Increment",
|
||||
"Moons:Masser Moon Shadow Early Fade Angle",
|
||||
"Moons:Masser Fade Start Angle",
|
||||
"Moons:Masser Fade End Angle",
|
||||
"Moons:Masser Fade In Start",
|
||||
"Moons:Masser Fade In Finish",
|
||||
"Moons:Masser Fade Out Start",
|
||||
"Moons:Masser Fade Out Finish",
|
||||
"Moons:Script Color",
|
||||
|
||||
0
|
||||
};
|
||||
|
||||
for(int i=0; map[i][0]; i++) {
|
||||
mMergeMap.insert(std::make_pair<std::string, std::string>(map[i][0], map[i][1]));
|
||||
}
|
||||
|
||||
for(int i=0; fallback[i]; i++) {
|
||||
mMergeFallback.push_back(fallback[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::setVerbose(bool verbose) {
|
||||
mVerbose = verbose;
|
||||
}
|
||||
|
||||
std::string MwIniImporter::numberToString(int n) {
|
||||
std::stringstream str;
|
||||
str << n;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filename) const {
|
||||
std::cout << "load ini file: " << filename << std::endl;
|
||||
|
||||
std::string section("");
|
||||
MwIniImporter::multistrmap map;
|
||||
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
||||
ToUTF8::Utf8Encoder encoder(mEncoding);
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
|
||||
line = encoder.getUtf8(line);
|
||||
|
||||
// unify Unix-style and Windows file ending
|
||||
if (!(line.empty()) && (line[line.length()-1]) == '\r') {
|
||||
line = line.substr(0, line.length()-1);
|
||||
}
|
||||
|
||||
if(line[0] == '[') {
|
||||
int pos = line.find(']');
|
||||
if(pos < 2) {
|
||||
std::cout << "Warning: ini file wrongly formatted (" << line << "). Line ignored." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
section = line.substr(1, line.find(']')-1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int comment_pos = line.find(";");
|
||||
if(comment_pos > 0) {
|
||||
line = line.substr(0,comment_pos);
|
||||
}
|
||||
|
||||
if(line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int pos = line.find("=");
|
||||
if(pos < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key(section + ":" + line.substr(0,pos));
|
||||
std::string value(line.substr(pos+1));
|
||||
if(value.empty()) {
|
||||
std::cout << "Warning: ignored empty value for key '" << key << "'." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
multistrmap::iterator it;
|
||||
if((it = map.find(key)) == map.end()) {
|
||||
map.insert( std::make_pair (key, std::vector<std::string>() ) );
|
||||
}
|
||||
map[key].push_back(value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::string& filename) {
|
||||
std::cout << "load cfg file: " << filename << std::endl;
|
||||
|
||||
MwIniImporter::multistrmap map;
|
||||
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
|
||||
// we cant say comment by only looking at first char anymore
|
||||
int comment_pos = line.find("#");
|
||||
if(comment_pos > 0) {
|
||||
line = line.substr(0,comment_pos);
|
||||
}
|
||||
|
||||
if(line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int pos = line.find("=");
|
||||
if(pos < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key(line.substr(0,pos));
|
||||
std::string value(line.substr(pos+1));
|
||||
|
||||
multistrmap::iterator it;
|
||||
if((it = map.find(key)) == map.end()) {
|
||||
map.insert( std::make_pair (key, std::vector<std::string>() ) );
|
||||
}
|
||||
map[key].push_back(value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void MwIniImporter::merge(multistrmap &cfg, const multistrmap &ini) const {
|
||||
multistrmap::const_iterator iniIt;
|
||||
for(strmap::const_iterator it=mMergeMap.begin(); it!=mMergeMap.end(); ++it) {
|
||||
if((iniIt = ini.find(it->second)) != ini.end()) {
|
||||
for(std::vector<std::string>::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) {
|
||||
cfg.erase(it->first);
|
||||
insertMultistrmap(cfg, it->first, *vc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::mergeFallback(multistrmap &cfg, const multistrmap &ini) const {
|
||||
cfg.erase("fallback");
|
||||
|
||||
multistrmap::const_iterator iniIt;
|
||||
for(std::vector<std::string>::const_iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); ++it) {
|
||||
if((iniIt = ini.find(*it)) != ini.end()) {
|
||||
for(std::vector<std::string>::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) {
|
||||
std::string value(*it);
|
||||
std::replace( value.begin(), value.end(), ' ', '_' );
|
||||
std::replace( value.begin(), value.end(), ':', '_' );
|
||||
value.append(",").append(vc->substr(0,vc->length()));
|
||||
insertMultistrmap(cfg, "fallback", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value) {
|
||||
const multistrmap::const_iterator it = cfg.find(key);
|
||||
if(it == cfg.end()) {
|
||||
cfg.insert(std::make_pair (key, std::vector<std::string>() ));
|
||||
}
|
||||
cfg[key].push_back(value);
|
||||
}
|
||||
|
||||
void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) const {
|
||||
std::vector<std::string> archives;
|
||||
std::string baseArchive("Archives:Archive ");
|
||||
std::string archive;
|
||||
|
||||
// Search archives listed in ini file
|
||||
multistrmap::const_iterator it = ini.begin();
|
||||
for(int i=0; it != ini.end(); i++) {
|
||||
archive = baseArchive;
|
||||
archive.append(this->numberToString(i));
|
||||
|
||||
it = ini.find(archive);
|
||||
if(it == ini.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
||||
archives.push_back(*entry);
|
||||
}
|
||||
}
|
||||
|
||||
cfg.erase("fallback-archive");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("fallback-archive", std::vector<std::string>()));
|
||||
|
||||
// Add Morrowind.bsa by default, since Vanilla loads this archive even if it
|
||||
// does not appears in the ini file
|
||||
cfg["fallback-archive"].push_back("Morrowind.bsa");
|
||||
|
||||
for(std::vector<std::string>::const_iterator it=archives.begin(); it!=archives.end(); ++it) {
|
||||
cfg["fallback-archive"].push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const {
|
||||
std::vector<std::string> esmFiles;
|
||||
std::vector<std::string> espFiles;
|
||||
std::string baseGameFile("Game Files:GameFile");
|
||||
std::string gameFile("");
|
||||
|
||||
multistrmap::const_iterator it = ini.begin();
|
||||
for(int i=0; it != ini.end(); i++) {
|
||||
gameFile = baseGameFile;
|
||||
gameFile.append(this->numberToString(i));
|
||||
|
||||
it = ini.find(gameFile);
|
||||
if(it == ini.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if(filetype.compare("esm") == 0) {
|
||||
esmFiles.push_back(*entry);
|
||||
}
|
||||
else if(filetype.compare("esp") == 0) {
|
||||
espFiles.push_back(*entry);
|
||||
}
|
||||
}
|
||||
|
||||
gameFile = "";
|
||||
}
|
||||
|
||||
cfg.erase("master");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) {
|
||||
cfg["master"].push_back(*it);
|
||||
}
|
||||
|
||||
cfg.erase("plugin");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) {
|
||||
cfg["plugin"].push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, const multistrmap &cfg) {
|
||||
|
||||
for(multistrmap::const_iterator it=cfg.begin(); it != cfg.end(); ++it) {
|
||||
for(std::vector<std::string>::const_iterator entry=it->second.begin(); entry != it->second.end(); ++entry) {
|
||||
out << (it->first) << "=" << (*entry) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)
|
||||
{
|
||||
mEncoding = encoding;
|
||||
}
|
39
apps/mwiniimporter/importer.hpp
Normal file
39
apps/mwiniimporter/importer.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef MWINIIMPORTER_IMPORTER
|
||||
#define MWINIIMPORTER_IMPORTER 1
|
||||
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
class MwIniImporter {
|
||||
public:
|
||||
typedef std::map<std::string, std::string> strmap;
|
||||
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
||||
|
||||
MwIniImporter();
|
||||
void setInputEncoding(const ToUTF8::FromType& encoding);
|
||||
void setVerbose(bool verbose);
|
||||
multistrmap loadIniFile(const std::string& filename) const;
|
||||
static multistrmap loadCfgFile(const std::string& filename);
|
||||
void merge(multistrmap &cfg, const multistrmap &ini) const;
|
||||
void mergeFallback(multistrmap &cfg, const multistrmap &ini) const;
|
||||
void importGameFiles(multistrmap &cfg, const multistrmap &ini) const;
|
||||
void importArchives(multistrmap &cfg, const multistrmap &ini) const;
|
||||
static void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, const multistrmap &cfg);
|
||||
|
||||
private:
|
||||
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
|
||||
static std::string numberToString(int n);
|
||||
bool mVerbose;
|
||||
strmap mMergeMap;
|
||||
std::vector<std::string> mMergeFallback;
|
||||
ToUTF8::FromType mEncoding;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
101
apps/mwiniimporter/main.cpp
Normal file
101
apps/mwiniimporter/main.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "importer.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
bpo::options_description desc("Syntax: mwiniimporter <options> inifile configfile\nAllowed options");
|
||||
bpo::positional_options_description p_desc;
|
||||
desc.add_options()
|
||||
("help,h", "produce help message")
|
||||
("verbose,v", "verbose output")
|
||||
("ini,i", bpo::value<std::string>(), "morrowind.ini file")
|
||||
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
|
||||
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
|
||||
("game-files,g", "import esm and esp files")
|
||||
("no-archives,A", "disable bsa archives import")
|
||||
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
|
||||
"Character encoding used in OpenMW game messages:\n"
|
||||
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||
;
|
||||
p_desc.add("ini", 1).add("cfg", 1);
|
||||
|
||||
bpo::variables_map vm;
|
||||
|
||||
try
|
||||
{
|
||||
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
|
||||
.options(desc)
|
||||
.positional(p_desc)
|
||||
.run();
|
||||
|
||||
bpo::store(parsed, vm);
|
||||
}
|
||||
catch(boost::program_options::unknown_option & x)
|
||||
{
|
||||
std::cerr << "ERROR: " << x.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
catch(boost::program_options::invalid_command_line_syntax & x)
|
||||
{
|
||||
std::cerr << "ERROR: " << x.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
|
||||
std::cout << desc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bpo::notify(vm);
|
||||
|
||||
std::string iniFile = vm["ini"].as<std::string>();
|
||||
std::string cfgFile = vm["cfg"].as<std::string>();
|
||||
|
||||
// if no output is given, write back to cfg file
|
||||
std::string outputFile(vm["output"].as<std::string>());
|
||||
if(vm["output"].defaulted()) {
|
||||
outputFile = vm["cfg"].as<std::string>();
|
||||
}
|
||||
|
||||
if(!boost::filesystem::exists(iniFile)) {
|
||||
std::cerr << "ini file does not exist" << std::endl;
|
||||
return -3;
|
||||
}
|
||||
if(!boost::filesystem::exists(cfgFile))
|
||||
std::cerr << "cfg file does not exist" << std::endl;
|
||||
|
||||
MwIniImporter importer;
|
||||
importer.setVerbose(vm.count("verbose"));
|
||||
|
||||
// Font encoding settings
|
||||
std::string encoding(vm["encoding"].as<std::string>());
|
||||
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
|
||||
|
||||
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
|
||||
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
||||
|
||||
importer.merge(cfg, ini);
|
||||
importer.mergeFallback(cfg, ini);
|
||||
|
||||
if(vm.count("game-files")) {
|
||||
importer.importGameFiles(cfg, ini);
|
||||
}
|
||||
|
||||
if(!vm.count("no-archives")) {
|
||||
importer.importArchives(cfg, ini);
|
||||
}
|
||||
|
||||
std::cout << "write to: " << outputFile << std::endl;
|
||||
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
||||
importer.writeToFile(file, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
158
apps/opencs/CMakeLists.txt
Normal file
158
apps/opencs/CMakeLists.txt
Normal file
|
@ -0,0 +1,158 @@
|
|||
set (OPENCS_SRC main.cpp)
|
||||
|
||||
opencs_units (. editor)
|
||||
|
||||
set (CMAKE_BUILD_TYPE DEBUG)
|
||||
|
||||
opencs_units (model/doc
|
||||
document
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/doc
|
||||
documentmanager
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/doc
|
||||
state
|
||||
)
|
||||
|
||||
|
||||
opencs_units (model/world
|
||||
idtable idtableproxymodel regionmap
|
||||
)
|
||||
|
||||
|
||||
opencs_units_noqt (model/world
|
||||
universalid data record commands columnbase scriptcontext cell refidcollection
|
||||
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/world
|
||||
columnimp idcollection collection
|
||||
)
|
||||
|
||||
|
||||
opencs_units (model/tools
|
||||
tools operation reportmodel
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/tools
|
||||
stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||
birthsigncheck spellcheck
|
||||
)
|
||||
|
||||
|
||||
opencs_units (view/doc
|
||||
viewmanager view operations operation subview startup filedialog
|
||||
)
|
||||
|
||||
|
||||
opencs_units_noqt (view/doc
|
||||
subviewfactory
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (view/doc
|
||||
subviewfactoryimp
|
||||
)
|
||||
|
||||
|
||||
opencs_units (view/world
|
||||
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
|
||||
cellcreator referenceablecreator referencecreator
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/world
|
||||
dialoguesubview subviews
|
||||
enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate
|
||||
scripthighlighter idvalidator
|
||||
)
|
||||
|
||||
|
||||
opencs_units (view/tools
|
||||
reportsubview
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/tools
|
||||
subviews
|
||||
)
|
||||
|
||||
opencs_units (view/settings
|
||||
abstractblock
|
||||
proxyblock
|
||||
abstractwidget
|
||||
usersettingsdialog
|
||||
datadisplayformatpage
|
||||
windowpage
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/settings
|
||||
abstractpage
|
||||
blankpage
|
||||
groupblock
|
||||
customblock
|
||||
groupbox
|
||||
itemblock
|
||||
settingwidget
|
||||
toggleblock
|
||||
support
|
||||
)
|
||||
|
||||
opencs_units (model/settings
|
||||
usersettings
|
||||
settingcontainer
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/settings
|
||||
support
|
||||
settingsitem
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/filter
|
||||
node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/filter
|
||||
filter
|
||||
)
|
||||
|
||||
opencs_units (view/filter
|
||||
filtercreator filterbox recordfilterbox editwidget
|
||||
)
|
||||
|
||||
set (OPENCS_US
|
||||
)
|
||||
|
||||
set (OPENCS_RES ../../files/opencs/resources.qrc
|
||||
../../files/launcher/launcher.qrc
|
||||
)
|
||||
|
||||
set (OPENCS_UI ../../files/ui/datafilespage.ui
|
||||
)
|
||||
|
||||
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})
|
||||
|
||||
if(WIN32)
|
||||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED)
|
||||
include(${QT_USE_FILE})
|
||||
|
||||
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
|
||||
qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
|
||||
qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_executable(opencs
|
||||
${OPENCS_SRC}
|
||||
${OPENCS_UI_HDR}
|
||||
${OPENCS_MOC_SRC}
|
||||
${OPENCS_RES_SRC}
|
||||
)
|
||||
|
||||
target_link_libraries(opencs
|
||||
${Boost_LIBRARIES}
|
||||
${QT_LIBRARIES}
|
||||
components
|
||||
)
|
157
apps/opencs/editor.cpp
Normal file
157
apps/opencs/editor.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
|
||||
#include "editor.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "model/doc/document.hpp"
|
||||
#include "model/world/data.hpp"
|
||||
|
||||
CS::Editor::Editor() : mViewManager (mDocumentManager)
|
||||
{
|
||||
mIpcServerName = "org.openmw.OpenCS";
|
||||
|
||||
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
|
||||
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
|
||||
|
||||
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ()));
|
||||
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
|
||||
|
||||
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
|
||||
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
|
||||
|
||||
setupDataFiles();
|
||||
}
|
||||
|
||||
void CS::Editor::setupDataFiles()
|
||||
{
|
||||
boost::program_options::variables_map variables;
|
||||
boost::program_options::options_description desc;
|
||||
|
||||
desc.add_options()
|
||||
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
|
||||
("data-local", boost::program_options::value<std::string>()->default_value(""))
|
||||
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
||||
|
||||
boost::program_options::notify(variables);
|
||||
|
||||
mCfgMgr.readConfiguration(variables, desc);
|
||||
|
||||
Files::PathContainer mDataDirs, mDataLocal;
|
||||
if (!variables["data"].empty()) {
|
||||
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
||||
}
|
||||
|
||||
std::string local = variables["data-local"].as<std::string>();
|
||||
if (!local.empty()) {
|
||||
mDataLocal.push_back(Files::PathContainer::value_type(local));
|
||||
}
|
||||
|
||||
mCfgMgr.processPaths(mDataDirs);
|
||||
mCfgMgr.processPaths(mDataLocal);
|
||||
|
||||
// Set the charset for reading the esm/esp files
|
||||
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||
mFileDialog.setEncoding(encoding);
|
||||
|
||||
Files::PathContainer dataDirs;
|
||||
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
|
||||
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
|
||||
|
||||
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||
{
|
||||
QString path = QString::fromStdString(iter->string());
|
||||
mFileDialog.addFiles(path);
|
||||
}
|
||||
|
||||
//load the settings into the userSettings instance.
|
||||
const QString settingFileName = "opencs.cfg";
|
||||
CSMSettings::UserSettings::instance().loadSettings(settingFileName);
|
||||
|
||||
}
|
||||
|
||||
void CS::Editor::createDocument()
|
||||
{
|
||||
mStartup.hide();
|
||||
|
||||
mFileDialog.newFile();
|
||||
}
|
||||
|
||||
void CS::Editor::loadDocument()
|
||||
{
|
||||
mStartup.hide();
|
||||
|
||||
mFileDialog.openFile();
|
||||
}
|
||||
|
||||
void CS::Editor::openFiles()
|
||||
{
|
||||
std::vector<boost::filesystem::path> files;
|
||||
QStringList paths = mFileDialog.checkedItemsPaths();
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
files.push_back(path.toStdString());
|
||||
}
|
||||
|
||||
CSMDoc::Document *document = mDocumentManager.addDocument(files, false);
|
||||
|
||||
mViewManager.addView (document);
|
||||
mFileDialog.hide();
|
||||
}
|
||||
|
||||
void CS::Editor::createNewFile()
|
||||
{
|
||||
std::vector<boost::filesystem::path> files;
|
||||
QStringList paths = mFileDialog.checkedItemsPaths();
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
files.push_back(path.toStdString());
|
||||
}
|
||||
|
||||
files.push_back(mFileDialog.fileName().toStdString());
|
||||
|
||||
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
|
||||
|
||||
mViewManager.addView (document);
|
||||
mFileDialog.hide();
|
||||
}
|
||||
|
||||
void CS::Editor::showStartup()
|
||||
{
|
||||
if(mStartup.isHidden())
|
||||
mStartup.show();
|
||||
mStartup.raise();
|
||||
mStartup.activateWindow();
|
||||
}
|
||||
|
||||
bool CS::Editor::makeIPCServer()
|
||||
{
|
||||
mServer = new QLocalServer(this);
|
||||
|
||||
if(mServer->listen(mIpcServerName))
|
||||
{
|
||||
connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup()));
|
||||
return true;
|
||||
}
|
||||
|
||||
mServer->close();
|
||||
return false;
|
||||
}
|
||||
|
||||
void CS::Editor::connectToIPCServer()
|
||||
{
|
||||
mClientSocket = new QLocalSocket(this);
|
||||
mClientSocket->connectToServer(mIpcServerName);
|
||||
mClientSocket->close();
|
||||
}
|
||||
|
||||
int CS::Editor::run()
|
||||
{
|
||||
mStartup.show();
|
||||
|
||||
QApplication::setQuitOnLastWindowClosed (true);
|
||||
|
||||
return QApplication::exec();
|
||||
}
|
66
apps/opencs/editor.hpp
Normal file
66
apps/opencs/editor.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#ifndef CS_EDITOR_H
|
||||
#define CS_EDITOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
#include "model/doc/documentmanager.hpp"
|
||||
|
||||
#include "view/doc/viewmanager.hpp"
|
||||
#include "view/doc/startup.hpp"
|
||||
#include "view/doc/filedialog.hpp"
|
||||
#include "model/settings/usersettings.hpp"
|
||||
|
||||
namespace CS
|
||||
{
|
||||
class Editor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
CSMSettings::UserSettings mUserSettings;
|
||||
CSMDoc::DocumentManager mDocumentManager;
|
||||
CSVDoc::ViewManager mViewManager;
|
||||
CSVDoc::StartupDialogue mStartup;
|
||||
FileDialog mFileDialog;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
void setupDataFiles();
|
||||
|
||||
// not implemented
|
||||
Editor (const Editor&);
|
||||
Editor& operator= (const Editor&);
|
||||
|
||||
public:
|
||||
|
||||
Editor();
|
||||
|
||||
bool makeIPCServer();
|
||||
void connectToIPCServer();
|
||||
|
||||
int run();
|
||||
///< \return error status
|
||||
|
||||
private slots:
|
||||
|
||||
void createDocument();
|
||||
|
||||
void loadDocument();
|
||||
void openFiles();
|
||||
void createNewFile();
|
||||
|
||||
void showStartup();
|
||||
|
||||
private:
|
||||
|
||||
QString mIpcServerName;
|
||||
QLocalServer *mServer;
|
||||
QLocalSocket *mClientSocket;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
49
apps/opencs/main.cpp
Normal file
49
apps/opencs/main.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
#include "editor.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QIcon>
|
||||
|
||||
class Application : public QApplication
|
||||
{
|
||||
private:
|
||||
|
||||
bool notify (QObject *receiver, QEvent *event)
|
||||
{
|
||||
try
|
||||
{
|
||||
return QApplication::notify (receiver, event);
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
std::cerr << "An exception has been caught: " << exception.what() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Q_INIT_RESOURCE (resources);
|
||||
Application mApplication (argc, argv);
|
||||
|
||||
mApplication.setWindowIcon (QIcon (":./opencs.png"));
|
||||
|
||||
CS::Editor editor;
|
||||
|
||||
if(!editor.makeIPCServer())
|
||||
{
|
||||
editor.connectToIPCServer();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return editor.run();
|
||||
}
|
2284
apps/opencs/model/doc/document.cpp
Normal file
2284
apps/opencs/model/doc/document.cpp
Normal file
File diff suppressed because it is too large
Load diff
111
apps/opencs/model/doc/document.hpp
Normal file
111
apps/opencs/model/doc/document.hpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
#ifndef CSM_DOC_DOCUMENT_H
|
||||
#define CSM_DOC_DOCUMENT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <QUndoStack>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "../world/data.hpp"
|
||||
|
||||
#include "../tools/tools.hpp"
|
||||
|
||||
#include "state.hpp"
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct GameSetting;
|
||||
struct Global;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
std::string mName; ///< \todo replace name with ESX list
|
||||
CSMWorld::Data mData;
|
||||
CSMTools::Tools mTools;
|
||||
|
||||
// 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
|
||||
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
|
||||
QUndoStack mUndoStack;
|
||||
|
||||
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
|
||||
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
|
||||
|
||||
// not implemented
|
||||
Document (const Document&);
|
||||
Document& operator= (const Document&);
|
||||
|
||||
void load (const std::vector<boost::filesystem::path>::const_iterator& begin,
|
||||
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified);
|
||||
///< \param lastAsModified Store the last file in Modified instead of merging it into Base.
|
||||
|
||||
void createBase();
|
||||
|
||||
void addGmsts();
|
||||
|
||||
void addOptionalGmsts();
|
||||
|
||||
void addOptionalGlobals();
|
||||
|
||||
void addOptionalGmst (const ESM::GameSetting& gmst);
|
||||
|
||||
void addOptionalGlobal (const ESM::Global& global);
|
||||
|
||||
public:
|
||||
|
||||
Document (const std::vector<boost::filesystem::path>& files, bool new_);
|
||||
~Document();
|
||||
|
||||
QUndoStack& getUndoStack();
|
||||
|
||||
int getState() const;
|
||||
|
||||
const std::string& getName() const;
|
||||
///< \todo replace with ESX list
|
||||
|
||||
void save();
|
||||
|
||||
CSMWorld::UniversalId verify();
|
||||
|
||||
void abortOperation (int type);
|
||||
|
||||
const CSMWorld::Data& getData() const;
|
||||
|
||||
CSMWorld::Data& getData();
|
||||
|
||||
CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id);
|
||||
///< The ownership of the returned report is not transferred.
|
||||
|
||||
signals:
|
||||
|
||||
void stateChanged (int state, CSMDoc::Document *document);
|
||||
|
||||
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
|
||||
|
||||
private slots:
|
||||
|
||||
void modificationStateChanged (bool clean);
|
||||
|
||||
void operationDone (int type);
|
||||
|
||||
void saving();
|
||||
///< dummy implementation -> remove when proper save is implemented.
|
||||
|
||||
public slots:
|
||||
|
||||
void progress (int current, int max, int type);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
38
apps/opencs/model/doc/documentmanager.cpp
Normal file
38
apps/opencs/model/doc/documentmanager.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
|
||||
#include "documentmanager.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "document.hpp"
|
||||
|
||||
CSMDoc::DocumentManager::DocumentManager() {}
|
||||
|
||||
CSMDoc::DocumentManager::~DocumentManager()
|
||||
{
|
||||
for (std::vector<Document *>::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter)
|
||||
delete *iter;
|
||||
}
|
||||
|
||||
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files,
|
||||
bool new_)
|
||||
{
|
||||
Document *document = new Document (files, new_);
|
||||
|
||||
mDocuments.push_back (document);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
bool CSMDoc::DocumentManager::removeDocument (Document *document)
|
||||
{
|
||||
std::vector<Document *>::iterator iter = std::find (mDocuments.begin(), mDocuments.end(), document);
|
||||
|
||||
if (iter==mDocuments.end())
|
||||
throw std::runtime_error ("removing invalid document");
|
||||
|
||||
mDocuments.erase (iter);
|
||||
delete document;
|
||||
|
||||
return mDocuments.empty();
|
||||
}
|
37
apps/opencs/model/doc/documentmanager.hpp
Normal file
37
apps/opencs/model/doc/documentmanager.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef CSM_DOC_DOCUMENTMGR_H
|
||||
#define CSM_DOC_DOCUMENTMGR_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
|
||||
class DocumentManager
|
||||
{
|
||||
std::vector<Document *> mDocuments;
|
||||
|
||||
DocumentManager (const DocumentManager&);
|
||||
DocumentManager& operator= (const DocumentManager&);
|
||||
|
||||
public:
|
||||
|
||||
DocumentManager();
|
||||
|
||||
~DocumentManager();
|
||||
|
||||
Document *addDocument (const std::vector<boost::filesystem::path>& files, bool new_);
|
||||
///< The ownership of the returned document is not transferred to the caller.
|
||||
///
|
||||
/// \param new_ Do not load the last content file in \a files and instead create in an
|
||||
/// appropriate way.
|
||||
|
||||
bool removeDocument (Document *document);
|
||||
///< \return last document removed?
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
19
apps/opencs/model/doc/state.hpp
Normal file
19
apps/opencs/model/doc/state.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef CSM_DOC_STATE_H
|
||||
#define CSM_DOC_STATE_H
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
enum State
|
||||
{
|
||||
State_Modified = 1,
|
||||
State_Locked = 2,
|
||||
State_Operation = 4,
|
||||
|
||||
State_Saving = 8,
|
||||
State_Verifying = 16,
|
||||
State_Compiling = 32, // not implemented yet
|
||||
State_Searching = 64 // not implemented yet
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
20
apps/opencs/model/filter/andnode.cpp
Normal file
20
apps/opencs/model/filter/andnode.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
#include "andnode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
CSMFilter::AndNode::AndNode (const std::vector<boost::shared_ptr<Node> >& nodes)
|
||||
: NAryNode (nodes, "and")
|
||||
{}
|
||||
|
||||
bool CSMFilter::AndNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
int size = getSize();
|
||||
|
||||
for (int i=0; i<size; ++i)
|
||||
if (!(*this)[i].test (table, row, columns))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
23
apps/opencs/model/filter/andnode.hpp
Normal file
23
apps/opencs/model/filter/andnode.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef CSM_FILTER_ANDNODE_H
|
||||
#define CSM_FILTER_ANDNODE_H
|
||||
|
||||
#include "narynode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class AndNode : public NAryNode
|
||||
{
|
||||
bool mAnd;
|
||||
|
||||
public:
|
||||
|
||||
AndNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
15
apps/opencs/model/filter/booleannode.cpp
Normal file
15
apps/opencs/model/filter/booleannode.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
#include "booleannode.hpp"
|
||||
|
||||
CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}
|
||||
|
||||
bool CSMFilter::BooleanNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
return mTrue;
|
||||
}
|
||||
|
||||
std::string CSMFilter::BooleanNode::toString (bool numericColumns) const
|
||||
{
|
||||
return mTrue ? "true" : "false";
|
||||
}
|
29
apps/opencs/model/filter/booleannode.hpp
Normal file
29
apps/opencs/model/filter/booleannode.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CSM_FILTER_BOOLEANNODE_H
|
||||
#define CSM_FILTER_BOOLEANNODE_H
|
||||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class BooleanNode : public LeafNode
|
||||
{
|
||||
bool mTrue;
|
||||
|
||||
public:
|
||||
|
||||
BooleanNode (bool true_);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
25
apps/opencs/model/filter/filter.hpp
Normal file
25
apps/opencs/model/filter/filter.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef CSM_FILTER_FILTER_H
|
||||
#define CSM_FILTER_FILTER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/filter.hpp>
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
/// \brief Wrapper for Filter record
|
||||
struct Filter : public ESM::Filter
|
||||
{
|
||||
enum Scope
|
||||
{
|
||||
Scope_Project = 0, // per project
|
||||
Scope_Session = 1, // exists only for one editing session; not saved
|
||||
Scope_Content = 2 // embedded in the edited content file
|
||||
};
|
||||
|
||||
Scope mScope;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
8
apps/opencs/model/filter/leafnode.cpp
Normal file
8
apps/opencs/model/filter/leafnode.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const
|
||||
{
|
||||
return std::vector<int>();
|
||||
}
|
||||
|
20
apps/opencs/model/filter/leafnode.hpp
Normal file
20
apps/opencs/model/filter/leafnode.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef CSM_FILTER_LEAFNODE_H
|
||||
#define CSM_FILTER_LEAFNODE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class LeafNode : public Node
|
||||
{
|
||||
public:
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
60
apps/opencs/model/filter/narynode.cpp
Normal file
60
apps/opencs/model/filter/narynode.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
#include "narynode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
CSMFilter::NAryNode::NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes,
|
||||
const std::string& name)
|
||||
: mNodes (nodes), mName (name)
|
||||
{}
|
||||
|
||||
int CSMFilter::NAryNode::getSize() const
|
||||
{
|
||||
return mNodes.size();
|
||||
}
|
||||
|
||||
const CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const
|
||||
{
|
||||
return *mNodes.at (index);
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::NAryNode::getReferencedColumns() const
|
||||
{
|
||||
std::vector<int> columns;
|
||||
|
||||
for (std::vector<boost::shared_ptr<Node> >::const_iterator iter (mNodes.begin());
|
||||
iter!=mNodes.end(); ++iter)
|
||||
{
|
||||
std::vector<int> columns2 = (*iter)->getReferencedColumns();
|
||||
|
||||
columns.insert (columns.end(), columns2.begin(), columns2.end());
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
std::string CSMFilter::NAryNode::toString (bool numericColumns) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << mName << " (";
|
||||
|
||||
bool first = true;
|
||||
int size = getSize();
|
||||
|
||||
for (int i=0; i<size; ++i)
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
stream << ", ";
|
||||
|
||||
stream << (*this)[i].toString (numericColumns);
|
||||
}
|
||||
|
||||
stream << ")";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
|
37
apps/opencs/model/filter/narynode.hpp
Normal file
37
apps/opencs/model/filter/narynode.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef CSM_FILTER_NARYNODE_H
|
||||
#define CSM_FILTER_NARYNODE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class NAryNode : public Node
|
||||
{
|
||||
std::vector<boost::shared_ptr<Node> > mNodes;
|
||||
std::string mName;
|
||||
|
||||
public:
|
||||
|
||||
NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes, const std::string& name);
|
||||
|
||||
int getSize() const;
|
||||
|
||||
const Node& operator[] (int index) const;
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
6
apps/opencs/model/filter/node.cpp
Normal file
6
apps/opencs/model/filter/node.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#include "node.hpp"
|
||||
|
||||
CSMFilter::Node::Node() {}
|
||||
|
||||
CSMFilter::Node::~Node() {}
|
53
apps/opencs/model/filter/node.hpp
Normal file
53
apps/opencs/model/filter/node.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef CSM_FILTER_NODE_H
|
||||
#define CSM_FILTER_NODE_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class IdTable;
|
||||
}
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
/// \brief Root class for the filter node hierarchy
|
||||
///
|
||||
/// \note When the function documentation for this class mentions "this node", this should be
|
||||
/// interpreted as "the node and all its children".
|
||||
class Node
|
||||
{
|
||||
// not implemented
|
||||
Node (const Node&);
|
||||
Node& operator= (const Node&);
|
||||
|
||||
public:
|
||||
|
||||
Node();
|
||||
|
||||
virtual ~Node();
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const = 0;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const = 0;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const = 0;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE (boost::shared_ptr<CSMFilter::Node>)
|
||||
|
||||
#endif
|
10
apps/opencs/model/filter/notnode.cpp
Normal file
10
apps/opencs/model/filter/notnode.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
#include "notnode.hpp"
|
||||
|
||||
CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {}
|
||||
|
||||
bool CSMFilter::NotNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
return !getChild().test (table, row, columns);
|
||||
}
|
21
apps/opencs/model/filter/notnode.hpp
Normal file
21
apps/opencs/model/filter/notnode.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef CSM_FILTER_NOTNODE_H
|
||||
#define CSM_FILTER_NOTNODE_H
|
||||
|
||||
#include "unarynode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class NotNode : public UnaryNode
|
||||
{
|
||||
public:
|
||||
|
||||
NotNode (boost::shared_ptr<Node> child);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
20
apps/opencs/model/filter/ornode.cpp
Normal file
20
apps/opencs/model/filter/ornode.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
#include "ornode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
CSMFilter::OrNode::OrNode (const std::vector<boost::shared_ptr<Node> >& nodes)
|
||||
: NAryNode (nodes, "or")
|
||||
{}
|
||||
|
||||
bool CSMFilter::OrNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
int size = getSize();
|
||||
|
||||
for (int i=0; i<size; ++i)
|
||||
if ((*this)[i].test (table, row, columns))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
23
apps/opencs/model/filter/ornode.hpp
Normal file
23
apps/opencs/model/filter/ornode.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef CSM_FILTER_ORNODE_H
|
||||
#define CSM_FILTER_ORNODE_H
|
||||
|
||||
#include "narynode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class OrNode : public NAryNode
|
||||
{
|
||||
bool mAnd;
|
||||
|
||||
public:
|
||||
|
||||
OrNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
595
apps/opencs/model/filter/parser.cpp
Normal file
595
apps/opencs/model/filter/parser.cpp
Normal file
|
@ -0,0 +1,595 @@
|
|||
|
||||
#include "parser.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../world/columns.hpp"
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "booleannode.hpp"
|
||||
#include "ornode.hpp"
|
||||
#include "andnode.hpp"
|
||||
#include "notnode.hpp"
|
||||
#include "textnode.hpp"
|
||||
#include "valuenode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
struct Token
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Type_EOS,
|
||||
Type_None,
|
||||
Type_String,
|
||||
Type_Number,
|
||||
Type_Open,
|
||||
Type_Close,
|
||||
Type_OpenSquare,
|
||||
Type_CloseSquare,
|
||||
Type_Comma,
|
||||
Type_OneShot,
|
||||
Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously.
|
||||
Type_Keyword_False,
|
||||
Type_Keyword_And,
|
||||
Type_Keyword_Or,
|
||||
Type_Keyword_Not,
|
||||
Type_Keyword_Text,
|
||||
Type_Keyword_Value
|
||||
};
|
||||
|
||||
Type mType;
|
||||
std::string mString;
|
||||
double mNumber;
|
||||
|
||||
Token (Type type = Type_None);
|
||||
|
||||
Token (const std::string& string);
|
||||
|
||||
Token (double number);
|
||||
|
||||
operator bool() const;
|
||||
};
|
||||
|
||||
Token::Token (Type type) : mType (type) {}
|
||||
|
||||
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
|
||||
|
||||
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
|
||||
|
||||
Token::operator bool() const
|
||||
{
|
||||
return mType!=Type_None;
|
||||
}
|
||||
|
||||
bool operator== (const Token& left, const Token& right)
|
||||
{
|
||||
if (left.mType!=right.mType)
|
||||
return false;
|
||||
|
||||
switch (left.mType)
|
||||
{
|
||||
case Token::Type_String: return left.mString==right.mString;
|
||||
case Token::Type_Number: return left.mNumber==right.mNumber;
|
||||
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::getStringToken()
|
||||
{
|
||||
std::string string;
|
||||
|
||||
int size = static_cast<int> (mInput.size());
|
||||
|
||||
for (; mIndex<size; ++mIndex)
|
||||
{
|
||||
char c = mInput[mIndex];
|
||||
|
||||
if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' ||
|
||||
(!string.empty() && string[0]=='"'))
|
||||
string += c;
|
||||
else
|
||||
break;
|
||||
|
||||
if (c=='"' && string.size()>1)
|
||||
{
|
||||
++mIndex;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (!string.empty())
|
||||
{
|
||||
if (string[0]=='"' && (string[string.size()-1]!='"' || string.size()<2) )
|
||||
{
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
if (string[0]!='"' && string[string.size()-1]=='"')
|
||||
{
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
if (string[0]=='"')
|
||||
string = string.substr (1, string.size()-2);
|
||||
}
|
||||
|
||||
return checkKeywords (string);
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::getNumberToken()
|
||||
{
|
||||
std::string string;
|
||||
|
||||
int size = static_cast<int> (mInput.size());
|
||||
|
||||
bool hasDecimalPoint = false;
|
||||
bool hasDigit = false;
|
||||
|
||||
for (; mIndex<size; ++mIndex)
|
||||
{
|
||||
char c = mInput[mIndex];
|
||||
|
||||
if (std::isdigit (c))
|
||||
{
|
||||
string += c;
|
||||
hasDigit = true;
|
||||
}
|
||||
else if (c=='.' && !hasDecimalPoint)
|
||||
{
|
||||
string += c;
|
||||
hasDecimalPoint = true;
|
||||
}
|
||||
else if (string.empty() && c=='-')
|
||||
string += c;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasDigit)
|
||||
{
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
float value;
|
||||
std::istringstream stream (string.c_str());
|
||||
stream >> value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)
|
||||
{
|
||||
static const char *sKeywords[] =
|
||||
{
|
||||
"true", "false",
|
||||
"and", "or", "not",
|
||||
"string", "value",
|
||||
0
|
||||
};
|
||||
|
||||
std::string string = Misc::StringUtils::lowerCase (token.mString);
|
||||
|
||||
for (int i=0; sKeywords[i]; ++i)
|
||||
if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0]))
|
||||
return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True));
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::getNextToken()
|
||||
{
|
||||
int size = static_cast<int> (mInput.size());
|
||||
|
||||
char c = 0;
|
||||
|
||||
for (; mIndex<size; ++mIndex)
|
||||
{
|
||||
c = mInput[mIndex];
|
||||
|
||||
if (c!=' ')
|
||||
break;
|
||||
}
|
||||
|
||||
if (mIndex>=size)
|
||||
return Token (Token::Type_EOS);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '(': ++mIndex; return Token (Token::Type_Open);
|
||||
case ')': ++mIndex; return Token (Token::Type_Close);
|
||||
case '[': ++mIndex; return Token (Token::Type_OpenSquare);
|
||||
case ']': ++mIndex; return Token (Token::Type_CloseSquare);
|
||||
case ',': ++mIndex; return Token (Token::Type_Comma);
|
||||
case '!': ++mIndex; return Token (Token::Type_OneShot);
|
||||
}
|
||||
|
||||
if (c=='"' || c=='_' || std::isalpha (c) || c==':')
|
||||
return getStringToken();
|
||||
|
||||
if (c=='-' || c=='.' || std::isdigit (c))
|
||||
return getNumberToken();
|
||||
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp (bool allowEmpty, bool ignoreOneShot)
|
||||
{
|
||||
if (Token token = getNextToken())
|
||||
{
|
||||
if (token==Token (Token::Type_OneShot))
|
||||
token = getNextToken();
|
||||
|
||||
if (token)
|
||||
switch (token.mType)
|
||||
{
|
||||
case Token::Type_Keyword_True:
|
||||
|
||||
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (true));
|
||||
|
||||
case Token::Type_Keyword_False:
|
||||
|
||||
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (false));
|
||||
|
||||
case Token::Type_Keyword_And:
|
||||
case Token::Type_Keyword_Or:
|
||||
|
||||
return parseNAry (token);
|
||||
|
||||
case Token::Type_Keyword_Not:
|
||||
{
|
||||
boost::shared_ptr<CSMFilter::Node> node = parseImp();
|
||||
|
||||
if (mError)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
return boost::shared_ptr<CSMFilter::Node> (new NotNode (node));
|
||||
}
|
||||
|
||||
case Token::Type_Keyword_Text:
|
||||
|
||||
return parseText();
|
||||
|
||||
case Token::Type_Keyword_Value:
|
||||
|
||||
return parseValue();
|
||||
|
||||
case Token::Type_EOS:
|
||||
|
||||
if (!allowEmpty)
|
||||
error();
|
||||
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
default:
|
||||
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& keyword)
|
||||
{
|
||||
std::vector<boost::shared_ptr<Node> > nodes;
|
||||
|
||||
Token token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
boost::shared_ptr<Node> node = parseImp();
|
||||
|
||||
if (mError)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
nodes.push_back (node);
|
||||
|
||||
Token token = getNextToken();
|
||||
|
||||
if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma))
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
if (token.mType==Token::Type_Close)
|
||||
break;
|
||||
}
|
||||
|
||||
if (nodes.empty())
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
switch (keyword.mType)
|
||||
{
|
||||
case Token::Type_Keyword_And: return boost::shared_ptr<CSMFilter::Node> (new AndNode (nodes));
|
||||
case Token::Type_Keyword_Or: return boost::shared_ptr<CSMFilter::Node> (new OrNode (nodes));
|
||||
default: error(); return boost::shared_ptr<Node>();
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText()
|
||||
{
|
||||
Token token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (!token)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
// parse column ID
|
||||
int columnId = -1;
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
if (static_cast<int> (token.mNumber)==token.mNumber)
|
||||
columnId = static_cast<int> (token.mNumber);
|
||||
}
|
||||
else if (token.mType==Token::Type_String)
|
||||
{
|
||||
columnId = CSMWorld::Columns::getId (token.mString);
|
||||
}
|
||||
|
||||
if (columnId<0)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Comma)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
// parse text pattern
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_String)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
std::string text = token.mString;
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Close)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
return boost::shared_ptr<Node> (new TextNode (columnId, text));
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue()
|
||||
{
|
||||
Token token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (!token)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
// parse column ID
|
||||
int columnId = -1;
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
if (static_cast<int> (token.mNumber)==token.mNumber)
|
||||
columnId = static_cast<int> (token.mNumber);
|
||||
}
|
||||
else if (token.mType==Token::Type_String)
|
||||
{
|
||||
columnId = CSMWorld::Columns::getId (token.mString);
|
||||
}
|
||||
|
||||
if (columnId<0)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Comma)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
// parse value
|
||||
double lower = 0;
|
||||
double upper = 0;
|
||||
bool min = false;
|
||||
bool max = false;
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
// single value
|
||||
min = max = true;
|
||||
lower = upper = token.mNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
// interval
|
||||
if (token.mType==Token::Type_OpenSquare)
|
||||
min = true;
|
||||
else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Number)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
lower = token.mNumber;
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Comma)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Number)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
upper = token.mNumber;
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType==Token::Type_CloseSquare)
|
||||
max = true;
|
||||
else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Close)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
return boost::shared_ptr<Node> (new ValueNode (columnId, lower, upper, min, max));
|
||||
}
|
||||
|
||||
void CSMFilter::Parser::error()
|
||||
{
|
||||
mError = true;
|
||||
}
|
||||
|
||||
CSMFilter::Parser::Parser (const CSMWorld::Data& data)
|
||||
: mIndex (0), mError (false), mData (data) {}
|
||||
|
||||
bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined)
|
||||
{
|
||||
// reset
|
||||
mFilter.reset();
|
||||
mError = false;
|
||||
mInput = filter;
|
||||
mIndex = 0;
|
||||
|
||||
Token token;
|
||||
|
||||
if (allowPredefined)
|
||||
token = getNextToken();
|
||||
|
||||
if (!allowPredefined || token==Token (Token::Type_OneShot))
|
||||
{
|
||||
boost::shared_ptr<Node> node = parseImp (true, token!=Token (Token::Type_OneShot));
|
||||
|
||||
if (mError)
|
||||
return false;
|
||||
|
||||
if (getNextToken()!=Token (Token::Type_EOS))
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node)
|
||||
mFilter = node;
|
||||
else
|
||||
{
|
||||
// Empty filter string equals to filter "true".
|
||||
mFilter.reset (new BooleanNode (true));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (token.mType==Token::Type_String && allowPredefined)
|
||||
{
|
||||
if (getNextToken()!=Token (Token::Type_EOS))
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = mData.getFilters().searchId (token.mString);
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
const CSMWorld::Record<CSMFilter::Filter>& record = mData.getFilters().getRecord (index);
|
||||
|
||||
if (record.isDeleted())
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
return parse (record.get().mFilter, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
|
||||
{
|
||||
if (mError)
|
||||
throw std::logic_error ("No filter available");
|
||||
|
||||
return mFilter;
|
||||
}
|
59
apps/opencs/model/filter/parser.hpp
Normal file
59
apps/opencs/model/filter/parser.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#ifndef CSM_FILTER_PARSER_H
|
||||
#define CSM_FILTER_PARSER_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
}
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
struct Token;
|
||||
|
||||
class Parser
|
||||
{
|
||||
boost::shared_ptr<Node> mFilter;
|
||||
std::string mInput;
|
||||
int mIndex;
|
||||
bool mError;
|
||||
const CSMWorld::Data& mData;
|
||||
|
||||
Token getStringToken();
|
||||
|
||||
Token getNumberToken();
|
||||
|
||||
Token getNextToken();
|
||||
|
||||
Token checkKeywords (const Token& token);
|
||||
///< Turn string token into keyword token, if possible.
|
||||
|
||||
boost::shared_ptr<Node> parseImp (bool allowEmpty = false, bool ignoreOneShot = false);
|
||||
///< Will return a null-pointer, if there is nothing more to parse.
|
||||
|
||||
boost::shared_ptr<Node> parseNAry (const Token& keyword);
|
||||
|
||||
boost::shared_ptr<Node> parseText();
|
||||
|
||||
boost::shared_ptr<Node> parseValue();
|
||||
|
||||
void error();
|
||||
|
||||
public:
|
||||
|
||||
Parser (const CSMWorld::Data& data);
|
||||
|
||||
bool parse (const std::string& filter, bool allowPredefined = true);
|
||||
///< Discards any previous calls to parse
|
||||
///
|
||||
/// \return Success?
|
||||
|
||||
boost::shared_ptr<Node> getFilter() const;
|
||||
///< Throws an exception if the last call to parse did not return true.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
62
apps/opencs/model/filter/textnode.cpp
Normal file
62
apps/opencs/model/filter/textnode.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
|
||||
#include "textnode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QRegExp>
|
||||
|
||||
#include "../world/columns.hpp"
|
||||
#include "../world/idtable.hpp"
|
||||
|
||||
CSMFilter::TextNode::TextNode (int columnId, const std::string& text)
|
||||
: mColumnId (columnId), mText (text)
|
||||
{}
|
||||
|
||||
bool CSMFilter::TextNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
|
||||
|
||||
if (iter==columns.end())
|
||||
throw std::logic_error ("invalid column in text node test");
|
||||
|
||||
if (iter->second==-1)
|
||||
return true;
|
||||
|
||||
QModelIndex index = table.index (row, iter->second);
|
||||
|
||||
QVariant data = table.data (index);
|
||||
|
||||
if (data.type()!=QVariant::String)
|
||||
return false;
|
||||
|
||||
/// \todo make pattern syntax configurable
|
||||
QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive);
|
||||
|
||||
return regExp.exactMatch (data.toString());
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::TextNode::getReferencedColumns() const
|
||||
{
|
||||
return std::vector<int> (1, mColumnId);
|
||||
}
|
||||
|
||||
std::string CSMFilter::TextNode::toString (bool numericColumns) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "text (";
|
||||
|
||||
if (numericColumns)
|
||||
stream << mColumnId;
|
||||
else
|
||||
stream
|
||||
<< "\""
|
||||
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
|
||||
<< "\"";
|
||||
|
||||
stream << ", \"" << mText << "\")";
|
||||
|
||||
return stream.str();
|
||||
}
|
33
apps/opencs/model/filter/textnode.hpp
Normal file
33
apps/opencs/model/filter/textnode.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef CSM_FILTER_TEXTNODE_H
|
||||
#define CSM_FILTER_TEXTNODE_H
|
||||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class TextNode : public LeafNode
|
||||
{
|
||||
int mColumnId;
|
||||
std::string mText;
|
||||
|
||||
public:
|
||||
|
||||
TextNode (int columnId, const std::string& text);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
26
apps/opencs/model/filter/unarynode.cpp
Normal file
26
apps/opencs/model/filter/unarynode.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
#include "unarynode.hpp"
|
||||
|
||||
CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr<Node> child, const std::string& name)
|
||||
: mChild (child), mName (name)
|
||||
{}
|
||||
|
||||
const CSMFilter::Node& CSMFilter::UnaryNode::getChild() const
|
||||
{
|
||||
return *mChild;
|
||||
}
|
||||
|
||||
CSMFilter::Node& CSMFilter::UnaryNode::getChild()
|
||||
{
|
||||
return *mChild;
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::UnaryNode::getReferencedColumns() const
|
||||
{
|
||||
return mChild->getReferencedColumns();
|
||||
}
|
||||
|
||||
std::string CSMFilter::UnaryNode::toString (bool numericColumns) const
|
||||
{
|
||||
return mName + " " + mChild->toString (numericColumns);
|
||||
}
|
34
apps/opencs/model/filter/unarynode.hpp
Normal file
34
apps/opencs/model/filter/unarynode.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef CSM_FILTER_UNARYNODE_H
|
||||
#define CSM_FILTER_UNARYNODE_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class UnaryNode : public Node
|
||||
{
|
||||
boost::shared_ptr<Node> mChild;
|
||||
std::string mName;
|
||||
|
||||
public:
|
||||
|
||||
UnaryNode (boost::shared_ptr<Node> child, const std::string& name);
|
||||
|
||||
const Node& getChild() const;
|
||||
|
||||
Node& getChild();
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
71
apps/opencs/model/filter/valuenode.cpp
Normal file
71
apps/opencs/model/filter/valuenode.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
|
||||
#include "valuenode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../world/columns.hpp"
|
||||
#include "../world/idtable.hpp"
|
||||
|
||||
CSMFilter::ValueNode::ValueNode (int columnId,
|
||||
double lower, double upper, bool min, bool max)
|
||||
: mColumnId (columnId), mLower (lower), mUpper (upper), mMin (min), mMax (max)
|
||||
{}
|
||||
|
||||
bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
|
||||
|
||||
if (iter==columns.end())
|
||||
throw std::logic_error ("invalid column in test value test");
|
||||
|
||||
if (iter->second==-1)
|
||||
return true;
|
||||
|
||||
QModelIndex index = table.index (row, iter->second);
|
||||
|
||||
QVariant data = table.data (index);
|
||||
|
||||
if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int &&
|
||||
data.type()!=QVariant::UInt)
|
||||
return false;
|
||||
|
||||
double value = data.toDouble();
|
||||
|
||||
if (mLower==mUpper && mMin && mMax)
|
||||
return value==mLower;
|
||||
|
||||
return (mMin ? value>=mLower : value>mLower) && (mMax ? value<=mUpper : value<mUpper);
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::ValueNode::getReferencedColumns() const
|
||||
{
|
||||
return std::vector<int> (1, mColumnId);
|
||||
}
|
||||
|
||||
std::string CSMFilter::ValueNode::toString (bool numericColumns) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "value (";
|
||||
|
||||
if (numericColumns)
|
||||
stream << mColumnId;
|
||||
else
|
||||
stream
|
||||
<< "\""
|
||||
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
|
||||
<< "\"";
|
||||
|
||||
stream << ", \"";
|
||||
|
||||
if (mLower==mUpper && mMin && mMax)
|
||||
stream << mLower;
|
||||
else
|
||||
stream << (mMin ? "[" : "(") << mLower << ", " << mUpper << (mMax ? "]" : ")");
|
||||
|
||||
stream << ")";
|
||||
|
||||
return stream.str();
|
||||
}
|
37
apps/opencs/model/filter/valuenode.hpp
Normal file
37
apps/opencs/model/filter/valuenode.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef CSM_FILTER_VALUENODE_H
|
||||
#define CSM_FILTER_VALUENODE_H
|
||||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class ValueNode : public LeafNode
|
||||
{
|
||||
int mColumnId;
|
||||
std::string mText;
|
||||
double mLower;
|
||||
double mUpper;
|
||||
bool mMin;
|
||||
bool mMax;
|
||||
|
||||
public:
|
||||
|
||||
ValueNode (int columnId, double lower, double upper, bool min, bool max);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
82
apps/opencs/model/settings/settingcontainer.cpp
Normal file
82
apps/opencs/model/settings/settingcontainer.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#include "settingcontainer.hpp"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
CSMSettings::SettingContainer::SettingContainer(QObject *parent) :
|
||||
QObject(parent), mValue (0), mValues (0)
|
||||
{
|
||||
}
|
||||
|
||||
CSMSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) :
|
||||
QObject(parent), mValue (new QString (value)), mValues (0)
|
||||
{
|
||||
}
|
||||
|
||||
void CSMSettings::SettingContainer::insert (const QString &value)
|
||||
{
|
||||
if (mValue)
|
||||
{
|
||||
mValues = new QStringList;
|
||||
mValues->push_back (*mValue);
|
||||
mValues->push_back (value);
|
||||
|
||||
delete mValue;
|
||||
mValue = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mValue;
|
||||
mValue = new QString (value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CSMSettings::SettingContainer::update (const QString &value, int index)
|
||||
{
|
||||
if (isEmpty())
|
||||
mValue = new QString(value);
|
||||
|
||||
else if (mValue)
|
||||
*mValue = value;
|
||||
|
||||
else if (mValues)
|
||||
mValues->replace(index, value);
|
||||
}
|
||||
|
||||
QString CSMSettings::SettingContainer::getValue (int index) const
|
||||
{
|
||||
QString retVal("");
|
||||
|
||||
//if mValue is valid, it's a single-value property.
|
||||
//ignore the index and return the value
|
||||
if (mValue)
|
||||
retVal = *mValue;
|
||||
|
||||
//otherwise, if it's a multivalued property
|
||||
//return the appropriate value at the index
|
||||
else if (mValues)
|
||||
{
|
||||
if (index == -1)
|
||||
retVal = mValues->at(0);
|
||||
|
||||
else if (index < mValues->size())
|
||||
retVal = mValues->at(index);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int CSMSettings::SettingContainer::count () const
|
||||
{
|
||||
int retVal = 0;
|
||||
|
||||
if (!isEmpty())
|
||||
{
|
||||
if (mValues)
|
||||
retVal = mValues->size();
|
||||
else
|
||||
retVal = 1;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
47
apps/opencs/model/settings/settingcontainer.hpp
Normal file
47
apps/opencs/model/settings/settingcontainer.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#ifndef SETTINGCONTAINER_HPP
|
||||
#define SETTINGCONTAINER_HPP
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QStringList;
|
||||
|
||||
namespace CSMSettings
|
||||
{
|
||||
class SettingContainer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString *mValue;
|
||||
QStringList *mValues;
|
||||
|
||||
public:
|
||||
|
||||
explicit SettingContainer (QObject *parent = 0);
|
||||
explicit SettingContainer (const QString &value, QObject *parent = 0);
|
||||
|
||||
/// add a value to the container
|
||||
/// multiple values supported
|
||||
void insert (const QString &value);
|
||||
|
||||
/// update an existing value
|
||||
/// index specifies multiple values
|
||||
void update (const QString &value, int index = 0);
|
||||
|
||||
/// return value at specified index
|
||||
QString getValue (int index = -1) const;
|
||||
|
||||
/// retrieve list of all values
|
||||
inline QStringList *getValues() const { return mValues; }
|
||||
|
||||
/// return size of list
|
||||
int count() const;
|
||||
|
||||
/// test for empty container
|
||||
/// useful for default-constructed containers returned by QMap when invalid key is passed
|
||||
inline bool isEmpty() const { return (!mValue && !mValues); }
|
||||
|
||||
inline bool isMultiValue() const { return (mValues); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SETTINGCONTAINER_HPP
|
104
apps/opencs/model/settings/settingsitem.cpp
Normal file
104
apps/opencs/model/settings/settingsitem.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "settingsitem.hpp"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
bool CSMSettings::SettingsItem::updateItem (const QStringList *values)
|
||||
{
|
||||
QStringList::ConstIterator it = values->begin();
|
||||
|
||||
//if the item is not multivalued,
|
||||
//save the last value passed in the container
|
||||
if (!mIsMultiValue)
|
||||
{
|
||||
it = values->end();
|
||||
it--;
|
||||
}
|
||||
|
||||
bool isValid = true;
|
||||
QString value ("");
|
||||
|
||||
for (; it != values->end(); ++it)
|
||||
{
|
||||
value = *it;
|
||||
isValid = validate(value);
|
||||
|
||||
//skip only the invalid values
|
||||
if (!isValid)
|
||||
continue;
|
||||
|
||||
insert(value);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
bool CSMSettings::SettingsItem::updateItem (const QString &value)
|
||||
{
|
||||
//takes a value or a SettingsContainer and updates itself accordingly
|
||||
//after validating the data against it's own definition
|
||||
|
||||
QString newValue = value;
|
||||
|
||||
if (!validate (newValue))
|
||||
newValue = mDefaultValue;
|
||||
|
||||
bool success = (getValue() != newValue);
|
||||
|
||||
if (success)
|
||||
{
|
||||
if (mIsMultiValue)
|
||||
insert (newValue);
|
||||
else
|
||||
update (newValue);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CSMSettings::SettingsItem::updateItem(int valueListIndex)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (mValueList)
|
||||
{
|
||||
if (mValueList->size() > valueListIndex)
|
||||
success = updateItem (mValueList->at(valueListIndex));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CSMSettings::SettingsItem::validate (const QString &value)
|
||||
{
|
||||
//if there is no value list or value pair, there is no validation to do
|
||||
bool isValid = !(!mValueList->isEmpty() || mValuePair);
|
||||
|
||||
if (!isValid && !mValueList->isEmpty())
|
||||
{
|
||||
for (QStringList::Iterator it = mValueList->begin(); it != mValueList->end(); ++it)
|
||||
// foreach (QString listItem, *mValueList)
|
||||
{
|
||||
isValid = (value == *it);
|
||||
|
||||
if (isValid)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!isValid && mValuePair)
|
||||
{
|
||||
int numVal = value.toInt();
|
||||
|
||||
isValid = (numVal > mValuePair->left.toInt() && numVal < mValuePair->right.toInt());
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
void CSMSettings::SettingsItem::setDefaultValue (const QString &value)
|
||||
{
|
||||
mDefaultValue = value;
|
||||
update (value);
|
||||
}
|
||||
|
||||
QString CSMSettings::SettingsItem::getDefaultValue() const
|
||||
{
|
||||
return mDefaultValue;
|
||||
}
|
63
apps/opencs/model/settings/settingsitem.hpp
Normal file
63
apps/opencs/model/settings/settingsitem.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifndef SETTINGSITEM_HPP
|
||||
#define SETTINGSITEM_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include "support.hpp"
|
||||
#include "settingcontainer.hpp"
|
||||
|
||||
namespace CSMSettings
|
||||
{
|
||||
/// Represents a setting including metadata
|
||||
/// (valid values, ranges, defaults, and multivalue status
|
||||
class SettingsItem : public SettingContainer
|
||||
{
|
||||
QStringPair *mValuePair;
|
||||
QStringList *mValueList;
|
||||
bool mIsMultiValue;
|
||||
QString mDefaultValue;
|
||||
|
||||
public:
|
||||
explicit SettingsItem(QString name, bool isMultiValue,
|
||||
const QString& defaultValue, QObject *parent = 0)
|
||||
: SettingContainer(defaultValue, parent),
|
||||
mIsMultiValue (isMultiValue), mValueList (0),
|
||||
mValuePair (0), mDefaultValue (defaultValue)
|
||||
{
|
||||
QObject::setObjectName(name);
|
||||
}
|
||||
|
||||
/// updateItem overloads for updating setting value
|
||||
/// provided a list of values (multi-valued),
|
||||
/// a specific value
|
||||
/// or an index value corresponding to the mValueList
|
||||
bool updateItem (const QStringList *values);
|
||||
bool updateItem (const QString &value);
|
||||
bool updateItem (int valueListIndex);
|
||||
|
||||
/// retrieve list of valid values for setting
|
||||
inline QStringList *getValueList() { return mValueList; }
|
||||
|
||||
/// write list of valid values for setting
|
||||
inline void setValueList (QStringList *valueList) { mValueList = valueList; }
|
||||
|
||||
/// valuePair used for spin boxes (max / min)
|
||||
inline QStringPair *getValuePair() { return mValuePair; }
|
||||
|
||||
/// set value range (spinbox / integer use)
|
||||
inline void setValuePair (QStringPair valuePair) { mValuePair = new QStringPair(valuePair); }
|
||||
|
||||
inline bool isMultivalue () { return mIsMultiValue; }
|
||||
|
||||
void setDefaultValue (const QString &value);
|
||||
QString getDefaultValue () const;
|
||||
|
||||
private:
|
||||
|
||||
/// Verifies that the supplied value is one of the following:
|
||||
/// 1. Within the limits of the value pair (min / max)
|
||||
/// 2. One of the values indicated in the value list
|
||||
bool validate (const QString &value);
|
||||
};
|
||||
}
|
||||
#endif // SETTINGSITEM_HPP
|
||||
|
1
apps/opencs/model/settings/support.cpp
Normal file
1
apps/opencs/model/settings/support.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "support.hpp"
|
39
apps/opencs/model/settings/support.hpp
Normal file
39
apps/opencs/model/settings/support.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef MODEL_SUPPORT_HPP
|
||||
#define MODEL_SUPPORT_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class QLayout;
|
||||
class QWidget;
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace CSMSettings
|
||||
{
|
||||
class SettingContainer;
|
||||
|
||||
typedef QList<SettingContainer *> SettingList;
|
||||
typedef QMap<QString, SettingContainer *> SettingMap;
|
||||
typedef QMap<QString, SettingMap *> SectionMap;
|
||||
|
||||
struct QStringPair
|
||||
{
|
||||
QStringPair(): left (""), right ("")
|
||||
{}
|
||||
|
||||
QStringPair (const QString &leftValue, const QString &rightValue)
|
||||
: left (leftValue), right(rightValue)
|
||||
{}
|
||||
|
||||
QStringPair (const QStringPair &pair)
|
||||
: left (pair.left), right (pair.right)
|
||||
{}
|
||||
|
||||
QString left;
|
||||
QString right;
|
||||
|
||||
bool isEmpty() const
|
||||
{ return (left.isEmpty() && right.isEmpty()); }
|
||||
};
|
||||
}
|
||||
#endif // MODEL_SUPPORT_HPP
|
355
apps/opencs/model/settings/usersettings.cpp
Normal file
355
apps/opencs/model/settings/usersettings.cpp
Normal file
|
@ -0,0 +1,355 @@
|
|||
#include "usersettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
#include <QMessageBox>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include "settingcontainer.hpp"
|
||||
#include <boost/version.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::mUserSettingsInstance = 0;
|
||||
|
||||
CSMSettings::UserSettings::UserSettings()
|
||||
{
|
||||
assert(!mUserSettingsInstance);
|
||||
mUserSettingsInstance = this;
|
||||
|
||||
mReadWriteMessage = QObject::tr("<br><b>Could not open or create file for writing</b><br><br> \
|
||||
Please make sure you have the right permissions and try again.<br>");
|
||||
|
||||
mReadOnlyMessage = QObject::tr("<br><b>Could not open file for reading</b><br><br> \
|
||||
Please make sure you have the right permissions and try again.<br>");
|
||||
|
||||
buildEditorSettingDefaults();
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::buildEditorSettingDefaults()
|
||||
{
|
||||
SettingContainer *windowHeight = new SettingContainer("768", this);
|
||||
SettingContainer *windowWidth = new SettingContainer("1024", this);
|
||||
SettingContainer *rsDelegate = new SettingContainer("Icon and Text", this);
|
||||
SettingContainer *refIdTypeDelegate = new SettingContainer("Icon and Text", this);
|
||||
|
||||
windowHeight->setObjectName ("Height");
|
||||
windowWidth->setObjectName ("Width");
|
||||
rsDelegate->setObjectName ("Record Status Display");
|
||||
refIdTypeDelegate->setObjectName ("Referenceable ID Type Display");
|
||||
|
||||
SettingMap *displayFormatMap = new SettingMap;
|
||||
SettingMap *windowSizeMap = new SettingMap;
|
||||
|
||||
displayFormatMap->insert (rsDelegate->objectName(), rsDelegate );
|
||||
displayFormatMap->insert (refIdTypeDelegate->objectName(), refIdTypeDelegate);
|
||||
|
||||
windowSizeMap->insert (windowWidth->objectName(), windowWidth );
|
||||
windowSizeMap->insert (windowHeight->objectName(), windowHeight );
|
||||
|
||||
mEditorSettingDefaults.insert ("Display Format", displayFormatMap);
|
||||
mEditorSettingDefaults.insert ("Window Size", windowSizeMap);
|
||||
}
|
||||
|
||||
CSMSettings::UserSettings::~UserSettings()
|
||||
{
|
||||
mUserSettingsInstance = 0;
|
||||
}
|
||||
|
||||
QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) const
|
||||
{
|
||||
QIODevice::OpenMode openFlags = QIODevice::Text;
|
||||
|
||||
if (isReadOnly)
|
||||
openFlags = QIODevice::ReadOnly | openFlags;
|
||||
else
|
||||
openFlags = QIODevice::ReadWrite | QIODevice::Truncate | openFlags;
|
||||
|
||||
QFile *file = new QFile(filePath);
|
||||
QTextStream *stream = 0;
|
||||
|
||||
if (file->open(openFlags))
|
||||
{
|
||||
stream = new QTextStream(file);
|
||||
stream->setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
||||
}
|
||||
|
||||
bool CSMSettings::UserSettings::writeSettings(QMap<QString, CSMSettings::SettingList *> &settings)
|
||||
{
|
||||
QTextStream *stream = openFileStream(mUserFilePath);
|
||||
|
||||
bool success = (stream);
|
||||
|
||||
if (success)
|
||||
{
|
||||
QList<QString> keyList = settings.keys();
|
||||
|
||||
foreach (QString key, keyList)
|
||||
{
|
||||
SettingList *sectionSettings = settings[key];
|
||||
|
||||
*stream << "[" << key << "]" << '\n';
|
||||
|
||||
foreach (SettingContainer *item, *sectionSettings)
|
||||
*stream << item->objectName() << " = " << item->getValue() << '\n';
|
||||
}
|
||||
|
||||
stream->device()->close();
|
||||
delete stream;
|
||||
stream = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayFileErrorMessage(mReadWriteMessage, false);
|
||||
}
|
||||
|
||||
return (success);
|
||||
}
|
||||
|
||||
|
||||
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSectionMap() const
|
||||
{
|
||||
return mSectionSettings;
|
||||
}
|
||||
|
||||
const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString §ionName) const
|
||||
{
|
||||
return getValidSettings(sectionName);
|
||||
}
|
||||
|
||||
bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
|
||||
{
|
||||
if (filePath.isEmpty())
|
||||
return false;
|
||||
|
||||
SectionMap loadedSettings;
|
||||
|
||||
QTextStream *stream = openFileStream (filePath, true);
|
||||
|
||||
bool success = (stream);
|
||||
|
||||
if (success)
|
||||
{
|
||||
//looks for a square bracket, "'\\["
|
||||
//that has one or more "not nothing" in it, "([^]]+)"
|
||||
//and is closed with a square bracket, "\\]"
|
||||
|
||||
QRegExp sectionRe("^\\[([^]]+)\\]");
|
||||
|
||||
//Find any character(s) that is/are not equal sign(s), "[^=]+"
|
||||
//followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
|
||||
//and one or more periods, "(.+)"
|
||||
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
CSMSettings::SettingMap *settings = 0;
|
||||
QString section = "none";
|
||||
|
||||
while (!stream->atEnd())
|
||||
{
|
||||
QString line = stream->readLine().simplified();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
//if a section is found, push it onto a new QStringList
|
||||
//and push the QStringList onto
|
||||
if (sectionRe.exactMatch(line))
|
||||
{
|
||||
//add the previous section's settings to the member map
|
||||
if (settings)
|
||||
loadedSettings.insert(section, settings);
|
||||
|
||||
//save new section and create a new list
|
||||
section = sectionRe.cap(1);
|
||||
settings = new SettingMap;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyRe.indexIn(line) != -1)
|
||||
{
|
||||
SettingContainer *sc = new SettingContainer (keyRe.cap(2).simplified());
|
||||
sc->setObjectName(keyRe.cap(1).simplified());
|
||||
(*settings)[keyRe.cap(1).simplified()] = sc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
loadedSettings.insert(section, settings);
|
||||
|
||||
stream->device()->close();
|
||||
delete stream;
|
||||
stream = 0;
|
||||
}
|
||||
|
||||
mergeMap (loadedSettings);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap §ionSettings)
|
||||
{
|
||||
foreach (QString key, sectionSettings.uniqueKeys())
|
||||
{
|
||||
// insert entire section if it does not already exist in the loaded files
|
||||
if (mSectionSettings.find(key) == mSectionSettings.end())
|
||||
mSectionSettings.insert(key, sectionSettings.value(key));
|
||||
else
|
||||
{
|
||||
SettingMap *passedSettings = sectionSettings.value(key);
|
||||
SettingMap *settings = mSectionSettings.value(key);
|
||||
|
||||
foreach (QString key2, passedSettings->uniqueKeys())
|
||||
{
|
||||
//insert section settings individially if they do not already exist
|
||||
if (settings->find(key2) == settings->end())
|
||||
settings->insert(key2, passedSettings->value(key2));
|
||||
else
|
||||
{
|
||||
settings->value(key2)->update(passedSettings->value(key2)->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::loadSettings (const QString &fileName)
|
||||
{
|
||||
mSectionSettings.clear();
|
||||
|
||||
//global
|
||||
QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName;
|
||||
bool globalOk = loadFromFile(globalFilePath);
|
||||
|
||||
|
||||
//local
|
||||
QString localFilePath = QString::fromStdString(mCfgMgr.getLocalPath().string()) + fileName;
|
||||
bool localOk = loadFromFile(localFilePath);
|
||||
|
||||
//user
|
||||
mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName;
|
||||
loadFromFile(mUserFilePath);
|
||||
|
||||
if (!(localOk || globalOk))
|
||||
{
|
||||
QString message = QObject::tr("<br><b>Could not open user settings files for reading</b><br><br> \
|
||||
Global and local settings files could not be read.\
|
||||
You may have incorrect file permissions or the OpenCS installation may be corrupted.<br>");
|
||||
|
||||
message += QObject::tr("<br>Global filepath: ") + globalFilePath;
|
||||
message += QObject::tr("<br>Local filepath: ") + localFilePath;
|
||||
|
||||
displayFileErrorMessage ( message, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::updateSettings (const QString §ionName, const QString &settingName)
|
||||
{
|
||||
|
||||
SettingMap *settings = getValidSettings(sectionName);
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
if (settingName.isEmpty())
|
||||
{
|
||||
foreach (const SettingContainer *setting, *settings)
|
||||
emit signalUpdateEditorSetting (setting->objectName(), setting->getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settings->find(settingName) != settings->end())
|
||||
{
|
||||
const SettingContainer *setting = settings->value(settingName);
|
||||
emit signalUpdateEditorSetting (setting->objectName(), setting->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString CSMSettings::UserSettings::getSetting (const QString §ion, const QString &setting) const
|
||||
{
|
||||
SettingMap *settings = getValidSettings(section);
|
||||
|
||||
QString retVal = "";
|
||||
|
||||
if (settings->find(setting) != settings->end())
|
||||
retVal = settings->value(setting)->getValue();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
CSMSettings::UserSettings& CSMSettings::UserSettings::instance()
|
||||
{
|
||||
assert(mUserSettingsInstance);
|
||||
return *mUserSettingsInstance;
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message, bool isReadOnly)
|
||||
{
|
||||
// File cannot be opened or created
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(QObject::tr("OpenCS configuration file I/O error"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
|
||||
if (!isReadOnly)
|
||||
msgBox.setText (mReadWriteMessage + message);
|
||||
else
|
||||
msgBox.setText (message);
|
||||
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
CSMSettings::SettingMap *
|
||||
CSMSettings::UserSettings::getValidSettings (const QString §ionName) const
|
||||
{
|
||||
SettingMap *settings = 0;
|
||||
|
||||
//copy the default values for the entire section if it's not found
|
||||
if (mSectionSettings.find(sectionName) == mSectionSettings.end())
|
||||
{
|
||||
if (mEditorSettingDefaults.find(sectionName) != mEditorSettingDefaults.end())
|
||||
settings = mEditorSettingDefaults.value (sectionName);
|
||||
}
|
||||
//otherwise, iterate the section's settings, looking for missing values and replacing them with defaults.
|
||||
else
|
||||
{
|
||||
SettingMap *loadedSettings = mSectionSettings[sectionName];
|
||||
SettingMap *defaultSettings = mEditorSettingDefaults[sectionName];
|
||||
|
||||
foreach (QString key, defaultSettings->uniqueKeys())
|
||||
{
|
||||
//write the default value to the loaded settings
|
||||
if (loadedSettings->find((key))==loadedSettings->end())
|
||||
loadedSettings->insert(key, defaultSettings->value(key));
|
||||
}
|
||||
|
||||
settings = mSectionSettings.value (sectionName);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
94
apps/opencs/model/settings/usersettings.hpp
Normal file
94
apps/opencs/model/settings/usersettings.hpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#ifndef USERSETTINGS_HPP
|
||||
#define USERSETTINGS_HPP
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#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;
|
||||
|
||||
namespace CSMSettings {
|
||||
|
||||
struct UserSettings: public QObject
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
SectionMap mSectionSettings;
|
||||
SectionMap mEditorSettingDefaults;
|
||||
|
||||
static UserSettings *mUserSettingsInstance;
|
||||
QString mUserFilePath;
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
QString mReadOnlyMessage;
|
||||
QString mReadWriteMessage;
|
||||
|
||||
public:
|
||||
|
||||
/// Singleton implementation
|
||||
static UserSettings& instance();
|
||||
|
||||
UserSettings();
|
||||
~UserSettings();
|
||||
|
||||
UserSettings (UserSettings const &); //not implemented
|
||||
void operator= (UserSettings const &); //not implemented
|
||||
|
||||
/// Writes settings to the last loaded settings file
|
||||
bool writeSettings(QMap<QString, SettingList *> §ions);
|
||||
|
||||
/// Called from editor to trigger signal to update the specified setting.
|
||||
/// If no setting name is specified, all settings found in the specified section are updated.
|
||||
void updateSettings (const QString §ionName, const QString &settingName = "");
|
||||
|
||||
/// Retrieves the settings file at all three levels (global, local and user).
|
||||
|
||||
/// \todo Multi-valued settings are not fully implemented. Setting values
|
||||
/// \todo loaded in later files will always overwrite previously loaded values.
|
||||
void loadSettings (const QString &fileName);
|
||||
|
||||
/// Returns the entire map of settings across all sections
|
||||
const SectionMap &getSectionMap () const;
|
||||
|
||||
const SettingMap *getSettings (const QString §ionName) const;
|
||||
|
||||
/// Retrieves the value as a QString of the specified setting in the specified section
|
||||
QString getSetting(const QString §ion, const QString &setting) const;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/// Opens a QTextStream from the provided path as read-only or read-write.
|
||||
QTextStream *openFileStream (const QString &filePath, bool isReadOnly = false) const;
|
||||
|
||||
/// Parses a setting file specified in filePath from the provided text stream.
|
||||
bool loadFromFile (const QString &filePath = "");
|
||||
|
||||
/// merge the passed map into mSectionSettings
|
||||
void mergeMap (const SectionMap &);
|
||||
|
||||
void displayFileErrorMessage(const QString &message, bool isReadOnly);
|
||||
|
||||
void buildEditorSettingDefaults();
|
||||
|
||||
SettingMap *getValidSettings (const QString §ionName) const;
|
||||
|
||||
signals:
|
||||
|
||||
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
|
||||
};
|
||||
}
|
||||
#endif // USERSETTINGS_HPP
|
39
apps/opencs/model/tools/birthsigncheck.cpp
Normal file
39
apps/opencs/model/tools/birthsigncheck.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
#include "birthsigncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadbsgn.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns)
|
||||
: mBirthsigns (birthsigns)
|
||||
{}
|
||||
|
||||
int CSMTools::BirthsignCheckStage::setup()
|
||||
{
|
||||
return mBirthsigns.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::BirthsignCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::BirthSign& birthsign = mBirthsigns.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);
|
||||
|
||||
// test for empty name, description and texture
|
||||
if (birthsign.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty name");
|
||||
|
||||
if (birthsign.mDescription.empty())
|
||||
messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty description");
|
||||
|
||||
if (birthsign.mTexture.empty())
|
||||
messages.push_back (id.toString() + "|" + birthsign.mId + " is missing a texture");
|
||||
|
||||
/// \todo test if the texture exists
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
29
apps/opencs/model/tools/birthsigncheck.hpp
Normal file
29
apps/opencs/model/tools/birthsigncheck.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CSM_TOOLS_BIRTHSIGNCHECK_H
|
||||
#define CSM_TOOLS_BIRTHSIGNCHECK_H
|
||||
|
||||
#include <components/esm/loadbsgn.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that birthsign records are internally consistent
|
||||
class BirthsignCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
|
||||
|
||||
public:
|
||||
|
||||
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
72
apps/opencs/model/tools/classcheck.cpp
Normal file
72
apps/opencs/model/tools/classcheck.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
|
||||
#include "classcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes)
|
||||
: mClasses (classes)
|
||||
{}
|
||||
|
||||
int CSMTools::ClassCheckStage::setup()
|
||||
{
|
||||
return mClasses.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::ClassCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Class& class_= mClasses.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId);
|
||||
|
||||
// test for empty name and description
|
||||
if (class_.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + class_.mId + " has an empty name");
|
||||
|
||||
if (class_.mDescription.empty())
|
||||
messages.push_back (id.toString() + "|" + class_.mId + " has an empty description");
|
||||
|
||||
// test for invalid attributes
|
||||
for (int i=0; i<2; ++i)
|
||||
if (class_.mData.mAttribute[i]==-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << id.toString() << "|Attribute #" << i << " of " << class_.mId << " is not set";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << id.toString() << "|Class lists same attribute twice";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
// test for non-unique skill
|
||||
std::map<int, int> skills; // ID, number of occurrences
|
||||
|
||||
for (int i=0; i<5; ++i)
|
||||
for (int i2=0; i2<2; ++i2)
|
||||
++skills[class_.mData.mSkills[i][i2]];
|
||||
|
||||
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
|
||||
if (iter->second>1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream
|
||||
<< id.toString() << "|"
|
||||
<< ESM::Skill::indexToId (iter->first) << " is listed more than once";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
}
|
29
apps/opencs/model/tools/classcheck.hpp
Normal file
29
apps/opencs/model/tools/classcheck.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CSM_TOOLS_CLASSCHECK_H
|
||||
#define CSM_TOOLS_CLASSCHECK_H
|
||||
|
||||
#include <components/esm/loadclas.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that class records are internally consistent
|
||||
class ClassCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
|
||||
public:
|
||||
|
||||
ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
61
apps/opencs/model/tools/factioncheck.cpp
Normal file
61
apps/opencs/model/tools/factioncheck.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
#include "factioncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadfact.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions)
|
||||
: mFactions (factions)
|
||||
{}
|
||||
|
||||
int CSMTools::FactionCheckStage::setup()
|
||||
{
|
||||
return mFactions.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::FactionCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Faction& faction = mFactions.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId);
|
||||
|
||||
// test for empty name
|
||||
if (faction.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + faction.mId + " has an empty name");
|
||||
|
||||
// test for invalid attributes
|
||||
if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << id.toString() << "|Faction lists same attribute twice";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
// test for non-unique skill
|
||||
std::map<int, int> skills; // ID, number of occurrences
|
||||
|
||||
for (int i=0; i<6; ++i)
|
||||
if (faction.mData.mSkills[i]!=-1)
|
||||
++skills[faction.mData.mSkills[i]];
|
||||
|
||||
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
|
||||
if (iter->second>1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream
|
||||
<< id.toString() << "|"
|
||||
<< ESM::Skill::indexToId (iter->first) << " is listed more than once";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
29
apps/opencs/model/tools/factioncheck.hpp
Normal file
29
apps/opencs/model/tools/factioncheck.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CSM_TOOLS_FACTIONCHECK_H
|
||||
#define CSM_TOOLS_FACTIONCHECK_H
|
||||
|
||||
#include <components/esm/loadfact.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that faction records are internally consistent
|
||||
class FactionCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
|
||||
public:
|
||||
|
||||
FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
23
apps/opencs/model/tools/mandatoryid.cpp
Normal file
23
apps/opencs/model/tools/mandatoryid.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
#include "mandatoryid.hpp"
|
||||
|
||||
#include "../world/collectionbase.hpp"
|
||||
|
||||
#include "../world/record.hpp"
|
||||
|
||||
CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::CollectionBase& idCollection,
|
||||
const CSMWorld::UniversalId& collectionId, const std::vector<std::string>& ids)
|
||||
: mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids)
|
||||
{}
|
||||
|
||||
int CSMTools::MandatoryIdStage::setup()
|
||||
{
|
||||
return mIds.size();
|
||||
}
|
||||
|
||||
void CSMTools::MandatoryIdStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
if (mIdCollection.searchId (mIds.at (stage))==-1 ||
|
||||
mIdCollection.getRecord (mIds.at (stage)).isDeleted())
|
||||
messages.push_back (mCollectionId.toString() + "|Missing mandatory record: " + mIds.at (stage));
|
||||
}
|
38
apps/opencs/model/tools/mandatoryid.hpp
Normal file
38
apps/opencs/model/tools/mandatoryid.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef CSM_TOOLS_MANDATORYID_H
|
||||
#define CSM_TOOLS_MANDATORYID_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CollectionBase;
|
||||
}
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief Verify stage: make sure that records with specific IDs exist.
|
||||
class MandatoryIdStage : public Stage
|
||||
{
|
||||
const CSMWorld::CollectionBase& mIdCollection;
|
||||
CSMWorld::UniversalId mCollectionId;
|
||||
std::vector<std::string> mIds;
|
||||
|
||||
public:
|
||||
|
||||
MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId,
|
||||
const std::vector<std::string>& ids);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
84
apps/opencs/model/tools/operation.cpp
Normal file
84
apps/opencs/model/tools/operation.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
|
||||
#include "operation.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
void CSMTools::Operation::prepareStages()
|
||||
{
|
||||
mCurrentStage = mStages.begin();
|
||||
mCurrentStep = 0;
|
||||
mCurrentStepTotal = 0;
|
||||
mTotalSteps = 0;
|
||||
|
||||
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||
{
|
||||
iter->second = iter->first->setup();
|
||||
mTotalSteps += iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
CSMTools::Operation::Operation (int type) : mType (type) {}
|
||||
|
||||
CSMTools::Operation::~Operation()
|
||||
{
|
||||
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||
delete iter->first;
|
||||
}
|
||||
|
||||
void CSMTools::Operation::run()
|
||||
{
|
||||
prepareStages();
|
||||
|
||||
QTimer timer;
|
||||
|
||||
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify()));
|
||||
|
||||
timer.start (0);
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
void CSMTools::Operation::appendStage (Stage *stage)
|
||||
{
|
||||
mStages.push_back (std::make_pair (stage, 0));
|
||||
}
|
||||
|
||||
void CSMTools::Operation::abort()
|
||||
{
|
||||
exit();
|
||||
}
|
||||
|
||||
void CSMTools::Operation::verify()
|
||||
{
|
||||
std::vector<std::string> messages;
|
||||
|
||||
while (mCurrentStage!=mStages.end())
|
||||
{
|
||||
if (mCurrentStep>=mCurrentStage->second)
|
||||
{
|
||||
mCurrentStep = 0;
|
||||
++mCurrentStage;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentStage->first->perform (mCurrentStep++, messages);
|
||||
++mCurrentStepTotal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType);
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (messages.begin()); iter!=messages.end(); ++iter)
|
||||
emit reportMessage (iter->c_str(), mType);
|
||||
|
||||
if (mCurrentStage==mStages.end())
|
||||
exit();
|
||||
}
|
54
apps/opencs/model/tools/operation.hpp
Normal file
54
apps/opencs/model/tools/operation.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef CSM_TOOLS_OPERATION_H
|
||||
#define CSM_TOOLS_OPERATION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QThread>
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
class Stage;
|
||||
|
||||
class Operation : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
int mType;
|
||||
std::vector<std::pair<Stage *, int> > mStages; // stage, number of steps
|
||||
std::vector<std::pair<Stage *, int> >::iterator mCurrentStage;
|
||||
int mCurrentStep;
|
||||
int mCurrentStepTotal;
|
||||
int mTotalSteps;
|
||||
|
||||
void prepareStages();
|
||||
|
||||
public:
|
||||
|
||||
Operation (int type);
|
||||
|
||||
virtual ~Operation();
|
||||
|
||||
virtual void run();
|
||||
|
||||
void appendStage (Stage *stage);
|
||||
///< The ownership of \a stage is transferred to *this.
|
||||
///
|
||||
/// \attention Do no call this function while this Operation is running.
|
||||
|
||||
signals:
|
||||
|
||||
void progress (int current, int max, int type);
|
||||
|
||||
void reportMessage (const QString& message, int type);
|
||||
|
||||
public slots:
|
||||
|
||||
void abort();
|
||||
|
||||
private slots:
|
||||
|
||||
void verify();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
68
apps/opencs/model/tools/racecheck.cpp
Normal file
68
apps/opencs/model/tools/racecheck.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
|
||||
#include "racecheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/esm/loadrace.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
void CSMTools::RaceCheckStage::performPerRecord (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Race& race = mRaces.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId);
|
||||
|
||||
// test for empty name and description
|
||||
if (race.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + race.mId + " has an empty name");
|
||||
|
||||
if (race.mDescription.empty())
|
||||
messages.push_back (id.toString() + "|" + race.mId + " has an empty description");
|
||||
|
||||
// test for positive height
|
||||
if (race.mData.mHeight.mMale<=0)
|
||||
messages.push_back (id.toString() + "|male " + race.mId + " has non-positive height");
|
||||
|
||||
if (race.mData.mHeight.mFemale<=0)
|
||||
messages.push_back (id.toString() + "|female " + race.mId + " has non-positive height");
|
||||
|
||||
// test for non-negative weight
|
||||
if (race.mData.mWeight.mMale<0)
|
||||
messages.push_back (id.toString() + "|male " + race.mId + " has negative weight");
|
||||
|
||||
if (race.mData.mWeight.mFemale<0)
|
||||
messages.push_back (id.toString() + "|female " + race.mId + " has negative weight");
|
||||
|
||||
// remember playable flag
|
||||
if (race.mData.mFlags & 0x1)
|
||||
mPlayable = true;
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
||||
|
||||
void CSMTools::RaceCheckStage::performFinal (std::vector<std::string>& messages)
|
||||
{
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);
|
||||
|
||||
if (!mPlayable)
|
||||
messages.push_back (id.toString() + "|No playable race");
|
||||
}
|
||||
|
||||
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)
|
||||
: mRaces (races), mPlayable (false)
|
||||
{}
|
||||
|
||||
int CSMTools::RaceCheckStage::setup()
|
||||
{
|
||||
mPlayable = false;
|
||||
return mRaces.getSize()+1;
|
||||
}
|
||||
|
||||
void CSMTools::RaceCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
if (stage==mRaces.getSize())
|
||||
performFinal (messages);
|
||||
else
|
||||
performPerRecord (stage, messages);
|
||||
}
|
34
apps/opencs/model/tools/racecheck.hpp
Normal file
34
apps/opencs/model/tools/racecheck.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef CSM_TOOLS_RACECHECK_H
|
||||
#define CSM_TOOLS_RACECHECK_H
|
||||
|
||||
#include <components/esm/loadrace.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that race records are internally consistent
|
||||
class RaceCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||
bool mPlayable;
|
||||
|
||||
void performPerRecord (int stage, std::vector<std::string>& messages);
|
||||
|
||||
void performFinal (std::vector<std::string>& messages);
|
||||
|
||||
public:
|
||||
|
||||
RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue