1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-30 16:45:33 +00:00

Merge pull request #2 from zinnschlag/master

Updating fork
This commit is contained in:
sirherrbatka 2013-09-23 10:47:06 -07:00
commit 0aebbcfdf5
1055 changed files with 84404 additions and 33789 deletions

5
.gitignore vendored
View file

@ -17,3 +17,8 @@ data
CMakeLists.txt.user CMakeLists.txt.user
*.swp *.swp
*.swo *.swo
*.kate-swp
.cproject
.project
.settings/
.directory

39
.travis.yml Normal file
View 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

View file

@ -4,6 +4,10 @@ if (APPLE)
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") 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) endif (APPLE)
# Macros # Macros
@ -15,7 +19,7 @@ include (OpenMWMacros)
# Version # Version
set (OPENMW_VERSION_MAJOR 0) set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 22) set (OPENMW_VERSION_MINOR 26)
set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
@ -27,6 +31,7 @@ configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_
option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE) 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(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(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 # Apps and tools
option(BUILD_BSATOOL "build BSA extractor" OFF) option(BUILD_BSATOOL "build BSA extractor" OFF)
@ -48,18 +53,16 @@ option(OPENMW_OSX_DEPLOYMENT OFF)
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems") find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
# Location of morrowind data files # Location of morrowind data files
if(DPKG_PROGRAM) 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_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") set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
else() else()
if (APPLE) set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
set(MORROWIND_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") endif(APPLE)
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)
endif(DPKG_PROGRAM)
if (WIN32) 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) 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)
@ -75,8 +78,12 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
set(OENGINE_OGRE set(OENGINE_OGRE
${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/renderer.cpp
${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/fader.cpp
${LIBDIR}/openengine/ogre/lights.cpp
${LIBDIR}/openengine/ogre/particles.cpp
${LIBDIR}/openengine/ogre/selectionbuffer.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp
${LIBDIR}/openengine/ogre/imagerotate.cpp
) )
set(OENGINE_GUI set(OENGINE_GUI
${LIBDIR}/openengine/gui/manager.cpp ${LIBDIR}/openengine/gui/manager.cpp
) )
@ -154,6 +161,20 @@ if (NOT FFMPEG_FOUND)
message(WARNING "--------------------") message(WARNING "--------------------")
endif (NOT FFMPEG_FOUND) endif (NOT FFMPEG_FOUND)
# TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
if(USE_SYSTEM_TINYXML)
find_library(TINYXML_LIBRARIES tinyxml)
find_path(TINYXML_INCLUDE_DIR tinyxml.h)
message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}")
add_definitions (-DTIXML_USE_STL)
if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR)
include_directories(${TINYXML_INCLUDE_DIR})
message(STATUS "Using system TinyXML library.")
else()
message(FATAL_ERROR "Detection of system TinyXML incomplete.")
endif()
endif()
# Platform specific # Platform specific
if (WIN32) if (WIN32)
@ -180,15 +201,14 @@ if (UNIX AND NOT APPLE)
find_package (Threads) find_package (Threads)
endif() endif()
# find boost without components so we can use Boost_VERSION include (CheckIncludeFileCXX)
find_package(Boost REQUIRED) 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)
if (Boost_VERSION LESS 104900) set(BOOST_COMPONENTS system filesystem program_options thread date_time wave)
set(SHINY_USE_WAVE_SYSTEM_INSTALL "TRUE")
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} wave)
endif()
IF(BOOST_STATIC) IF(BOOST_STATIC)
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
@ -197,7 +217,7 @@ endif()
find_package(OGRE REQUIRED) find_package(OGRE REQUIRED)
find_package(MyGUI REQUIRED) find_package(MyGUI REQUIRED)
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
find_package(OIS REQUIRED) find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED) find_package(OpenAL REQUIRED)
find_package(Bullet REQUIRED) find_package(Bullet REQUIRED)
IF(OGRE_STATIC) IF(OGRE_STATIC)
@ -210,8 +230,8 @@ ENDIF(WIN32)
ENDIF(OGRE_STATIC) ENDIF(OGRE_STATIC)
include_directories("." include_directories("."
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS} ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
${OGRE_Terrain_INCLUDE_DIR} ${SDL2_INCLUDE_DIR}
${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR} ${Boost_INCLUDE_DIR}
${PLATFORM_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR}
${MYGUI_INCLUDE_DIRS} ${MYGUI_INCLUDE_DIRS}
${MYGUI_PLATFORM_INCLUDE_DIRS} ${MYGUI_PLATFORM_INCLUDE_DIRS}
@ -220,7 +240,7 @@ include_directories("."
${LIBDIR} ${LIBDIR}
) )
link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MYGUI_LIB_DIR}) link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MYGUI_LIB_DIR})
if (APPLE) if (APPLE)
# List used Ogre plugins # List used Ogre plugins
@ -293,9 +313,12 @@ configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg") "${OpenMW_BINARY_DIR}/openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install") "${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) if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
@ -306,71 +329,122 @@ endif()
# Compiler settings # Compiler settings
if (CMAKE_COMPILER_IS_GNUCC) if (CMAKE_COMPILER_IS_GNUCC)
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long) 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! # Silence warnings in OGRE headers. Remove once OGRE got fixed!
add_definitions (-Wno-ignored-qualifiers) SET(CMAKE_CXX_FLAGS "-Wno-ignored-qualifiers ${CMAKE_CXX_FLAGS}")
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION) OUTPUT_VARIABLE GCC_VERSION)
if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
add_definitions (-Wno-unused-but-set-parameter) 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("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
endif (CMAKE_COMPILER_IS_GNUCC) endif (CMAKE_COMPILER_IS_GNUCC)
if(DPKG_PROGRAM) IF(NOT WIN32 AND NOT APPLE)
SET(CMAKE_INSTALL_PREFIX "/usr") ## 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")
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") # Install binaries
exec_program("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}") IF(BUILD_LAUNCHER)
exec_program("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
exec_program("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL) ENDIF(BUILD_LAUNCHER)
set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>") IF(BUILD_BSATOOL)
else() INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
set(VERSION_STRING "${OPENMW_VERSION}") ENDIF(BUILD_BSATOOL)
set(PACKAGE_MAINTAINER "unknown") IF(BUILD_ESMTOOL)
endif() 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 icon and desktop file # Install licenses
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "share/applications/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") 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 global configuration files # Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "../etc/openmw/" RENAME "openmw.cfg" 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 resources # Install global configuration files
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "share/games/openmw/" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(DIRECTORY DESTINATION "share/games/openmw/data/" COMPONENT "Resources") 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)
SET(CPACK_GENERATOR "DEB") # Install resources
SET(CPACK_PACKAGE_NAME "openmw") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org") INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}") IF (DPKG_PROGRAM)
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind ## 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. 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.") Data files from the original game is required to run it.")
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") 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), libois-1.3.0 (>= 1.3.0), 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_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") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE) STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
execute_process( EXECUTE_PROCESS(
COMMAND ${DPKG_PROGRAM} --print-architecture COMMAND ${DPKG_PROGRAM} --print-architecture
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_STRIP_TRAILING_WHITESPACE
) )
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
include(CPack) INCLUDE(CPack)
endif(DPKG_PROGRAM) ENDIF(DPKG_PROGRAM)
ENDIF(NOT WIN32 AND NOT APPLE)
if(WIN32) if(WIN32)
FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll") FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll")
@ -443,6 +517,7 @@ endif(WIN32)
# Extern # Extern
add_subdirectory (extern/shiny) add_subdirectory (extern/shiny)
add_subdirectory (extern/oics) add_subdirectory (extern/oics)
add_subdirectory (extern/sdl4ogre)
# Components # Components
add_subdirectory (components) add_subdirectory (components)
@ -459,6 +534,13 @@ if (BUILD_ESMTOOL)
endif() endif()
if (BUILD_LAUNCHER) 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 ) add_subdirectory( apps/launcher )
endif() endif()
@ -578,6 +660,7 @@ if (APPLE)
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" 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}/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}/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_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
@ -651,45 +734,3 @@ if (APPLE)
include(CPack) include(CPack)
endif (APPLE) endif (APPLE)
if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
## Non Debian based Linux building
# paths
set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/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}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
set(DOCDIR "${DATAROOTDIR}/doc/openmw" CACHE PATH "Sets the doc directory to a non-default location.")
set(MANDIR "${DATAROOTDIR}/man" CACHE PATH "Where to install manpages")
set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc/openmw" CACHE PATH "Set config dir")
set(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon 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 icon and .desktop
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications")
# Install global configuration files
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" )
#INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${SYSCONFDIR}" )
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" )
INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" )
# Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" )
endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)

View file

@ -17,11 +17,6 @@ target_link_libraries(esmtool
components components
) )
#if (APPLE)
# find_library(CARBON_FRAMEWORK Carbon)
# target_link_libraries(openmw ${CARBON_FRAMEWORK})
#endif (APPLE)
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(esmtool gcov) target_link_libraries(esmtool gcov)

View file

@ -23,8 +23,7 @@ struct ESMData
std::string author; std::string author;
std::string description; std::string description;
int version; int version;
int type; std::vector<ESM::Header::MasterData> masters;
ESM::ESMReader::MasterList masters;
std::deque<EsmTool::RecordBase *> mRecords; std::deque<EsmTool::RecordBase *> mRecords;
std::map<ESM::Cell *, std::deque<ESM::CellRef> > mCellRefs; std::map<ESM::Cell *, std::deque<ESM::CellRef> > mCellRefs;
@ -52,6 +51,7 @@ struct Arguments
unsigned int raw_given; unsigned int raw_given;
unsigned int quiet_given; unsigned int quiet_given;
unsigned int loadcells_given; unsigned int loadcells_given;
bool plain_given;
std::string mode; std::string mode;
std::string encoding; std::string encoding;
@ -78,6 +78,9 @@ bool parseOptions (int argc, char** argv, Arguments &info)
("type,t", bpo::value< std::vector<std::string> >(), ("type,t", bpo::value< std::vector<std::string> >(),
"Show only records of this type (four character record code). May " "Show only records of this type (four character record code). May "
"be specified multiple times. Only affects dump mode.") "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.") ("quiet,q", "Supress all record information. Useful for speed tests.")
("loadcells,C", "Browse through contents of all cells.") ("loadcells,C", "Browse through contents of all cells.")
@ -105,11 +108,26 @@ bool parseOptions (int argc, char** argv, Arguments &info)
// there might be a better way to do this // there might be a better way to do this
bpo::options_description all; bpo::options_description all;
all.add(desc).add(hidden); all.add(desc).add(hidden);
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
.options(all).positional(p).run();
bpo::variables_map variables; bpo::variables_map variables;
bpo::store(valid_opts, 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); bpo::notify(variables);
if (variables.count ("help")) if (variables.count ("help"))
@ -162,6 +180,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
info.raw_given = variables.count ("raw"); info.raw_given = variables.count ("raw");
info.quiet_given = variables.count ("quiet"); info.quiet_given = variables.count ("quiet");
info.loadcells_given = variables.count ("loadcells"); info.loadcells_given = variables.count ("loadcells");
info.plain_given = (variables.count("plain") > 0);
// Font encoding settings // Font encoding settings
info.encoding = variables["encoding"].as<std::string>(); info.encoding = variables["encoding"].as<std::string>();
@ -228,7 +247,10 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
std::cout << " Refnum: " << ref.mRefnum << std::endl; std::cout << " Refnum: " << ref.mRefnum << std::endl;
std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " ID: '" << ref.mRefID << "'\n";
std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n";
std::cout << " INTV: " << ref.mIntv << " NAM9: " << ref.mIntv << std::endl; 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;
} }
} }
@ -284,16 +306,13 @@ int load(Arguments& info)
info.data.author = esm.getAuthor(); info.data.author = esm.getAuthor();
info.data.description = esm.getDesc(); info.data.description = esm.getDesc();
info.data.masters = esm.getMasters(); info.data.masters = esm.getMasters();
info.data.version = esm.getVer();
info.data.type = esm.getType();
if (!quiet) if (!quiet)
{ {
std::cout << "Author: " << esm.getAuthor() << std::endl std::cout << "Author: " << esm.getAuthor() << std::endl
<< "Description: " << esm.getDesc() << std::endl << "Description: " << esm.getDesc() << std::endl
<< "File format version: " << esm.getFVer() << std::endl << "File format version: " << esm.getFVer() << std::endl;
<< "Special flag: " << esm.getSpecial() << std::endl; std::vector<ESM::Header::MasterData> m = esm.getMasters();
ESM::ESMReader::MasterList m = esm.getMasters();
if (!m.empty()) if (!m.empty())
{ {
std::cout << "Masters:" << std::endl; std::cout << "Masters:" << std::endl;
@ -344,6 +363,7 @@ int load(Arguments& info)
} }
record->setId(id); record->setId(id);
record->setFlags((int) flags); record->setFlags((int) flags);
record->setPrintPlain(info.plain_given);
record->load(esm); record->load(esm);
if (!quiet && interested) record->print(); if (!quiet && interested) record->print();
@ -430,9 +450,9 @@ int clone(Arguments& info)
esm.setAuthor(info.data.author); esm.setAuthor(info.data.author);
esm.setDescription(info.data.description); esm.setDescription(info.data.description);
esm.setVersion(info.data.version); esm.setVersion(info.data.version);
esm.setType(info.data.type); esm.setRecordCount (recordCount);
for (ESM::ESMReader::MasterList::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) for (std::vector<ESM::Header::MasterData>::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it)
esm.addMaster(it->name, it->size); esm.addMaster(it->name, it->size);
std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary); std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary);

View file

@ -609,8 +609,7 @@ std::string ruleFunction(int idx)
"Alarm", "Alarm",
"Flee", "Flee",
"Should Attack", "Should Attack",
//Unkown but causes NPCs to growl and roar. "Werewolf"
"UNKNOWN 72"
}; };
if (idx >= 0 && idx <= 72) if (idx >= 0 && idx <= 72)
return ruleFunctions[idx]; return ruleFunctions[idx];
@ -627,10 +626,10 @@ std::string bodyPartFlags(int flags)
std::string properties = ""; std::string properties = "";
if (flags == 0) properties += "[None] "; if (flags == 0) properties += "[None] ";
if (flags & ESM::BodyPart::BPF_Female) properties += "Female "; if (flags & ESM::BodyPart::BPF_Female) properties += "Female ";
if (flags & ESM::BodyPart::BPF_Playable) properties += "Playable "; if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable ";
int unused = (0xFFFFFFFF ^ int unused = (0xFFFFFFFF ^
(ESM::BodyPart::BPF_Female| (ESM::BodyPart::BPF_Female|
ESM::BodyPart::BPF_Playable)); ESM::BodyPart::BPF_NotPlayable));
if (flags & unused) properties += "Invalid "; if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags); properties += str(boost::format("(0x%08X)") % flags);
return properties; return properties;

View file

@ -106,10 +106,10 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
{ {
case '0': oper_str = "=="; break; case '0': oper_str = "=="; break;
case '1': oper_str = "!="; break; case '1': oper_str = "!="; break;
case '2': oper_str = "< "; break; case '2': oper_str = "> "; break;
case '3': oper_str = "<="; break; case '3': oper_str = ">="; break;
case '4': oper_str = "> "; break; case '4': oper_str = "< "; break;
case '5': oper_str = ">="; break; case '5': oper_str = "<="; break;
} }
std::ostringstream stream; std::ostringstream stream;
@ -275,7 +275,7 @@ RecordBase::create(ESM::NAME type)
} }
case ESM::REC_LOCK: case ESM::REC_LOCK:
{ {
record = new EsmTool::Record<ESM::Tool>; record = new EsmTool::Record<ESM::Lockpick>;
break; break;
} }
case ESM::REC_LTEX: case ESM::REC_LTEX:
@ -439,7 +439,7 @@ void Record<ESM::Apparatus>::print()
template<> template<>
void Record<ESM::BodyPart>::print() void Record<ESM::BodyPart>::print()
{ {
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Race: " << mData.mRace << std::endl;
std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Type: " << meshTypeLabel(mData.mData.mType) std::cout << " Type: " << meshTypeLabel(mData.mData.mType)
<< " (" << (int)mData.mData.mType << ")" << std::endl; << " (" << (int)mData.mData.mType << ")" << std::endl;
@ -464,12 +464,17 @@ void Record<ESM::Book>::print()
std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl; std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl;
std::cout << " SkillID: " << mData.mData.mSkillID << std::endl; std::cout << " SkillID: " << mData.mData.mSkillID << std::endl;
std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl;
std::cout << " Text: [skipped]" << std::endl; if (mPrintPlain)
// Skip until multi-line fields is controllable by a command line option. {
// Mildly problematic because there are no parameter to print() currently. std::cout << " Text:" << std::endl;
// std::cout << "-------------------------------------------" << std::endl; std::cout << "START--------------------------------------" << std::endl;
// std::cout << mData.mText << std::endl; std::cout << mData.mText << std::endl;
// std::cout << "-------------------------------------------" << std::endl; std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Text: [skipped]" << std::endl;
}
} }
template<> template<>
@ -679,14 +684,14 @@ void Record<ESM::Faction>::print()
std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl; std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl;
if (mData.mData.mUnknown != -1) if (mData.mData.mUnknown != -1)
std::cout << " Unknown: " << mData.mData.mUnknown << std::endl; std::cout << " Unknown: " << mData.mData.mUnknown << std::endl;
std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute1) std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0])
<< " (" << mData.mData.mAttribute1 << ")" << std::endl; << " (" << mData.mData.mAttribute[0] << ")" << std::endl;
std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute2) std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1])
<< " (" << mData.mData.mAttribute2 << ")" << std::endl; << " (" << mData.mData.mAttribute[1] << ")" << std::endl;
for (int i = 0; i != 6; i++) for (int i = 0; i != 6; i++)
if (mData.mData.mSkillID[i] != -1) if (mData.mData.mSkills[i] != -1)
std::cout << " Skill: " << skillLabel(mData.mData.mSkillID[i]) std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i])
<< " (" << mData.mData.mSkillID[i] << ")" << std::endl; << " (" << mData.mData.mSkills[i] << ")" << std::endl;
for (int i = 0; i != 10; i++) for (int i = 0; i != 10; i++)
if (mData.mRanks[i] != "") if (mData.mRanks[i] != "")
{ {
@ -753,15 +758,6 @@ void Record<ESM::DialInfo>::print()
if (mData.mSound != "") if (mData.mSound != "")
std::cout << " Sound File: " << mData.mSound << std::endl; std::cout << " Sound File: " << mData.mSound << std::endl;
if (mData.mResultScript != "")
{
std::cout << " Result Script: [skipped]" << std::endl;
// Skip until multi-line fields is controllable by a command line option.
// Mildly problematic because there are no parameter to print() currently.
// std::cout << "-------------------------------------------" << std::endl;
// std::cout << mData.mResultScript << std::endl;
// std::cout << "-------------------------------------------" << std::endl;
}
std::cout << " Quest Status: " << questStatusLabel(mData.mQuestStatus) std::cout << " Quest Status: " << questStatusLabel(mData.mQuestStatus)
<< " (" << mData.mQuestStatus << ")" << std::endl; << " (" << mData.mQuestStatus << ")" << std::endl;
@ -771,6 +767,21 @@ void Record<ESM::DialInfo>::print()
std::vector<ESM::DialInfo::SelectStruct>::iterator sit; std::vector<ESM::DialInfo::SelectStruct>::iterator sit;
for (sit = mData.mSelects.begin(); sit != mData.mSelects.end(); sit++) for (sit = mData.mSelects.begin(); sit != mData.mSelects.end(); sit++)
std::cout << " Select Rule: " << ruleString(*sit) << std::endl; std::cout << " Select Rule: " << ruleString(*sit) << std::endl;
if (mData.mResultScript != "")
{
if (mPrintPlain)
{
std::cout << " Result Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mResultScript << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Result Script: [skipped]" << std::endl;
}
}
} }
template<> template<>
@ -864,14 +875,13 @@ void Record<ESM::Light>::print()
} }
template<> template<>
void Record<ESM::Tool>::print() void Record<ESM::Lockpick>::print()
{ {
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl;
if (mData.mScript != "") if (mData.mScript != "")
std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Type: " << mData.mType << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl;
@ -886,8 +896,6 @@ void Record<ESM::Probe>::print()
std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl;
if (mData.mScript != "") if (mData.mScript != "")
std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Script: " << mData.mScript << std::endl;
// BUG? No Type Label?
std::cout << " Type: " << mData.mType << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl;
@ -902,7 +910,6 @@ void Record<ESM::Repair>::print()
std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl;
if (mData.mScript != "") if (mData.mScript != "")
std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Type: " << mData.mType << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl;
@ -1103,53 +1110,29 @@ void Record<ESM::Pathgrid>::print()
template<> template<>
void Record<ESM::Race>::print() void Record<ESM::Race>::print()
{ {
static const char *sAttributeNames[8] =
{
"Strength", "Intelligence", "Willpower", "Agility",
"Speed", "Endurance", "Personality", "Luck"
};
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Description: " << mData.mDescription << std::endl;
std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl; std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl;
std::cout << " Male:" << std::endl; for (int i=0; i<2; ++i)
std::cout << " Strength: " {
<< mData.mData.mStrength.mMale << std::endl; bool male = i==0;
std::cout << " Intelligence: "
<< mData.mData.mIntelligence.mMale << std::endl;
std::cout << " Willpower: "
<< mData.mData.mWillpower.mMale << std::endl;
std::cout << " Agility: "
<< mData.mData.mAgility.mMale << std::endl;
std::cout << " Speed: "
<< mData.mData.mSpeed.mMale << std::endl;
std::cout << " Endurance: "
<< mData.mData.mEndurance.mMale << std::endl;
std::cout << " Personality: "
<< mData.mData.mPersonality.mMale << std::endl;
std::cout << " Luck: "
<< mData.mData.mLuck.mMale << std::endl;
std::cout << " Height: "
<< mData.mData.mHeight.mMale << std::endl;
std::cout << " Weight: "
<< mData.mData.mWeight.mMale << std::endl;
std::cout << " Female:" << std::endl; std::cout << (male ? " Male:" : " Female:") << std::endl;
std::cout << " Strength: "
<< mData.mData.mStrength.mFemale << std::endl; for (int i=0; i<8; ++i)
std::cout << " Intelligence: " std::cout << " " << sAttributeNames[i] << ": "
<< mData.mData.mIntelligence.mFemale << std::endl; << mData.mData.mAttributeValues[i].getValue (male) << std::endl;
std::cout << " Willpower: "
<< mData.mData.mWillpower.mFemale << std::endl; std::cout << " Height: " << mData.mData.mHeight.getValue (male) << std::endl;
std::cout << " Agility: " std::cout << " Weight: " << mData.mData.mWeight.getValue (male) << std::endl;
<< mData.mData.mAgility.mFemale << std::endl; }
std::cout << " Speed: "
<< mData.mData.mSpeed.mFemale << std::endl;
std::cout << " Endurance: "
<< mData.mData.mEndurance.mFemale << std::endl;
std::cout << " Personality: "
<< mData.mData.mPersonality.mFemale << std::endl;
std::cout << " Luck: "
<< mData.mData.mLuck.mFemale << std::endl;
std::cout << " Height: "
<< mData.mData.mHeight.mFemale << std::endl;
std::cout << " Weight: "
<< mData.mData.mWeight.mFemale << std::endl;
for (int i = 0; i != 7; i++) for (int i = 0; i != 7; i++)
// Not all races have 7 skills. // Not all races have 7 skills.
@ -1199,21 +1182,28 @@ void Record<ESM::Script>::print()
std::cout << " Script Data Size: " << mData.mData.mScriptDataSize << std::endl; std::cout << " Script Data Size: " << mData.mData.mScriptDataSize << std::endl;
std::cout << " Table Size: " << mData.mData.mStringTableSize << std::endl; std::cout << " Table Size: " << mData.mData.mStringTableSize << std::endl;
std::cout << " Script: [skipped]" << std::endl;
// Skip until multi-line fields is controllable by a command line option.
// Mildly problematic because there are no parameter to print() currently.
// std::cout << "-------------------------------------------" << std::endl;
// std::cout << s->scriptText << std::endl;
// std::cout << "-------------------------------------------" << std::endl;
std::vector<std::string>::iterator vit; std::vector<std::string>::iterator vit;
for (vit = mData.mVarNames.begin(); vit != mData.mVarNames.end(); vit++) for (vit = mData.mVarNames.begin(); vit != mData.mVarNames.end(); vit++)
std::cout << " Variable: " << *vit << std::endl; std::cout << " Variable: " << *vit << std::endl;
std::cout << " ByteCode: "; std::cout << " ByteCode: ";
std::vector<char>::iterator cit; std::vector<unsigned char>::iterator cit;
for (cit = mData.mScriptData.begin(); cit != mData.mScriptData.end(); cit++) for (cit = mData.mScriptData.begin(); cit != mData.mScriptData.end(); cit++)
std::cout << boost::format("%02X") % (int)(*cit); std::cout << boost::format("%02X") % (int)(*cit);
std::cout << std::endl; std::cout << std::endl;
if (mPrintPlain)
{
std::cout << " Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mScriptText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Script: [skipped]" << std::endl;
}
} }
template<> template<>

View file

@ -21,9 +21,10 @@ namespace EsmTool
std::string mId; std::string mId;
int mFlags; int mFlags;
ESM::NAME mType; ESM::NAME mType;
bool mPrintPlain;
public: public:
RecordBase () {} RecordBase () { mPrintPlain = false; }
virtual ~RecordBase() {} virtual ~RecordBase() {}
const std::string &getId() const { const std::string &getId() const {
@ -46,6 +47,14 @@ namespace EsmTool
return mType; return mType;
} }
bool getPrintPlain() const {
return mPrintPlain;
}
void setPrintPlain(bool plain) {
mPrintPlain = plain;
}
virtual void load(ESM::ESMReader &esm) = 0; virtual void load(ESM::ESMReader &esm) = 0;
virtual void save(ESM::ESMWriter &esm) = 0; virtual void save(ESM::ESMWriter &esm) = 0;
virtual void print() = 0; virtual void print() = 0;
@ -104,7 +113,7 @@ namespace EsmTool
template<> void Record<ESM::CreatureLevList>::print(); template<> void Record<ESM::CreatureLevList>::print();
template<> void Record<ESM::ItemLevList>::print(); template<> void Record<ESM::ItemLevList>::print();
template<> void Record<ESM::Light>::print(); template<> void Record<ESM::Light>::print();
template<> void Record<ESM::Tool>::print(); template<> void Record<ESM::Lockpick>::print();
template<> void Record<ESM::Probe>::print(); template<> void Record<ESM::Probe>::print();
template<> void Record<ESM::Repair>::print(); template<> void Record<ESM::Repair>::print();
template<> void Record<ESM::LandTexture>::print(); template<> void Record<ESM::LandTexture>::print();

View file

@ -4,6 +4,7 @@ set(LAUNCHER
main.cpp main.cpp
maindialog.cpp maindialog.cpp
playpage.cpp playpage.cpp
textslotmsgbox.cpp
settings/gamesettings.cpp settings/gamesettings.cpp
settings/graphicssettings.cpp settings/graphicssettings.cpp
@ -14,12 +15,16 @@ set(LAUNCHER
${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc
) )
if(NOT WIN32)
LIST(APPEND LAUNCHER unshieldthread.cpp)
endif(NOT WIN32)
set(LAUNCHER_HEADER set(LAUNCHER_HEADER
datafilespage.hpp datafilespage.hpp
graphicspage.hpp graphicspage.hpp
maindialog.hpp maindialog.hpp
playpage.hpp playpage.hpp
textslotmsgbox.hpp
settings/gamesettings.hpp settings/gamesettings.hpp
settings/graphicssettings.hpp settings/graphicssettings.hpp
@ -30,6 +35,10 @@ set(LAUNCHER_HEADER
utils/textinputdialog.hpp utils/textinputdialog.hpp
) )
if(NOT WIN32)
LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp)
endif(NOT WIN32)
# Headers that must be pre-processed # Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC set(LAUNCHER_HEADER_MOC
@ -37,11 +46,17 @@ set(LAUNCHER_HEADER_MOC
graphicspage.hpp graphicspage.hpp
maindialog.hpp maindialog.hpp
playpage.hpp playpage.hpp
textslotmsgbox.hpp
utils/checkablemessagebox.hpp utils/checkablemessagebox.hpp
utils/textinputdialog.hpp utils/textinputdialog.hpp
) )
if(NOT WIN32)
LIST(APPEND LAUNCHER_HEADER_MOC unshieldthread.hpp)
endif(NOT WIN32)
set(LAUNCHER_UI set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
@ -64,8 +79,12 @@ QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
include(${QT_USE_FILE}) include(${QT_USE_FILE})
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(NOT WIN32)
include_directories(${LIBUNSHIELD_INCLUDE})
endif(NOT WIN32)
# Main executable # Main executable
IF(OGRE_STATIC) IF(OGRE_STATIC)
@ -90,9 +109,17 @@ target_link_libraries(omwlauncher
${Boost_LIBRARIES} ${Boost_LIBRARIES}
${OGRE_LIBRARIES} ${OGRE_LIBRARIES}
${OGRE_STATIC_PLUGINS} ${OGRE_STATIC_PLUGINS}
${SDL2_LIBRARY}
${QT_LIBRARIES} ${QT_LIBRARIES}
components components
) )
if(NOT WIN32)
target_link_libraries(omwlauncher
${LIBUNSHIELD_LIBRARY}
)
endif(NOT WIN32)
if(DPKG_PROGRAM) if(DPKG_PROGRAM)
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher) INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)
@ -102,3 +129,9 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(omwlauncher gcov) target_link_libraries(omwlauncher gcov)
endif() 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()

View file

@ -4,6 +4,12 @@
#include <QMessageBox> #include <QMessageBox>
#include <QDir> #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 <cstdlib>
#include <boost/math/common_factor.hpp> #include <boost/math/common_factor.hpp>
@ -35,13 +41,14 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g
setupUi(this); setupUi(this);
// Set the maximum res we can set in windowed mode // Set the maximum res we can set in windowed mode
QRect res = QApplication::desktop()->screenGeometry(); QRect res = getMaximumResolution();
customWidthSpinBox->setMaximum(res.width()); customWidthSpinBox->setMaximum(res.width());
customHeightSpinBox->setMaximum(res.height()); customHeightSpinBox->setMaximum(res.height());
connect(rendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&))); connect(rendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int))); connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int)));
connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool))); connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));
connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int)));
} }
@ -144,17 +151,41 @@ bool GraphicsPage::setupOgre()
} }
antiAliasingComboBox->clear(); antiAliasingComboBox->clear();
resolutionComboBox->clear();
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
// Load the rest of the values
loadSettings();
return true; return true;
} }
void GraphicsPage::loadSettings() 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")) if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))
vSyncCheckBox->setCheckState(Qt::Checked); vSyncCheckBox->setCheckState(Qt::Checked);
@ -168,6 +199,9 @@ void GraphicsPage::loadSettings()
QString width = mGraphicsSettings.value(QString("Video/resolution x")); QString width = mGraphicsSettings.value(QString("Video/resolution x"));
QString height = mGraphicsSettings.value(QString("Video/resolution y")); QString height = mGraphicsSettings.value(QString("Video/resolution y"));
QString resolution = width + QString(" x ") + height; QString resolution = width + QString(" x ") + height;
QString screen = mGraphicsSettings.value(QString("Video/screen"));
screenComboBox->setCurrentIndex(screen.toInt());
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith); int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
@ -180,6 +214,8 @@ void GraphicsPage::loadSettings()
customHeightSpinBox->setValue(height.toInt()); customHeightSpinBox->setValue(height.toInt());
} }
return true;
} }
void GraphicsPage::saveSettings() void GraphicsPage::saveSettings()
@ -205,6 +241,8 @@ void GraphicsPage::saveSettings()
mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value())); mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value()));
mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->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 GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
@ -240,64 +278,83 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
return result; return result;
} }
QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer) QStringList GraphicsPage::getAvailableResolutions(int screen)
{ {
QString key("Video Mode");
QStringList result; QStringList result;
SDL_DisplayMode mode;
int modeIndex, modes = SDL_GetNumDisplayModes(screen);
uint row = 0; if (modes < 0)
Ogre::ConfigOptionMap options = renderer->getConfigOptions();
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++)
{ {
if (key.toStdString() != i->first) QMessageBox msgBox;
continue; msgBox.setWindowTitle(tr("Error receiving resolutions"));
msgBox.setIcon(QMessageBox::Critical);
Ogre::StringVector::iterator opt_it; msgBox.setStandardButtons(QMessageBox::Ok);
uint idx = 0; msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>");
msgBox.exec();
for (opt_it = i->second.possibleValues.begin (); return result;
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
{
QRegExp resolutionRe(QString("(\\d+) x (\\d+).*"));
QString resolution = QString::fromStdString(*opt_it).simplified();
if (resolutionRe.exactMatch(resolution)) {
int width = resolutionRe.cap(1).toInt();
int height = resolutionRe.cap(2).toInt();
QString aspect = getAspect(width, height);
QString cleanRes = resolutionRe.cap(1) + QString(" x ") + resolutionRe.cap(2);
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
cleanRes.append(tr("\t(Wide ") + aspect + ")");
} else if (aspect == QLatin1String("4:3")) {
cleanRes.append(tr("\t(Standard 4:3)"));
}
// do not add duplicate resolutions
if (!result.contains(cleanRes))
result.append(cleanRes);
}
}
} }
// Sort the resolutions in descending order for (modeIndex = 0; modeIndex < modes; modeIndex++)
qSort(result.begin(), result.end(), naturalSortGreaterThanCI); {
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; 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) void GraphicsPage::rendererChanged(const QString &renderer)
{ {
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString()); mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
antiAliasingComboBox->clear(); antiAliasingComboBox->clear();
resolutionComboBox->clear();
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem)); }
void GraphicsPage::screenChanged(int screen)
{
if (screen >= 0) {
resolutionComboBox->clear();
resolutionComboBox->addItems(getAvailableResolutions(screen));
}
} }
void GraphicsPage::slotFullScreenChanged(int state) void GraphicsPage::slotFullScreenChanged(int state)

View file

@ -30,10 +30,11 @@ public:
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
void saveSettings(); void saveSettings();
bool setupOgre(); bool loadSettings();
public slots: public slots:
void rendererChanged(const QString &renderer); void rendererChanged(const QString &renderer);
void screenChanged(int screen);
private slots: private slots:
void slotFullScreenChanged(int state); void slotFullScreenChanged(int state);
@ -55,10 +56,11 @@ private:
GraphicsSettings &mGraphicsSettings; GraphicsSettings &mGraphicsSettings;
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
QStringList getAvailableResolutions(Ogre::RenderSystem *renderer); QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution();
void loadSettings();
bool setupOgre();
bool setupSDL();
}; };
#endif #endif

View file

@ -1,11 +1,27 @@
#include <QApplication> #include <QApplication>
#include <QTextCodec> #include <QTextCodec>
#include <QDir> #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" #include "maindialog.hpp"
// SDL workaround
#include "graphicspage.hpp"
int main(int argc, char *argv[]) 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); QApplication app(argc, argv);
// Now we make sure the current dir is set to application path // Now we make sure the current dir is set to application path
@ -41,6 +57,8 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
return app.exec(); int returnValue = app.exec();
SDL_Quit();
return returnValue;
} }

View file

@ -11,6 +11,12 @@
#include <QDebug> #include <QDebug>
#ifndef WIN32
#include "unshieldthread.hpp"
#endif
#include "textslotmsgbox.hpp"
#include "utils/checkablemessagebox.hpp" #include "utils/checkablemessagebox.hpp"
#include "playpage.hpp" #include "playpage.hpp"
@ -128,11 +134,16 @@ bool MainDialog::showFirstRunDialog()
QDir dir(path); QDir dir(path);
dir.setPath(dir.canonicalPath()); // Resolve symlinks dir.setPath(dir.canonicalPath()); // Resolve symlinks
if (!dir.cdUp())
continue; // Cannot move from Data Files
if (dir.exists(QString("Morrowind.ini"))) if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(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 // Ask the user where the Morrowind.ini is
@ -292,8 +303,8 @@ bool MainDialog::setup()
// Now create the pages as they need the settings // Now create the pages as they need the settings
createPages(); createPages();
// Call this so we can exit on Ogre errors before mainwindow is shown // Call this so we can exit on Ogre/SDL errors before mainwindow is shown
if (!mGraphicsPage->setupOgre()) if (!mGraphicsPage->loadSettings())
return false; return false;
loadSettings(); loadSettings();
@ -310,6 +321,8 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
bool MainDialog::setupLauncherSettings() bool MainDialog::setupLauncherSettings()
{ {
mLauncherSettings.setMultiValueEnabled(true);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QStringList paths; QStringList paths;
@ -342,6 +355,78 @@ bool MainDialog::setupLauncherSettings()
return true; 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() bool MainDialog::setupGameSettings()
{ {
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
@ -399,9 +484,15 @@ bool MainDialog::setupGameSettings()
Press \"Browse...\" to specify the location manually.<br>")); Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton = QAbstractButton *dirSelectButton =
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole); 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(); msgBox.exec();
QString selectedFile; QString selectedFile;
if (msgBox.clickedButton() == dirSelectButton) { if (msgBox.clickedButton() == dirSelectButton) {
@ -411,6 +502,40 @@ bool MainDialog::setupGameSettings()
QDir::currentPath(), QDir::currentPath(),
QString(tr("Morrowind master file (*.esm)"))); 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()) if (selectedFile.isEmpty())
return false; // Cancel was clicked; return false; // Cancel was clicked;
@ -427,6 +552,8 @@ bool MainDialog::setupGameSettings()
bool MainDialog::setupGraphicsSettings() bool MainDialog::setupGraphicsSettings()
{ {
mGraphicsSettings.setMultiValueEnabled(false);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
@ -608,8 +735,21 @@ void MainDialog::closeEvent(QCloseEvent *event)
void MainDialog::play() void MainDialog::play()
{ {
if (!writeSettings()) if (!writeSettings()) {
qApp->quit(); 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 // Launch the game detached
startProgram(QString("openmw"), true); startProgram(QString("openmw"), true);

View file

@ -2,9 +2,9 @@
#define MAINDIALOG_H #define MAINDIALOG_H
#include <QMainWindow> #include <QMainWindow>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#endif
#include "settings/gamesettings.hpp" #include "settings/gamesettings.hpp"
#include "settings/graphicssettings.hpp" #include "settings/graphicssettings.hpp"
#include "settings/launchersettings.hpp" #include "settings/launchersettings.hpp"

View file

@ -6,8 +6,6 @@
#include <QRegExp> #include <QRegExp>
#include <QMap> #include <QMap>
#include <QDebug>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <boost/version.hpp> #include <boost/version.hpp>
@ -96,15 +94,15 @@ bool GameSettings::readFile(QTextStream &stream)
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) { while (!stream.atEnd()) {
QString line = stream.readLine().simplified(); QString line = stream.readLine();
if (line.isEmpty() || line.startsWith("#")) if (line.isEmpty() || line.startsWith("#"))
continue; continue;
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1).simplified(); QString key = keyRe.cap(1).trimmed();
QString value = keyRe.cap(2).simplified(); QString value = keyRe.cap(2).trimmed();
// Don't remove existing data entries // Don't remove existing data entries
if (key != QLatin1String("data")) if (key != QLatin1String("data"))

View file

@ -43,6 +43,7 @@ public:
inline QStringList getDataDirs() { return mDataDirs; } inline QStringList getDataDirs() { return mDataDirs; }
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
inline QString getDataLocal() {return mDataLocal; } inline QString getDataLocal() {return mDataLocal; }
inline bool hasMaster() { return mSettings.count(QString("master")) > 0; }
QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); QStringList values(const QString &key, const QStringList &defaultValues = QStringList());
bool readFile(QTextStream &stream); bool readFile(QTextStream &stream);

View file

@ -7,14 +7,12 @@
#include <QRegExp> #include <QRegExp>
#include <QMap> #include <QMap>
#include <QDebug>
template <class Map> template <class Map>
class SettingsBase class SettingsBase
{ {
public: public:
SettingsBase() {} SettingsBase() { mMultiValue = false; }
~SettingsBase() {} ~SettingsBase() {}
inline QString value(const QString &key, const QString &defaultValue = QString()) inline QString value(const QString &key, const QString &defaultValue = QString())
@ -36,6 +34,11 @@ public:
mSettings.insertMulti(key, value); mSettings.insertMulti(key, value);
} }
inline void setMultiValueEnabled(bool enable)
{
mMultiValue = enable;
}
inline void remove(const QString &key) inline void remove(const QString &key)
{ {
mSettings.remove(key); mSettings.remove(key);
@ -48,11 +51,12 @@ public:
mCache.clear(); mCache.clear();
QString sectionPrefix; QString sectionPrefix;
QRegExp sectionRe("^\\[([^]]+)\\]"); QRegExp sectionRe("^\\[([^]]+)\\]");
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) { while (!stream.atEnd()) {
QString line = stream.readLine().simplified(); QString line = stream.readLine();
if (line.isEmpty() || line.startsWith("#")) if (line.isEmpty() || line.startsWith("#"))
continue; continue;
@ -65,8 +69,8 @@ public:
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1).simplified(); QString key = keyRe.cap(1).trimmed();
QString value = keyRe.cap(2).simplified(); QString value = keyRe.cap(2).trimmed();
if (!sectionPrefix.isEmpty()) if (!sectionPrefix.isEmpty())
key.prepend(sectionPrefix); key.prepend(sectionPrefix);
@ -74,8 +78,13 @@ public:
mSettings.remove(key); mSettings.remove(key);
QStringList values = mCache.values(key); QStringList values = mCache.values(key);
if (!values.contains(value)) { if (!values.contains(value)) {
mCache.insertMulti(key, value); if (mMultiValue) {
mCache.insertMulti(key, value);
} else {
mCache.insert(key, value);
}
} }
} }
} }
@ -93,6 +102,8 @@ public:
private: private:
Map mSettings; Map mSettings;
Map mCache; Map mCache;
bool mMultiValue;
}; };
#endif // SETTINGSBASE_HPP #endif // SETTINGSBASE_HPP

View file

@ -0,0 +1,6 @@
#include "textslotmsgbox.hpp"
void TextSlotMsgBox::setTextSlot(const QString& string)
{
setText(string);
}

View 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

View file

@ -0,0 +1,493 @@
#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::path dest = to / dir->path().filename();
if(bfs::exists(dest))
bfs::remove_all(dest);
bfs::copy_file(dir->path(), dest);
}
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()
{
unshield_set_log_level(0);
mMorrowindDone = false;
mTribunalDone = false;
mBloodmoonDone = false;
}

View file

@ -0,0 +1,56 @@
#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

View file

@ -22,3 +22,8 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(mwiniimport gcov) target_link_libraries(mwiniimport gcov)
endif() endif()
if(DPKG_PROGRAM)
INSTALL(TARGETS mwiniimport RUNTIME DESTINATION games COMPONENT mwiniimport)
endif()

View file

@ -645,7 +645,7 @@ std::string MwIniImporter::numberToString(int n) {
return str.str(); return str.str();
} }
MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filename) const {
std::cout << "load ini file: " << filename << std::endl; std::cout << "load ini file: " << filename << std::endl;
std::string section(""); std::string section("");
@ -690,6 +690,10 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) {
std::string key(section + ":" + line.substr(0,pos)); std::string key(section + ":" + line.substr(0,pos));
std::string value(line.substr(pos+1)); 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; multistrmap::iterator it;
if((it = map.find(key)) == map.end()) { if((it = map.find(key)) == map.end()) {
@ -701,7 +705,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) {
return map; return map;
} }
MwIniImporter::multistrmap MwIniImporter::loadCfgFile(std::string filename) { MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::string& filename) {
std::cout << "load cfg file: " << filename << std::endl; std::cout << "load cfg file: " << filename << std::endl;
MwIniImporter::multistrmap map; MwIniImporter::multistrmap map;
@ -738,12 +742,11 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(std::string filename) {
return map; return map;
} }
void MwIniImporter::merge(multistrmap &cfg, multistrmap &ini) { void MwIniImporter::merge(multistrmap &cfg, const multistrmap &ini) const {
multistrmap::iterator cfgIt; multistrmap::const_iterator iniIt;
multistrmap::iterator iniIt; for(strmap::const_iterator it=mMergeMap.begin(); it!=mMergeMap.end(); ++it) {
for(strmap::iterator it=mMergeMap.begin(); it!=mMergeMap.end(); ++it) {
if((iniIt = ini.find(it->second)) != ini.end()) { if((iniIt = ini.find(it->second)) != ini.end()) {
for(std::vector<std::string>::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { for(std::vector<std::string>::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) {
cfg.erase(it->first); cfg.erase(it->first);
insertMultistrmap(cfg, it->first, *vc); insertMultistrmap(cfg, it->first, *vc);
} }
@ -751,14 +754,13 @@ void MwIniImporter::merge(multistrmap &cfg, multistrmap &ini) {
} }
} }
void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) { void MwIniImporter::mergeFallback(multistrmap &cfg, const multistrmap &ini) const {
cfg.erase("fallback"); cfg.erase("fallback");
multistrmap::iterator cfgIt; multistrmap::const_iterator iniIt;
multistrmap::iterator iniIt; for(std::vector<std::string>::const_iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); ++it) {
for(std::vector<std::string>::iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); ++it) {
if((iniIt = ini.find(*it)) != ini.end()) { if((iniIt = ini.find(*it)) != ini.end()) {
for(std::vector<std::string>::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { for(std::vector<std::string>::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) {
std::string value(*it); std::string value(*it);
std::replace( value.begin(), value.end(), ' ', '_' ); std::replace( value.begin(), value.end(), ' ', '_' );
std::replace( value.begin(), value.end(), ':', '_' ); std::replace( value.begin(), value.end(), ':', '_' );
@ -769,21 +771,21 @@ void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) {
} }
} }
void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::string value) { void MwIniImporter::insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value) {
multistrmap::iterator it = cfg.find(key); const multistrmap::const_iterator it = cfg.find(key);
if(it == cfg.end()) { if(it == cfg.end()) {
cfg.insert(std::make_pair (key, std::vector<std::string>() )); cfg.insert(std::make_pair (key, std::vector<std::string>() ));
} }
cfg[key].push_back(value); cfg[key].push_back(value);
} }
void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) { void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) const {
std::vector<std::string> archives; std::vector<std::string> archives;
std::string baseArchive("Archives:Archive "); std::string baseArchive("Archives:Archive ");
std::string archive; std::string archive;
// Search archives listed in ini file // Search archives listed in ini file
multistrmap::iterator it = ini.begin(); multistrmap::const_iterator it = ini.begin();
for(int i=0; it != ini.end(); i++) { for(int i=0; it != ini.end(); i++) {
archive = baseArchive; archive = baseArchive;
archive.append(this->numberToString(i)); archive.append(this->numberToString(i));
@ -793,7 +795,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) {
break; break;
} }
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
archives.push_back(*entry); archives.push_back(*entry);
} }
} }
@ -805,18 +807,18 @@ void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) {
// does not appears in the ini file // does not appears in the ini file
cfg["fallback-archive"].push_back("Morrowind.bsa"); cfg["fallback-archive"].push_back("Morrowind.bsa");
for(std::vector<std::string>::iterator it=archives.begin(); it!=archives.end(); ++it) { for(std::vector<std::string>::const_iterator it=archives.begin(); it!=archives.end(); ++it) {
cfg["fallback-archive"].push_back(*it); cfg["fallback-archive"].push_back(*it);
} }
} }
void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) { void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const {
std::vector<std::string> esmFiles; std::vector<std::string> esmFiles;
std::vector<std::string> espFiles; std::vector<std::string> espFiles;
std::string baseGameFile("Game Files:GameFile"); std::string baseGameFile("Game Files:GameFile");
std::string gameFile(""); std::string gameFile("");
multistrmap::iterator it = ini.begin(); multistrmap::const_iterator it = ini.begin();
for(int i=0; it != ini.end(); i++) { for(int i=0; it != ini.end(); i++) {
gameFile = baseGameFile; gameFile = baseGameFile;
gameFile.append(this->numberToString(i)); gameFile.append(this->numberToString(i));
@ -826,7 +828,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
break; break;
} }
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
std::string filetype(entry->substr(entry->length()-3)); std::string filetype(entry->substr(entry->length()-3));
Misc::StringUtils::toLower(filetype); Misc::StringUtils::toLower(filetype);
@ -844,22 +846,22 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
cfg.erase("master"); cfg.erase("master");
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) ); cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) );
for(std::vector<std::string>::iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) { for(std::vector<std::string>::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) {
cfg["master"].push_back(*it); cfg["master"].push_back(*it);
} }
cfg.erase("plugin"); cfg.erase("plugin");
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) ); cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
for(std::vector<std::string>::iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { for(std::vector<std::string>::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) {
cfg["plugin"].push_back(*it); cfg["plugin"].push_back(*it);
} }
} }
void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg) { void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, const multistrmap &cfg) {
for(multistrmap::iterator it=cfg.begin(); it != cfg.end(); ++it) { for(multistrmap::const_iterator it=cfg.begin(); it != cfg.end(); ++it) {
for(std::vector<std::string>::iterator entry=it->second.begin(); entry != it->second.end(); ++entry) { for(std::vector<std::string>::const_iterator entry=it->second.begin(); entry != it->second.end(); ++entry) {
out << (it->first) << "=" << (*entry) << std::endl; out << (it->first) << "=" << (*entry) << std::endl;
} }
} }

View file

@ -18,18 +18,17 @@ class MwIniImporter {
MwIniImporter(); MwIniImporter();
void setInputEncoding(const ToUTF8::FromType& encoding); void setInputEncoding(const ToUTF8::FromType& encoding);
void setVerbose(bool verbose); void setVerbose(bool verbose);
multistrmap loadIniFile(std::string filename); multistrmap loadIniFile(const std::string& filename) const;
multistrmap loadCfgFile(std::string filename); static multistrmap loadCfgFile(const std::string& filename);
void merge(multistrmap &cfg, multistrmap &ini); void merge(multistrmap &cfg, const multistrmap &ini) const;
void mergeFallback(multistrmap &cfg, multistrmap &ini); void mergeFallback(multistrmap &cfg, const multistrmap &ini) const;
void importGameFiles(multistrmap &cfg, multistrmap &ini); void importGameFiles(multistrmap &cfg, const multistrmap &ini) const;
void importArchives(multistrmap &cfg, multistrmap &ini); void importArchives(multistrmap &cfg, const multistrmap &ini) const;
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg); static void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, const multistrmap &cfg);
private: private:
void insertMultistrmap(multistrmap &cfg, std::string key, std::string value); static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
std::string numberToString(int n); static std::string numberToString(int n);
std::string toUTF8(const std::string &str);
bool mVerbose; bool mVerbose;
strmap mMergeMap; strmap mMergeMap;
std::vector<std::string> mMergeFallback; std::vector<std::string> mMergeFallback;

View file

@ -28,12 +28,26 @@ int main(int argc, char *argv[]) {
p_desc.add("ini", 1).add("cfg", 1); p_desc.add("ini", 1).add("cfg", 1);
bpo::variables_map vm; bpo::variables_map vm;
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
.options(desc)
.positional(p_desc)
.run();
bpo::store(parsed, 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")) { if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
std::cout << desc; std::cout << desc;
@ -55,10 +69,8 @@ int main(int argc, char *argv[]) {
std::cerr << "ini file does not exist" << std::endl; std::cerr << "ini file does not exist" << std::endl;
return -3; return -3;
} }
if(!boost::filesystem::exists(cfgFile)) { if(!boost::filesystem::exists(cfgFile))
std::cerr << "cfg file does not exist" << std::endl; std::cerr << "cfg file does not exist" << std::endl;
return -4;
}
MwIniImporter importer; MwIniImporter importer;
importer.setVerbose(vm.count("verbose")); importer.setVerbose(vm.count("verbose"));

View file

@ -1,8 +1,8 @@
set (OPENCS_SRC main.cpp) set (OPENCS_SRC main.cpp)
opencs_units (. editor) opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc opencs_units (model/doc
document document
@ -18,16 +18,17 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world opencs_units (model/world
idtable idtableproxymodel idtable idtableproxymodel regionmap data
) )
opencs_units_noqt (model/world opencs_units_noqt (model/world
universalid data record idcollection commands columnbase universalid record commands columnbase scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns
) )
opencs_hdrs_noqt (model/world opencs_hdrs_noqt (model/world
columns columnimp idcollection collection
) )
@ -36,12 +37,14 @@ opencs_units (model/tools
) )
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
stage verifier mandatoryid stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck
) )
opencs_units (view/doc opencs_units (view/doc
viewmanager view operations operation subview startup filedialog viewmanager view operations operation subview startup filedialog newgame filewidget
adjusterwidget
) )
@ -55,11 +58,14 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world opencs_units (view/world
table tablesubview table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world
dialoguesubview util subviews enumdelegate vartypedelegate dialoguesubview subviews
enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator
) )
@ -71,6 +77,48 @@ opencs_units_noqt (view/tools
subviews 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_US
) )
@ -88,7 +136,7 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE) set(QT_USE_QTMAIN TRUE)
endif(WIN32) endif(WIN32)
find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtXmlPatterns REQUIRED) find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED)
include(${QT_USE_FILE}) include(${QT_USE_FILE})
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})

View file

@ -1,23 +1,38 @@
#include "editor.hpp" #include "editor.hpp"
#include <QtGui/QApplication> #include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include <QMessageBox>
#include "model/doc/document.hpp" #include "model/doc/document.hpp"
#include "model/world/data.hpp" #include "model/world/data.hpp"
CS::Editor::Editor() : mViewManager (mDocumentManager) CS::Editor::Editor() : mViewManager (mDocumentManager)
{ {
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ())); mIpcServerName = "org.openmw.OpenCS";
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ())); setupDataFiles();
mNewGame.setLocalData (mLocal);
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));
connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ()));
connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ()));
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ()));
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
setupDataFiles(); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
this, SLOT (createNewGame (const boost::filesystem::path&)));
} }
void CS::Editor::setupDataFiles() void CS::Editor::setupDataFiles()
@ -35,35 +50,63 @@ void CS::Editor::setupDataFiles()
mCfgMgr.readConfiguration(variables, desc); mCfgMgr.readConfiguration(variables, desc);
Files::PathContainer mDataDirs, mDataLocal; Files::PathContainer dataDirs, dataLocal;
if (!variables["data"].empty()) { if (!variables["data"].empty()) {
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>()); dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
} }
std::string local = variables["data-local"].as<std::string>(); std::string local = variables["data-local"].as<std::string>();
if (!local.empty()) { if (!local.empty()) {
mDataLocal.push_back(Files::PathContainer::value_type(local)); dataLocal.push_back(Files::PathContainer::value_type(local));
} }
mCfgMgr.processPaths(mDataDirs); mCfgMgr.processPaths (dataDirs);
mCfgMgr.processPaths(mDataLocal); mCfgMgr.processPaths (dataLocal, true);
if (!dataLocal.empty())
mLocal = dataLocal[0];
else
{
QMessageBox messageBox;
messageBox.setWindowTitle (tr ("No local data path available"));
messageBox.setIcon (QMessageBox::Critical);
messageBox.setStandardButtons (QMessageBox::Ok);
messageBox.setText(tr("<br><b>OpenCS is unable to access the local data directory. This may indicate a faulty configuration or a broken install.</b>"));
messageBox.exec();
QApplication::exit (1);
return;
}
// Set the charset for reading the esm/esp files // Set the charset for reading the esm/esp files
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>()); QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
mFileDialog.setEncoding(encoding); mFileDialog.setEncoding(encoding);
Files::PathContainer dataDirs; dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
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) for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{ {
QString path = QString::fromStdString(iter->string()); QString path = QString::fromStdString(iter->string());
mFileDialog.addFiles(path); mFileDialog.addFiles(path);
} }
//load the settings into the userSettings instance.
const QString settingFileName = "opencs.cfg";
CSMSettings::UserSettings::instance().loadSettings(settingFileName);
} }
void CS::Editor::createDocument() void CS::Editor::createGame()
{
mStartup.hide();
if (mNewGame.isHidden())
mNewGame.show();
mNewGame.raise();
mNewGame.activateWindow();
}
void CS::Editor::createAddon()
{ {
mStartup.hide(); mStartup.hide();
@ -86,7 +129,9 @@ void CS::Editor::openFiles()
files.push_back(path.toStdString()); files.push_back(path.toStdString());
} }
CSMDoc::Document *document = mDocumentManager.addDocument(files, false); /// \todo Get the save path from the file dialogue
CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false);
mViewManager.addView (document); mViewManager.addView (document);
mFileDialog.hide(); mFileDialog.hide();
@ -103,15 +148,73 @@ void CS::Editor::createNewFile()
files.push_back(mFileDialog.fileName().toStdString()); files.push_back(mFileDialog.fileName().toStdString());
CSMDoc::Document *document = mDocumentManager.addDocument (files, true); /// \todo Get the save path from the file dialogue.
CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true);
mViewManager.addView (document); mViewManager.addView (document);
mFileDialog.hide(); mFileDialog.hide();
} }
void CS::Editor::createNewGame (const boost::filesystem::path& file)
{
std::vector<boost::filesystem::path> files;
files.push_back (file);
CSMDoc::Document *document = mDocumentManager.addDocument (files, file, true);
mViewManager.addView (document);
mNewGame.hide();
}
void CS::Editor::showStartup()
{
if(mStartup.isHidden())
mStartup.show();
mStartup.raise();
mStartup.activateWindow();
}
void CS::Editor::showSettings()
{
if (mSettings.isHidden())
mSettings.show();
mSettings.raise();
mSettings.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() int CS::Editor::run()
{ {
if (mLocal.empty())
return 1;
mStartup.show(); mStartup.show();
QApplication::setQuitOnLastWindowClosed (true);
return QApplication::exec(); return QApplication::exec();
} }

View file

@ -2,14 +2,23 @@
#define CS_EDITOR_H #define CS_EDITOR_H
#include <QObject> #include <QObject>
#include <QString>
#include <QLocalServer>
#include <QLocalSocket>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#endif
#include "model/settings/usersettings.hpp"
#include "model/doc/documentmanager.hpp" #include "model/doc/documentmanager.hpp"
#include "view/doc/viewmanager.hpp" #include "view/doc/viewmanager.hpp"
#include "view/doc/startup.hpp" #include "view/doc/startup.hpp"
#include "view/doc/filedialog.hpp" #include "view/doc/filedialog.hpp"
#include "view/doc/newgame.hpp"
#include "view/settings/usersettingsdialog.hpp"
namespace CS namespace CS
{ {
@ -17,12 +26,16 @@ namespace CS
{ {
Q_OBJECT Q_OBJECT
CSMSettings::UserSettings mUserSettings;
CSMDoc::DocumentManager mDocumentManager; CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager; CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup; CSVDoc::StartupDialogue mStartup;
CSVDoc::NewGameDialogue mNewGame;
CSVSettings::UserSettingsDialog mSettings;
FileDialog mFileDialog; FileDialog mFileDialog;
Files::ConfigurationManager mCfgMgr; Files::ConfigurationManager mCfgMgr;
boost::filesystem::path mLocal;
void setupDataFiles(); void setupDataFiles();
@ -34,16 +47,31 @@ namespace CS
Editor(); Editor();
bool makeIPCServer();
void connectToIPCServer();
int run(); int run();
///< \return error status ///< \return error status
private slots: private slots:
void createDocument(); void createGame();
void createAddon();
void loadDocument(); void loadDocument();
void openFiles(); void openFiles();
void createNewFile(); void createNewFile();
void createNewGame (const boost::filesystem::path& file);
void showStartup();
void showSettings();
private:
QString mIpcServerName;
QLocalServer *mServer;
QLocalSocket *mClientSocket;
}; };
} }

View file

@ -4,7 +4,7 @@
#include <exception> #include <exception>
#include <iostream> #include <iostream>
#include <QtGui/QApplication> #include <QApplication>
#include <QIcon> #include <QIcon>
class Application : public QApplication class Application : public QApplication
@ -39,5 +39,11 @@ int main(int argc, char *argv[])
CS::Editor editor; CS::Editor editor;
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
return editor.run(); return editor.run();
} }

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@ namespace CSMDoc
private: private:
std::string mName; ///< \todo replace name with ESX list boost::filesystem::path mSavePath;
CSMWorld::Data mData; CSMWorld::Data mData;
CSMTools::Tools mTools; CSMTools::Tools mTools;
@ -52,6 +52,8 @@ namespace CSMDoc
void createBase(); void createBase();
void addGmsts();
void addOptionalGmsts(); void addOptionalGmsts();
void addOptionalGlobals(); void addOptionalGlobals();
@ -62,14 +64,16 @@ namespace CSMDoc
public: public:
Document (const std::vector<boost::filesystem::path>& files, bool new_); Document (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_);
~Document();
QUndoStack& getUndoStack(); QUndoStack& getUndoStack();
int getState() const; int getState() const;
const std::string& getName() const; const boost::filesystem::path& getSavePath() const;
///< \todo replace with ESX list
void save(); void save();
@ -105,4 +109,4 @@ namespace CSMDoc
}; };
} }
#endif #endif

View file

@ -14,10 +14,10 @@ CSMDoc::DocumentManager::~DocumentManager()
delete *iter; delete *iter;
} }
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_) bool new_)
{ {
Document *document = new Document (files, new_); Document *document = new Document (files, savePath, new_);
mDocuments.push_back (document); mDocuments.push_back (document);

View file

@ -23,7 +23,8 @@ namespace CSMDoc
~DocumentManager(); ~DocumentManager();
Document *addDocument (const std::vector<boost::filesystem::path>& files, bool new_); Document *addDocument (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_);
///< The ownership of the returned document is not transferred to the caller. ///< 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 /// \param new_ Do not load the last content file in \a files and instead create in an

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

View 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

View 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";
}

View 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

View 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

View file

@ -0,0 +1,8 @@
#include "leafnode.hpp"
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const
{
return std::vector<int>();
}

View 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

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

View 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

View file

@ -0,0 +1,6 @@
#include "node.hpp"
CSMFilter::Node::Node() {}
CSMFilter::Node::~Node() {}

View 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

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

View 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

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

View 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

View file

@ -0,0 +1,617 @@
#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 (Type type, const std::string& string);
///< Non-string type that can also be interpreted as a string.
Token (const std::string& string);
Token (double number);
operator bool() const;
bool isString() const;
};
Token::Token (Type type) : mType (type) {}
Token::Token (Type type, const std::string& string) : mType (type), mString (string) {}
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
bool Token::isString() const
{
return mType==Type_String || mType>=Type_Keyword_True;
}
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]=='"')
return 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), token.mString);
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.isString())
{
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.isString())
{
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.isString())
{
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;
ValueNode::Type lowerType = ValueNode::Type_Open;
ValueNode::Type upperType = ValueNode::Type_Open;
token = getNextToken();
if (token.mType==Token::Type_Number)
{
// single value
lower = upper = token.mNumber;
lowerType = upperType = ValueNode::Type_Closed;
}
else
{
// interval
if (token.mType==Token::Type_OpenSquare)
lowerType = ValueNode::Type_Closed;
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)
{
lower = token.mNumber;
token = getNextToken();
if (token.mType!=Token::Type_Comma)
{
error();
return boost::shared_ptr<Node>();
}
}
else if (token.mType==Token::Type_Comma)
{
lowerType = ValueNode::Type_Infinite;
}
else
{
error();
return boost::shared_ptr<Node>();
}
token = getNextToken();
if (token.mType==Token::Type_Number)
{
upper = token.mNumber;
token = getNextToken();
}
else
upperType = ValueNode::Type_Infinite;
if (token.mType==Token::Type_CloseSquare)
{
if (upperType!=ValueNode::Type_Infinite)
upperType = ValueNode::Type_Closed;
}
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, lowerType, upperType, lower, upper));
}
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;
}
// We do not use isString() here, because there could be a pre-defined filter with an ID that is
// equal a filter keyword.
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;
}

View 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

View file

@ -0,0 +1,83 @@
#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);
QString string;
if (data.type()==QVariant::String)
{
string = data.toString();
}
else if (data.type()==QVariant::Int || data.type()==QVariant::UInt ||
CSMWorld::Columns::hasEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId)))
{
int value = data.toInt();
std::vector<std::string> enums =
CSMWorld::Columns::getEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId));
if (value>=0 && value<static_cast<int> (enums.size()))
string = QString::fromUtf8 (enums[value].c_str());
}
else if (data.type()==QVariant::Bool)
{
string = data.toBool() ? "true" : " false";
}
else
return false;
/// \todo make pattern syntax configurable
QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive);
return regExp.exactMatch (string);
}
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();
}

View 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

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

View 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

View file

@ -0,0 +1,97 @@
#include "valuenode.hpp"
#include <sstream>
#include <stdexcept>
#include "../world/columns.hpp"
#include "../world/idtable.hpp"
CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType,
double lower, double upper)
: mColumnId (columnId), mLowerType (lowerType), mUpperType (upperType), mLower (lower), mUpper (upper){}
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();
switch (mLowerType)
{
case Type_Closed: if (value<mLower) return false; break;
case Type_Open: if (value<=mLower) return false; break;
case Type_Infinite: break;
}
switch (mUpperType)
{
case Type_Closed: if (value>mUpper) return false; break;
case Type_Open: if (value>=mUpper) return false; break;
case Type_Infinite: break;
}
return true;
}
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 && mLowerType!=Type_Infinite && mUpperType!=Type_Infinite)
stream << mLower;
else
{
switch (mLowerType)
{
case Type_Closed: stream << "[" << mLower; break;
case Type_Open: stream << "(" << mLower; break;
case Type_Infinite: stream << "("; break;
}
stream << ", ";
switch (mUpperType)
{
case Type_Closed: stream << mUpper << "]"; break;
case Type_Open: stream << mUpper << ")"; break;
case Type_Infinite: stream << ")"; break;
}
}
stream << ")";
return stream.str();
}

View file

@ -0,0 +1,46 @@
#ifndef CSM_FILTER_VALUENODE_H
#define CSM_FILTER_VALUENODE_H
#include "leafnode.hpp"
namespace CSMFilter
{
class ValueNode : public LeafNode
{
public:
enum Type
{
Type_Closed, Type_Open, Type_Infinite
};
private:
int mColumnId;
std::string mText;
double mLower;
double mUpper;
Type mLowerType;
Type mUpperType;
public:
ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper);
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

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

View 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

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

View 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

View file

@ -0,0 +1 @@
#include "support.hpp"

View 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

View 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 &sectionName) 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 &sectionSettings)
{
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 &sectionName, 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 &section, 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 &sectionName) 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;
}

View 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 *> &sections);
/// 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 &sectionName, 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 &sectionName) const;
/// Retrieves the value as a QString of the specified setting in the specified section
QString getSetting(const QString &section, 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 &sectionName) const;
signals:
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);
};
}
#endif // USERSETTINGS_HPP

View 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
}

View 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

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

View 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

View 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
}

View 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

View file

@ -1,9 +1,11 @@
#include "mandatoryid.hpp" #include "mandatoryid.hpp"
#include "../world/idcollection.hpp" #include "../world/collectionbase.hpp"
CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, #include "../world/record.hpp"
CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::CollectionBase& idCollection,
const CSMWorld::UniversalId& collectionId, const std::vector<std::string>& ids) const CSMWorld::UniversalId& collectionId, const std::vector<std::string>& ids)
: mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids) : mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids)
{} {}

View file

@ -10,7 +10,7 @@
namespace CSMWorld namespace CSMWorld
{ {
class IdCollectionBase; class CollectionBase;
} }
namespace CSMTools namespace CSMTools
@ -18,13 +18,13 @@ namespace CSMTools
/// \brief Verify stage: make sure that records with specific IDs exist. /// \brief Verify stage: make sure that records with specific IDs exist.
class MandatoryIdStage : public Stage class MandatoryIdStage : public Stage
{ {
const CSMWorld::IdCollectionBase& mIdCollection; const CSMWorld::CollectionBase& mIdCollection;
CSMWorld::UniversalId mCollectionId; CSMWorld::UniversalId mCollectionId;
std::vector<std::string> mIds; std::vector<std::string> mIds;
public: public:
MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, const CSMWorld::UniversalId& collectionId, MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId,
const std::vector<std::string>& ids); const std::vector<std::string>& ids);
virtual int setup(); virtual int setup();

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

View 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

View file

@ -0,0 +1,33 @@
#include "regioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadregn.hpp>
#include "../world/universalid.hpp"
CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions)
: mRegions (regions)
{}
int CSMTools::RegionCheckStage::setup()
{
return mRegions.getSize();
}
void CSMTools::RegionCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Region& region = mRegions.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId);
// test for empty name
if (region.mName.empty())
messages.push_back (id.toString() + "|" + region.mId + " has an empty name");
/// \todo test that the ID in mSleeplist exists
/// \todo check data members that can't be edited in the table view
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_REGIONCHECK_H
#define CSM_TOOLS_REGIONCHECK_H
#include <components/esm/loadregn.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that region records are internally consistent
class RegionCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Region>& mRegions;
public:
RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions);
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

View file

@ -0,0 +1,37 @@
#include "skillcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../world/universalid.hpp"
CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills)
: mSkills (skills)
{}
int CSMTools::SkillCheckStage::setup()
{
return mSkills.getSize();
}
void CSMTools::SkillCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Skill& skill = mSkills.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);
for (int i=0; i<4; ++i)
if (skill.mData.mUseValue[i]<0)
{
std::ostringstream stream;
stream << id.toString() << "|Use value #" << i << " of " << skill.mId << " is negative";
messages.push_back (stream.str());
}
if (skill.mDescription.empty())
messages.push_back (id.toString() + "|" + skill.mId + " has an empty description");
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_SKILLCHECK_H
#define CSM_TOOLS_SKILLCHECK_H
#include <components/esm/loadskil.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that skill records are internally consistent
class SkillCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Skill>& mSkills;
public:
SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills);
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

View file

@ -0,0 +1,29 @@
#include "soundcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
: mSounds (sounds)
{}
int CSMTools::SoundCheckStage::setup()
{
return mSounds.getSize();
}
void CSMTools::SoundCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Sound& sound = mSounds.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
if (sound.mData.mMinRange>sound.mData.mMaxRange)
messages.push_back (id.toString() + "|Maximum range larger than minimum range");
/// \todo check, if the sound file exists
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_SOUNDCHECK_H
#define CSM_TOOLS_SOUNDCHECK_H
#include <components/esm/loadsoun.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that sound records are internally consistent
class SoundCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
public:
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds);
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

View file

@ -0,0 +1,35 @@
#include "spellcheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadspel.hpp>
#include "../world/universalid.hpp"
CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells)
: mSpells (spells)
{}
int CSMTools::SpellCheckStage::setup()
{
return mSpells.getSize();
}
void CSMTools::SpellCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Spell& spell = mSpells.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);
// test for empty name and description
if (spell.mName.empty())
messages.push_back (id.toString() + "|" + spell.mId + " has an empty name");
// test for invalid cost values
if (spell.mData.mCost<0)
messages.push_back (id.toString() + "|" + spell.mId + " has a negative spell costs");
/// \todo check data members that can't be edited in the table view
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_SPELLCHECK_H
#define CSM_TOOLS_SPELLCHECK_H
#include <components/esm/loadspel.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that spell records are internally consistent
class SpellCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Spell>& mSpells;
public:
SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells);
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

View file

@ -12,6 +12,14 @@
#include "reportmodel.hpp" #include "reportmodel.hpp"
#include "mandatoryid.hpp" #include "mandatoryid.hpp"
#include "skillcheck.hpp"
#include "classcheck.hpp"
#include "factioncheck.hpp"
#include "racecheck.hpp"
#include "soundcheck.hpp"
#include "regioncheck.hpp"
#include "birthsigncheck.hpp"
#include "spellcheck.hpp"
CSMTools::Operation *CSMTools::Tools::get (int type) CSMTools::Operation *CSMTools::Tools::get (int type)
{ {
@ -51,6 +59,22 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier()
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));
mVerifier->appendStage (new SkillCheckStage (mData.getSkills()));
mVerifier->appendStage (new ClassCheckStage (mData.getClasses()));
mVerifier->appendStage (new FactionCheckStage (mData.getFactions()));
mVerifier->appendStage (new RaceCheckStage (mData.getRaces()));
mVerifier->appendStage (new SoundCheckStage (mData.getSounds()));
mVerifier->appendStage (new RegionCheckStage (mData.getRegions()));
mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns()));
mVerifier->appendStage (new SpellCheckStage (mData.getSpells()));
} }
return mVerifier; return mVerifier;

View file

@ -0,0 +1,25 @@
#include "cell.hpp"
#include <sstream>
void CSMWorld::Cell::load (ESM::ESMReader &esm)
{
mName = mId;
ESM::Cell::load (esm, false);
if (!(mData.mFlags & Interior))
{
std::ostringstream stream;
stream << "#" << mData.mX << " " << mData.mY;
mId = stream.str();
}
}
void CSMWorld::Cell::addRef (const std::string& id)
{
mRefs.push_back (std::make_pair (id, false));
}

View file

@ -0,0 +1,24 @@
#ifndef CSM_WOLRD_CELL_H
#define CSM_WOLRD_CELL_H
#include <vector>
#include <string>
#include <components/esm/loadcell.hpp>
namespace CSMWorld
{
/// \brief Wrapper for Cell record
struct Cell : public ESM::Cell
{
std::string mId;
std::vector<std::pair<std::string, bool> > mRefs; // ID, modified
std::vector<std::string> mDeletedRefs;
void load (ESM::ESMReader &esm);
void addRef (const std::string& id);
};
}
#endif

View file

@ -0,0 +1,339 @@
#ifndef CSM_WOLRD_COLLECTION_H
#define CSM_WOLRD_COLLECTION_H
#include <vector>
#include <map>
#include <algorithm>
#include <cctype>
#include <stdexcept>
#include <functional>
#include <QVariant>
#include <components/misc/stringops.hpp>
#include "columnbase.hpp"
#include "collectionbase.hpp"
namespace CSMWorld
{
/// \brief Access to ID field in records
template<typename ESXRecordT>
struct IdAccessor
{
std::string& getId (ESXRecordT& record);
const std::string getId (const ESXRecordT& record) const;
};
template<typename ESXRecordT>
std::string& IdAccessor<ESXRecordT>::getId (ESXRecordT& record)
{
return record.mId;
}
template<typename ESXRecordT>
const std::string IdAccessor<ESXRecordT>::getId (const ESXRecordT& record) const
{
return record.mId;
}
/// \brief Single-type record collection
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class Collection : public CollectionBase
{
std::vector<Record<ESXRecordT> > mRecords;
std::map<std::string, int> mIndex;
std::vector<Column<ESXRecordT> *> mColumns;
// not implemented
Collection (const Collection&);
Collection& operator= (const Collection&);
public:
Collection();
virtual ~Collection();
void add (const ESXRecordT& record);
///< Add a new record (modified)
virtual int getSize() const;
virtual std::string getId (int index) const;
virtual int getIndex (const std::string& id) const;
virtual int getColumns() const;
virtual QVariant getData (int index, int column) const;
virtual void setData (int index, int column, const QVariant& data);
virtual const ColumnBase& getColumn (int column) const;
virtual void merge();
///< Merge modified into base.
virtual void purge();
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) ;
virtual void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
virtual int searchId (const std::string& id) const;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record);
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record,
UniversalId::Type type = UniversalId::Type_None);
///< If the record type does not match, an exception is thrown.
///< \param type Will be ignored, unless the collection supports multiple record types
virtual const Record<ESXRecordT>& getRecord (const std::string& id) const;
virtual const Record<ESXRecordT>& getRecord (int index) const;
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted = true) const;
///< Return a sorted collection of all IDs
///
/// \param listDeleted include deleted record in the list
void addColumn (Column<ESXRecordT> *column);
void setRecord (int index, const Record<ESXRecordT>& record);
///< \attention This function must not change the ID.
};
template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::Collection()
{}
template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::~Collection()
{
for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
delete *iter;
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::add (const ESXRecordT& record)
{
std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record));
std::map<std::string, int>::iterator iter = mIndex.find (id);
if (iter==mIndex.end())
{
Record<ESXRecordT> record2;
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
mRecords.push_back (record2);
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
}
else
{
mRecords[iter->second].setModified (record);
}
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getSize() const
{
return mRecords.size();
}
template<typename ESXRecordT, typename IdAccessorT>
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
{
return IdAccessorT().getId (mRecords.at (index).get());
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getIndex (const std::string& id) const
{
int index = searchId (id);
if (index==-1)
throw std::runtime_error ("invalid ID: " + id);
return index;
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getColumns() const
{
return mColumns.size();
}
template<typename ESXRecordT, typename IdAccessorT>
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
{
return mColumns.at (column)->get (mRecords.at (index));
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
{
return mColumns.at (column)->set (mRecords.at (index), data);
}
template<typename ESXRecordT, typename IdAccessorT>
const ColumnBase& Collection<ESXRecordT, IdAccessorT>::getColumn (int column) const
{
return *mColumns.at (column);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::addColumn (Column<ESXRecordT> *column)
{
mColumns.push_back (column);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::merge()
{
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
iter->merge();
purge();
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::purge()
{
int i = 0;
while (i<static_cast<int> (mRecords.size()))
{
if (mRecords[i].isErased())
removeRows (i, 1);
else
++i;
}
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::removeRows (int index, int count)
{
mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count);
typename std::map<std::string, int>::iterator iter = mIndex.begin();
while (iter!=mIndex.end())
{
if (iter->second>=index)
{
if (iter->second>=index+count)
{
iter->second -= count;
++iter;
}
else
{
mIndex.erase (iter++);
}
}
else
++iter;
}
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::appendBlankRecord (const std::string& id,
UniversalId::Type type)
{
ESXRecordT record;
IdAccessorT().getId (record) = id;
record.blank();
add (record);
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::searchId (const std::string& id) const
{
std::string id2 = Misc::StringUtils::lowerCase(id);
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
if (iter==mIndex.end())
return -1;
return iter->second;
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record)
{
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
UniversalId::Type type)
{
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
dynamic_cast<const Record<ESXRecordT>&> (record).get())),
mRecords.size()-1));
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (UniversalId::Type type) const
{
return static_cast<int> (mRecords.size());
}
template<typename ESXRecordT, typename IdAccessorT>
std::vector<std::string> Collection<ESXRecordT, IdAccessorT>::getIds (bool listDeleted) const
{
std::vector<std::string> ids;
for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();
iter!=mIndex.end(); ++iter)
{
if (listDeleted || !mRecords[iter->second].isDeleted())
ids.push_back (IdAccessorT().getId (mRecords[iter->second].get()));
}
return ids;
}
template<typename ESXRecordT, typename IdAccessorT>
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
{
int index = getIndex (id);
return mRecords.at (index);
}
template<typename ESXRecordT, typename IdAccessorT>
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
{
return mRecords.at (index);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
{
if (IdAccessorT().getId (mRecords.at (index).get())!=IdAccessorT().getId (record.get()))
throw std::runtime_error ("attempt to change the ID of a record");
mRecords.at (index) = record;
}
}
#endif

View file

@ -0,0 +1,6 @@
#include "collectionbase.hpp"
CSMWorld::CollectionBase::CollectionBase() {}
CSMWorld::CollectionBase::~CollectionBase() {}

View file

@ -0,0 +1,89 @@
#ifndef CSM_WOLRD_COLLECTIONBASE_H
#define CSM_WOLRD_COLLECTIONBASE_H
#include <string>
#include "universalid.hpp"
class QVariant;
namespace CSMWorld
{
struct ColumnBase;
struct RecordBase;
/// \brief Base class for record collections
///
/// \attention Modifying records through the interface does not update connected views.
/// Such modifications should be done through the table model interface instead unless no views
/// are connected to the model or special precautions have been taken to send update signals
/// manually.
class CollectionBase
{
// not implemented
CollectionBase (const CollectionBase&);
CollectionBase& operator= (const CollectionBase&);
public:
CollectionBase();
virtual ~CollectionBase();
virtual int getSize() const = 0;
virtual std::string getId (int index) const = 0;
virtual int getIndex (const std::string& id) const = 0;
virtual int getColumns() const = 0;
virtual const ColumnBase& getColumn (int column) const = 0;
virtual QVariant getData (int index, int column) const = 0;
virtual void setData (int index, int column, const QVariant& data) = 0;
// Not in use. Temporarily removed so that the implementation of RefIdCollection can continue without
// these functions for now.
// virtual void merge() = 0;
///< Merge modified into base.
// virtual void purge() = 0;
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) = 0;
virtual void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) = 0;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual int searchId (const std::string& id) const = 0;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record) = 0;
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
///< \param type Will be ignored, unless the collection supports multiple record types
virtual void appendRecord (const RecordBase& record,
UniversalId::Type type = UniversalId::Type_None) = 0;
///< If the record type does not match, an exception is thrown.
virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0;
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted = true) const = 0;
///< Return a sorted collection of all IDs
///
/// \param listDeleted include deleted record in the list
};
}
#endif

View file

@ -1,8 +1,10 @@
#include "columnbase.hpp" #include "columnbase.hpp"
CSMWorld::ColumnBase::ColumnBase (const std::string& title, Display displayType, int flags) #include "columns.hpp"
: mTitle (title), mDisplayType (displayType), mFlags (flags)
CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags)
: mColumnId (columnId), mDisplayType (displayType), mFlags (flags)
{} {}
CSMWorld::ColumnBase::~ColumnBase() {} CSMWorld::ColumnBase::~ColumnBase() {}
@ -10,4 +12,9 @@ CSMWorld::ColumnBase::~ColumnBase() {}
bool CSMWorld::ColumnBase::isUserEditable() const bool CSMWorld::ColumnBase::isUserEditable() const
{ {
return isEditable(); return isEditable();
}
std::string CSMWorld::ColumnBase::getTitle() const
{
return Columns::getName (static_cast<Columns::ColumnId> (mColumnId));
} }

View file

@ -31,14 +31,26 @@ namespace CSMWorld
Display_Float, Display_Float,
Display_Var, Display_Var,
Display_GmstVarType, Display_GmstVarType,
Display_GlobalVarType Display_GlobalVarType,
Display_Specialisation,
Display_Attribute,
Display_Boolean,
Display_SpellType,
Display_Script,
Display_ApparatusType,
Display_ArmorType,
Display_ClothingType,
Display_CreatureType,
Display_WeaponType,
Display_RecordState,
Display_RefRecordType
}; };
std::string mTitle; int mColumnId;
int mFlags; int mFlags;
Display mDisplayType; Display mDisplayType;
ColumnBase (const std::string& title, Display displayType, int flag); ColumnBase (int columnId, Display displayType, int flag);
virtual ~ColumnBase(); virtual ~ColumnBase();
@ -47,22 +59,22 @@ namespace CSMWorld
virtual bool isUserEditable() const; virtual bool isUserEditable() const;
///< Can this column be edited directly by the user? ///< Can this column be edited directly by the user?
virtual std::string getTitle() const;
}; };
template<typename ESXRecordT> template<typename ESXRecordT>
struct Column : public ColumnBase struct Column : public ColumnBase
{ {
std::string mTitle;
int mFlags; int mFlags;
Column (const std::string& title, Display displayType, int flags = Flag_Table | Flag_Dialogue) Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue)
: ColumnBase (title, displayType, flags) {} : ColumnBase (columnId, displayType, flags) {}
virtual QVariant get (const Record<ESXRecordT>& record) const = 0; virtual QVariant get (const Record<ESXRecordT>& record) const = 0;
virtual void set (Record<ESXRecordT>& record, const QVariant& data) virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{ {
throw std::logic_error ("Column " + mTitle + " is not editable"); throw std::logic_error ("Column " + getTitle() + " is not editable");
} }
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,313 @@
#include "columns.hpp"
#include <components/misc/stringops.hpp>
#include "universalid.hpp"
namespace CSMWorld
{
namespace Columns
{
struct ColumnDesc
{
int mId;
const char *mName;
};
const ColumnDesc sNames[] =
{
{ ColumnId_Value, "Value" },
{ ColumnId_Id, "ID" },
{ ColumnId_Modification, "Modified" },
{ ColumnId_RecordType, "Record Type" },
{ ColumnId_ValueType, "Value Type" },
{ ColumnId_Description, "Description" },
{ ColumnId_Specialisation, "Specialisation" },
{ ColumnId_Attribute, "Attribute" },
{ ColumnId_Name, "Name" },
{ ColumnId_Playable, "Playable" },
{ ColumnId_Hidden, "Hidden" },
{ ColumnId_MaleWeight, "Male Weight" },
{ ColumnId_FemaleWeight, "Female Weight" },
{ ColumnId_MaleHeight, "Male Height" },
{ ColumnId_FemaleHeight, "Female Height" },
{ ColumnId_Volume, "Volume" },
{ ColumnId_MinRange, "Min Range" },
{ ColumnId_MaxRange, "Max Range" },
{ ColumnId_SoundFile, "Sound File" },
{ ColumnId_MapColour, "Map Colour" },
{ ColumnId_SleepEncounter, "Sleep Encounter" },
{ ColumnId_Texture, "Texture" },
{ ColumnId_SpellType, "Spell Type" },
{ ColumnId_Cost, "Cost" },
{ ColumnId_ScriptText, "Script Text" },
{ ColumnId_Region, "Region" },
{ ColumnId_Cell, "Cell" },
{ ColumnId_Scale, "Scale" },
{ ColumnId_Owner, "Owner" },
{ ColumnId_Soul, "Soul" },
{ ColumnId_Faction, "Faction" },
{ ColumnId_FactionIndex, "Faction Index" },
{ ColumnId_Charges, "Charges" },
{ ColumnId_Enchantment, "Enchantment" },
{ ColumnId_Value, "Coin Value" },
{ ColumnId_Teleport, "Teleport" },
{ ColumnId_TeleportCell, "Teleport Cell" },
{ ColumnId_LockLevel, "Lock Level" },
{ ColumnId_Key, "Key" },
{ ColumnId_Trap, "Trap" },
{ ColumnId_BeastRace, "Beast Race" },
{ ColumnId_AutoCalc, "Auto Calc" },
{ ColumnId_StarterSpell, "Starter Spell" },
{ ColumnId_AlwaysSucceeds, "Always Succeeds" },
{ ColumnId_SleepForbidden, "Sleep Forbidden" },
{ ColumnId_InteriorWater, "Interior Water" },
{ ColumnId_InteriorSky, "Interior Sky" },
{ ColumnId_Model, "Model" },
{ ColumnId_Script, "Script" },
{ ColumnId_Icon, "Icon" },
{ ColumnId_Weight, "Weight" },
{ ColumnId_EnchantmentPoints, "Enchantment Points" },
{ ColumnId_Quality, "Quality" },
{ ColumnId_Ai, "AI" },
{ ColumnId_AiHello, "AI Hello" },
{ ColumnId_AiFlee, "AI Flee" },
{ ColumnId_AiFight, "AI Fight" },
{ ColumnId_AiAlarm, "AI Alarm" },
{ ColumnId_BuysWeapons, "Buys Weapons" },
{ ColumnId_BuysArmor, "Buys Armor" },
{ ColumnId_BuysClothing, "Buys Clothing" },
{ ColumnId_BuysBooks, "Buys Books" },
{ ColumnId_BuysIngredients, "Buys Ingredients" },
{ ColumnId_BuysLockpicks, "Buys Lockpicks" },
{ ColumnId_BuysProbes, "Buys Probes" },
{ ColumnId_BuysLights, "Buys Lights" },
{ ColumnId_BuysApparati, "Buys Apparati" },
{ ColumnId_BuysRepairItems, "Buys Repair Items" },
{ ColumnId_BuysMiscItems, "Buys Misc Items" },
{ ColumnId_BuysPotions, "Buys Potions" },
{ ColumnId_BuysMagicItems, "Buys Magic Items" },
{ ColumnId_SellsSpells, "Sells Spells" },
{ ColumnId_Trainer, "Trainer" },
{ ColumnId_Spellmaking, "Spellmaking" },
{ ColumnId_EnchantingService, "Enchanting Service" },
{ ColumnId_RepairService, "Repair Serivce" },
{ ColumnId_ApparatusType, "Apparatus Type" },
{ ColumnId_ArmorType, "Armor Type" },
{ ColumnId_Health, "Health" },
{ ColumnId_ArmorValue, "Armor Value" },
{ ColumnId_Scroll, "Scroll" },
{ ColumnId_ClothingType, "Clothing Type" },
{ ColumnId_WeightCapacity, "Weight Capacity" },
{ ColumnId_OrganicContainer, "Organic Container" },
{ ColumnId_Respawn, "Respawn" },
{ ColumnId_CreatureType, "Creature Type" },
{ ColumnId_SoulPoints, "Soul Points" },
{ ColumnId_OriginalCreature, "Original Creature" },
{ ColumnId_Biped, "Biped" },
{ ColumnId_HasWeapon, "Has Weapon" },
{ ColumnId_NoMovement, "No Movement" },
{ ColumnId_Swims, "Swims" },
{ ColumnId_Flies, "Flies" },
{ ColumnId_Walks, "Walks" },
{ ColumnId_Essential, "Essential" },
{ ColumnId_SkeletonBlood, "Skeleton Blood" },
{ ColumnId_MetalBlood, "Metal Blood" },
{ ColumnId_OpenSound, "Open Sound" },
{ ColumnId_CloseSound, "Close Sound" },
{ ColumnId_Duration, "Duration" },
{ ColumnId_Radius, "Radius" },
{ ColumnId_Colour, "Colour" },
{ ColumnId_Sound, "Sound" },
{ ColumnId_Dynamic, "Dynamic" },
{ ColumnId_Portable, "Portable" },
{ ColumnId_NegativeLight, "Negative Light" },
{ ColumnId_Flickering, "Flickering" },
{ ColumnId_SlowFlickering, "Slow Flickering" },
{ ColumnId_Pulsing, "Pulsing" },
{ ColumnId_SlowPulsing, "Slow Pulsing" },
{ ColumnId_Fire, "Fire" },
{ ColumnId_OffByDefault, "Off by default" },
{ ColumnId_IsKey, "Is Key" },
{ ColumnId_Race, "Race" },
{ ColumnId_Class, "Class" },
{ Columnid_Hair, "Hair" },
{ ColumnId_Head, "Head" },
{ ColumnId_Female, "Female" },
{ ColumnId_WeaponType, "Weapon Type" },
{ ColumnId_WeaponSpeed, "Weapon Speed" },
{ ColumnId_WeaponReach, "Weapon Reach" },
{ ColumnId_MinChop, "Min Chop" },
{ ColumnId_MaxChip, "Max Chop" },
{ Columnid_MinSlash, "Min Slash" },
{ ColumnId_MaxSlash, "Max Slash" },
{ ColumnId_MinThrust, "Min Thrust" },
{ ColumnId_MaxThrust, "Max Thrust" },
{ ColumnId_Magical, "Magical" },
{ ColumnId_Silver, "Silver" },
{ ColumnId_Filter, "Filter" },
{ ColumnId_PositionXPos, "Pos X" },
{ ColumnId_PositionYPos, "Pos Y" },
{ ColumnId_PositionZPos, "Pos Z" },
{ ColumnId_PositionXRot, "Rot X" },
{ ColumnId_PositionYRot, "Rot Y" },
{ ColumnId_PositionZRot, "Rot Z" },
{ ColumnId_DoorPositionXPos, "Teleport Pos X" },
{ ColumnId_DoorPositionYPos, "Teleport Pos Y" },
{ ColumnId_DoorPositionZPos, "Teleport Pos Z" },
{ ColumnId_DoorPositionXRot, "Teleport Rot X" },
{ ColumnId_DoorPositionYRot, "Teleport Rot Y" },
{ ColumnId_DoorPositionZRot, "Teleport Rot Z" },
{ ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" },
{ ColumnId_UseValue4, "Use value 4" },
{ ColumnId_Attribute1, "Attribute 1" },
{ ColumnId_Attribute2, "Attribute 2" },
{ ColumnId_MajorSkill1, "Major Skill 1" },
{ ColumnId_MajorSkill2, "Major Skill 2" },
{ ColumnId_MajorSkill3, "Major Skill 3" },
{ ColumnId_MajorSkill4, "Major Skill 4" },
{ ColumnId_MajorSkill5, "Major Skill 5" },
{ ColumnId_MinorSkill1, "Minor Skill 1" },
{ ColumnId_MinorSkill2, "Minor Skill 2" },
{ ColumnId_MinorSkill3, "Minor Skill 3" },
{ ColumnId_MinorSkill4, "Minor Skill 4" },
{ ColumnId_MinorSkill5, "Minor Skill 5" },
{ ColumnId_Skill1, "Skill 1" },
{ ColumnId_Skill2, "Skill 2" },
{ ColumnId_Skill3, "Skill 3" },
{ ColumnId_Skill4, "Skill 4" },
{ ColumnId_Skill5, "Skill 5" },
{ ColumnId_Skill6, "Skill 6" },
{ -1, 0 } // end marker
};
}
}
std::string CSMWorld::Columns::getName (ColumnId column)
{
for (int i=0; sNames[i].mName; ++i)
if (column==sNames[i].mId)
return sNames[i].mName;
return "";
}
int CSMWorld::Columns::getId (const std::string& name)
{
std::string name2 = Misc::StringUtils::lowerCase (name);
for (int i=0; sNames[i].mName; ++i)
if (name2==Misc::StringUtils::lowerCase (sNames[i].mName))
return sNames[i].mId;
return -1;
}
namespace
{
static const char *sSpecialisations[] =
{
"Combat", "Magic", "Stealth", 0
};
static const char *sAttributes[] =
{
"Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality",
"Luck", 0
};
static const char *sSpellTypes[] =
{
"Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0
};
static const char *sApparatusTypes[] =
{
"Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0
};
static const char *sArmorTypes[] =
{
"Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet",
"Right Gauntlet", "Shield", "Left Bracer", "Right Bracer", 0
};
static const char *sClothingTypes[] =
{
"Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring",
"Amulet", 0
};
static const char *sCreatureTypes[] =
{
"Creature", "Deadra", "Undead", "Humanoid", 0
};
static const char *sWeaponTypes[] =
{
"Short Blade 1H", "Long Blade 1H", "Long Blade 2H", "Blunt 1H", "Blunt 2H Close",
"Blunt 2H Wide", "Spear 2H", "Axe 1H", "Axe 2H", "Bow", "Crossbow", "Thrown", "Arrow",
"Bolt", 0
};
static const char *sModificationEnums[] =
{
"Base", "Modified", "Added", "Deleted", "Deleted", 0
};
static const char *sVarTypeEnums[] =
{
"unknown", "none", "short", "integer", "long", "float", "string", 0
};
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{
switch (column)
{
case CSMWorld::Columns::ColumnId_Specialisation: return sSpecialisations;
case CSMWorld::Columns::ColumnId_Attribute: return sAttributes;
case CSMWorld::Columns::ColumnId_SpellType: return sSpellTypes;
case CSMWorld::Columns::ColumnId_ApparatusType: return sApparatusTypes;
case CSMWorld::Columns::ColumnId_ArmorType: return sArmorTypes;
case CSMWorld::Columns::ColumnId_ClothingType: return sClothingTypes;
case CSMWorld::Columns::ColumnId_CreatureType: return sCreatureTypes;
case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes;
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
default: return 0;
}
}
}
bool CSMWorld::Columns::hasEnums (ColumnId column)
{
return getEnumNames (column)!=0 || column==ColumnId_RecordType;
}
std::vector<std::string> CSMWorld::Columns::getEnums (ColumnId column)
{
std::vector<std::string> enums;
if (const char **table = getEnumNames (column))
for (int i=0; table[i]; ++i)
enums.push_back (table[i]);
else if (column==ColumnId_RecordType)
{
enums.push_back (""); // none
for (int i=UniversalId::Type_None+1; i<UniversalId::NumberOfTypes; ++i)
enums.push_back (UniversalId (static_cast<UniversalId::Type> (i)).getTypeName());
}
return enums;
}

View file

@ -1,182 +1,204 @@
#ifndef CSM_WOLRD_COLUMNS_H #ifndef CSM_WOLRD_COLUMNS_H
#define CSM_WOLRD_COLUMNS_H #define CSM_WOLRD_COLUMNS_H
#include "columnbase.hpp" #include <string>
#include <vector>
namespace CSMWorld namespace CSMWorld
{ {
template<typename ESXRecordT> namespace Columns
struct FloatValueColumn : public Column<ESXRecordT>
{ {
FloatValueColumn() : Column<ESXRecordT> ("Value", ColumnBase::Display_Float) {} enum ColumnId
virtual QVariant get (const Record<ESXRecordT>& record) const
{ {
return record.get().mValue.getFloat(); ColumnId_Value = 0,
} ColumnId_Id = 1,
ColumnId_Modification = 2,
ColumnId_RecordType = 3,
ColumnId_ValueType = 4,
ColumnId_Description = 5,
ColumnId_Specialisation = 6,
ColumnId_Attribute = 7,
ColumnId_Name = 8,
ColumnId_Playable = 9,
ColumnId_Hidden = 10,
ColumnId_MaleWeight = 11,
ColumnId_FemaleWeight = 12,
ColumnId_MaleHeight = 13,
ColumnId_FemaleHeight = 14,
ColumnId_Volume = 15,
ColumnId_MinRange = 16,
ColumnId_MaxRange = 17,
ColumnId_SoundFile = 18,
ColumnId_MapColour = 19,
ColumnId_SleepEncounter = 20,
ColumnId_Texture = 21,
ColumnId_SpellType = 22,
ColumnId_Cost = 23,
ColumnId_ScriptText = 24,
ColumnId_Region = 25,
ColumnId_Cell = 26,
ColumnId_Scale = 27,
ColumnId_Owner = 28,
ColumnId_Soul = 29,
ColumnId_Faction = 30,
ColumnId_FactionIndex = 31,
ColumnId_Charges = 32,
ColumnId_Enchantment = 33,
ColumnId_CoinValue = 34,
ColumnId_Teleport = 25,
ColumnId_TeleportCell = 26,
ColumnId_LockLevel = 27,
ColumnId_Key = 28,
ColumnId_Trap = 29,
ColumnId_BeastRace = 30,
ColumnId_AutoCalc = 31,
ColumnId_StarterSpell = 32,
ColumnId_AlwaysSucceeds = 33,
ColumnId_SleepForbidden = 34,
ColumnId_InteriorWater = 35,
ColumnId_InteriorSky = 36,
ColumnId_Model = 37,
ColumnId_Script = 38,
ColumnId_Icon = 39,
ColumnId_Weight = 40,
ColumnId_EnchantmentPoints = 31,
ColumnId_Quality = 32,
ColumnId_Ai = 33,
ColumnId_AiHello = 34,
ColumnId_AiFlee = 35,
ColumnId_AiFight = 36,
ColumnId_AiAlarm = 37,
ColumnId_BuysWeapons = 38,
ColumnId_BuysArmor = 39,
ColumnId_BuysClothing = 40,
ColumnId_BuysBooks = 41,
ColumnId_BuysIngredients = 42,
ColumnId_BuysLockpicks = 43,
ColumnId_BuysProbes = 44,
ColumnId_BuysLights = 45,
ColumnId_BuysApparati = 46,
ColumnId_BuysRepairItems = 47,
ColumnId_BuysMiscItems = 48,
ColumnId_BuysPotions = 49,
ColumnId_BuysMagicItems = 50,
ColumnId_SellsSpells = 51,
ColumnId_Trainer = 52,
ColumnId_Spellmaking = 53,
ColumnId_EnchantingService = 54,
ColumnId_RepairService = 55,
ColumnId_ApparatusType = 56,
ColumnId_ArmorType = 57,
ColumnId_Health = 58,
ColumnId_ArmorValue = 59,
ColumnId_Scroll = 60,
ColumnId_ClothingType = 61,
ColumnId_WeightCapacity = 62,
ColumnId_OrganicContainer = 63,
ColumnId_Respawn = 64,
ColumnId_CreatureType = 65,
ColumnId_SoulPoints = 66,
ColumnId_OriginalCreature = 67,
ColumnId_Biped = 68,
ColumnId_HasWeapon = 69,
ColumnId_NoMovement = 70,
ColumnId_Swims = 71,
ColumnId_Flies = 72,
ColumnId_Walks = 73,
ColumnId_Essential = 74,
ColumnId_SkeletonBlood = 75,
ColumnId_MetalBlood = 76,
ColumnId_OpenSound = 77,
ColumnId_CloseSound = 78,
ColumnId_Duration = 79,
ColumnId_Radius = 80,
ColumnId_Colour = 81,
ColumnId_Sound = 82,
ColumnId_Dynamic = 83,
ColumnId_Portable = 84,
ColumnId_NegativeLight = 85,
ColumnId_Flickering = 86,
ColumnId_SlowFlickering = 87,
ColumnId_Pulsing = 88,
ColumnId_SlowPulsing = 89,
ColumnId_Fire = 90,
ColumnId_OffByDefault = 91,
ColumnId_IsKey = 92,
ColumnId_Race = 93,
ColumnId_Class = 94,
Columnid_Hair = 95,
ColumnId_Head = 96,
ColumnId_Female = 97,
ColumnId_WeaponType = 98,
ColumnId_WeaponSpeed = 99,
ColumnId_WeaponReach = 100,
ColumnId_MinChop = 101,
ColumnId_MaxChip = 102,
Columnid_MinSlash = 103,
ColumnId_MaxSlash = 104,
ColumnId_MinThrust = 105,
ColumnId_MaxThrust = 106,
ColumnId_Magical = 107,
ColumnId_Silver = 108,
ColumnId_Filter = 109,
ColumnId_PositionXPos = 110,
ColumnId_PositionYPos = 111,
ColumnId_PositionZPos = 112,
ColumnId_PositionXRot = 113,
ColumnId_PositionYRot = 114,
ColumnId_PositionZRot = 115,
ColumnId_DoorPositionXPos = 116,
ColumnId_DoorPositionYPos = 117,
ColumnId_DoorPositionZPos = 118,
ColumnId_DoorPositionXRot = 119,
ColumnId_DoorPositionYRot = 120,
ColumnId_DoorPositionZRot = 121,
virtual void set (Record<ESXRecordT>& record, const QVariant& data) // Allocated to a separate value range, so we don't get a collision should we ever need
{ // to extend the number of use values.
ESXRecordT record2 = record.get(); ColumnId_UseValue1 = 0x10000,
record2.mValue.setFloat (data.toFloat()); ColumnId_UseValue2 = 0x10001,
record.setModified (record2); ColumnId_UseValue3 = 0x10002,
} ColumnId_UseValue4 = 0x10003,
virtual bool isEditable() const // Allocated to a separate value range, so we don't get a collision should we ever need
{ // to extend the number of attributes. Note that this is not the number of different
return true; // attributes, but the number of attributes that can be references from a record.
} ColumnId_Attribute1 = 0x20000,
}; ColumnId_Attribute2 = 0x20001,
template<typename ESXRecordT> // Allocated to a separate value range, so we don't get a collision should we ever need
struct StringIdColumn : public Column<ESXRecordT> // to extend the number of skills. Note that this is not the number of different
{ // skills, but the number of skills that can be references from a record.
StringIdColumn() : Column<ESXRecordT> ("ID", ColumnBase::Display_String) {} ColumnId_MajorSkill1 = 0x30000,
ColumnId_MajorSkill2 = 0x30001,
ColumnId_MajorSkill3 = 0x30002,
ColumnId_MajorSkill4 = 0x30003,
ColumnId_MajorSkill5 = 0x30004,
virtual QVariant get (const Record<ESXRecordT>& record) const ColumnId_MinorSkill1 = 0x40000,
{ ColumnId_MinorSkill2 = 0x40001,
return record.get().mId.c_str(); ColumnId_MinorSkill3 = 0x40002,
} ColumnId_MinorSkill4 = 0x40003,
ColumnId_MinorSkill5 = 0x40004,
virtual bool isEditable() const ColumnId_Skill1 = 0x50000,
{ ColumnId_Skill2 = 0x50001,
return false; ColumnId_Skill3 = 0x50002,
} ColumnId_Skill4 = 0x50003,
}; ColumnId_Skill5 = 0x50004,
ColumnId_Skill6 = 0x50005
};
template<typename ESXRecordT> std::string getName (ColumnId column);
struct RecordStateColumn : public Column<ESXRecordT>
{
RecordStateColumn() : Column<ESXRecordT> ("*", ColumnBase::Display_Integer) {}
virtual QVariant get (const Record<ESXRecordT>& record) const int getId (const std::string& name);
{ ///< Will return -1 for an invalid name.
if (record.mState==Record<ESXRecordT>::State_Erased)
return static_cast<int> (Record<ESXRecordT>::State_Deleted);
return static_cast<int> (record.mState); bool hasEnums (ColumnId column);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data) std::vector<std::string> getEnums (ColumnId column);
{ ///< Returns an empty vector, if \æ column isn't an enum type column.
record.mState = static_cast<RecordBase::State> (data.toInt()); }
}
virtual bool isEditable() const
{
return true;
}
virtual bool isUserEditable() const
{
return false;
}
};
template<typename ESXRecordT>
struct FixedRecordTypeColumn : public Column<ESXRecordT>
{
int mType;
FixedRecordTypeColumn (int type)
: Column<ESXRecordT> ("Record Type", ColumnBase::Display_Integer, 0), mType (type) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return mType;
}
virtual bool isEditable() const
{
return false;
}
};
/// \attention A var type column must be immediately followed by a suitable value column.
template<typename ESXRecordT>
struct VarTypeColumn : public Column<ESXRecordT>
{
VarTypeColumn (ColumnBase::Display display) : Column<ESXRecordT> ("Type", display) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mValue.getType());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mValue.setType (static_cast<ESM::VarType> (data.toInt()));
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct VarValueColumn : public Column<ESXRecordT>
{
VarValueColumn() : Column<ESXRecordT> ("Value", ColumnBase::Display_Var) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
switch (record.get().mValue.getType())
{
case ESM::VT_String:
return record.get().mValue.getString().c_str(); break;
case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
return record.get().mValue.getInteger(); break;
case ESM::VT_Float:
return record.get().mValue.getFloat(); break;
default: return QVariant();
}
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
switch (record2.mValue.getType())
{
case ESM::VT_String:
record2.mValue.setString (data.toString().toUtf8().constData());
break;
case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
record2.mValue.setInteger (data.toInt());
break;
case ESM::VT_Float:
record2.mValue.setFloat (data.toFloat());
break;
default: break;
}
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
} }
#endif #endif

View file

@ -1,9 +1,9 @@
#include "commands.hpp" #include "commands.hpp"
#include <QAbstractTableModel> #include <QAbstractItemModel>
#include "idtableproxymodel.hpp" #include "idtable.hpp"
#include "idtable.hpp" #include "idtable.hpp"
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
@ -25,15 +25,28 @@ void CSMWorld::ModifyCommand::undo()
mModel.setData (mIndex, mOld); mModel.setData (mIndex, mOld);
} }
CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent) CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id) : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
{ {
setText (("Create record " + id).c_str()); setText (("Create record " + id).c_str());
} }
void CSMWorld::CreateCommand::addValue (int column, const QVariant& value)
{
mValues[column] = value;
}
void CSMWorld::CreateCommand::setType (UniversalId::Type type)
{
mType = type;
}
void CSMWorld::CreateCommand::redo() void CSMWorld::CreateCommand::redo()
{ {
mModel.addRecord (mId); mModel.addRecord (mId, mType);
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
} }
void CSMWorld::CreateCommand::undo() void CSMWorld::CreateCommand::undo()
@ -56,7 +69,9 @@ CSMWorld::RevertCommand::~RevertCommand()
void CSMWorld::RevertCommand::redo() void CSMWorld::RevertCommand::redo()
{ {
QModelIndex index = mModel.getModelIndex (mId, 1); int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt()); RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly) if (state==RecordBase::State_ModifiedOnly)
@ -71,7 +86,7 @@ void CSMWorld::RevertCommand::redo()
void CSMWorld::RevertCommand::undo() void CSMWorld::RevertCommand::undo()
{ {
mModel.setRecord (*mOld); mModel.setRecord (mId, *mOld);
} }
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent) CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
@ -89,7 +104,9 @@ CSMWorld::DeleteCommand::~DeleteCommand()
void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::redo()
{ {
QModelIndex index = mModel.getModelIndex (mId, 1); int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt()); RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly) if (state==RecordBase::State_ModifiedOnly)
@ -104,5 +121,5 @@ void CSMWorld::DeleteCommand::redo()
void CSMWorld::DeleteCommand::undo() void CSMWorld::DeleteCommand::undo()
{ {
mModel.setRecord (*mOld); mModel.setRecord (mId, *mOld);
} }

View file

@ -4,17 +4,20 @@
#include "record.hpp" #include "record.hpp"
#include <string> #include <string>
#include <map>
#include <QVariant> #include <QVariant>
#include <QUndoCommand> #include <QUndoCommand>
#include <QModelIndex> #include <QModelIndex>
#include "universalid.hpp"
class QModelIndex; class QModelIndex;
class QAbstractItemModel; class QAbstractItemModel;
namespace CSMWorld namespace CSMWorld
{ {
class IdTableProxyModel; class IdTable;
class IdTable; class IdTable;
class RecordBase; class RecordBase;
@ -37,12 +40,18 @@ namespace CSMWorld
class CreateCommand : public QUndoCommand class CreateCommand : public QUndoCommand
{ {
IdTableProxyModel& mModel; IdTable& mModel;
std::string mId; std::string mId;
UniversalId::Type mType;
std::map<int, QVariant> mValues;
public: public:
CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0); CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
void setType (UniversalId::Type type);
void addValue (int column, const QVariant& value);
virtual void redo(); virtual void redo();

View file

@ -2,27 +2,49 @@
#include "data.hpp" #include "data.hpp"
#include <stdexcept> #include <stdexcept>
#include <algorithm>
#include <QAbstractTableModel> #include <QAbstractItemModel>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/esm/cellref.hpp>
#include "idtable.hpp" #include "idtable.hpp"
#include "columnimp.hpp"
#include "regionmap.hpp"
#include "columns.hpp" #include "columns.hpp"
void CSMWorld::Data::addModel (QAbstractTableModel *model, UniversalId::Type type1, void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1,
UniversalId::Type type2) UniversalId::Type type2, bool update)
{ {
mModels.push_back (model); mModels.push_back (model);
mModelIndex.insert (std::make_pair (type1, model)); mModelIndex.insert (std::make_pair (type1, model));
if (type2!=UniversalId::Type_None) if (type2!=UniversalId::Type_None)
mModelIndex.insert (std::make_pair (type2, model)); mModelIndex.insert (std::make_pair (type2, model));
if (update)
{
connect (model, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&)));
connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (rowsChanged (const QModelIndex&, int, int)));
connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
this, SLOT (rowsChanged (const QModelIndex&, int, int)));
}
} }
CSMWorld::Data::Data() void CSMWorld::Data::appendIds (std::vector<std::string>& ids, const CollectionBase& collection,
bool listDeleted)
{
std::vector<std::string> ids2 = collection.getIds (listDeleted);
ids.insert (ids.end(), ids2.begin(), ids2.end());
}
CSMWorld::Data::Data() : mRefs (mCells)
{ {
mGlobals.addColumn (new StringIdColumn<ESM::Global>); mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>); mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
@ -33,16 +55,157 @@ CSMWorld::Data::Data()
mGmsts.addColumn (new StringIdColumn<ESM::GameSetting>); mGmsts.addColumn (new StringIdColumn<ESM::GameSetting>);
mGmsts.addColumn (new RecordStateColumn<ESM::GameSetting>); mGmsts.addColumn (new RecordStateColumn<ESM::GameSetting>);
mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst)); mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst));
mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst));
mGmsts.addColumn (new VarTypeColumn<ESM::GameSetting> (ColumnBase::Display_GmstVarType)); mGmsts.addColumn (new VarTypeColumn<ESM::GameSetting> (ColumnBase::Display_GmstVarType));
mGmsts.addColumn (new VarValueColumn<ESM::GameSetting>); mGmsts.addColumn (new VarValueColumn<ESM::GameSetting>);
mSkills.addColumn (new StringIdColumn<ESM::Skill>);
mSkills.addColumn (new RecordStateColumn<ESM::Skill>);
mSkills.addColumn (new FixedRecordTypeColumn<ESM::Skill> (UniversalId::Type_Skill));
mSkills.addColumn (new AttributeColumn<ESM::Skill>);
mSkills.addColumn (new SpecialisationColumn<ESM::Skill>);
for (int i=0; i<4; ++i)
mSkills.addColumn (new UseValueColumn<ESM::Skill> (i));
mSkills.addColumn (new DescriptionColumn<ESM::Skill>);
mClasses.addColumn (new StringIdColumn<ESM::Class>);
mClasses.addColumn (new RecordStateColumn<ESM::Class>);
mClasses.addColumn (new FixedRecordTypeColumn<ESM::Class> (UniversalId::Type_Class));
mClasses.addColumn (new NameColumn<ESM::Class>);
mClasses.addColumn (new AttributesColumn<ESM::Class> (0));
mClasses.addColumn (new AttributesColumn<ESM::Class> (1));
mClasses.addColumn (new SpecialisationColumn<ESM::Class>);
for (int i=0; i<5; ++i)
mClasses.addColumn (new SkillsColumn<ESM::Class> (i, true, true));
for (int i=0; i<5; ++i)
mClasses.addColumn (new SkillsColumn<ESM::Class> (i, true, false));
mClasses.addColumn (new PlayableColumn<ESM::Class>);
mClasses.addColumn (new DescriptionColumn<ESM::Class>);
mFactions.addColumn (new StringIdColumn<ESM::Faction>);
mFactions.addColumn (new RecordStateColumn<ESM::Faction>);
mFactions.addColumn (new FixedRecordTypeColumn<ESM::Faction> (UniversalId::Type_Faction));
mFactions.addColumn (new NameColumn<ESM::Faction>);
mFactions.addColumn (new AttributesColumn<ESM::Faction> (0));
mFactions.addColumn (new AttributesColumn<ESM::Faction> (1));
mFactions.addColumn (new HiddenColumn<ESM::Faction>);
for (int i=0; i<6; ++i)
mFactions.addColumn (new SkillsColumn<ESM::Faction> (i));
mRaces.addColumn (new StringIdColumn<ESM::Race>);
mRaces.addColumn (new RecordStateColumn<ESM::Race>);
mRaces.addColumn (new FixedRecordTypeColumn<ESM::Race> (UniversalId::Type_Race));
mRaces.addColumn (new NameColumn<ESM::Race>);
mRaces.addColumn (new DescriptionColumn<ESM::Race>);
mRaces.addColumn (new FlagColumn<ESM::Race> (Columns::ColumnId_Playable, 0x1));
mRaces.addColumn (new FlagColumn<ESM::Race> (Columns::ColumnId_BeastRace, 0x2));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, true));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, false));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, true));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, false));
mSounds.addColumn (new StringIdColumn<ESM::Sound>);
mSounds.addColumn (new RecordStateColumn<ESM::Sound>);
mSounds.addColumn (new FixedRecordTypeColumn<ESM::Sound> (UniversalId::Type_Sound));
mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_Volume));
mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_MinRange));
mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_MaxRange));
mSounds.addColumn (new SoundFileColumn<ESM::Sound>);
mScripts.addColumn (new StringIdColumn<ESM::Script>);
mScripts.addColumn (new RecordStateColumn<ESM::Script>);
mScripts.addColumn (new FixedRecordTypeColumn<ESM::Script> (UniversalId::Type_Script));
mScripts.addColumn (new ScriptColumn<ESM::Script>);
mRegions.addColumn (new StringIdColumn<ESM::Region>);
mRegions.addColumn (new RecordStateColumn<ESM::Region>);
mRegions.addColumn (new FixedRecordTypeColumn<ESM::Region> (UniversalId::Type_Region));
mRegions.addColumn (new NameColumn<ESM::Region>);
mRegions.addColumn (new MapColourColumn<ESM::Region>);
mRegions.addColumn (new SleepListColumn<ESM::Region>);
mBirthsigns.addColumn (new StringIdColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new RecordStateColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new FixedRecordTypeColumn<ESM::BirthSign> (UniversalId::Type_Birthsign));
mBirthsigns.addColumn (new NameColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new TextureColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new DescriptionColumn<ESM::BirthSign>);
mSpells.addColumn (new StringIdColumn<ESM::Spell>);
mSpells.addColumn (new RecordStateColumn<ESM::Spell>);
mSpells.addColumn (new FixedRecordTypeColumn<ESM::Spell> (UniversalId::Type_Spell));
mSpells.addColumn (new NameColumn<ESM::Spell>);
mSpells.addColumn (new SpellTypeColumn<ESM::Spell>);
mSpells.addColumn (new CostColumn<ESM::Spell>);
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AutoCalc, 0x1));
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2));
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4));
mCells.addColumn (new StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
mCells.addColumn (new NameColumn<Cell>);
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx));
mCells.addColumn (new RegionColumn<Cell>);
mRefs.addColumn (new StringIdColumn<CellRef> (true));
mRefs.addColumn (new RecordStateColumn<CellRef>);
mRefs.addColumn (new CellColumn<CellRef>);
mRefs.addColumn (new IdColumn<CellRef>);
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 1, false));
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 2, false));
mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 0, false));
mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 1, false));
mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 2, false));
mRefs.addColumn (new ScaleColumn<CellRef>);
mRefs.addColumn (new OwnerColumn<CellRef>);
mRefs.addColumn (new SoulColumn<CellRef>);
mRefs.addColumn (new FactionColumn<CellRef>);
mRefs.addColumn (new FactionIndexColumn<CellRef>);
mRefs.addColumn (new ChargesColumn<CellRef>);
mRefs.addColumn (new EnchantmentChargesColumn<CellRef>);
mRefs.addColumn (new GoldValueColumn<CellRef>);
mRefs.addColumn (new TeleportColumn<CellRef>);
mRefs.addColumn (new TeleportCellColumn<CellRef>);
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 0, true));
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 1, true));
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 2, true));
mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 0, true));
mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 1, true));
mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 2, true));
mRefs.addColumn (new LockLevelColumn<CellRef>);
mRefs.addColumn (new KeyColumn<CellRef>);
mRefs.addColumn (new TrapColumn<CellRef>);
mFilters.addColumn (new StringIdColumn<CSMFilter::Filter>);
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill, false);
addModel (new IdTable (&mClasses), UniversalId::Type_Classes, UniversalId::Type_Class);
addModel (new IdTable (&mFactions), UniversalId::Type_Factions, UniversalId::Type_Faction);
addModel (new IdTable (&mRaces), UniversalId::Type_Races, UniversalId::Type_Race);
addModel (new IdTable (&mSounds), UniversalId::Type_Sounds, UniversalId::Type_Sound);
addModel (new IdTable (&mScripts), UniversalId::Type_Scripts, UniversalId::Type_Script);
addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region);
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign);
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable);
addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference, false);
addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false);
} }
CSMWorld::Data::~Data() CSMWorld::Data::~Data()
{ {
for (std::vector<QAbstractTableModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
delete *iter; delete *iter;
} }
@ -66,12 +229,155 @@ CSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts()
return mGmsts; return mGmsts;
} }
QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) const CSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills() const
{ {
std::map<UniversalId::Type, QAbstractTableModel *>::iterator iter = mModelIndex.find (id.getType()); return mSkills;
}
CSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills()
{
return mSkills;
}
const CSMWorld::IdCollection<ESM::Class>& CSMWorld::Data::getClasses() const
{
return mClasses;
}
CSMWorld::IdCollection<ESM::Class>& CSMWorld::Data::getClasses()
{
return mClasses;
}
const CSMWorld::IdCollection<ESM::Faction>& CSMWorld::Data::getFactions() const
{
return mFactions;
}
CSMWorld::IdCollection<ESM::Faction>& CSMWorld::Data::getFactions()
{
return mFactions;
}
const CSMWorld::IdCollection<ESM::Race>& CSMWorld::Data::getRaces() const
{
return mRaces;
}
CSMWorld::IdCollection<ESM::Race>& CSMWorld::Data::getRaces()
{
return mRaces;
}
const CSMWorld::IdCollection<ESM::Sound>& CSMWorld::Data::getSounds() const
{
return mSounds;
}
CSMWorld::IdCollection<ESM::Sound>& CSMWorld::Data::getSounds()
{
return mSounds;
}
const CSMWorld::IdCollection<ESM::Script>& CSMWorld::Data::getScripts() const
{
return mScripts;
}
CSMWorld::IdCollection<ESM::Script>& CSMWorld::Data::getScripts()
{
return mScripts;
}
const CSMWorld::IdCollection<ESM::Region>& CSMWorld::Data::getRegions() const
{
return mRegions;
}
CSMWorld::IdCollection<ESM::Region>& CSMWorld::Data::getRegions()
{
return mRegions;
}
const CSMWorld::IdCollection<ESM::BirthSign>& CSMWorld::Data::getBirthsigns() const
{
return mBirthsigns;
}
CSMWorld::IdCollection<ESM::BirthSign>& CSMWorld::Data::getBirthsigns()
{
return mBirthsigns;
}
const CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells() const
{
return mSpells;
}
CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()
{
return mSpells;
}
const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
{
return mCells;
}
CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells()
{
return mCells;
}
const CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() const
{
return mReferenceables;
}
CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables()
{
return mReferenceables;
}
const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const
{
return mRefs;
}
CSMWorld::RefCollection& CSMWorld::Data::getReferences()
{
return mRefs;
}
const CSMWorld::IdCollection<CSMFilter::Filter>& CSMWorld::Data::getFilters() const
{
return mFilters;
}
CSMWorld::IdCollection<CSMFilter::Filter>& CSMWorld::Data::getFilters()
{
return mFilters;
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id)
{
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
if (iter==mModelIndex.end()) if (iter==mModelIndex.end())
{
// try creating missing (secondary) tables on the fly
//
// Note: We create these tables here so we don't have to deal with them during load/initial
// construction of the ESX data where no update signals are available.
if (id.getType()==UniversalId::Type_RegionMap)
{
RegionMap *table = 0;
addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap,
UniversalId::Type_None, false);
return table;
}
throw std::logic_error ("No table model available for " + id.toString()); throw std::logic_error ("No table model available for " + id.toString());
}
return iter->second; return iter->second;
} }
@ -102,7 +408,44 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
{ {
case ESM::REC_GLOB: mGlobals.load (reader, base); break; case ESM::REC_GLOB: mGlobals.load (reader, base); break;
case ESM::REC_GMST: mGmsts.load (reader, base); break; case ESM::REC_GMST: mGmsts.load (reader, base); break;
case ESM::REC_SKIL: mSkills.load (reader, base); break;
case ESM::REC_CLAS: mClasses.load (reader, base); break;
case ESM::REC_FACT: mFactions.load (reader, base); break;
case ESM::REC_RACE: mRaces.load (reader, base); break;
case ESM::REC_SOUN: mSounds.load (reader, base); break;
case ESM::REC_SCPT: mScripts.load (reader, base); break;
case ESM::REC_REGN: mRegions.load (reader, base); break;
case ESM::REC_BSGN: mBirthsigns.load (reader, base); break;
case ESM::REC_SPEL: mSpells.load (reader, base); break;
case ESM::REC_CELL:
mCells.load (reader, base);
mRefs.load (reader, mCells.getSize()-1, base);
break;
case ESM::REC_ACTI: mReferenceables.load (reader, base, UniversalId::Type_Activator); break;
case ESM::REC_ALCH: mReferenceables.load (reader, base, UniversalId::Type_Potion); break;
case ESM::REC_APPA: mReferenceables.load (reader, base, UniversalId::Type_Apparatus); break;
case ESM::REC_ARMO: mReferenceables.load (reader, base, UniversalId::Type_Armor); break;
case ESM::REC_BOOK: mReferenceables.load (reader, base, UniversalId::Type_Book); break;
case ESM::REC_CLOT: mReferenceables.load (reader, base, UniversalId::Type_Clothing); break;
case ESM::REC_CONT: mReferenceables.load (reader, base, UniversalId::Type_Container); break;
case ESM::REC_CREA: mReferenceables.load (reader, base, UniversalId::Type_Creature); break;
case ESM::REC_DOOR: mReferenceables.load (reader, base, UniversalId::Type_Door); break;
case ESM::REC_INGR: mReferenceables.load (reader, base, UniversalId::Type_Ingredient); break;
case ESM::REC_LEVC:
mReferenceables.load (reader, base, UniversalId::Type_CreatureLevelledList); break;
case ESM::REC_LEVI:
mReferenceables.load (reader, base, UniversalId::Type_ItemLevelledList); break;
case ESM::REC_LIGH: mReferenceables.load (reader, base, UniversalId::Type_Light); break;
case ESM::REC_LOCK: mReferenceables.load (reader, base, UniversalId::Type_Lockpick); break;
case ESM::REC_MISC:
mReferenceables.load (reader, base, UniversalId::Type_Miscellaneous); break;
case ESM::REC_NPC_: mReferenceables.load (reader, base, UniversalId::Type_Npc); break;
case ESM::REC_PROB: mReferenceables.load (reader, base, UniversalId::Type_Probe); break;
case ESM::REC_REPA: mReferenceables.load (reader, base, UniversalId::Type_Repair); break;
case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break;
case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break;
default: default:
@ -110,4 +453,55 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
reader.skipRecord(); reader.skipRecord();
} }
} }
}
bool CSMWorld::Data::hasId (const std::string& id) const
{
return
getGlobals().searchId (id)!=-1 ||
getGmsts().searchId (id)!=-1 ||
getSkills().searchId (id)!=-1 ||
getClasses().searchId (id)!=-1 ||
getFactions().searchId (id)!=-1 ||
getRaces().searchId (id)!=-1 ||
getSounds().searchId (id)!=-1 ||
getScripts().searchId (id)!=-1 ||
getRegions().searchId (id)!=-1 ||
getBirthsigns().searchId (id)!=-1 ||
getSpells().searchId (id)!=-1 ||
getCells().searchId (id)!=-1 ||
getReferenceables().searchId (id)!=-1;
}
std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
{
std::vector<std::string> ids;
appendIds (ids, mGlobals, listDeleted);
appendIds (ids, mGmsts, listDeleted);
appendIds (ids, mClasses, listDeleted);
appendIds (ids, mFactions, listDeleted);
appendIds (ids, mRaces, listDeleted);
appendIds (ids, mSounds, listDeleted);
appendIds (ids, mScripts, listDeleted);
appendIds (ids, mRegions, listDeleted);
appendIds (ids, mBirthsigns, listDeleted);
appendIds (ids, mSpells, listDeleted);
appendIds (ids, mCells, listDeleted);
appendIds (ids, mReferenceables, listDeleted);
std::sort (ids.begin(), ids.end());
return ids;
}
void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
if (topLeft.column()<=0)
emit idListChanged();
}
void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end)
{
emit idListChanged();
} }

View file

@ -6,35 +6,71 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <QObject>
#include <QModelIndex>
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/esm/loadgmst.hpp> #include <components/esm/loadgmst.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadclas.hpp>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadrace.hpp>
#include <components/esm/loadsoun.hpp>
#include <components/esm/loadscpt.hpp>
#include <components/esm/loadregn.hpp>
#include <components/esm/loadbsgn.hpp>
#include <components/esm/loadspel.hpp>
#include "../filter/filter.hpp"
#include "idcollection.hpp" #include "idcollection.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "cell.hpp"
#include "refidcollection.hpp"
#include "refcollection.hpp"
class QAbstractTableModel; class QAbstractItemModel;
namespace CSMWorld namespace CSMWorld
{ {
class Data class Data : public QObject
{ {
Q_OBJECT
IdCollection<ESM::Global> mGlobals; IdCollection<ESM::Global> mGlobals;
IdCollection<ESM::GameSetting> mGmsts; IdCollection<ESM::GameSetting> mGmsts;
std::vector<QAbstractTableModel *> mModels; IdCollection<ESM::Skill> mSkills;
std::map<UniversalId::Type, QAbstractTableModel *> mModelIndex; IdCollection<ESM::Class> mClasses;
IdCollection<ESM::Faction> mFactions;
IdCollection<ESM::Race> mRaces;
IdCollection<ESM::Sound> mSounds;
IdCollection<ESM::Script> mScripts;
IdCollection<ESM::Region> mRegions;
IdCollection<ESM::BirthSign> mBirthsigns;
IdCollection<ESM::Spell> mSpells;
IdCollection<Cell> mCells;
RefIdCollection mReferenceables;
RefCollection mRefs;
IdCollection<CSMFilter::Filter> mFilters;
std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
// not implemented // not implemented
Data (const Data&); Data (const Data&);
Data& operator= (const Data&); Data& operator= (const Data&);
void addModel (QAbstractTableModel *model, UniversalId::Type type1, void addModel (QAbstractItemModel *model, UniversalId::Type type1,
UniversalId::Type type2 = UniversalId::Type_None); UniversalId::Type type2 = UniversalId::Type_None, bool update = true);
static void appendIds (std::vector<std::string>& ids, const CollectionBase& collection,
bool listDeleted);
///< Append all IDs from collection to \a ids.
public: public:
Data(); Data();
~Data(); virtual ~Data();
const IdCollection<ESM::Global>& getGlobals() const; const IdCollection<ESM::Global>& getGlobals() const;
@ -44,7 +80,59 @@ namespace CSMWorld
IdCollection<ESM::GameSetting>& getGmsts(); IdCollection<ESM::GameSetting>& getGmsts();
QAbstractTableModel *getTableModel (const UniversalId& id); const IdCollection<ESM::Skill>& getSkills() const;
IdCollection<ESM::Skill>& getSkills();
const IdCollection<ESM::Class>& getClasses() const;
IdCollection<ESM::Class>& getClasses();
const IdCollection<ESM::Faction>& getFactions() const;
IdCollection<ESM::Faction>& getFactions();
const IdCollection<ESM::Race>& getRaces() const;
IdCollection<ESM::Race>& getRaces();
const IdCollection<ESM::Sound>& getSounds() const;
IdCollection<ESM::Sound>& getSounds();
const IdCollection<ESM::Script>& getScripts() const;
IdCollection<ESM::Script>& getScripts();
const IdCollection<ESM::Region>& getRegions() const;
IdCollection<ESM::Region>& getRegions();
const IdCollection<ESM::BirthSign>& getBirthsigns() const;
IdCollection<ESM::BirthSign>& getBirthsigns();
const IdCollection<ESM::Spell>& getSpells() const;
IdCollection<ESM::Spell>& getSpells();
const IdCollection<Cell>& getCells() const;
IdCollection<Cell>& getCells();
const RefIdCollection& getReferenceables() const;
RefIdCollection& getReferenceables();
const RefCollection& getReferences() const;
RefCollection& getReferences();
const IdCollection<CSMFilter::Filter>& getFilters() const;
IdCollection<CSMFilter::Filter>& getFilters();
QAbstractItemModel *getTableModel (const UniversalId& id);
///< If no table model is available for \a id, an exception is thrown. ///< If no table model is available for \a id, an exception is thrown.
/// ///
/// \note The returned table may either be the model for the ID itself or the model that /// \note The returned table may either be the model for the ID itself or the model that
@ -55,6 +143,23 @@ namespace CSMWorld
void loadFile (const boost::filesystem::path& path, bool base); void loadFile (const boost::filesystem::path& path, bool base);
///< Merging content of a file into base or modified. ///< Merging content of a file into base or modified.
bool hasId (const std::string& id) const;
std::vector<std::string> getIds (bool listDeleted = true) const;
///< Return a sorted collection of all IDs that are not internal to the editor.
///
/// \param listDeleted include deleted record in the list
signals:
void idListChanged();
private slots:
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void rowsChanged (const QModelIndex& parent, int start, int end);
}; };
} }

View file

@ -1,6 +0,0 @@
#include "idcollection.hpp"
CSMWorld::IdCollectionBase::IdCollectionBase() {}
CSMWorld::IdCollectionBase::~IdCollectionBase() {}

View file

@ -1,330 +1,34 @@
#ifndef CSM_WOLRD_IDCOLLECTION_H #ifndef CSM_WOLRD_IDCOLLECTION_H
#define CSM_WOLRD_IDCOLLECTION_H #define CSM_WOLRD_IDCOLLECTION_H
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <cctype>
#include <stdexcept>
#include <functional>
#include <QVariant>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/misc/stringops.hpp> #include "collection.hpp"
#include "columnbase.hpp"
namespace CSMWorld namespace CSMWorld
{ {
class IdCollectionBase
{
// not implemented
IdCollectionBase (const IdCollectionBase&);
IdCollectionBase& operator= (const IdCollectionBase&);
/// \brief Single type collection of top level records
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
{
public: public:
IdCollectionBase(); void load (ESM::ESMReader& reader, bool base,
UniversalId::Type type = UniversalId::Type_None);
virtual ~IdCollectionBase(); ///< \param type Will be ignored, unless the collection supports multiple record types
virtual int getSize() const = 0;
virtual std::string getId (int index) const = 0;
virtual int getIndex (const std::string& id) const = 0;
virtual int getColumns() const = 0;
virtual const ColumnBase& getColumn (int column) const = 0;
virtual QVariant getData (int index, int column) const = 0;
virtual void setData (int index, int column, const QVariant& data) = 0;
virtual void merge() = 0;
///< Merge modified into base.
virtual void purge() = 0;
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) = 0;
virtual void appendBlankRecord (const std::string& id) = 0;
virtual int searchId (const std::string& id) const = 0;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record) = 0;
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record) = 0;
///< If the record type does not match, an exception is thrown.
virtual std::string getId (const RecordBase& record) const = 0;
///< Return ID for \a record.
///
/// \attention Throws an exception, if the type of \a record does not match.
virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual void load (ESM::ESMReader& reader, bool base) = 0;
}; };
///< \brief Collection of ID-based records template<typename ESXRecordT, typename IdAccessorT>
template<typename ESXRecordT> void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base,
class IdCollection : public IdCollectionBase UniversalId::Type type)
{
std::vector<Record<ESXRecordT> > mRecords;
std::map<std::string, int> mIndex;
std::vector<Column<ESXRecordT> *> mColumns;
// not implemented
IdCollection (const IdCollection&);
IdCollection& operator= (const IdCollection&);
public:
IdCollection();
virtual ~IdCollection();
void add (const ESXRecordT& record);
///< Add a new record (modified)
virtual int getSize() const;
virtual std::string getId (int index) const;
virtual int getIndex (const std::string& id) const;
virtual int getColumns() const;
virtual QVariant getData (int index, int column) const;
virtual void setData (int index, int column, const QVariant& data);
virtual const ColumnBase& getColumn (int column) const;
virtual void merge();
///< Merge modified into base.
virtual void purge();
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) ;
virtual void appendBlankRecord (const std::string& id);
virtual int searchId (const std::string& id) const;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record);
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record);
///< If the record type does not match, an exception is thrown.
virtual std::string getId (const RecordBase& record) const;
///< Return ID for \a record.
///
/// \attention Throw san exception, if the type of \a record does not match.
virtual const RecordBase& getRecord (const std::string& id) const;
virtual void load (ESM::ESMReader& reader, bool base);
void addColumn (Column<ESXRecordT> *column);
};
template<typename ESXRecordT>
IdCollection<ESXRecordT>::IdCollection()
{}
template<typename ESXRecordT>
IdCollection<ESXRecordT>::~IdCollection()
{
for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
delete *iter;
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::add (const ESXRecordT& record)
{
std::string id = Misc::StringUtils::lowerCase(record.mId);
std::map<std::string, int>::iterator iter = mIndex.find (id);
if (iter==mIndex.end())
{
Record<ESXRecordT> record2;
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
mRecords.push_back (record2);
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
}
else
{
mRecords[iter->second].setModified (record);
}
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::getSize() const
{
return mRecords.size();
}
template<typename ESXRecordT>
std::string IdCollection<ESXRecordT>::getId (int index) const
{
return mRecords.at (index).get().mId;
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::getIndex (const std::string& id) const
{
int index = searchId (id);
if (index==-1)
throw std::runtime_error ("invalid ID: " + id);
return index;
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::getColumns() const
{
return mColumns.size();
}
template<typename ESXRecordT>
QVariant IdCollection<ESXRecordT>::getData (int index, int column) const
{
return mColumns.at (column)->get (mRecords.at (index));
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::setData (int index, int column, const QVariant& data)
{
return mColumns.at (column)->set (mRecords.at (index), data);
}
template<typename ESXRecordT>
const ColumnBase& IdCollection<ESXRecordT>::getColumn (int column) const
{
return *mColumns.at (column);
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::addColumn (Column<ESXRecordT> *column)
{
mColumns.push_back (column);
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::merge()
{
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
iter->merge();
purge();
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::purge()
{
mRecords.erase (std::remove_if (mRecords.begin(), mRecords.end(),
std::mem_fun_ref (&Record<ESXRecordT>::isErased) // I want lambda :(
), mRecords.end());
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::removeRows (int index, int count)
{
mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count);
typename std::map<std::string, int>::iterator iter = mIndex.begin();
while (iter!=mIndex.end())
{
if (iter->second>=index)
{
if (iter->second>=index+count)
{
iter->second -= count;
}
else
{
mIndex.erase (iter++);
}
}
++iter;
}
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::appendBlankRecord (const std::string& id)
{
ESXRecordT record;
record.mId = id;
record.blank();
add (record);
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::searchId (const std::string& id) const
{
std::string id2 = Misc::StringUtils::lowerCase(id);
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
if (iter==mIndex.end())
return -1;
return iter->second;
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::replace (int index, const RecordBase& record)
{
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::appendRecord (const RecordBase& record)
{
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (getId (record)), mRecords.size()-1));
}
template<typename ESXRecordT>
std::string IdCollection<ESXRecordT>::getId (const RecordBase& record) const
{
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
return (record2.isModified() ? record2.mModified : record2.mBase).mId;
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::load (ESM::ESMReader& reader, bool base)
{ {
std::string id = reader.getHNOString ("NAME"); std::string id = reader.getHNOString ("NAME");
int index = searchId (id);
if (reader.isNextSub ("DELE")) if (reader.isNextSub ("DELE"))
{ {
int index = Collection<ESXRecordT, IdAccessorT>::searchId (id);
reader.skipRecord(); reader.skipRecord();
if (index==-1) if (index==-1)
@ -337,19 +41,23 @@ namespace CSMWorld
} }
else if (base) else if (base)
{ {
removeRows (index, 1); Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
} }
else else
{ {
mRecords[index].mState = RecordBase::State_Deleted; Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
record.mState = RecordBase::State_Deleted;
this->setRecord (index, record);
} }
} }
else else
{ {
ESXRecordT record; ESXRecordT record;
record.mId = id; IdAccessorT().getId (record) = id;
record.load (reader); record.load (reader);
int index = this->searchId (IdAccessorT().getId (record));
if (index==-1) if (index==-1)
{ {
// new record // new record
@ -357,27 +65,22 @@ namespace CSMWorld
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record; (base ? record2.mBase : record2.mModified) = record;
appendRecord (record2); this->appendRecord (record2);
} }
else else
{ {
// old record // old record
Record<ESXRecordT>& record2 = mRecords[index]; Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (base) if (base)
record2.mBase = record; record2.mBase = record;
else else
record2.setModified (record); record2.setModified (record);
this->setRecord (index, record2);
} }
} }
} }
template<typename ESXRecordT>
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
{
int index = getIndex (id);
return mRecords.at (index);
}
} }
#endif #endif

View file

@ -1,9 +1,10 @@
#include "idtable.hpp" #include "idtable.hpp"
#include "idcollection.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp"
CSMWorld::IdTable::IdTable (IdCollectionBase *idCollection) : mIdCollection (idCollection) CSMWorld::IdTable::IdTable (CollectionBase *idCollection) : mIdCollection (idCollection)
{ {
} }
@ -46,7 +47,7 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation
return QVariant(); return QVariant();
if (role==Qt::DisplayRole) if (role==Qt::DisplayRole)
return tr (mIdCollection->getColumn (section).mTitle.c_str()); return tr (mIdCollection->getColumn (section).getTitle().c_str());
if (role==ColumnBase::Role_Flags) if (role==ColumnBase::Role_Flags)
return mIdCollection->getColumn (section).mFlags; return mIdCollection->getColumn (section).mFlags;
@ -96,13 +97,32 @@ bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& paren
return true; return true;
} }
void CSMWorld::IdTable::addRecord (const std::string& id) QModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const
{ {
int index = mIdCollection->getSize(); if (parent.isValid())
return QModelIndex();
if (row<0 || row>=mIdCollection->getSize())
return QModelIndex();
if (column<0 || column>=mIdCollection->getColumns())
return QModelIndex();
return createIndex (row, column);
}
QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
{
return QModelIndex();
}
void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex();
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendBlankRecord (id); mIdCollection->appendBlankRecord (id, type);
endInsertRows(); endInsertRows();
} }
@ -112,13 +132,13 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column)
return index (mIdCollection->getIndex (id), column); return index (mIdCollection->getIndex (id), column);
} }
void CSMWorld::IdTable::setRecord (const RecordBase& record) void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record)
{ {
int index = mIdCollection->searchId (mIdCollection->getId (record)); int index = mIdCollection->searchId (id);
if (index==-1) if (index==-1)
{ {
int index = mIdCollection->getSize(); int index = mIdCollection->getAppendIndex();
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);
@ -137,4 +157,25 @@ void CSMWorld::IdTable::setRecord (const RecordBase& record)
const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const
{ {
return mIdCollection->getRecord (id); return mIdCollection->getRecord (id);
}
int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
{
int columns = mIdCollection->getColumns();
for (int i=0; i<columns; ++i)
if (mIdCollection->getColumn (i).mColumnId==id)
return i;
return -1;
}
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
{
int index = searchColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
} }

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